diff options
373 files changed, 13762 insertions, 4816 deletions
diff --git a/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt b/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt index ed4710c40641..bbf4a13f6d75 100644 --- a/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt +++ b/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt @@ -2,8 +2,8 @@ Vitesse VSC73xx Switches ======================== This defines device tree bindings for the Vitesse VSC73xx switch chips. -The Vitesse company has been acquired by Microsemi and Microsemi in turn -acquired by Microchip but retains this vendor branding. +The Vitesse company has been acquired by Microsemi and Microsemi has +been acquired Microchip but retains this vendor branding. The currently supported switch chips are: Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch @@ -11,8 +11,14 @@ Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch -The device tree node is an SPI device so it must reside inside a SPI bus -device tree node, see spi/spi-bus.txt +This switch could have two different management interface. + +If SPI interface is used, the device tree node is an SPI device so it must +reside inside a SPI bus device tree node, see spi/spi-bus.txt + +When the chip is connected to a parallel memory bus and work in memory-mapped +I/O mode, a platform device is used to represent the vsc73xx. In this case it +must reside inside a platform bus device tree node. Required properties: @@ -38,6 +44,7 @@ and subnodes of DSA switches. Examples: +SPI: switch@0 { compatible = "vitesse,vsc7395"; reg = <0>; @@ -79,3 +86,46 @@ switch@0 { }; }; }; + +Platform: +switch@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "vitesse,vsc7385"; + reg = <0x2 0x0 0x20000>; + reset-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + }; + port@1 { + reg = <1>; + label = "lan2"; + }; + port@2 { + reg = <2>; + label = "lan3"; + }; + port@3 { + reg = <3>; + label = "lan4"; + }; + vsc: port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&enet0>; + phy-mode = "rgmii"; + fixed-link { + speed = <1000>; + full-duplex; + pause; + }; + }; + }; + +}; diff --git a/Documentation/devicetree/bindings/net/marvell-bluetooth.txt b/Documentation/devicetree/bindings/net/marvell-bluetooth.txt new file mode 100644 index 000000000000..0e2842296032 --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell-bluetooth.txt @@ -0,0 +1,25 @@ +Marvell Bluetooth Chips +----------------------- + +This documents the binding structure and common properties for serial +attached Marvell Bluetooth devices. The following chips are included in +this binding: + +* Marvell 88W8897 Bluetooth devices + +Required properties: + - compatible: should be: + "mrvl,88w8897" + +Optional properties: +None so far + +Example: + +&serial0 { + compatible = "ns16550a"; + ... + bluetooth { + compatible = "mrvl,88w8897"; + }; +}; diff --git a/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt b/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt index 41a7dcc80f5b..112011c51d5e 100644 --- a/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt +++ b/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt @@ -50,16 +50,33 @@ Required properties: "mediatek,mt7663u-bluetooth": for MT7663U device "mediatek,mt7668u-bluetooth": for MT7668U device - vcc-supply: Main voltage regulator + +If the pin controller on the platform can support both pinmux and GPIO +control such as the most of MediaTek platform. Please use below properties. + - pinctrl-names: Should be "default", "runtime" - pinctrl-0: Should contain UART RXD low when the device is powered up to enter proper bootstrap mode. - pinctrl-1: Should contain UART mode pin ctrl +Else, the pin controller on the platform only can support pinmux control and +the GPIO control still has to rely on the dedicated GPIO controller such as +a legacy MediaTek SoC, MT7621. Please use the below properties. + +- boot-gpios: GPIO same to the pin as UART RXD and used to keep LOW when + the device is powered up to enter proper bootstrap mode when +- pinctrl-names: Should be "default" +- pinctrl-0: Should contain UART mode pin ctrl + Optional properties: - reset-gpios: GPIO used to reset the device whose initial state keeps low, if the GPIO is missing, then board-level design should be guaranteed. +- clocks: Should be the clock specifiers corresponding to the entry in + clock-names property. If the clock is missing, then board-level + design should be guaranteed. +- clock-names: Should contain "osc" entry for the external oscillator. - current-speed: Current baud rate of the device whose defaults to 921600 Example: diff --git a/Documentation/devicetree/bindings/net/qualcomm-bluetooth.txt b/Documentation/devicetree/bindings/net/qualcomm-bluetooth.txt index 7ef6118abd3d..68b67d9db63a 100644 --- a/Documentation/devicetree/bindings/net/qualcomm-bluetooth.txt +++ b/Documentation/devicetree/bindings/net/qualcomm-bluetooth.txt @@ -17,6 +17,7 @@ Optional properties for compatible string qcom,qca6174-bt: - enable-gpios: gpio specifier used to enable chip - clocks: clock provided to the controller (SUSCLK_32KHZ) + - firmware-name: specify the name of nvm firmware to load Required properties for compatible string qcom,wcn399x-bt: @@ -28,6 +29,7 @@ Required properties for compatible string qcom,wcn399x-bt: Optional properties for compatible string qcom,wcn399x-bt: - max-speed: see Documentation/devicetree/bindings/serial/slave-device.txt + - firmware-name: specify the name of nvm firmware to load Examples: @@ -40,6 +42,7 @@ serial@7570000 { enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>; clocks = <&divclk4>; + firmware-name = "nvm_00440302.bin"; }; }; @@ -52,5 +55,6 @@ serial@898000 { vddrf-supply = <&vreg_l17a_1p3>; vddch0-supply = <&vreg_l25a_3p3>; max-speed = <3200000>; + firmware-name = "crnv21.bin"; }; }; diff --git a/Documentation/networking/device_drivers/mellanox/mlx5.rst b/Documentation/networking/device_drivers/mellanox/mlx5.rst index 4eeef2df912f..214325897732 100644 --- a/Documentation/networking/device_drivers/mellanox/mlx5.rst +++ b/Documentation/networking/device_drivers/mellanox/mlx5.rst @@ -10,6 +10,7 @@ Contents ======== - `Enabling the driver and kconfig options`_ +- `Devlink info`_ - `Devlink health reporters`_ Enabling the driver and kconfig options @@ -101,6 +102,24 @@ Enabling the driver and kconfig options - CONFIG_VXLAN: When chosen, mlx5 vxaln support will be enabled. - CONFIG_MLXFW: When chosen, mlx5 firmware flashing support will be enabled (via devlink and ethtool). +Devlink info +============ + +The devlink info reports the running and stored firmware versions on device. +It also prints the device PSID which represents the HCA board type ID. + +User command example:: + + $ devlink dev info pci/0000:00:06.0 + pci/0000:00:06.0: + driver mlx5_core + versions: + fixed: + fw.psid MT_0000000009 + running: + fw.version 16.26.0100 + stored: + fw.version 16.26.0100 Devlink health reporters ======================== diff --git a/Documentation/networking/dsa/b53.rst b/Documentation/networking/dsa/b53.rst new file mode 100644 index 000000000000..b41637cdb82b --- /dev/null +++ b/Documentation/networking/dsa/b53.rst @@ -0,0 +1,183 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================== +Broadcom RoboSwitch Ethernet switch driver +========================================== + +The Broadcom RoboSwitch Ethernet switch family is used in quite a range of +xDSL router, cable modems and other multimedia devices. + +The actual implementation supports the devices BCM5325E, BCM5365, BCM539x, +BCM53115 and BCM53125 as well as BCM63XX. + +Implementation details +====================== + +The driver is located in ``drivers/net/dsa/b53/`` and is implemented as a +DSA driver; see ``Documentation/networking/dsa/dsa.rst`` for details on the +subsystem and what it provides. + +The switch is, if possible, configured to enable a Broadcom specific 4-bytes +switch tag which gets inserted by the switch for every packet forwarded to the +CPU interface, conversely, the CPU network interface should insert a similar +tag for packets entering the CPU port. The tag format is described in +``net/dsa/tag_brcm.c``. + +The configuration of the device depends on whether or not tagging is +supported. + +The interface names and example network configuration are used according the +configuration described in the :ref:`dsa-config-showcases`. + +Configuration with tagging support +---------------------------------- + +The tagging based configuration is desired. It is not specific to the b53 +DSA driver and will work like all DSA drivers which supports tagging. + +See :ref:`dsa-tagged-configuration`. + +Configuration without tagging support +------------------------------------- + +Older models (5325, 5365) support a different tag format that is not supported +yet. 539x and 531x5 require managed mode and some special handling, which is +also not yet supported. The tagging support is disabled in these cases and the +switch need a different configuration. + +The configuration slightly differ from the :ref:`dsa-vlan-configuration`. + +The b53 tags the CPU port in all VLANs, since otherwise any PVID untagged +VLAN programming would basically change the CPU port's default PVID and make +it untagged, undesirable. + +In difference to the configuration described in :ref:`dsa-vlan-configuration` +the default VLAN 1 has to be removed from the slave interface configuration in +single port and gateway configuration, while there is no need to add an extra +VLAN configuration in the bridge showcase. + +single port +~~~~~~~~~~~ +The configuration can only be set up via VLAN tagging and bridge setup. +By default packages are tagged with vid 1: + +.. code-block:: sh + + # tag traffic on CPU port + ip link add link eth0 name eth0.1 type vlan id 1 + ip link add link eth0 name eth0.2 type vlan id 2 + ip link add link eth0 name eth0.3 type vlan id 3 + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + ip link set eth0.1 up + ip link set eth0.2 up + ip link set eth0.3 up + + # bring up the slave interfaces + ip link set wan up + ip link set lan1 up + ip link set lan2 up + + # create bridge + ip link add name br0 type bridge + + # activate VLAN filtering + ip link set dev br0 type bridge vlan_filtering 1 + + # add ports to bridges + ip link set dev wan master br0 + ip link set dev lan1 master br0 + ip link set dev lan2 master br0 + + # tag traffic on ports + bridge vlan add dev lan1 vid 2 pvid untagged + bridge vlan del dev lan1 vid 1 + bridge vlan add dev lan2 vid 3 pvid untagged + bridge vlan del dev lan2 vid 1 + + # configure the VLANs + ip addr add 192.0.2.1/30 dev eth0.1 + ip addr add 192.0.2.5/30 dev eth0.2 + ip addr add 192.0.2.9/30 dev eth0.3 + + # bring up the bridge devices + ip link set br0 up + + +bridge +~~~~~~ + +.. code-block:: sh + + # tag traffic on CPU port + ip link add link eth0 name eth0.1 type vlan id 1 + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + ip link set eth0.1 up + + # bring up the slave interfaces + ip link set wan up + ip link set lan1 up + ip link set lan2 up + + # create bridge + ip link add name br0 type bridge + + # activate VLAN filtering + ip link set dev br0 type bridge vlan_filtering 1 + + # add ports to bridge + ip link set dev wan master br0 + ip link set dev lan1 master br0 + ip link set dev lan2 master br0 + ip link set eth0.1 master br0 + + # configure the bridge + ip addr add 192.0.2.129/25 dev br0 + + # bring up the bridge + ip link set dev br0 up + +gateway +~~~~~~~ + +.. code-block:: sh + + # tag traffic on CPU port + ip link add link eth0 name eth0.1 type vlan id 1 + ip link add link eth0 name eth0.2 type vlan id 2 + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + ip link set eth0.1 up + ip link set eth0.2 up + + # bring up the slave interfaces + ip link set wan up + ip link set lan1 up + ip link set lan2 up + + # create bridge + ip link add name br0 type bridge + + # activate VLAN filtering + ip link set dev br0 type bridge vlan_filtering 1 + + # add ports to bridges + ip link set dev wan master br0 + ip link set eth0.1 master br0 + ip link set dev lan1 master br0 + ip link set dev lan2 master br0 + + # tag traffic on ports + bridge vlan add dev wan vid 2 pvid untagged + bridge vlan del dev wan vid 1 + + # configure the VLANs + ip addr add 192.0.2.1/30 dev eth0.2 + ip addr add 192.0.2.129/25 dev br0 + + # bring up the bridge devices + ip link set br0 up diff --git a/Documentation/networking/dsa/configuration.rst b/Documentation/networking/dsa/configuration.rst new file mode 100644 index 000000000000..af029b3ca2ab --- /dev/null +++ b/Documentation/networking/dsa/configuration.rst @@ -0,0 +1,292 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================================= +DSA switch configuration from userspace +======================================= + +The DSA switch configuration is not integrated into the main userspace +network configuration suites by now and has to be performed manualy. + +.. _dsa-config-showcases: + +Configuration showcases +----------------------- + +To configure a DSA switch a couple of commands need to be executed. In this +documentation some common configuration scenarios are handled as showcases: + +*single port* + Every switch port acts as a different configurable Ethernet port + +*bridge* + Every switch port is part of one configurable Ethernet bridge + +*gateway* + Every switch port except one upstream port is part of a configurable + Ethernet bridge. + The upstream port acts as different configurable Ethernet port. + +All configurations are performed with tools from iproute2, which is available +at https://www.kernel.org/pub/linux/utils/net/iproute2/ + +Through DSA every port of a switch is handled like a normal linux Ethernet +interface. The CPU port is the switch port connected to an Ethernet MAC chip. +The corresponding linux Ethernet interface is called the master interface. +All other corresponding linux interfaces are called slave interfaces. + +The slave interfaces depend on the master interface. They can only brought up, +when the master interface is up. + +In this documentation the following Ethernet interfaces are used: + +*eth0* + the master interface + +*lan1* + a slave interface + +*lan2* + another slave interface + +*lan3* + a third slave interface + +*wan* + A slave interface dedicated for upstream traffic + +Further Ethernet interfaces can be configured similar. +The configured IPs and networks are: + +*single port* + * lan1: 192.0.2.1/30 (192.0.2.0 - 192.0.2.3) + * lan2: 192.0.2.5/30 (192.0.2.4 - 192.0.2.7) + * lan3: 192.0.2.9/30 (192.0.2.8 - 192.0.2.11) + +*bridge* + * br0: 192.0.2.129/25 (192.0.2.128 - 192.0.2.255) + +*gateway* + * br0: 192.0.2.129/25 (192.0.2.128 - 192.0.2.255) + * wan: 192.0.2.1/30 (192.0.2.0 - 192.0.2.3) + +.. _dsa-tagged-configuration: + +Configuration with tagging support +---------------------------------- + +The tagging based configuration is desired and supported by the majority of +DSA switches. These switches are capable to tag incoming and outgoing traffic +without using a VLAN based configuration. + +single port +~~~~~~~~~~~ + +.. code-block:: sh + + # configure each interface + ip addr add 192.0.2.1/30 dev lan1 + ip addr add 192.0.2.5/30 dev lan2 + ip addr add 192.0.2.9/30 dev lan3 + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + + # bring up the slave interfaces + ip link set lan1 up + ip link set lan2 up + ip link set lan3 up + +bridge +~~~~~~ + +.. code-block:: sh + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + + # bring up the slave interfaces + ip link set lan1 up + ip link set lan2 up + ip link set lan3 up + + # create bridge + ip link add name br0 type bridge + + # add ports to bridge + ip link set dev lan1 master br0 + ip link set dev lan2 master br0 + ip link set dev lan3 master br0 + + # configure the bridge + ip addr add 192.0.2.129/25 dev br0 + + # bring up the bridge + ip link set dev br0 up + +gateway +~~~~~~~ + +.. code-block:: sh + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + + # bring up the slave interfaces + ip link set wan up + ip link set lan1 up + ip link set lan2 up + + # configure the upstream port + ip addr add 192.0.2.1/30 dev wan + + # create bridge + ip link add name br0 type bridge + + # add ports to bridge + ip link set dev lan1 master br0 + ip link set dev lan2 master br0 + + # configure the bridge + ip addr add 192.0.2.129/25 dev br0 + + # bring up the bridge + ip link set dev br0 up + +.. _dsa-vlan-configuration: + +Configuration without tagging support +------------------------------------- + +A minority of switches are not capable to use a taging protocol +(DSA_TAG_PROTO_NONE). These switches can be configured by a VLAN based +configuration. + +single port +~~~~~~~~~~~ +The configuration can only be set up via VLAN tagging and bridge setup. + +.. code-block:: sh + + # tag traffic on CPU port + ip link add link eth0 name eth0.1 type vlan id 1 + ip link add link eth0 name eth0.2 type vlan id 2 + ip link add link eth0 name eth0.3 type vlan id 3 + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + ip link set eth0.1 up + ip link set eth0.2 up + ip link set eth0.3 up + + # bring up the slave interfaces + ip link set lan1 up + ip link set lan1 up + ip link set lan3 up + + # create bridge + ip link add name br0 type bridge + + # activate VLAN filtering + ip link set dev br0 type bridge vlan_filtering 1 + + # add ports to bridges + ip link set dev lan1 master br0 + ip link set dev lan2 master br0 + ip link set dev lan3 master br0 + + # tag traffic on ports + bridge vlan add dev lan1 vid 1 pvid untagged + bridge vlan add dev lan2 vid 2 pvid untagged + bridge vlan add dev lan3 vid 3 pvid untagged + + # configure the VLANs + ip addr add 192.0.2.1/30 dev eth0.1 + ip addr add 192.0.2.5/30 dev eth0.2 + ip addr add 192.0.2.9/30 dev eth0.3 + + # bring up the bridge devices + ip link set br0 up + + +bridge +~~~~~~ + +.. code-block:: sh + + # tag traffic on CPU port + ip link add link eth0 name eth0.1 type vlan id 1 + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + ip link set eth0.1 up + + # bring up the slave interfaces + ip link set lan1 up + ip link set lan2 up + ip link set lan3 up + + # create bridge + ip link add name br0 type bridge + + # activate VLAN filtering + ip link set dev br0 type bridge vlan_filtering 1 + + # add ports to bridge + ip link set dev lan1 master br0 + ip link set dev lan2 master br0 + ip link set dev lan3 master br0 + ip link set eth0.1 master br0 + + # tag traffic on ports + bridge vlan add dev lan1 vid 1 pvid untagged + bridge vlan add dev lan2 vid 1 pvid untagged + bridge vlan add dev lan3 vid 1 pvid untagged + + # configure the bridge + ip addr add 192.0.2.129/25 dev br0 + + # bring up the bridge + ip link set dev br0 up + +gateway +~~~~~~~ + +.. code-block:: sh + + # tag traffic on CPU port + ip link add link eth0 name eth0.1 type vlan id 1 + ip link add link eth0 name eth0.2 type vlan id 2 + + # The master interface needs to be brought up before the slave ports. + ip link set eth0 up + ip link set eth0.1 up + ip link set eth0.2 up + + # bring up the slave interfaces + ip link set wan up + ip link set lan1 up + ip link set lan2 up + + # create bridge + ip link add name br0 type bridge + + # activate VLAN filtering + ip link set dev br0 type bridge vlan_filtering 1 + + # add ports to bridges + ip link set dev wan master br0 + ip link set eth0.1 master br0 + ip link set dev lan1 master br0 + ip link set dev lan2 master br0 + + # tag traffic on ports + bridge vlan add dev lan1 vid 1 pvid untagged + bridge vlan add dev lan2 vid 1 pvid untagged + bridge vlan add dev wan vid 2 pvid untagged + + # configure the VLANs + ip addr add 192.0.2.1/30 dev eth0.2 + ip addr add 192.0.2.129/25 dev br0 + + # bring up the bridge devices + ip link set br0 up diff --git a/Documentation/networking/dsa/index.rst b/Documentation/networking/dsa/index.rst index 0e5b7a9be406..ee631e2d646f 100644 --- a/Documentation/networking/dsa/index.rst +++ b/Documentation/networking/dsa/index.rst @@ -6,6 +6,8 @@ Distributed Switch Architecture :maxdepth: 1 dsa + b53 bcm_sf2 lan9303 sja1105 + configuration diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index f0e6d1f53485..48c79e78817b 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1473,6 +1473,7 @@ fib_multipath_hash_policy - INTEGER Possible values: 0 - Layer 3 (source and destination addresses plus flow label) 1 - Layer 4 (standard 5-tuple) + 2 - Layer 3 or inner Layer 3 if present anycast_src_echo_reply - BOOLEAN Controls the use of anycast addresses as source addresses for ICMPv6 diff --git a/MAINTAINERS b/MAINTAINERS index 449e7cdb3303..22655aa84a46 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11902,6 +11902,14 @@ F: kernel/padata.c F: include/linux/padata.h F: Documentation/padata.txt +PAGE POOL +M: Jesper Dangaard Brouer <[email protected]> +M: Ilias Apalodimas <[email protected]> +S: Supported +F: net/core/page_pool.c +F: include/net/page_pool.h + PANASONIC LAPTOP ACPI EXTRAS DRIVER M: Harald Welte <[email protected]> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index b9c34ff9a0d3..aae665a3a254 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -52,6 +52,17 @@ config BT_HCIBTUSB_BCM Say Y here to compile support for Broadcom protocol. +config BT_HCIBTUSB_MTK + bool "MediaTek protocol support" + depends on BT_HCIBTUSB + default n + help + The MediaTek protocol support enables firmware download + support and chip initialization for MediaTek Bluetooth + USB controllers. + + Say Y here to compile support for MediaTek protocol. + config BT_HCIBTUSB_RTL bool "Realtek protocol support" depends on BT_HCIBTUSB @@ -237,6 +248,7 @@ config BT_HCIUART_AG6XX config BT_HCIUART_MRVL bool "Marvell protocol support" depends on BT_HCIUART + depends on BT_HCIUART_SERDEV select BT_HCIUART_H4 help Marvell is serial protocol for communication between Bluetooth diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index a346ccb5450d..a0e84538cec8 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -359,7 +359,8 @@ static int bpa10x_set_diag(struct hci_dev *hdev, bool enable) return 0; } -static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id) +static int bpa10x_probe(struct usb_interface *intf, + const struct usb_device_id *id) { struct bpa10x_data *data; struct hci_dev *hdev; diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 3fe941539a1f..124ef0a3e1dd 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -335,6 +335,7 @@ static const struct bcm_subver_table bcm_uart_subver_table[] = { { 0x230f, "BCM4356A2" }, /* 001.003.015 */ { 0x220e, "BCM20702A1" }, /* 001.002.014 */ { 0x4217, "BCM4329B1" }, /* 002.002.023 */ + { 0x6106, "BCM4359C0" }, /* 003.001.006 */ { } }; diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index f5dbeec8e274..e11169ad8247 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -115,10 +115,12 @@ struct btmtk_hci_wmt_params { struct btmtkuart_dev { struct hci_dev *hdev; struct serdev_device *serdev; - struct clk *clk; + struct clk *clk; + struct clk *osc; struct regulator *vcc; struct gpio_desc *reset; + struct gpio_desc *boot; struct pinctrl *pinctrl; struct pinctrl_state *pins_runtime; struct pinctrl_state *pins_boot; @@ -911,6 +913,19 @@ static int btmtkuart_parse_dt(struct serdev_device *serdev) return err; } + bdev->osc = devm_clk_get_optional(&serdev->dev, "osc"); + if (IS_ERR(bdev->osc)) { + err = PTR_ERR(bdev->osc); + return err; + } + + bdev->boot = devm_gpiod_get_optional(&serdev->dev, "boot", + GPIOD_OUT_LOW); + if (IS_ERR(bdev->boot)) { + err = PTR_ERR(bdev->boot); + return err; + } + bdev->pinctrl = devm_pinctrl_get(&serdev->dev); if (IS_ERR(bdev->pinctrl)) { err = PTR_ERR(bdev->pinctrl); @@ -919,8 +934,10 @@ static int btmtkuart_parse_dt(struct serdev_device *serdev) bdev->pins_boot = pinctrl_lookup_state(bdev->pinctrl, "default"); - if (IS_ERR(bdev->pins_boot)) { + if (IS_ERR(bdev->pins_boot) && !bdev->boot) { err = PTR_ERR(bdev->pins_boot); + dev_err(&serdev->dev, + "Should assign RXD to LOW at boot stage\n"); return err; } @@ -996,13 +1013,25 @@ static int btmtkuart_probe(struct serdev_device *serdev) set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); if (btmtkuart_is_standalone(bdev)) { - /* Switch to the specific pin state for the booting requires */ - pinctrl_select_state(bdev->pinctrl, bdev->pins_boot); + err = clk_prepare_enable(bdev->osc); + if (err < 0) + return err; + + if (bdev->boot) { + gpiod_set_value_cansleep(bdev->boot, 1); + } else { + /* Switch to the specific pin state for the booting + * requires. + */ + pinctrl_select_state(bdev->pinctrl, bdev->pins_boot); + } /* Power on */ err = regulator_enable(bdev->vcc); - if (err < 0) + if (err < 0) { + clk_disable_unprepare(bdev->osc); return err; + } /* Reset if the reset-gpios is available otherwise the board * -level design should be guaranteed. @@ -1017,6 +1046,10 @@ static int btmtkuart_probe(struct serdev_device *serdev) * mode the device requires for UART transfers. */ msleep(50); + + if (bdev->boot) + devm_gpiod_put(&serdev->dev, bdev->boot); + pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime); /* A standalone device doesn't depends on power domain on SoC, @@ -1037,10 +1070,8 @@ static int btmtkuart_probe(struct serdev_device *serdev) return 0; err_regulator_disable: - if (btmtkuart_is_standalone(bdev)) { - pinctrl_select_state(bdev->pinctrl, bdev->pins_boot); + if (btmtkuart_is_standalone(bdev)) regulator_disable(bdev->vcc); - } return err; } @@ -1050,9 +1081,9 @@ static void btmtkuart_remove(struct serdev_device *serdev) struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev); struct hci_dev *hdev = bdev->hdev; - if (btmtkuart_is_standalone(bdev)) { - pinctrl_select_state(bdev->pinctrl, bdev->pins_boot); + if (btmtkuart_is_standalone(bdev)) { regulator_disable(bdev->vcc); + clk_disable_unprepare(bdev->osc); } hci_unregister_dev(hdev); diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index aff1d22223bd..8b33128dccee 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -131,6 +131,7 @@ static void qca_tlv_check_data(struct rome_config *config, * In case VSE is skipped, only the last segment is acked. */ config->dnld_mode = tlv_patch->download_mode; + config->dnld_type = config->dnld_mode; BT_DBG("Total Length : %d bytes", le32_to_cpu(tlv_patch->total_size)); @@ -251,6 +252,31 @@ out: return err; } +static int qca_inject_cmd_complete_event(struct hci_dev *hdev) +{ + struct hci_event_hdr *hdr; + struct hci_ev_cmd_complete *evt; + struct sk_buff *skb; + + skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdr = skb_put(skb, sizeof(*hdr)); + hdr->evt = HCI_EV_CMD_COMPLETE; + hdr->plen = sizeof(*evt) + 1; + + evt = skb_put(skb, sizeof(*evt)); + evt->ncmd = 1; + evt->opcode = QCA_HCI_CC_OPCODE; + + skb_put_u8(skb, QCA_HCI_CC_SUCCESS); + + hci_skb_pkt_type(skb) = HCI_EVENT_PKT; + + return hci_recv_frame(hdev, skb); +} + static int qca_download_firmware(struct hci_dev *hdev, struct rome_config *config) { @@ -284,11 +310,22 @@ static int qca_download_firmware(struct hci_dev *hdev, ret = qca_tlv_send_segment(hdev, segsize, segment, config->dnld_mode); if (ret) - break; + goto out; segment += segsize; } + /* Latest qualcomm chipsets are not sending a command complete event + * for every fw packet sent. They only respond with a vendor specific + * event for the last packet. This optimization in the chip will + * decrease the BT in initialization time. Here we will inject a command + * complete event to avoid a command timeout error message. + */ + if (config->dnld_type == ROME_SKIP_EVT_VSE_CC || + config->dnld_type == ROME_SKIP_EVT_VSE) + return qca_inject_cmd_complete_event(hdev); + +out: release_firmware(fw); return ret; @@ -319,7 +356,8 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome); int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, - enum qca_btsoc_type soc_type, u32 soc_ver) + enum qca_btsoc_type soc_type, u32 soc_ver, + const char *firmware_name) { struct rome_config config; int err; @@ -352,7 +390,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, /* Download NVM configuration */ config.type = TLV_TYPE_NVM; - if (qca_is_wcn399x(soc_type)) + if (firmware_name) + snprintf(config.fwname, sizeof(config.fwname), + "qca/%s", firmware_name); + else if (qca_is_wcn399x(soc_type)) snprintf(config.fwname, sizeof(config.fwname), "qca/crnv%02x.bin", rom_ver); else diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index e9c999959603..6a291a7a5d96 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -28,6 +28,9 @@ #define QCA_WCN3990_POWERON_PULSE 0xFC #define QCA_WCN3990_POWEROFF_PULSE 0xC0 +#define QCA_HCI_CC_OPCODE 0xFC00 +#define QCA_HCI_CC_SUCCESS 0x00 + enum qca_baudrate { QCA_BAUDRATE_115200 = 0, QCA_BAUDRATE_57600, @@ -69,6 +72,7 @@ struct rome_config { char fwname[64]; uint8_t user_baud_rate; enum rome_tlv_dnld_mode dnld_mode; + enum rome_tlv_dnld_mode dnld_type; }; struct edl_event_hdr { @@ -127,7 +131,8 @@ enum qca_btsoc_type { int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr); int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, - enum qca_btsoc_type soc_type, u32 soc_ver); + enum qca_btsoc_type soc_type, u32 soc_ver, + const char *firmware_name); int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version); int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type) @@ -142,7 +147,8 @@ static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdad } static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, - enum qca_btsoc_type soc_type, u32 soc_ver) + enum qca_btsoc_type soc_type, u32 soc_ver, + const char *firmware_name) { return -EOPNOTSUPP; } diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 208feef63de4..4f75a9b61d09 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -21,6 +21,7 @@ #define RTL_ROM_LMP_3499 0x3499 #define RTL_ROM_LMP_8723A 0x1200 #define RTL_ROM_LMP_8723B 0x8723 +#define RTL_ROM_LMP_8723D 0x8873 #define RTL_ROM_LMP_8821A 0x8821 #define RTL_ROM_LMP_8761A 0x8761 #define RTL_ROM_LMP_8822B 0x8822 @@ -107,6 +108,13 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8723ds_fw.bin", .cfg_name = "rtl_bt/rtl8723ds_config" }, + /* 8723DU */ + { IC_INFO(RTL_ROM_LMP_8723D, 0x826C), + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723d_fw.bin", + .cfg_name = "rtl_bt/rtl8723d_config" }, + /* 8821A */ { IC_INFO(RTL_ROM_LMP_8821A, 0xa), .config_needed = false, @@ -637,6 +645,26 @@ int btrtl_setup_realtek(struct hci_dev *hdev) } EXPORT_SYMBOL_GPL(btrtl_setup_realtek); +int btrtl_shutdown_realtek(struct hci_dev *hdev) +{ + struct sk_buff *skb; + int ret; + + /* According to the vendor driver, BT must be reset on close to avoid + * firmware crash. + */ + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + bt_dev_err(hdev, "HCI reset during shutdown failed"); + return ret; + } + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek); + static unsigned int btrtl_convert_baudrate(u32 device_baudrate) { switch (device_baudrate) { diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h index f1676144fce8..10ad40c3e42c 100644 --- a/drivers/bluetooth/btrtl.h +++ b/drivers/bluetooth/btrtl.h @@ -55,6 +55,7 @@ void btrtl_free(struct btrtl_device_info *btrtl_dev); int btrtl_download_firmware(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev); int btrtl_setup_realtek(struct hci_dev *hdev); +int btrtl_shutdown_realtek(struct hci_dev *hdev); int btrtl_get_uart_settings(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev, unsigned int *controller_baudrate, @@ -83,6 +84,11 @@ static inline int btrtl_setup_realtek(struct hci_dev *hdev) return -EOPNOTSUPP; } +static inline int btrtl_shutdown_realtek(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} + static inline int btrtl_get_uart_settings(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev, unsigned int *controller_baudrate, diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 83748b7b2033..fd9571d5fdac 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -286,6 +286,7 @@ static int btsdio_probe(struct sdio_func *func, switch (func->device) { case SDIO_DEVICE_ID_BROADCOM_43341: case SDIO_DEVICE_ID_BROADCOM_43430: + case SDIO_DEVICE_ID_BROADCOM_4356: return -ENODEV; } } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 50aed5259c2b..3876fee6ad13 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -11,6 +11,7 @@ #include <linux/usb.h> #include <linux/usb/quirks.h> #include <linux/firmware.h> +#include <linux/iopoll.h> #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/suspend.h> @@ -55,6 +56,7 @@ static struct usb_driver btusb_driver; #define BTUSB_BCM2045 0x40000 #define BTUSB_IFNUM_2 0x80000 #define BTUSB_CW6622 0x100000 +#define BTUSB_MEDIATEK 0x200000 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -264,7 +266,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x04ca, 0x301a), .driver_info = BTUSB_QCA_ROME }, + { USB_DEVICE(0x13d3, 0x3491), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x13d3, 0x3496), .driver_info = BTUSB_QCA_ROME }, + { USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME }, /* Broadcom BCM2035 */ { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 }, @@ -346,6 +350,10 @@ static const struct usb_device_id blacklist_table[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), .driver_info = BTUSB_REALTEK }, + /* MediaTek Bluetooth devices */ + { USB_VENDOR_AND_INTERFACE_INFO(0x0e8d, 0xe0, 0x01, 0x01), + .driver_info = BTUSB_MEDIATEK }, + /* Additional Realtek 8723AE Bluetooth devices */ { USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK }, @@ -426,6 +434,7 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = { #define BTUSB_DIAG_RUNNING 10 #define BTUSB_OOB_WAKE_ENABLED 11 #define BTUSB_HW_RESET_ACTIVE 12 +#define BTUSB_TX_WAIT_VND_EVT 13 struct btusb_data { struct hci_dev *hdev; @@ -449,6 +458,7 @@ struct btusb_data { struct usb_anchor bulk_anchor; struct usb_anchor isoc_anchor; struct usb_anchor diag_anchor; + struct usb_anchor ctrl_anchor; spinlock_t rxlock; struct sk_buff *evt_skb; @@ -1202,6 +1212,7 @@ static void btusb_stop_traffic(struct btusb_data *data) usb_kill_anchored_urbs(&data->bulk_anchor); usb_kill_anchored_urbs(&data->isoc_anchor); usb_kill_anchored_urbs(&data->diag_anchor); + usb_kill_anchored_urbs(&data->ctrl_anchor); } static int btusb_close(struct hci_dev *hdev) @@ -2437,6 +2448,568 @@ static int btusb_shutdown_intel_new(struct hci_dev *hdev) return 0; } +#ifdef CONFIG_BT_HCIBTUSB_MTK + +#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin" +#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin" + +#define HCI_WMT_MAX_EVENT_SIZE 64 + +enum { + BTMTK_WMT_PATCH_DWNLD = 0x1, + BTMTK_WMT_FUNC_CTRL = 0x6, + BTMTK_WMT_RST = 0x7, + BTMTK_WMT_SEMAPHORE = 0x17, +}; + +enum { + BTMTK_WMT_INVALID, + BTMTK_WMT_PATCH_UNDONE, + BTMTK_WMT_PATCH_DONE, + BTMTK_WMT_ON_UNDONE, + BTMTK_WMT_ON_DONE, + BTMTK_WMT_ON_PROGRESS, +}; + +struct btmtk_wmt_hdr { + u8 dir; + u8 op; + __le16 dlen; + u8 flag; +} __packed; + +struct btmtk_hci_wmt_cmd { + struct btmtk_wmt_hdr hdr; + u8 data[256]; +} __packed; + +struct btmtk_hci_wmt_evt { + struct hci_event_hdr hhdr; + struct btmtk_wmt_hdr whdr; +} __packed; + +struct btmtk_hci_wmt_evt_funcc { + struct btmtk_hci_wmt_evt hwhdr; + __be16 status; +} __packed; + +struct btmtk_tci_sleep { + u8 mode; + __le16 duration; + __le16 host_duration; + u8 host_wakeup_pin; + u8 time_compensation; +} __packed; + +struct btmtk_hci_wmt_params { + u8 op; + u8 flag; + u16 dlen; + const void *data; + u32 *status; +}; + +static void btusb_mtk_wmt_recv(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct btusb_data *data = hci_get_drvdata(hdev); + struct hci_event_hdr *hdr; + struct sk_buff *skb; + int err; + + if (urb->status == 0 && urb->actual_length > 0) { + hdev->stat.byte_rx += urb->actual_length; + + /* WMT event shouldn't be fragmented and the size should be + * less than HCI_WMT_MAX_EVENT_SIZE. + */ + skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC); + if (!skb) { + hdev->stat.err_rx++; + goto err_out; + } + + hci_skb_pkt_type(skb) = HCI_EVENT_PKT; + skb_put_data(skb, urb->transfer_buffer, urb->actual_length); + + hdr = (void *)skb->data; + /* Fix up the vendor event id with 0xff for vendor specific + * instead of 0xe4 so that event send via monitoring socket can + * be parsed properly. + */ + hdr->evt = 0xff; + + /* When someone waits for the WMT event, the skb is being cloned + * and being processed the events from there then. + */ + if (test_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags)) { + data->evt_skb = skb_clone(skb, GFP_KERNEL); + if (!data->evt_skb) + goto err_out; + } + + err = hci_recv_frame(hdev, skb); + if (err < 0) + goto err_free_skb; + + if (test_and_clear_bit(BTUSB_TX_WAIT_VND_EVT, + &data->flags)) { + /* Barrier to sync with other CPUs */ + smp_mb__after_atomic(); + wake_up_bit(&data->flags, + BTUSB_TX_WAIT_VND_EVT); + } +err_out: + return; +err_free_skb: + kfree_skb(data->evt_skb); + data->evt_skb = NULL; + return; + } else if (urb->status == -ENOENT) { + /* Avoid suspend failed when usb_kill_urb */ + return; + } + + usb_mark_last_busy(data->udev); + + /* The URB complete handler is still called with urb->actual_length = 0 + * when the event is not available, so we should keep re-submitting + * URB until WMT event returns, Also, It's necessary to wait some time + * between the two consecutive control URBs to relax the target device + * to generate the event. Otherwise, the WMT event cannot return from + * the device successfully. + */ + udelay(100); + + usb_anchor_urb(urb, &data->ctrl_anchor); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + /* -EPERM: urb is being killed; + * -ENODEV: device got disconnected + */ + if (err != -EPERM && err != -ENODEV) + bt_dev_err(hdev, "urb %p failed to resubmit (%d)", + urb, -err); + usb_unanchor_urb(urb); + } +} + +static int btusb_mtk_submit_wmt_recv_urb(struct hci_dev *hdev) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + struct usb_ctrlrequest *dr; + unsigned char *buf; + int err, size = 64; + unsigned int pipe; + struct urb *urb; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + dr = kmalloc(sizeof(*dr), GFP_KERNEL); + if (!dr) { + usb_free_urb(urb); + return -ENOMEM; + } + + dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; + dr->bRequest = 1; + dr->wIndex = cpu_to_le16(0); + dr->wValue = cpu_to_le16(48); + dr->wLength = cpu_to_le16(size); + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + kfree(dr); + return -ENOMEM; + } + + pipe = usb_rcvctrlpipe(data->udev, 0); + + usb_fill_control_urb(urb, data->udev, pipe, (void *)dr, + buf, size, btusb_mtk_wmt_recv, hdev); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_anchor_urb(urb, &data->ctrl_anchor); + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) { + if (err != -EPERM && err != -ENODEV) + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev, + struct btmtk_hci_wmt_params *wmt_params) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc; + u32 hlen, status = BTMTK_WMT_INVALID; + struct btmtk_hci_wmt_evt *wmt_evt; + struct btmtk_hci_wmt_cmd wc; + struct btmtk_wmt_hdr *hdr; + int err; + + /* Submit control IN URB on demand to process the WMT event */ + err = btusb_mtk_submit_wmt_recv_urb(hdev); + if (err < 0) + return err; + + /* Send the WMT command and wait until the WMT event returns */ + hlen = sizeof(*hdr) + wmt_params->dlen; + if (hlen > 255) + return -EINVAL; + + hdr = (struct btmtk_wmt_hdr *)&wc; + hdr->dir = 1; + hdr->op = wmt_params->op; + hdr->dlen = cpu_to_le16(wmt_params->dlen + 1); + hdr->flag = wmt_params->flag; + memcpy(wc.data, wmt_params->data, wmt_params->dlen); + + set_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags); + + err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc); + + if (err < 0) { + clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags); + return err; + } + + /* The vendor specific WMT commands are all answered by a vendor + * specific event and will have the Command Status or Command + * Complete as with usual HCI command flow control. + * + * After sending the command, wait for BTUSB_TX_WAIT_VND_EVT + * state to be cleared. The driver specific event receive routine + * will clear that state and with that indicate completion of the + * WMT command. + */ + err = wait_on_bit_timeout(&data->flags, BTUSB_TX_WAIT_VND_EVT, + TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT); + if (err == -EINTR) { + bt_dev_err(hdev, "Execution of wmt command interrupted"); + clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags); + return err; + } + + if (err) { + bt_dev_err(hdev, "Execution of wmt command timed out"); + clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags); + return -ETIMEDOUT; + } + + /* Parse and handle the return WMT event */ + wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data; + if (wmt_evt->whdr.op != hdr->op) { + bt_dev_err(hdev, "Wrong op received %d expected %d", + wmt_evt->whdr.op, hdr->op); + err = -EIO; + goto err_free_skb; + } + + switch (wmt_evt->whdr.op) { + case BTMTK_WMT_SEMAPHORE: + if (wmt_evt->whdr.flag == 2) + status = BTMTK_WMT_PATCH_UNDONE; + else + status = BTMTK_WMT_PATCH_DONE; + break; + case BTMTK_WMT_FUNC_CTRL: + wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; + if (be16_to_cpu(wmt_evt_funcc->status) == 0x404) + status = BTMTK_WMT_ON_DONE; + else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420) + status = BTMTK_WMT_ON_PROGRESS; + else + status = BTMTK_WMT_ON_UNDONE; + break; + } + + if (wmt_params->status) + *wmt_params->status = status; + +err_free_skb: + kfree_skb(data->evt_skb); + data->evt_skb = NULL; + + return err; +} + +static int btusb_mtk_setup_firmware(struct hci_dev *hdev, const char *fwname) +{ + struct btmtk_hci_wmt_params wmt_params; + const struct firmware *fw; + const u8 *fw_ptr; + size_t fw_size; + int err, dlen; + u8 flag; + + err = request_firmware(&fw, fwname, &hdev->dev); + if (err < 0) { + bt_dev_err(hdev, "Failed to load firmware file (%d)", err); + return err; + } + + fw_ptr = fw->data; + fw_size = fw->size; + + /* The size of patch header is 30 bytes, should be skip */ + if (fw_size < 30) + goto err_release_fw; + + fw_size -= 30; + fw_ptr += 30; + flag = 1; + + wmt_params.op = BTMTK_WMT_PATCH_DWNLD; + wmt_params.status = NULL; + + while (fw_size > 0) { + dlen = min_t(int, 250, fw_size); + + /* Tell deivice the position in sequence */ + if (fw_size - dlen <= 0) + flag = 3; + else if (fw_size < fw->size - 30) + flag = 2; + + wmt_params.flag = flag; + wmt_params.dlen = dlen; + wmt_params.data = fw_ptr; + + err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", + err); + goto err_release_fw; + } + + fw_size -= dlen; + fw_ptr += dlen; + } + + wmt_params.op = BTMTK_WMT_RST; + wmt_params.flag = 4; + wmt_params.dlen = 0; + wmt_params.data = NULL; + wmt_params.status = NULL; + + /* Activate funciton the firmware providing to */ + err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt rst (%d)", err); + return err; + } + + /* Wait a few moments for firmware activation done */ + usleep_range(10000, 12000); + +err_release_fw: + release_firmware(fw); + + return err; +} + +static int btusb_mtk_func_query(struct hci_dev *hdev) +{ + struct btmtk_hci_wmt_params wmt_params; + int status, err; + u8 param = 0; + + /* Query whether the function is enabled */ + wmt_params.op = BTMTK_WMT_FUNC_CTRL; + wmt_params.flag = 4; + wmt_params.dlen = sizeof(param); + wmt_params.data = ¶m; + wmt_params.status = &status; + + err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to query function status (%d)", err); + return err; + } + + return status; +} + +static int btusb_mtk_reg_read(struct btusb_data *data, u32 reg, u32 *val) +{ + int pipe, err, size = sizeof(u32); + void *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pipe = usb_rcvctrlpipe(data->udev, 0); + err = usb_control_msg(data->udev, pipe, 0x63, + USB_TYPE_VENDOR | USB_DIR_IN, + reg >> 16, reg & 0xffff, + buf, size, USB_CTRL_SET_TIMEOUT); + if (err < 0) + goto err_free_buf; + + *val = get_unaligned_le32(buf); + +err_free_buf: + kfree(buf); + + return err; +} + +static int btusb_mtk_id_get(struct btusb_data *data, u32 *id) +{ + return btusb_mtk_reg_read(data, 0x80000008, id); +} + +static int btusb_mtk_setup(struct hci_dev *hdev) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + struct btmtk_hci_wmt_params wmt_params; + ktime_t calltime, delta, rettime; + struct btmtk_tci_sleep tci_sleep; + unsigned long long duration; + struct sk_buff *skb; + const char *fwname; + int err, status; + u32 dev_id; + u8 param; + + calltime = ktime_get(); + + err = btusb_mtk_id_get(data, &dev_id); + if (err < 0) { + bt_dev_err(hdev, "Failed to get device id (%d)", err); + return err; + } + + switch (dev_id) { + case 0x7663: + fwname = FIRMWARE_MT7663; + break; + case 0x7668: + fwname = FIRMWARE_MT7668; + break; + default: + bt_dev_err(hdev, "Unsupported support hardware variant (%08x)", + dev_id); + return -ENODEV; + } + + /* Query whether the firmware is already download */ + wmt_params.op = BTMTK_WMT_SEMAPHORE; + wmt_params.flag = 1; + wmt_params.dlen = 0; + wmt_params.data = NULL; + wmt_params.status = &status; + + err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to query firmware status (%d)", err); + return err; + } + + if (status == BTMTK_WMT_PATCH_DONE) { + bt_dev_info(hdev, "firmware already downloaded"); + goto ignore_setup_fw; + } + + /* Setup a firmware which the device definitely requires */ + err = btusb_mtk_setup_firmware(hdev, fwname); + if (err < 0) + return err; + +ignore_setup_fw: + err = readx_poll_timeout(btusb_mtk_func_query, hdev, status, + status < 0 || status != BTMTK_WMT_ON_PROGRESS, + 2000, 5000000); + /* -ETIMEDOUT happens */ + if (err < 0) + return err; + + /* The other errors happen in btusb_mtk_func_query */ + if (status < 0) + return status; + + if (status == BTMTK_WMT_ON_DONE) { + bt_dev_info(hdev, "function already on"); + goto ignore_func_on; + } + + /* Enable Bluetooth protocol */ + param = 1; + wmt_params.op = BTMTK_WMT_FUNC_CTRL; + wmt_params.flag = 0; + wmt_params.dlen = sizeof(param); + wmt_params.data = ¶m; + wmt_params.status = NULL; + + err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; + } + +ignore_func_on: + /* Apply the low power environment setup */ + tci_sleep.mode = 0x5; + tci_sleep.duration = cpu_to_le16(0x640); + tci_sleep.host_duration = cpu_to_le16(0x640); + tci_sleep.host_wakeup_pin = 0; + tci_sleep.time_compensation = 0; + + skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "Failed to apply low power setting (%d)", err); + return err; + } + kfree_skb(skb); + + rettime = ktime_get(); + delta = ktime_sub(rettime, calltime); + duration = (unsigned long long)ktime_to_ns(delta) >> 10; + + bt_dev_info(hdev, "Device setup in %llu usecs", duration); + + return 0; +} + +static int btusb_mtk_shutdown(struct hci_dev *hdev) +{ + struct btmtk_hci_wmt_params wmt_params; + u8 param = 0; + int err; + + /* Disable the device */ + wmt_params.op = BTMTK_WMT_FUNC_CTRL; + wmt_params.flag = 0; + wmt_params.dlen = sizeof(param); + wmt_params.data = ¶m; + wmt_params.status = NULL; + + err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; + } + + return 0; +} + +MODULE_FIRMWARE(FIRMWARE_MT7663); +MODULE_FIRMWARE(FIRMWARE_MT7668); +#endif + #ifdef CONFIG_PM /* Configure an out-of-band gpio as wake-up pin, if specified in device tree */ static int marvell_config_oob_wake(struct hci_dev *hdev) @@ -3044,6 +3617,7 @@ static int btusb_probe(struct usb_interface *intf, init_usb_anchor(&data->bulk_anchor); init_usb_anchor(&data->isoc_anchor); init_usb_anchor(&data->diag_anchor); + init_usb_anchor(&data->ctrl_anchor); spin_lock_init(&data->rxlock); if (id->driver_info & BTUSB_INTEL_NEW) { @@ -3157,6 +3731,15 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_MARVELL) hdev->set_bdaddr = btusb_set_bdaddr_marvell; +#ifdef CONFIG_BT_HCIBTUSB_MTK + if (id->driver_info & BTUSB_MEDIATEK) { + hdev->setup = btusb_mtk_setup; + hdev->shutdown = btusb_mtk_shutdown; + hdev->manufacturer = 70; + set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + } +#endif + if (id->driver_info & BTUSB_SWAVE) { set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks); set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); @@ -3184,6 +3767,7 @@ static int btusb_probe(struct usb_interface *intf, #ifdef CONFIG_BT_HCIBTUSB_RTL if (id->driver_info & BTUSB_REALTEK) { hdev->setup = btrtl_setup_realtek; + hdev->shutdown = btrtl_shutdown_realtek; /* Realtek devices lose their updated firmware over suspend, * but the USB hub doesn't notice any status change. diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index 82b13faa9422..fe2e307009f4 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -744,6 +744,11 @@ static int bcsp_close(struct hci_uart *hu) skb_queue_purge(&bcsp->rel); skb_queue_purge(&bcsp->unrel); + if (bcsp->rx_skb) { + kfree_skb(bcsp->rx_skb); + bcsp->rx_skb = NULL; + } + kfree(bcsp); return 0; } diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index c84f985f348d..8950e07889fe 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -178,6 +178,7 @@ restart: goto restart; clear_bit(HCI_UART_SENDING, &hu->tx_state); + wake_up_bit(&hu->tx_state, HCI_UART_SENDING); } void hci_uart_init_work(struct work_struct *work) @@ -213,6 +214,13 @@ int hci_uart_init_ready(struct hci_uart *hu) return 0; } +int hci_uart_wait_until_sent(struct hci_uart *hu) +{ + return wait_on_bit_timeout(&hu->tx_state, HCI_UART_SENDING, + TASK_INTERRUPTIBLE, + msecs_to_jiffies(2000)); +} + /* ------- Interface to HCI layer ------ */ /* Reset device */ static int hci_uart_flush(struct hci_dev *hdev) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index c04f5f9e1ed0..285706618f8a 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -128,6 +128,7 @@ static int ll_open(struct hci_uart *hu) if (hu->serdev) { struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev); + if (!IS_ERR(lldev->ext_clk)) clk_prepare_enable(lldev->ext_clk); } @@ -162,6 +163,7 @@ static int ll_close(struct hci_uart *hu) if (hu->serdev) { struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev); + gpiod_set_value_cansleep(lldev->enable_gpio, 0); clk_disable_unprepare(lldev->ext_clk); @@ -227,7 +229,8 @@ static void ll_device_want_to_wakeup(struct hci_uart *hu) break; default: /* any other state is illegal */ - BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state); + BT_ERR("received HCILL_WAKE_UP_IND in state %ld", + ll->hcill_state); break; } @@ -256,7 +259,8 @@ static void ll_device_want_to_sleep(struct hci_uart *hu) /* sanity check */ if (ll->hcill_state != HCILL_AWAKE) - BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state); + BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", + ll->hcill_state); /* acknowledge device sleep */ if (send_hcill_cmd(HCILL_GO_TO_SLEEP_ACK, hu) < 0) { @@ -289,7 +293,8 @@ static void ll_device_woke_up(struct hci_uart *hu) /* sanity check */ if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE) - BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state); + BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", + ll->hcill_state); /* send pending packets and change state to HCILL_AWAKE */ __ll_do_awake(ll); @@ -338,7 +343,8 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) skb_queue_tail(&ll->tx_wait_q, skb); break; default: - BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state); + BT_ERR("illegal hcill state: %ld (losing packet)", + ll->hcill_state); kfree_skb(skb); break; } @@ -438,6 +444,7 @@ static int ll_recv(struct hci_uart *hu, const void *data, int count) static struct sk_buff *ll_dequeue(struct hci_uart *hu) { struct ll_struct *ll = hu->priv; + return skb_dequeue(&ll->txq); } @@ -449,7 +456,8 @@ static int read_local_version(struct hci_dev *hdev) struct sk_buff *skb; struct hci_rp_read_local_version *ver; - skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT); + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading TI version information failed (%ld)", PTR_ERR(skb)); @@ -469,11 +477,38 @@ static int read_local_version(struct hci_dev *hdev) version = le16_to_cpu(ver->lmp_subver); out: - if (err) bt_dev_err(hdev, "Failed to read TI version info: %d", err); + if (err) + bt_dev_err(hdev, "Failed to read TI version info: %d", err); kfree_skb(skb); return err ? err : version; } +static int send_command_from_firmware(struct ll_device *lldev, + struct hci_command *cmd) +{ + struct sk_buff *skb; + + if (cmd->opcode == HCI_VS_UPDATE_UART_HCI_BAUDRATE) { + /* ignore remote change + * baud rate HCI VS command + */ + bt_dev_warn(lldev->hu.hdev, + "change remote baud rate command in firmware"); + return 0; + } + if (cmd->prefix != 1) + bt_dev_dbg(lldev->hu.hdev, "command type %d", cmd->prefix); + + skb = __hci_cmd_sync(lldev->hu.hdev, cmd->opcode, cmd->plen, + &cmd->speed, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(lldev->hu.hdev, "send command failed"); + return PTR_ERR(skb); + } + kfree_skb(skb); + return 0; +} + /** * download_firmware - * internal function which parses through the .bts firmware @@ -486,7 +521,6 @@ static int download_firmware(struct ll_device *lldev) unsigned char *ptr, *action_ptr; unsigned char bts_scr_name[40]; /* 40 char long bts scr name? */ const struct firmware *fw; - struct sk_buff *skb; struct hci_command *cmd; version = read_local_version(lldev->hu.hdev); @@ -528,23 +562,9 @@ static int download_firmware(struct ll_device *lldev) case ACTION_SEND_COMMAND: /* action send */ bt_dev_dbg(lldev->hu.hdev, "S"); cmd = (struct hci_command *)action_ptr; - if (cmd->opcode == HCI_VS_UPDATE_UART_HCI_BAUDRATE) { - /* ignore remote change - * baud rate HCI VS command - */ - bt_dev_warn(lldev->hu.hdev, "change remote baud rate command in firmware"); - break; - } - if (cmd->prefix != 1) - bt_dev_dbg(lldev->hu.hdev, "command type %d", cmd->prefix); - - skb = __hci_cmd_sync(lldev->hu.hdev, cmd->opcode, cmd->plen, &cmd->speed, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - bt_dev_err(lldev->hu.hdev, "send command failed"); - err = PTR_ERR(skb); + err = send_command_from_firmware(lldev, cmd); + if (err) goto out_rel_fw; - } - kfree_skb(skb); break; case ACTION_WAIT_EVENT: /* wait */ /* no need to wait as command was synchronous */ @@ -601,6 +621,13 @@ static int ll_setup(struct hci_uart *hu) serdev_device_set_flow_control(serdev, true); + if (hu->oper_speed) + speed = hu->oper_speed; + else if (hu->proto->oper_speed) + speed = hu->proto->oper_speed; + else + speed = 0; + do { /* Reset the Bluetooth device */ gpiod_set_value_cansleep(lldev->enable_gpio, 0); @@ -612,6 +639,20 @@ static int ll_setup(struct hci_uart *hu) return err; } + if (speed) { + __le32 speed_le = cpu_to_le32(speed); + struct sk_buff *skb; + + skb = __hci_cmd_sync(hu->hdev, + HCI_VS_UPDATE_UART_HCI_BAUDRATE, + sizeof(speed_le), &speed_le, + HCI_INIT_TIMEOUT); + if (!IS_ERR(skb)) { + kfree_skb(skb); + serdev_device_set_baudrate(serdev, speed); + } + } + err = download_firmware(lldev); if (!err) break; @@ -636,25 +677,7 @@ static int ll_setup(struct hci_uart *hu) } /* Operational speed if any */ - if (hu->oper_speed) - speed = hu->oper_speed; - else if (hu->proto->oper_speed) - speed = hu->proto->oper_speed; - else - speed = 0; - - if (speed) { - __le32 speed_le = cpu_to_le32(speed); - struct sk_buff *skb; - skb = __hci_cmd_sync(hu->hdev, HCI_VS_UPDATE_UART_HCI_BAUDRATE, - sizeof(speed_le), &speed_le, - HCI_INIT_TIMEOUT); - if (!IS_ERR(skb)) { - kfree_skb(skb); - serdev_device_set_baudrate(serdev, speed); - } - } return 0; } @@ -676,7 +699,9 @@ static int hci_ti_probe(struct serdev_device *serdev) serdev_device_set_drvdata(serdev, lldev); lldev->serdev = hu->serdev = serdev; - lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW); + lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, + "enable", + GPIOD_OUT_LOW); if (IS_ERR(lldev->enable_gpio)) return PTR_ERR(lldev->enable_gpio); diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c index 50212ac629e3..f98e5cc343b2 100644 --- a/drivers/bluetooth/hci_mrvl.c +++ b/drivers/bluetooth/hci_mrvl.c @@ -13,6 +13,8 @@ #include <linux/firmware.h> #include <linux/module.h> #include <linux/tty.h> +#include <linux/of.h> +#include <linux/serdev.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -40,6 +42,10 @@ struct mrvl_data { u8 id, rev; }; +struct mrvl_serdev { + struct hci_uart hu; +}; + struct hci_mrvl_pkt { __le16 lhs; __le16 rhs; @@ -49,6 +55,7 @@ struct hci_mrvl_pkt { static int mrvl_open(struct hci_uart *hu) { struct mrvl_data *mrvl; + int ret; BT_DBG("hu %p", hu); @@ -62,7 +69,18 @@ static int mrvl_open(struct hci_uart *hu) set_bit(STATE_CHIP_VER_PENDING, &mrvl->flags); hu->priv = mrvl; + + if (hu->serdev) { + ret = serdev_device_open(hu->serdev); + if (ret) + goto err; + } + return 0; +err: + kfree(mrvl); + + return ret; } static int mrvl_close(struct hci_uart *hu) @@ -71,6 +89,9 @@ static int mrvl_close(struct hci_uart *hu) BT_DBG("hu %p", hu); + if (hu->serdev) + serdev_device_close(hu->serdev); + skb_queue_purge(&mrvl->txq); skb_queue_purge(&mrvl->rawq); kfree_skb(mrvl->rx_skb); @@ -339,7 +360,14 @@ static int mrvl_setup(struct hci_uart *hu) return -EINVAL; } - hci_uart_set_baudrate(hu, 3000000); + /* Let the final ack go out before switching the baudrate */ + hci_uart_wait_until_sent(hu); + + if (hu->serdev) + serdev_device_set_baudrate(hu->serdev, 3000000); + else + hci_uart_set_baudrate(hu, 3000000); + hci_uart_set_flow_control(hu, false); err = mrvl_load_firmware(hu->hdev, "mrvl/uart8897_bt.bin"); @@ -362,12 +390,54 @@ static const struct hci_uart_proto mrvl_proto = { .dequeue = mrvl_dequeue, }; +static int mrvl_serdev_probe(struct serdev_device *serdev) +{ + struct mrvl_serdev *mrvldev; + + mrvldev = devm_kzalloc(&serdev->dev, sizeof(*mrvldev), GFP_KERNEL); + if (!mrvldev) + return -ENOMEM; + + mrvldev->hu.serdev = serdev; + serdev_device_set_drvdata(serdev, mrvldev); + + return hci_uart_register_device(&mrvldev->hu, &mrvl_proto); +} + +static void mrvl_serdev_remove(struct serdev_device *serdev) +{ + struct mrvl_serdev *mrvldev = serdev_device_get_drvdata(serdev); + + hci_uart_unregister_device(&mrvldev->hu); +} + +#ifdef CONFIG_OF +static const struct of_device_id mrvl_bluetooth_of_match[] = { + { .compatible = "mrvl,88w8897" }, + { }, +}; +MODULE_DEVICE_TABLE(of, mrvl_bluetooth_of_match); +#endif + +static struct serdev_device_driver mrvl_serdev_driver = { + .probe = mrvl_serdev_probe, + .remove = mrvl_serdev_remove, + .driver = { + .name = "hci_uart_mrvl", + .of_match_table = of_match_ptr(mrvl_bluetooth_of_match), + }, +}; + int __init mrvl_init(void) { + serdev_device_driver_register(&mrvl_serdev_driver); + return hci_uart_register_proto(&mrvl_proto); } int __exit mrvl_deinit(void) { + serdev_device_driver_unregister(&mrvl_serdev_driver); + return hci_uart_unregister_proto(&mrvl_proto); } diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 9d273cdde563..9a5c9c1f9484 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -17,6 +17,7 @@ #include <linux/kernel.h> #include <linux/clk.h> +#include <linux/completion.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/device.h> @@ -53,6 +54,7 @@ enum qca_flags { QCA_IBS_ENABLED, + QCA_DROP_VENDOR_EVENT, }; /* HCI_IBS transmit side sleep protocol states */ @@ -97,6 +99,7 @@ struct qca_data { struct work_struct ws_rx_vote_off; struct work_struct ws_tx_vote_off; unsigned long flags; + struct completion drop_ev_comp; /* For debugging purpose */ u64 ibs_sent_wacks; @@ -156,6 +159,7 @@ struct qca_serdev { struct qca_power *bt_power; u32 init_speed; u32 oper_speed; + const char *firmware_name; }; static int qca_power_setup(struct hci_uart *hu, bool on); @@ -177,6 +181,17 @@ static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu) return soc_type; } +static const char *qca_get_firmware_name(struct hci_uart *hu) +{ + if (hu->serdev) { + struct qca_serdev *qsd = serdev_device_get_drvdata(hu->serdev); + + return qsd->firmware_name; + } else { + return NULL; + } +} + static void __serial_clock_on(struct tty_struct *tty) { /* TODO: Some chipset requires to enable UART clock on client @@ -478,6 +493,7 @@ static int qca_open(struct hci_uart *hu) INIT_WORK(&qca->ws_tx_vote_off, qca_wq_serial_tx_clock_vote_off); qca->hu = hu; + init_completion(&qca->drop_ev_comp); /* Assume we start with both sides asleep -- extra wakes OK */ qca->tx_ibs_state = HCI_IBS_TX_ASLEEP; @@ -872,6 +888,35 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb) return hci_recv_frame(hdev, skb); } +static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct qca_data *qca = hu->priv; + + if (test_bit(QCA_DROP_VENDOR_EVENT, &qca->flags)) { + struct hci_event_hdr *hdr = (void *)skb->data; + + /* For the WCN3990 the vendor command for a baudrate change + * isn't sent as synchronous HCI command, because the + * controller sends the corresponding vendor event with the + * new baudrate. The event is received and properly decoded + * after changing the baudrate of the host port. It needs to + * be dropped, otherwise it can be misinterpreted as + * response to a later firmware download command (also a + * vendor command). + */ + + if (hdr->evt == HCI_EV_VENDOR) + complete(&qca->drop_ev_comp); + + kfree(skb); + + return 0; + } + + return hci_recv_frame(hdev, skb); +} + #define QCA_IBS_SLEEP_IND_EVENT \ .type = HCI_IBS_SLEEP_IND, \ .hlen = 0, \ @@ -896,7 +941,7 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb) static const struct h4_recv_pkt qca_recv_pkts[] = { { H4_RECV_ACL, .recv = qca_recv_acl_data }, { H4_RECV_SCO, .recv = hci_recv_frame }, - { H4_RECV_EVENT, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = qca_recv_event }, { QCA_IBS_WAKE_IND_EVENT, .recv = qca_ibs_wake_ind }, { QCA_IBS_WAKE_ACK_EVENT, .recv = qca_ibs_wake_ack }, { QCA_IBS_SLEEP_IND_EVENT, .recv = qca_ibs_sleep_ind }, @@ -1091,6 +1136,7 @@ static int qca_check_speeds(struct hci_uart *hu) static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) { unsigned int speed, qca_baudrate; + struct qca_data *qca = hu->priv; int ret = 0; if (speed_type == QCA_INIT_SPEED) { @@ -1110,6 +1156,11 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) if (qca_is_wcn399x(soc_type)) hci_uart_set_flow_control(hu, true); + if (soc_type == QCA_WCN3990) { + reinit_completion(&qca->drop_ev_comp); + set_bit(QCA_DROP_VENDOR_EVENT, &qca->flags); + } + qca_baudrate = qca_get_baudrate_value(speed); bt_dev_dbg(hu->hdev, "Set UART speed to %d", speed); ret = qca_set_baudrate(hu->hdev, qca_baudrate); @@ -1121,6 +1172,20 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) error: if (qca_is_wcn399x(soc_type)) hci_uart_set_flow_control(hu, false); + + if (soc_type == QCA_WCN3990) { + /* Wait for the controller to send the vendor event + * for the baudrate change command. + */ + if (!wait_for_completion_timeout(&qca->drop_ev_comp, + msecs_to_jiffies(100))) { + bt_dev_err(hu->hdev, + "Failed to change controller baudrate\n"); + ret = -ETIMEDOUT; + } + + clear_bit(QCA_DROP_VENDOR_EVENT, &qca->flags); + } } return ret; @@ -1182,6 +1247,7 @@ static int qca_setup(struct hci_uart *hu) struct qca_data *qca = hu->priv; unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200; enum qca_btsoc_type soc_type = qca_soc_type(hu); + const char *firmware_name = qca_get_firmware_name(hu); int ret; int soc_ver = 0; @@ -1232,7 +1298,8 @@ static int qca_setup(struct hci_uart *hu) bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver); /* Setup patch / NVM configurations */ - ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver); + ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver, + firmware_name); if (!ret) { set_bit(QCA_IBS_ENABLED, &qca->flags); qca_debugfs_init(hdev); @@ -1426,6 +1493,8 @@ static int qca_serdev_probe(struct serdev_device *serdev) qcadev->serdev_hu.serdev = serdev; data = of_device_get_match_data(&serdev->dev); serdev_device_set_drvdata(serdev, qcadev); + device_property_read_string(&serdev->dev, "firmware-name", + &qcadev->firmware_name); if (data && qca_is_wcn399x(data->soc_type)) { qcadev->btsoc_type = data->soc_type; qcadev->bt_power = devm_kzalloc(&serdev->dev, diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index d8cf005e3c5d..f11af3912ce6 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -100,6 +100,7 @@ int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p void hci_uart_unregister_device(struct hci_uart *hu); int hci_uart_tx_wakeup(struct hci_uart *hu); +int hci_uart_wait_until_sent(struct hci_uart *hu); int hci_uart_init_ready(struct hci_uart *hu); void hci_uart_init_work(struct work_struct *work); void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed); diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index 0220736b073e..4efbbd2fce0c 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -37,7 +37,7 @@ #include "mlx5_ib.h" #include "srq.h" -static void mlx5_ib_cq_comp(struct mlx5_core_cq *cq) +static void mlx5_ib_cq_comp(struct mlx5_core_cq *cq, struct mlx5_eqe *eqe) { struct ib_cq *ibcq = &to_mibcq(cq)->ibcq; @@ -891,6 +891,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries = attr->cqe; int vector = attr->comp_vector; struct mlx5_ib_dev *dev = to_mdev(ibdev); + u32 out[MLX5_ST_SZ_DW(create_cq_out)]; struct mlx5_ib_cq *cq; int uninitialized_var(index); int uninitialized_var(inlen); @@ -958,7 +959,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, if (cq->create_flags & IB_UVERBS_CQ_FLAGS_IGNORE_OVERRUN) MLX5_SET(cqc, cqc, oi, 1); - err = mlx5_core_create_cq(dev->mdev, &cq->mcq, cqb, inlen); + err = mlx5_core_create_cq(dev->mdev, &cq->mcq, cqb, inlen, out, sizeof(out)); if (err) goto err_cqb; diff --git a/drivers/infiniband/hw/mlx5/ib_rep.c b/drivers/infiniband/hw/mlx5/ib_rep.c index aa9acebfcc23..74ce9249e75a 100644 --- a/drivers/infiniband/hw/mlx5/ib_rep.c +++ b/drivers/infiniband/hw/mlx5/ib_rep.c @@ -14,9 +14,10 @@ mlx5_ib_set_vport_rep(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) int vport_index; ibdev = mlx5_ib_get_uplink_ibdev(dev->priv.eswitch); - vport_index = ibdev->free_port++; + vport_index = rep->vport_index; ibdev->port[vport_index].rep = rep; + rep->rep_data[REP_IB].priv = ibdev; write_lock(&ibdev->port[vport_index].roce.netdev_lock); ibdev->port[vport_index].roce.netdev = mlx5_ib_get_rep_netdev(dev->priv.eswitch, rep->vport); @@ -28,7 +29,7 @@ mlx5_ib_set_vport_rep(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) static int mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) { - int num_ports = MLX5_TOTAL_VPORTS(dev); + int num_ports = mlx5_eswitch_get_total_vports(dev); const struct mlx5_ib_profile *profile; struct mlx5_ib_dev *ibdev; int vport_index; @@ -50,7 +51,7 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) } ibdev->is_rep = true; - vport_index = ibdev->free_port++; + vport_index = rep->vport_index; ibdev->port[vport_index].rep = rep; ibdev->port[vport_index].roce.netdev = mlx5_ib_get_rep_netdev(dev->priv.eswitch, rep->vport); @@ -68,15 +69,18 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) static void mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep) { - struct mlx5_ib_dev *dev; - - if (!rep->rep_data[REP_IB].priv || - rep->vport != MLX5_VPORT_UPLINK) - return; + struct mlx5_ib_dev *dev = mlx5_ib_rep_to_dev(rep); + struct mlx5_ib_port *port; - dev = mlx5_ib_rep_to_dev(rep); - __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX); + port = &dev->port[rep->vport_index]; + write_lock(&port->roce.netdev_lock); + port->roce.netdev = NULL; + write_unlock(&port->roce.netdev_lock); rep->rep_data[REP_IB].priv = NULL; + port->rep = NULL; + + if (rep->vport == MLX5_VPORT_UPLINK) + __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX); } static void *mlx5_ib_vport_get_proto_dev(struct mlx5_eswitch_rep *rep) diff --git a/drivers/infiniband/hw/mlx5/ib_rep.h b/drivers/infiniband/hw/mlx5/ib_rep.h index 7a917e6d5c09..de43b423bafc 100644 --- a/drivers/infiniband/hw/mlx5/ib_rep.h +++ b/drivers/infiniband/hw/mlx5/ib_rep.h @@ -28,7 +28,7 @@ struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw, #else /* CONFIG_MLX5_ESWITCH */ static inline u8 mlx5_ib_eswitch_mode(struct mlx5_eswitch *esw) { - return SRIOV_NONE; + return MLX5_ESWITCH_NONE; } static inline diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index b1d5f4382d85..ba312bf59c7a 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4492,7 +4492,7 @@ static void mlx5_ib_handle_internal_error(struct mlx5_ib_dev *ibdev) * lock/unlock above locks Now need to arm all involved CQs. */ list_for_each_entry(mcq, &cq_armed_list, reset_notify) { - mcq->comp(mcq); + mcq->comp(mcq, NULL); } spin_unlock_irqrestore(&ibdev->reset_flow_resource_lock, flags); } @@ -6814,7 +6814,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) printk_once(KERN_INFO "%s", mlx5_version); if (MLX5_ESWITCH_MANAGER(mdev) && - mlx5_ib_eswitch_mode(mdev->priv.eswitch) == SRIOV_OFFLOADS) { + mlx5_ib_eswitch_mode(mdev->priv.eswitch) == MLX5_ESWITCH_OFFLOADS) { if (!mlx5_core_mp_enabled(mdev)) mlx5_ib_register_vport_reps(mdev); return mdev; diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 1c205c2bd486..ee73dc122d28 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -978,7 +978,6 @@ struct mlx5_ib_dev { u16 devx_whitelist_uid; struct mlx5_srq_table srq_table; struct mlx5_async_ctx async_ctx; - int free_port; }; static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq) diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index c594489eb2d7..831c450b271a 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1558,9 +1558,9 @@ mlx5_ib_create_pf_eq(struct mlx5_ib_dev *dev, struct mlx5_ib_pf_eq *eq) eq->irq_nb.notifier_call = mlx5_ib_eq_pf_int; param = (struct mlx5_eq_param) { .irq_index = 0, - .mask = 1 << MLX5_EVENT_TYPE_PAGE_FAULT, .nent = MLX5_IB_NUM_PF_EQE, }; + param.mask[0] = 1ull << MLX5_EVENT_TYPE_PAGE_FAULT; eq->core = mlx5_eq_create_generic(dev->mdev, ¶m); if (IS_ERR(eq->core)) { err = PTR_ERR(eq->core); diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index f6623c77443a..768c7e81f688 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -6297,7 +6297,7 @@ static void handle_drain_completion(struct ib_cq *cq, /* Run the CQ handler - this makes sure that the drain WR will * be processed if wasn't processed yet. */ - mcq->mcq.comp(&mcq->mcq); + mcq->mcq.comp(&mcq->mcq, NULL); } wait_for_completion(&sdrain->done); diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index 68bb58a57f3b..8242fb287cbb 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -683,7 +683,7 @@ static void softing_netdev_cleanup(struct net_device *netdev) static ssize_t show_##name(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ - struct softing *card = platform_get_drvdata(to_platform_device(dev)); \ + struct softing *card = dev_get_drvdata(dev); \ return sprintf(buf, "%u\n", card->member); \ } \ static DEVICE_ATTR(name, 0444, show_##name, NULL) @@ -692,7 +692,7 @@ static DEVICE_ATTR(name, 0444, show_##name, NULL) static ssize_t show_##name(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ - struct softing *card = platform_get_drvdata(to_platform_device(dev)); \ + struct softing *card = dev_get_drvdata(dev); \ return sprintf(buf, "%s\n", card->member); \ } \ static DEVICE_ATTR(name, 0444, show_##name, NULL) diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index b91e78e3598f..cf9dbd15dd2d 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -99,8 +99,8 @@ config NET_DSA_SMSC_LAN9303_MDIO for MDIO managed mode. config NET_DSA_VITESSE_VSC73XX - tristate "Vitesse VSC7385/7388/7395/7398 support" - depends on OF && SPI + tristate + depends on OF depends on NET_DSA select FIXED_PHY select VITESSE_PHY @@ -109,4 +109,20 @@ config NET_DSA_VITESSE_VSC73XX This enables support for the Vitesse VSC7385, VSC7388, VSC7395 and VSC7398 SparX integrated ethernet switches. +config NET_DSA_VITESSE_VSC73XX_SPI + tristate "Vitesse VSC7385/7388/7395/7398 SPI mode support" + depends on SPI + select NET_DSA_VITESSE_VSC73XX + ---help--- + This enables support for the Vitesse VSC7385, VSC7388, VSC7395 + and VSC7398 SparX integrated ethernet switches in SPI managed mode. + +config NET_DSA_VITESSE_VSC73XX_PLATFORM + tristate "Vitesse VSC7385/7388/7395/7398 Platform mode support" + depends on HAS_IOMEM + select NET_DSA_VITESSE_VSC73XX + ---help--- + This enables support for the Vitesse VSC7385, VSC7388, VSC7395 + and VSC7398 SparX integrated ethernet switches, connected over + a CPU-attached address bus and work in memory-mapped I/O mode. endmenu diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index d99dc6de0006..ae70b79628d6 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -14,7 +14,9 @@ realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o -obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx.o +obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o +obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o +obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o obj-y += b53/ obj-y += microchip/ obj-y += mv88e6xxx/ diff --git a/drivers/net/dsa/vitesse-vsc73xx.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index d4780610ea8a..614377ef7956 100644 --- a/drivers/net/dsa/vitesse-vsc73xx.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -10,10 +10,6 @@ * handling the switch in a memory-mapped manner by connecting to that external * CPU's memory bus. * - * This driver (currently) only takes control of the switch chip over SPI and - * configures it to route packages around when connected to a CPU port. The - * chip has embedded PHYs and VLAN support so we model it using DSA. - * * Copyright (C) 2018 Linus Wallej <[email protected]> * Includes portions of code from the firmware uploader by: * Copyright (C) 2009 Gabor Juhos <[email protected]> @@ -24,8 +20,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_mdio.h> -#include <linux/platform_device.h> -#include <linux/spi/spi.h> #include <linux/bitops.h> #include <linux/if_bridge.h> #include <linux/etherdevice.h> @@ -34,6 +28,8 @@ #include <linux/random.h> #include <net/dsa.h> +#include "vitesse-vsc73xx.h" + #define VSC73XX_BLOCK_MAC 0x1 /* Subblocks 0-4, 6 (CPU port) */ #define VSC73XX_BLOCK_ANALYZER 0x2 /* Only subblock 0 */ #define VSC73XX_BLOCK_MII 0x3 /* Subblocks 0 and 1 */ @@ -255,13 +251,6 @@ #define VSC73XX_GLORESET_PHY_RESET BIT(1) #define VSC73XX_GLORESET_MASTER_RESET BIT(0) -#define VSC73XX_CMD_MODE_READ 0 -#define VSC73XX_CMD_MODE_WRITE 1 -#define VSC73XX_CMD_MODE_SHIFT 4 -#define VSC73XX_CMD_BLOCK_SHIFT 5 -#define VSC73XX_CMD_BLOCK_MASK 0x7 -#define VSC73XX_CMD_SUBBLOCK_MASK 0xf - #define VSC7385_CLOCK_DELAY ((3 << 4) | 3) #define VSC7385_CLOCK_DELAY_MASK ((3 << 4) | 3) @@ -274,20 +263,6 @@ VSC73XX_ICPU_CTRL_CLK_EN | \ VSC73XX_ICPU_CTRL_SRST) -/** - * struct vsc73xx - VSC73xx state container - */ -struct vsc73xx { - struct device *dev; - struct gpio_desc *reset; - struct spi_device *spi; - struct dsa_switch *ds; - struct gpio_chip gc; - u16 chipid; - u8 addr[ETH_ALEN]; - struct mutex lock; /* Protects SPI traffic */ -}; - #define IS_7385(a) ((a)->chipid == VSC73XX_CHIPID_ID_7385) #define IS_7388(a) ((a)->chipid == VSC73XX_CHIPID_ID_7388) #define IS_7395(a) ((a)->chipid == VSC73XX_CHIPID_ID_7395) @@ -365,7 +340,7 @@ static const struct vsc73xx_counter vsc73xx_tx_counters[] = { { 29, "TxQoSClass3" }, /* non-standard counter */ }; -static int vsc73xx_is_addr_valid(u8 block, u8 subblock) +int vsc73xx_is_addr_valid(u8 block, u8 subblock) { switch (block) { case VSC73XX_BLOCK_MAC: @@ -396,96 +371,18 @@ static int vsc73xx_is_addr_valid(u8 block, u8 subblock) return 0; } - -static u8 vsc73xx_make_addr(u8 mode, u8 block, u8 subblock) -{ - u8 ret; - - ret = (block & VSC73XX_CMD_BLOCK_MASK) << VSC73XX_CMD_BLOCK_SHIFT; - ret |= (mode & 1) << VSC73XX_CMD_MODE_SHIFT; - ret |= subblock & VSC73XX_CMD_SUBBLOCK_MASK; - - return ret; -} +EXPORT_SYMBOL(vsc73xx_is_addr_valid); static int vsc73xx_read(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, u32 *val) { - struct spi_transfer t[2]; - struct spi_message m; - u8 cmd[4]; - u8 buf[4]; - int ret; - - if (!vsc73xx_is_addr_valid(block, subblock)) - return -EINVAL; - - spi_message_init(&m); - - memset(&t, 0, sizeof(t)); - - t[0].tx_buf = cmd; - t[0].len = sizeof(cmd); - spi_message_add_tail(&t[0], &m); - - t[1].rx_buf = buf; - t[1].len = sizeof(buf); - spi_message_add_tail(&t[1], &m); - - cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_MODE_READ, block, subblock); - cmd[1] = reg; - cmd[2] = 0; - cmd[3] = 0; - - mutex_lock(&vsc->lock); - ret = spi_sync(vsc->spi, &m); - mutex_unlock(&vsc->lock); - - if (ret) - return ret; - - *val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; - - return 0; + return vsc->ops->read(vsc, block, subblock, reg, val); } static int vsc73xx_write(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, u32 val) { - struct spi_transfer t[2]; - struct spi_message m; - u8 cmd[2]; - u8 buf[4]; - int ret; - - if (!vsc73xx_is_addr_valid(block, subblock)) - return -EINVAL; - - spi_message_init(&m); - - memset(&t, 0, sizeof(t)); - - t[0].tx_buf = cmd; - t[0].len = sizeof(cmd); - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - t[1].len = sizeof(buf); - spi_message_add_tail(&t[1], &m); - - cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_MODE_WRITE, block, subblock); - cmd[1] = reg; - - buf[0] = (val >> 24) & 0xff; - buf[1] = (val >> 16) & 0xff; - buf[2] = (val >> 8) & 0xff; - buf[3] = val & 0xff; - - mutex_lock(&vsc->lock); - ret = spi_sync(vsc->spi, &m); - mutex_unlock(&vsc->lock); - - return ret; + return vsc->ops->write(vsc, block, subblock, reg, val); } static int vsc73xx_update_bits(struct vsc73xx *vsc, u8 block, u8 subblock, @@ -520,22 +417,8 @@ static int vsc73xx_detect(struct vsc73xx *vsc) } if (val == 0xffffffff) { - dev_info(vsc->dev, "chip seems dead, assert reset\n"); - gpiod_set_value_cansleep(vsc->reset, 1); - /* Reset pulse should be 20ns minimum, according to datasheet - * table 245, so 10us should be fine - */ - usleep_range(10, 100); - gpiod_set_value_cansleep(vsc->reset, 0); - /* Wait 20ms according to datasheet table 245 */ - msleep(20); - - ret = vsc73xx_read(vsc, VSC73XX_BLOCK_SYSTEM, 0, - VSC73XX_ICPU_MBOX_VAL, &val); - if (val == 0xffffffff) { - dev_err(vsc->dev, "seems not to help, giving up\n"); - return -ENODEV; - } + dev_info(vsc->dev, "chip seems dead.\n"); + return -EAGAIN; } ret = vsc73xx_read(vsc, VSC73XX_BLOCK_SYSTEM, 0, @@ -586,9 +469,8 @@ static int vsc73xx_detect(struct vsc73xx *vsc) } if (icpu_si_boot_en && !icpu_pi_en) { dev_err(vsc->dev, - "iCPU enabled boots from SI, no external memory\n"); - dev_err(vsc->dev, "no idea how to deal with this\n"); - return -ENODEV; + "iCPU enabled boots from PI/SI, no external memory\n"); + return -EAGAIN; } if (!icpu_si_boot_en && icpu_pi_en) { dev_err(vsc->dev, @@ -1245,21 +1127,11 @@ static int vsc73xx_gpio_probe(struct vsc73xx *vsc) return 0; } -static int vsc73xx_probe(struct spi_device *spi) +int vsc73xx_probe(struct vsc73xx *vsc) { - struct device *dev = &spi->dev; - struct vsc73xx *vsc; + struct device *dev = vsc->dev; int ret; - vsc = devm_kzalloc(dev, sizeof(*vsc), GFP_KERNEL); - if (!vsc) - return -ENOMEM; - - spi_set_drvdata(spi, vsc); - vsc->spi = spi_dev_get(spi); - vsc->dev = dev; - mutex_init(&vsc->lock); - /* Release reset, if any */ vsc->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(vsc->reset)) { @@ -1270,15 +1142,20 @@ static int vsc73xx_probe(struct spi_device *spi) /* Wait 20ms according to datasheet table 245 */ msleep(20); - spi->mode = SPI_MODE_0; - spi->bits_per_word = 8; - ret = spi_setup(spi); - if (ret < 0) { - dev_err(dev, "spi setup failed.\n"); - return ret; - } - ret = vsc73xx_detect(vsc); + if (ret == -EAGAIN) { + dev_err(vsc->dev, + "Chip seems to be out of control. Assert reset and try again.\n"); + gpiod_set_value_cansleep(vsc->reset, 1); + /* Reset pulse should be 20ns minimum, according to datasheet + * table 245, so 10us should be fine + */ + usleep_range(10, 100); + gpiod_set_value_cansleep(vsc->reset, 0); + /* Wait 20ms according to datasheet table 245 */ + msleep(20); + ret = vsc73xx_detect(vsc); + } if (ret) { dev_err(dev, "no chip found (%d)\n", ret); return -ENODEV; @@ -1321,43 +1198,16 @@ static int vsc73xx_probe(struct spi_device *spi) return 0; } +EXPORT_SYMBOL(vsc73xx_probe); -static int vsc73xx_remove(struct spi_device *spi) +int vsc73xx_remove(struct vsc73xx *vsc) { - struct vsc73xx *vsc = spi_get_drvdata(spi); - dsa_unregister_switch(vsc->ds); gpiod_set_value(vsc->reset, 1); return 0; } - -static const struct of_device_id vsc73xx_of_match[] = { - { - .compatible = "vitesse,vsc7385", - }, - { - .compatible = "vitesse,vsc7388", - }, - { - .compatible = "vitesse,vsc7395", - }, - { - .compatible = "vitesse,vsc7398", - }, - { }, -}; -MODULE_DEVICE_TABLE(of, vsc73xx_of_match); - -static struct spi_driver vsc73xx_driver = { - .probe = vsc73xx_probe, - .remove = vsc73xx_remove, - .driver = { - .name = "vsc73xx", - .of_match_table = vsc73xx_of_match, - }, -}; -module_spi_driver(vsc73xx_driver); +EXPORT_SYMBOL(vsc73xx_remove); MODULE_AUTHOR("Linus Walleij <[email protected]>"); MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 driver"); diff --git a/drivers/net/dsa/vitesse-vsc73xx-platform.c b/drivers/net/dsa/vitesse-vsc73xx-platform.c new file mode 100644 index 000000000000..0541785f9fee --- /dev/null +++ b/drivers/net/dsa/vitesse-vsc73xx-platform.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 +/* DSA driver for: + * Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch + * Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch + * Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch + * Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch + * + * This driver takes control of the switch chip connected over CPU-attached + * address bus and configures it to route packages around when connected to + * a CPU port. + * + * Copyright (C) 2019 Pawel Dembicki <[email protected]> + * Based on vitesse-vsc-spi.c by: + * Copyright (C) 2018 Linus Wallej <[email protected]> + * Includes portions of code from the firmware uploader by: + * Copyright (C) 2009 Gabor Juhos <[email protected]> + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "vitesse-vsc73xx.h" + +#define VSC73XX_CMD_PLATFORM_BLOCK_SHIFT 14 +#define VSC73XX_CMD_PLATFORM_BLOCK_MASK 0x7 +#define VSC73XX_CMD_PLATFORM_SUBBLOCK_SHIFT 10 +#define VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK 0xf +#define VSC73XX_CMD_PLATFORM_REGISTER_SHIFT 2 + +/** + * struct vsc73xx_platform - VSC73xx Platform state container + */ +struct vsc73xx_platform { + struct platform_device *pdev; + void __iomem *base_addr; + struct vsc73xx vsc; +}; + +static const struct vsc73xx_ops vsc73xx_platform_ops; + +static u32 vsc73xx_make_addr(u8 block, u8 subblock, u8 reg) +{ + u32 ret; + + ret = (block & VSC73XX_CMD_PLATFORM_BLOCK_MASK) + << VSC73XX_CMD_PLATFORM_BLOCK_SHIFT; + ret |= (subblock & VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK) + << VSC73XX_CMD_PLATFORM_SUBBLOCK_SHIFT; + ret |= reg << VSC73XX_CMD_PLATFORM_REGISTER_SHIFT; + + return ret; +} + +static int vsc73xx_platform_read(struct vsc73xx *vsc, u8 block, u8 subblock, + u8 reg, u32 *val) +{ + struct vsc73xx_platform *vsc_platform = vsc->priv; + u32 offset; + + if (!vsc73xx_is_addr_valid(block, subblock)) + return -EINVAL; + + offset = vsc73xx_make_addr(block, subblock, reg); + /* By default vsc73xx running in big-endian mode. + * (See "Register Addressing" section 5.5.3 in the VSC7385 manual.) + */ + *val = ioread32be(vsc_platform->base_addr + offset); + + return 0; +} + +static int vsc73xx_platform_write(struct vsc73xx *vsc, u8 block, u8 subblock, + u8 reg, u32 val) +{ + struct vsc73xx_platform *vsc_platform = vsc->priv; + u32 offset; + + if (!vsc73xx_is_addr_valid(block, subblock)) + return -EINVAL; + + offset = vsc73xx_make_addr(block, subblock, reg); + iowrite32be(val, vsc_platform->base_addr + offset); + + return 0; +} + +static int vsc73xx_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vsc73xx_platform *vsc_platform; + struct resource *res = NULL; + int ret; + + vsc_platform = devm_kzalloc(dev, sizeof(*vsc_platform), GFP_KERNEL); + if (!vsc_platform) + return -ENOMEM; + + platform_set_drvdata(pdev, vsc_platform); + vsc_platform->pdev = pdev; + vsc_platform->vsc.dev = dev; + vsc_platform->vsc.priv = vsc_platform; + vsc_platform->vsc.ops = &vsc73xx_platform_ops; + + /* obtain I/O memory space */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot obtain I/O memory space\n"); + ret = -ENXIO; + return ret; + } + + vsc_platform->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(vsc_platform->base_addr)) { + dev_err(&pdev->dev, "cannot request I/O memory space\n"); + ret = -ENXIO; + return ret; + } + + return vsc73xx_probe(&vsc_platform->vsc); +} + +static int vsc73xx_platform_remove(struct platform_device *pdev) +{ + struct vsc73xx_platform *vsc_platform = platform_get_drvdata(pdev); + + return vsc73xx_remove(&vsc_platform->vsc); +} + +static const struct vsc73xx_ops vsc73xx_platform_ops = { + .read = vsc73xx_platform_read, + .write = vsc73xx_platform_write, +}; + +static const struct of_device_id vsc73xx_of_match[] = { + { + .compatible = "vitesse,vsc7385", + }, + { + .compatible = "vitesse,vsc7388", + }, + { + .compatible = "vitesse,vsc7395", + }, + { + .compatible = "vitesse,vsc7398", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, vsc73xx_of_match); + +static struct platform_driver vsc73xx_platform_driver = { + .probe = vsc73xx_platform_probe, + .remove = vsc73xx_platform_remove, + .driver = { + .name = "vsc73xx-platform", + .of_match_table = vsc73xx_of_match, + }, +}; +module_platform_driver(vsc73xx_platform_driver); + +MODULE_AUTHOR("Pawel Dembicki <[email protected]>"); +MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 Platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/vitesse-vsc73xx-spi.c b/drivers/net/dsa/vitesse-vsc73xx-spi.c new file mode 100644 index 000000000000..e73c8fcddc9f --- /dev/null +++ b/drivers/net/dsa/vitesse-vsc73xx-spi.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 +/* DSA driver for: + * Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch + * Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch + * Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch + * Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch + * + * This driver takes control of the switch chip over SPI and + * configures it to route packages around when connected to a CPU port. + * + * Copyright (C) 2018 Linus Wallej <[email protected]> + * Includes portions of code from the firmware uploader by: + * Copyright (C) 2009 Gabor Juhos <[email protected]> + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/spi/spi.h> + +#include "vitesse-vsc73xx.h" + +#define VSC73XX_CMD_SPI_MODE_READ 0 +#define VSC73XX_CMD_SPI_MODE_WRITE 1 +#define VSC73XX_CMD_SPI_MODE_SHIFT 4 +#define VSC73XX_CMD_SPI_BLOCK_SHIFT 5 +#define VSC73XX_CMD_SPI_BLOCK_MASK 0x7 +#define VSC73XX_CMD_SPI_SUBBLOCK_MASK 0xf + +/** + * struct vsc73xx_spi - VSC73xx SPI state container + */ +struct vsc73xx_spi { + struct spi_device *spi; + struct mutex lock; /* Protects SPI traffic */ + struct vsc73xx vsc; +}; + +static const struct vsc73xx_ops vsc73xx_spi_ops; + +static u8 vsc73xx_make_addr(u8 mode, u8 block, u8 subblock) +{ + u8 ret; + + ret = + (block & VSC73XX_CMD_SPI_BLOCK_MASK) << VSC73XX_CMD_SPI_BLOCK_SHIFT; + ret |= (mode & 1) << VSC73XX_CMD_SPI_MODE_SHIFT; + ret |= subblock & VSC73XX_CMD_SPI_SUBBLOCK_MASK; + + return ret; +} + +static int vsc73xx_spi_read(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, + u32 *val) +{ + struct vsc73xx_spi *vsc_spi = vsc->priv; + struct spi_transfer t[2]; + struct spi_message m; + u8 cmd[4]; + u8 buf[4]; + int ret; + + if (!vsc73xx_is_addr_valid(block, subblock)) + return -EINVAL; + + spi_message_init(&m); + + memset(&t, 0, sizeof(t)); + + t[0].tx_buf = cmd; + t[0].len = sizeof(cmd); + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = sizeof(buf); + spi_message_add_tail(&t[1], &m); + + cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_SPI_MODE_READ, block, subblock); + cmd[1] = reg; + cmd[2] = 0; + cmd[3] = 0; + + mutex_lock(&vsc_spi->lock); + ret = spi_sync(vsc_spi->spi, &m); + mutex_unlock(&vsc_spi->lock); + + if (ret) + return ret; + + *val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + return 0; +} + +static int vsc73xx_spi_write(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, + u32 val) +{ + struct vsc73xx_spi *vsc_spi = vsc->priv; + struct spi_transfer t[2]; + struct spi_message m; + u8 cmd[2]; + u8 buf[4]; + int ret; + + if (!vsc73xx_is_addr_valid(block, subblock)) + return -EINVAL; + + spi_message_init(&m); + + memset(&t, 0, sizeof(t)); + + t[0].tx_buf = cmd; + t[0].len = sizeof(cmd); + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = buf; + t[1].len = sizeof(buf); + spi_message_add_tail(&t[1], &m); + + cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_SPI_MODE_WRITE, block, subblock); + cmd[1] = reg; + + buf[0] = (val >> 24) & 0xff; + buf[1] = (val >> 16) & 0xff; + buf[2] = (val >> 8) & 0xff; + buf[3] = val & 0xff; + + mutex_lock(&vsc_spi->lock); + ret = spi_sync(vsc_spi->spi, &m); + mutex_unlock(&vsc_spi->lock); + + return ret; +} + +static int vsc73xx_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct vsc73xx_spi *vsc_spi; + int ret; + + vsc_spi = devm_kzalloc(dev, sizeof(*vsc_spi), GFP_KERNEL); + if (!vsc_spi) + return -ENOMEM; + + spi_set_drvdata(spi, vsc_spi); + vsc_spi->spi = spi_dev_get(spi); + vsc_spi->vsc.dev = dev; + vsc_spi->vsc.priv = vsc_spi; + vsc_spi->vsc.ops = &vsc73xx_spi_ops; + mutex_init(&vsc_spi->lock); + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) { + dev_err(dev, "spi setup failed.\n"); + return ret; + } + + return vsc73xx_probe(&vsc_spi->vsc); +} + +static int vsc73xx_spi_remove(struct spi_device *spi) +{ + struct vsc73xx_spi *vsc_spi = spi_get_drvdata(spi); + + return vsc73xx_remove(&vsc_spi->vsc); +} + +static const struct vsc73xx_ops vsc73xx_spi_ops = { + .read = vsc73xx_spi_read, + .write = vsc73xx_spi_write, +}; + +static const struct of_device_id vsc73xx_of_match[] = { + { + .compatible = "vitesse,vsc7385", + }, + { + .compatible = "vitesse,vsc7388", + }, + { + .compatible = "vitesse,vsc7395", + }, + { + .compatible = "vitesse,vsc7398", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, vsc73xx_of_match); + +static struct spi_driver vsc73xx_spi_driver = { + .probe = vsc73xx_spi_probe, + .remove = vsc73xx_spi_remove, + .driver = { + .name = "vsc73xx-spi", + .of_match_table = vsc73xx_of_match, + }, +}; +module_spi_driver(vsc73xx_spi_driver); + +MODULE_AUTHOR("Linus Walleij <[email protected]>"); +MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/vitesse-vsc73xx.h b/drivers/net/dsa/vitesse-vsc73xx.h new file mode 100644 index 000000000000..7478f8d4e0a9 --- /dev/null +++ b/drivers/net/dsa/vitesse-vsc73xx.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/device.h> +#include <linux/etherdevice.h> +#include <linux/gpio/driver.h> + +/** + * struct vsc73xx - VSC73xx state container + */ +struct vsc73xx { + struct device *dev; + struct gpio_desc *reset; + struct dsa_switch *ds; + struct gpio_chip gc; + u16 chipid; + u8 addr[ETH_ALEN]; + const struct vsc73xx_ops *ops; + void *priv; +}; + +struct vsc73xx_ops { + int (*read)(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, + u32 *val); + int (*write)(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, + u32 val); +}; + +int vsc73xx_is_addr_valid(u8 block, u8 subblock); +int vsc73xx_probe(struct vsc73xx *vsc); +int vsc73xx_remove(struct vsc73xx *vsc); diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 9e06dff619c3..6253e5ed6e16 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -818,7 +818,6 @@ static int emac_probe(struct platform_device *pdev) SET_NETDEV_DEV(ndev, &pdev->dev); db = netdev_priv(ndev); - memset(db, 0, sizeof(*db)); db->dev = &pdev->dev; db->ndev = ndev; diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 2e4a8c7237ef..e9017caf024d 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -199,6 +199,7 @@ config BNXT select FW_LOADER select LIBCRC32C select NET_DEVLINK + select PAGE_POOL ---help--- This driver supports Broadcom NetXtreme-C/E 10/25/40/50 gigabit Ethernet cards. To compile this driver as a module, choose M here: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index b7b62273c955..d25bb385b3b9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -54,6 +54,7 @@ #include <net/pkt_cls.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <net/page_pool.h> #include "bnxt_hsi.h" #include "bnxt.h" @@ -668,19 +669,20 @@ next_tx_int: } static struct page *__bnxt_alloc_rx_page(struct bnxt *bp, dma_addr_t *mapping, + struct bnxt_rx_ring_info *rxr, gfp_t gfp) { struct device *dev = &bp->pdev->dev; struct page *page; - page = alloc_page(gfp); + page = page_pool_dev_alloc_pages(rxr->page_pool); if (!page) return NULL; *mapping = dma_map_page_attrs(dev, page, 0, PAGE_SIZE, bp->rx_dir, DMA_ATTR_WEAK_ORDERING); if (dma_mapping_error(dev, *mapping)) { - __free_page(page); + page_pool_recycle_direct(rxr->page_pool, page); return NULL; } *mapping += bp->rx_dma_offset; @@ -716,7 +718,8 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, dma_addr_t mapping; if (BNXT_RX_PAGE_MODE(bp)) { - struct page *page = __bnxt_alloc_rx_page(bp, &mapping, gfp); + struct page *page = + __bnxt_alloc_rx_page(bp, &mapping, rxr, gfp); if (!page) return -ENOMEM; @@ -1989,6 +1992,9 @@ static int __bnxt_poll_work(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, } } + if (event & BNXT_REDIRECT_EVENT) + xdp_do_flush_map(); + if (event & BNXT_TX_EVENT) { struct bnxt_tx_ring_info *txr = bnapi->tx_ring; u16 prod = txr->tx_prod; @@ -2254,9 +2260,23 @@ static void bnxt_free_tx_skbs(struct bnxt *bp) for (j = 0; j < max_idx;) { struct bnxt_sw_tx_bd *tx_buf = &txr->tx_buf_ring[j]; - struct sk_buff *skb = tx_buf->skb; + struct sk_buff *skb; int k, last; + if (i < bp->tx_nr_rings_xdp && + tx_buf->action == XDP_REDIRECT) { + dma_unmap_single(&pdev->dev, + dma_unmap_addr(tx_buf, mapping), + dma_unmap_len(tx_buf, len), + PCI_DMA_TODEVICE); + xdp_return_frame(tx_buf->xdpf); + tx_buf->action = 0; + tx_buf->xdpf = NULL; + j++; + continue; + } + + skb = tx_buf->skb; if (!skb) { j++; continue; @@ -2343,7 +2363,7 @@ static void bnxt_free_rx_skbs(struct bnxt *bp) dma_unmap_page_attrs(&pdev->dev, mapping, PAGE_SIZE, bp->rx_dir, DMA_ATTR_WEAK_ORDERING); - __free_page(data); + page_pool_recycle_direct(rxr->page_pool, data); } else { dma_unmap_single_attrs(&pdev->dev, mapping, bp->rx_buf_use_size, @@ -2480,6 +2500,8 @@ static void bnxt_free_rx_rings(struct bnxt *bp) if (xdp_rxq_info_is_reg(&rxr->xdp_rxq)) xdp_rxq_info_unreg(&rxr->xdp_rxq); + rxr->page_pool = NULL; + kfree(rxr->rx_tpa); rxr->rx_tpa = NULL; @@ -2494,6 +2516,26 @@ static void bnxt_free_rx_rings(struct bnxt *bp) } } +static int bnxt_alloc_rx_page_pool(struct bnxt *bp, + struct bnxt_rx_ring_info *rxr) +{ + struct page_pool_params pp = { 0 }; + + pp.pool_size = bp->rx_ring_size; + pp.nid = dev_to_node(&bp->pdev->dev); + pp.dev = &bp->pdev->dev; + pp.dma_dir = DMA_BIDIRECTIONAL; + + rxr->page_pool = page_pool_create(&pp); + if (IS_ERR(rxr->page_pool)) { + int err = PTR_ERR(rxr->page_pool); + + rxr->page_pool = NULL; + return err; + } + return 0; +} + static int bnxt_alloc_rx_rings(struct bnxt *bp) { int i, rc, agg_rings = 0, tpa_rings = 0; @@ -2513,9 +2555,26 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp) ring = &rxr->rx_ring_struct; + rc = bnxt_alloc_rx_page_pool(bp, rxr); + if (rc) + return rc; + rc = xdp_rxq_info_reg(&rxr->xdp_rxq, bp->dev, i); - if (rc < 0) + if (rc < 0) { + page_pool_free(rxr->page_pool); + rxr->page_pool = NULL; return rc; + } + + rc = xdp_rxq_info_reg_mem_model(&rxr->xdp_rxq, + MEM_TYPE_PAGE_POOL, + rxr->page_pool); + if (rc) { + xdp_rxq_info_unreg(&rxr->xdp_rxq); + page_pool_free(rxr->page_pool); + rxr->page_pool = NULL; + return rc; + } rc = bnxt_alloc_ring(bp, &ring->ring_mem); if (rc) @@ -10233,6 +10292,7 @@ static const struct net_device_ops bnxt_netdev_ops = { .ndo_udp_tunnel_add = bnxt_udp_tunnel_add, .ndo_udp_tunnel_del = bnxt_udp_tunnel_del, .ndo_bpf = bnxt_xdp, + .ndo_xdp_xmit = bnxt_xdp_xmit, .ndo_bridge_getlink = bnxt_bridge_getlink, .ndo_bridge_setlink = bnxt_bridge_setlink, .ndo_get_devlink_port = bnxt_get_devlink_port, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 4b3ae92a082b..16694b704d15 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -26,6 +26,8 @@ #include <net/xdp.h> #include <linux/dim.h> +struct page_pool; + struct tx_bd { __le32 tx_bd_len_flags_type; #define TX_BD_TYPE (0x3f << 0) @@ -587,15 +589,21 @@ struct nqe_cn { #define BNXT_HWRM_CHNL_CHIMP 0 #define BNXT_HWRM_CHNL_KONG 1 -#define BNXT_RX_EVENT 1 -#define BNXT_AGG_EVENT 2 -#define BNXT_TX_EVENT 4 +#define BNXT_RX_EVENT 1 +#define BNXT_AGG_EVENT 2 +#define BNXT_TX_EVENT 4 +#define BNXT_REDIRECT_EVENT 8 struct bnxt_sw_tx_bd { - struct sk_buff *skb; + union { + struct sk_buff *skb; + struct xdp_frame *xdpf; + }; DEFINE_DMA_UNMAP_ADDR(mapping); + DEFINE_DMA_UNMAP_LEN(len); u8 is_gso; u8 is_push; + u8 action; union { unsigned short nr_frags; u16 rx_prod; @@ -793,6 +801,7 @@ struct bnxt_rx_ring_info { struct bnxt_ring_struct rx_ring_struct; struct bnxt_ring_struct rx_agg_ring_struct; struct xdp_rxq_info xdp_rxq; + struct page_pool *page_pool; }; struct bnxt_cp_ring_info { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index a6c7baf38036..a0f3277447f5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2799,7 +2799,7 @@ static int bnxt_run_loopback(struct bnxt *bp) dev_kfree_skb(skb); return -EIO; } - bnxt_xmit_xdp(bp, txr, map, pkt_size, 0); + bnxt_xmit_bd(bp, txr, map, pkt_size); /* Sync BD data before updating doorbell */ wmb(); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 0184ef6f05a7..c6f6f2033880 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -15,12 +15,14 @@ #include <linux/bpf.h> #include <linux/bpf_trace.h> #include <linux/filter.h> +#include <net/page_pool.h> #include "bnxt_hsi.h" #include "bnxt.h" #include "bnxt_xdp.h" -void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, - dma_addr_t mapping, u32 len, u16 rx_prod) +struct bnxt_sw_tx_bd *bnxt_xmit_bd(struct bnxt *bp, + struct bnxt_tx_ring_info *txr, + dma_addr_t mapping, u32 len) { struct bnxt_sw_tx_bd *tx_buf; struct tx_bd *txbd; @@ -29,7 +31,6 @@ void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, prod = txr->tx_prod; tx_buf = &txr->tx_buf_ring[prod]; - tx_buf->rx_prod = rx_prod; txbd = &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)]; flags = (len << TX_BD_LEN_SHIFT) | (1 << TX_BD_FLAGS_BD_CNT_SHIFT) | @@ -40,30 +41,67 @@ void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, prod = NEXT_TX(prod); txr->tx_prod = prod; + return tx_buf; +} + +static void __bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, + dma_addr_t mapping, u32 len, u16 rx_prod) +{ + struct bnxt_sw_tx_bd *tx_buf; + + tx_buf = bnxt_xmit_bd(bp, txr, mapping, len); + tx_buf->rx_prod = rx_prod; + tx_buf->action = XDP_TX; +} + +static void __bnxt_xmit_xdp_redirect(struct bnxt *bp, + struct bnxt_tx_ring_info *txr, + dma_addr_t mapping, u32 len, + struct xdp_frame *xdpf) +{ + struct bnxt_sw_tx_bd *tx_buf; + + tx_buf = bnxt_xmit_bd(bp, txr, mapping, len); + tx_buf->action = XDP_REDIRECT; + tx_buf->xdpf = xdpf; + dma_unmap_addr_set(tx_buf, mapping, mapping); + dma_unmap_len_set(tx_buf, len, 0); } void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts) { struct bnxt_tx_ring_info *txr = bnapi->tx_ring; struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; + bool rx_doorbell_needed = false; struct bnxt_sw_tx_bd *tx_buf; u16 tx_cons = txr->tx_cons; u16 last_tx_cons = tx_cons; - u16 rx_prod; int i; for (i = 0; i < nr_pkts; i++) { - last_tx_cons = tx_cons; + tx_buf = &txr->tx_buf_ring[tx_cons]; + + if (tx_buf->action == XDP_REDIRECT) { + struct pci_dev *pdev = bp->pdev; + + dma_unmap_single(&pdev->dev, + dma_unmap_addr(tx_buf, mapping), + dma_unmap_len(tx_buf, len), + PCI_DMA_TODEVICE); + xdp_return_frame(tx_buf->xdpf); + tx_buf->action = 0; + tx_buf->xdpf = NULL; + } else if (tx_buf->action == XDP_TX) { + rx_doorbell_needed = true; + last_tx_cons = tx_cons; + } tx_cons = NEXT_TX(tx_cons); } txr->tx_cons = tx_cons; - if (bnxt_tx_avail(bp, txr) == bp->tx_ring_size) { - rx_prod = rxr->rx_prod; - } else { + if (rx_doorbell_needed) { tx_buf = &txr->tx_buf_ring[last_tx_cons]; - rx_prod = tx_buf->rx_prod; + bnxt_db_write(bp, &rxr->rx_db, tx_buf->rx_prod); } - bnxt_db_write(bp, &rxr->rx_db, rx_prod); } /* returns the following: @@ -88,19 +126,19 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, return false; pdev = bp->pdev; - txr = rxr->bnapi->tx_ring; rx_buf = &rxr->rx_buf_ring[cons]; offset = bp->rx_offset; + mapping = rx_buf->mapping - bp->rx_dma_offset; + dma_sync_single_for_cpu(&pdev->dev, mapping + offset, *len, bp->rx_dir); + + txr = rxr->bnapi->tx_ring; xdp.data_hard_start = *data_ptr - offset; xdp.data = *data_ptr; xdp_set_data_meta_invalid(&xdp); xdp.data_end = *data_ptr + *len; xdp.rxq = &rxr->xdp_rxq; orig_data = xdp.data; - mapping = rx_buf->mapping - bp->rx_dma_offset; - - dma_sync_single_for_cpu(&pdev->dev, mapping + offset, *len, bp->rx_dir); rcu_read_lock(); act = bpf_prog_run_xdp(xdp_prog, &xdp); @@ -132,10 +170,34 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, *event = BNXT_TX_EVENT; dma_sync_single_for_device(&pdev->dev, mapping + offset, *len, bp->rx_dir); - bnxt_xmit_xdp(bp, txr, mapping + offset, *len, - NEXT_RX(rxr->rx_prod)); + __bnxt_xmit_xdp(bp, txr, mapping + offset, *len, + NEXT_RX(rxr->rx_prod)); bnxt_reuse_rx_data(rxr, cons, page); return true; + case XDP_REDIRECT: + /* if we are calling this here then we know that the + * redirect is coming from a frame received by the + * bnxt_en driver. + */ + dma_unmap_page_attrs(&pdev->dev, mapping, + PAGE_SIZE, bp->rx_dir, + DMA_ATTR_WEAK_ORDERING); + + /* if we are unable to allocate a new buffer, abort and reuse */ + if (bnxt_alloc_rx_data(bp, rxr, rxr->rx_prod, GFP_ATOMIC)) { + trace_xdp_exception(bp->dev, xdp_prog, act); + bnxt_reuse_rx_data(rxr, cons, page); + return true; + } + + if (xdp_do_redirect(bp->dev, &xdp, xdp_prog)) { + trace_xdp_exception(bp->dev, xdp_prog, act); + page_pool_recycle_direct(rxr->page_pool, page); + return true; + } + + *event |= BNXT_REDIRECT_EVENT; + break; default: bpf_warn_invalid_xdp_action(act); /* Fall thru */ @@ -149,6 +211,56 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, return true; } +int bnxt_xdp_xmit(struct net_device *dev, int num_frames, + struct xdp_frame **frames, u32 flags) +{ + struct bnxt *bp = netdev_priv(dev); + struct bpf_prog *xdp_prog = READ_ONCE(bp->xdp_prog); + struct pci_dev *pdev = bp->pdev; + struct bnxt_tx_ring_info *txr; + dma_addr_t mapping; + int drops = 0; + int ring; + int i; + + if (!test_bit(BNXT_STATE_OPEN, &bp->state) || + !bp->tx_nr_rings_xdp || + !xdp_prog) + return -EINVAL; + + ring = smp_processor_id() % bp->tx_nr_rings_xdp; + txr = &bp->tx_ring[ring]; + + for (i = 0; i < num_frames; i++) { + struct xdp_frame *xdp = frames[i]; + + if (!txr || !bnxt_tx_avail(bp, txr) || + !(bp->bnapi[ring]->flags & BNXT_NAPI_FLAG_XDP)) { + xdp_return_frame_rx_napi(xdp); + drops++; + continue; + } + + mapping = dma_map_single(&pdev->dev, xdp->data, xdp->len, + DMA_TO_DEVICE); + + if (dma_mapping_error(&pdev->dev, mapping)) { + xdp_return_frame_rx_napi(xdp); + drops++; + continue; + } + __bnxt_xmit_xdp_redirect(bp, txr, mapping, xdp->len, xdp); + } + + if (flags & XDP_XMIT_FLUSH) { + /* Sync BD data before updating doorbell */ + wmb(); + bnxt_db_write(bp, &txr->tx_db, txr->tx_prod); + } + + return num_frames - drops; +} + /* Under rtnl_lock */ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h index 414b748038ca..0df40c3beb05 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h @@ -10,12 +10,15 @@ #ifndef BNXT_XDP_H #define BNXT_XDP_H -void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, - dma_addr_t mapping, u32 len, u16 rx_prod); +struct bnxt_sw_tx_bd *bnxt_xmit_bd(struct bnxt *bp, + struct bnxt_tx_ring_info *txr, + dma_addr_t mapping, u32 len); void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts); bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, struct page *page, u8 **data_ptr, unsigned int *len, u8 *event); int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp); +int bnxt_xdp_xmit(struct net_device *dev, int num_frames, + struct xdp_frame **frames, u32 flags); #endif diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index a27d32f69de9..5ca17e62dc3e 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -45,8 +45,6 @@ struct sifive_fu540_macb_mgmt { struct clk_hw hw; }; -static struct sifive_fu540_macb_mgmt *mgmt; - #define MACB_RX_BUFFER_SIZE 128 #define RX_BUFFER_MULTIPLE 64 /* bytes */ @@ -3628,6 +3626,8 @@ static int macb_init(struct platform_device *pdev) /* max number of receive buffers */ #define AT91ETHER_MAX_RX_DESCR 9 +static struct sifive_fu540_macb_mgmt *mgmt; + /* Initialize and start the Receiver and Transmit subsystems */ static int at91ether_start(struct net_device *dev) { diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 11d4e91ea754..99f49d059414 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -1855,7 +1855,7 @@ static void xgmac_pmt(void __iomem *ioaddr, unsigned long mode) static int xgmac_suspend(struct device *dev) { - struct net_device *ndev = platform_get_drvdata(to_platform_device(dev)); + struct net_device *ndev = dev_get_drvdata(dev); struct xgmac_priv *priv = netdev_priv(ndev); u32 value; @@ -1881,7 +1881,7 @@ static int xgmac_suspend(struct device *dev) static int xgmac_resume(struct device *dev) { - struct net_device *ndev = platform_get_drvdata(to_platform_device(dev)); + struct net_device *ndev = dev_get_drvdata(dev); struct xgmac_priv *priv = netdev_priv(ndev); void __iomem *ioaddr = priv->base; diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index eef500bd2ff7..24f16e3368cd 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -625,8 +625,10 @@ static int gve_alloc_qpls(struct gve_priv *priv) sizeof(unsigned long) * BITS_PER_BYTE; priv->qpl_cfg.qpl_id_map = kvzalloc(BITS_TO_LONGS(num_qpls) * sizeof(unsigned long), GFP_KERNEL); - if (!priv->qpl_cfg.qpl_id_map) + if (!priv->qpl_cfg.qpl_id_map) { + err = -ENOMEM; goto free_qpls; + } return 0; diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index 84e0ecce14c4..c1aeabd1c594 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -297,41 +297,41 @@ static bool gve_rx(struct gve_rx_ring *rx, struct gve_rx_desc *rx_desc, * it so that we can return it to the device. */ -#if PAGE_SIZE == 4096 - if (len <= priv->rx_copybreak) { - /* Just copy small packets */ - skb = gve_rx_copy(dev, napi, page_info, len); - goto have_skb; - } - if (unlikely(!gve_can_recycle_pages(dev))) { - skb = gve_rx_copy(dev, napi, page_info, len); - goto have_skb; - } - pagecount = page_count(page_info->page); - if (pagecount == 1) { - /* No part of this page is used by any SKBs; we attach - * the page fragment to a new SKB and pass it up the - * stack. - */ - skb = gve_rx_add_frags(dev, napi, page_info, len); - if (!skb) - return true; - /* Make sure the kernel stack can't release the page */ - get_page(page_info->page); - /* "flip" to other packet buffer on this page */ - gve_rx_flip_buff(page_info, &rx->data.data_ring[idx]); - } else if (pagecount >= 2) { - /* We have previously passed the other half of this - * page up the stack, but it has not yet been freed. - */ - skb = gve_rx_copy(dev, napi, page_info, len); + if (PAGE_SIZE == 4096) { + if (len <= priv->rx_copybreak) { + /* Just copy small packets */ + skb = gve_rx_copy(dev, napi, page_info, len); + goto have_skb; + } + if (unlikely(!gve_can_recycle_pages(dev))) { + skb = gve_rx_copy(dev, napi, page_info, len); + goto have_skb; + } + pagecount = page_count(page_info->page); + if (pagecount == 1) { + /* No part of this page is used by any SKBs; we attach + * the page fragment to a new SKB and pass it up the + * stack. + */ + skb = gve_rx_add_frags(dev, napi, page_info, len); + if (!skb) + return true; + /* Make sure the kernel stack can't release the page */ + get_page(page_info->page); + /* "flip" to other packet buffer on this page */ + gve_rx_flip_buff(page_info, &rx->data.data_ring[idx]); + } else if (pagecount >= 2) { + /* We have previously passed the other half of this + * page up the stack, but it has not yet been freed. + */ + skb = gve_rx_copy(dev, napi, page_info, len); + } else { + WARN(pagecount < 1, "Pagecount should never be < 1"); + return false; + } } else { - WARN(pagecount < 1, "Pagecount should never be < 1"); - return false; + skb = gve_rx_copy(dev, napi, page_info, len); } -#else - skb = gve_rx_copy(dev, napi, page_info, len); -#endif have_skb: /* We didn't manage to allocate an skb but we haven't had any diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index ab5a339cf261..310afa708831 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -951,8 +951,9 @@ static int hns3_set_l2l3l4(struct sk_buff *skb, u8 ol4_proto, static void hns3_set_txbd_baseinfo(u16 *bdtp_fe_sc_vld_ra_ri, int frag_end) { /* Config bd buffer end */ - hns3_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_FE_B, !!frag_end); - hns3_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_VLD_B, 1); + if (!!frag_end) + hns3_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_FE_B, 1U); + hns3_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_VLD_B, 1U); } static int hns3_fill_desc_vtags(struct sk_buff *skb, @@ -2575,7 +2576,7 @@ static bool hns3_parse_vlan_tag(struct hns3_enet_ring *ring, } } -static int hns3_alloc_skb(struct hns3_enet_ring *ring, int length, +static int hns3_alloc_skb(struct hns3_enet_ring *ring, unsigned int length, unsigned char *va) { #define HNS3_NEED_ADD_FRAG 1 @@ -2818,8 +2819,8 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring, struct sk_buff *skb = ring->skb; struct hns3_desc_cb *desc_cb; struct hns3_desc *desc; + unsigned int length; u32 bd_base_info; - int length; int ret; desc = &ring->desc[ring->next_to_clean]; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index a2b73d6aa2f3..848b866761df 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -610,7 +610,7 @@ static inline bool hns3_nic_resetting(struct net_device *netdev) #define hnae3_buf_size(_ring) ((_ring)->buf_size) #define hnae3_page_order(_ring) (get_order(hnae3_buf_size(_ring))) -#define hnae3_page_size(_ring) (PAGE_SIZE << hnae3_page_order(_ring)) +#define hnae3_page_size(_ring) (PAGE_SIZE << (u32)hnae3_page_order(_ring)) /* iterator for handling rings in ring group */ #define hns3_for_each_ring(pos, head) \ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c index 667c3be3bacc..22f6acd45d9a 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c @@ -188,12 +188,43 @@ static bool hclge_is_special_opcode(u16 opcode) return false; } +static int hclge_cmd_convert_err_code(u16 desc_ret) +{ + switch (desc_ret) { + case HCLGE_CMD_EXEC_SUCCESS: + return 0; + case HCLGE_CMD_NO_AUTH: + return -EPERM; + case HCLGE_CMD_NOT_SUPPORTED: + return -EOPNOTSUPP; + case HCLGE_CMD_QUEUE_FULL: + return -EXFULL; + case HCLGE_CMD_NEXT_ERR: + return -ENOSR; + case HCLGE_CMD_UNEXE_ERR: + return -ENOTBLK; + case HCLGE_CMD_PARA_ERR: + return -EINVAL; + case HCLGE_CMD_RESULT_ERR: + return -ERANGE; + case HCLGE_CMD_TIMEOUT: + return -ETIME; + case HCLGE_CMD_HILINK_ERR: + return -ENOLINK; + case HCLGE_CMD_QUEUE_ILLEGAL: + return -ENXIO; + case HCLGE_CMD_INVALID: + return -EBADR; + default: + return -EIO; + } +} + static int hclge_cmd_check_retval(struct hclge_hw *hw, struct hclge_desc *desc, int num, int ntc) { u16 opcode, desc_ret; int handle; - int retval; opcode = le16_to_cpu(desc[0].opcode); for (handle = 0; handle < num; handle++) { @@ -207,17 +238,9 @@ static int hclge_cmd_check_retval(struct hclge_hw *hw, struct hclge_desc *desc, else desc_ret = le16_to_cpu(desc[0].retval); - if (desc_ret == HCLGE_CMD_EXEC_SUCCESS) - retval = 0; - else if (desc_ret == HCLGE_CMD_NO_AUTH) - retval = -EPERM; - else if (desc_ret == HCLGE_CMD_NOT_SUPPORTED) - retval = -EOPNOTSUPP; - else - retval = -EIO; hw->cmq.last_status = desc_ret; - return retval; + return hclge_cmd_convert_err_code(desc_ret); } /** diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h index d23ab2b819d9..96840d8f3e24 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -41,6 +41,14 @@ enum hclge_cmd_return_status { HCLGE_CMD_NO_AUTH = 1, HCLGE_CMD_NOT_SUPPORTED = 2, HCLGE_CMD_QUEUE_FULL = 3, + HCLGE_CMD_NEXT_ERR = 4, + HCLGE_CMD_UNEXE_ERR = 5, + HCLGE_CMD_PARA_ERR = 6, + HCLGE_CMD_RESULT_ERR = 7, + HCLGE_CMD_TIMEOUT = 8, + HCLGE_CMD_HILINK_ERR = 9, + HCLGE_CMD_QUEUE_ILLEGAL = 10, + HCLGE_CMD_INVALID = 11, }; enum hclge_cmd_status { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 62c6263fc12c..3fde5471e1c0 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1360,8 +1360,9 @@ static int hclge_map_tqps_to_func(struct hclge_dev *hdev, u16 func_id, req = (struct hclge_tqp_map_cmd *)desc.data; req->tqp_id = cpu_to_le16(tqp_pid); req->tqp_vf = func_id; - req->tqp_flag = !is_pf << HCLGE_TQP_MAP_TYPE_B | - 1 << HCLGE_TQP_MAP_EN_B; + req->tqp_flag = 1U << HCLGE_TQP_MAP_EN_B; + if (!is_pf) + req->tqp_flag |= 1U << HCLGE_TQP_MAP_TYPE_B; req->tqp_vid = cpu_to_le16(tqp_vid); ret = hclge_cmd_send(&hdev->hw, &desc, 1); @@ -2320,7 +2321,8 @@ static int hclge_set_autoneg_en(struct hclge_dev *hdev, bool enable) hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_AN_MODE, false); req = (struct hclge_config_auto_neg_cmd *)desc.data; - hnae3_set_bit(flag, HCLGE_MAC_CFG_AN_EN_B, !!enable); + if (enable) + hnae3_set_bit(flag, HCLGE_MAC_CFG_AN_EN_B, 1U); req->cfg_an_cmd_flag = cpu_to_le32(flag); ret = hclge_cmd_send(&hdev->hw, &desc, 1); @@ -2663,6 +2665,7 @@ static int hclge_get_sfp_info(struct hclge_dev *hdev, struct hclge_mac *mac) mac->speed_ability = le32_to_cpu(resp->speed_ability); mac->autoneg = resp->autoneg; mac->support_autoneg = resp->autoneg_ability; + mac->speed_type = QUERY_ACTIVE_SPEED; if (!resp->active_fec) mac->fec_mode = 0; else @@ -4091,11 +4094,11 @@ int hclge_rss_init_hw(struct hclge_dev *hdev) struct hclge_vport *vport = hdev->vport; u8 *rss_indir = vport[0].rss_indirection_tbl; u16 rss_size = vport[0].alloc_rss_size; + u16 tc_offset[HCLGE_MAX_TC_NUM] = {0}; + u16 tc_size[HCLGE_MAX_TC_NUM] = {0}; u8 *key = vport[0].rss_hash_key; u8 hfunc = vport[0].rss_algo; - u16 tc_offset[HCLGE_MAX_TC_NUM]; u16 tc_valid[HCLGE_MAX_TC_NUM]; - u16 tc_size[HCLGE_MAX_TC_NUM]; u16 roundup_size; unsigned int i; int ret; @@ -5934,20 +5937,20 @@ static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) int ret; hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, false); - hnae3_set_bit(loop_en, HCLGE_MAC_TX_EN_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_RX_EN_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_PAD_TX_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_PAD_RX_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_1588_TX_B, 0); - hnae3_set_bit(loop_en, HCLGE_MAC_1588_RX_B, 0); - hnae3_set_bit(loop_en, HCLGE_MAC_APP_LP_B, 0); - hnae3_set_bit(loop_en, HCLGE_MAC_LINE_LP_B, 0); - hnae3_set_bit(loop_en, HCLGE_MAC_FCS_TX_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_STRIP_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B, enable); - hnae3_set_bit(loop_en, HCLGE_MAC_TX_UNDER_MIN_ERR_B, enable); + + if (enable) { + hnae3_set_bit(loop_en, HCLGE_MAC_TX_EN_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_RX_EN_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_PAD_TX_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_PAD_RX_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_FCS_TX_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_STRIP_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B, 1U); + hnae3_set_bit(loop_en, HCLGE_MAC_TX_UNDER_MIN_ERR_B, 1U); + } + req->txrx_pad_fcs_loop_en = cpu_to_le32(loop_en); ret = hclge_cmd_send(&hdev->hw, &desc, 1); @@ -6309,8 +6312,8 @@ static int hclge_update_desc_vfid(struct hclge_desc *desc, int vfid, bool clr) { #define HCLGE_VF_NUM_IN_FIRST_DESC 192 - int word_num; - int bit_num; + unsigned int word_num; + unsigned int bit_num; if (vfid > 255 || vfid < 0) return -EIO; @@ -7971,7 +7974,8 @@ static int hclge_send_reset_tqp_cmd(struct hclge_dev *hdev, u16 queue_id, req = (struct hclge_reset_tqp_queue_cmd *)desc.data; req->tqp_id = cpu_to_le16(queue_id & HCLGE_RING_ID_MASK); - hnae3_set_bit(req->reset_req, HCLGE_TQP_RESET_B, enable); + if (enable) + hnae3_set_bit(req->reset_req, HCLGE_TQP_RESET_B, 1U); ret = hclge_cmd_send(&hdev->hw, &desc, 1); if (ret) { @@ -8179,8 +8183,9 @@ static void hclge_get_pauseparam(struct hnae3_handle *handle, u32 *auto_neg, { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; + struct phy_device *phydev = hdev->hw.mac.phydev; - *auto_neg = hclge_get_autoneg(handle); + *auto_neg = phydev ? hclge_get_autoneg(handle) : 0; if (hdev->tm_info.fc_mode == HCLGE_FC_PFC) { *rx_en = 0; @@ -8211,11 +8216,13 @@ static int hclge_set_pauseparam(struct hnae3_handle *handle, u32 auto_neg, struct phy_device *phydev = hdev->hw.mac.phydev; u32 fc_autoneg; - fc_autoneg = hclge_get_autoneg(handle); - if (auto_neg != fc_autoneg) { - dev_info(&hdev->pdev->dev, - "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n"); - return -EOPNOTSUPP; + if (phydev) { + fc_autoneg = hclge_get_autoneg(handle); + if (auto_neg != fc_autoneg) { + dev_info(&hdev->pdev->dev, + "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n"); + return -EOPNOTSUPP; + } } if (hdev->tm_info.fc_mode == HCLGE_FC_PFC) { @@ -8226,16 +8233,13 @@ static int hclge_set_pauseparam(struct hnae3_handle *handle, u32 auto_neg, hclge_set_flowctrl_adv(hdev, rx_en, tx_en); - if (!fc_autoneg) + if (!auto_neg) return hclge_cfg_pauseparam(hdev, rx_en, tx_en); if (phydev) return phy_start_aneg(phydev); - if (hdev->pdev->revision == 0x20) - return -EOPNOTSUPP; - - return hclge_restart_autoneg(handle); + return -EOPNOTSUPP; } static void hclge_get_ksettings_an_result(struct hnae3_handle *handle, @@ -9035,12 +9039,12 @@ static int hclge_set_channels(struct hnae3_handle *handle, u32 new_tqps_num, { struct hclge_vport *vport = hclge_get_vport(handle); struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + u16 tc_offset[HCLGE_MAX_TC_NUM] = {0}; struct hclge_dev *hdev = vport->back; + u16 tc_size[HCLGE_MAX_TC_NUM] = {0}; int cur_rss_size = kinfo->rss_size; int cur_tqps = kinfo->num_tqps; - u16 tc_offset[HCLGE_MAX_TC_NUM]; u16 tc_valid[HCLGE_MAX_TC_NUM]; - u16 tc_size[HCLGE_MAX_TC_NUM]; u16 roundup_size; u32 *rss_indir; unsigned int i; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c index 9adeba931902..a38ac7cfe16b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c @@ -29,6 +29,10 @@ static int hclge_gen_resp_to_vf(struct hclge_vport *vport, "PF fail to gen resp to VF len %d exceeds max len %d\n", resp_data_len, HCLGE_MBX_MAX_RESP_DATA_SIZE); + /* If resp_data_len is too long, set the value to max length + * and return the msg to VF + */ + resp_data_len = HCLGE_MBX_MAX_RESP_DATA_SIZE; } hclge_cmd_setup_basic_desc(&desc, HCLGEVF_OPC_MBX_PF_TO_VF, false); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c index 31db6d6a6d84..652b796044e3 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c @@ -177,6 +177,38 @@ void hclgevf_cmd_setup_basic_desc(struct hclgevf_desc *desc, desc->flag &= cpu_to_le16(~HCLGEVF_CMD_FLAG_WR); } +static int hclgevf_cmd_convert_err_code(u16 desc_ret) +{ + switch (desc_ret) { + case HCLGEVF_CMD_EXEC_SUCCESS: + return 0; + case HCLGEVF_CMD_NO_AUTH: + return -EPERM; + case HCLGEVF_CMD_NOT_SUPPORTED: + return -EOPNOTSUPP; + case HCLGEVF_CMD_QUEUE_FULL: + return -EXFULL; + case HCLGEVF_CMD_NEXT_ERR: + return -ENOSR; + case HCLGEVF_CMD_UNEXE_ERR: + return -ENOTBLK; + case HCLGEVF_CMD_PARA_ERR: + return -EINVAL; + case HCLGEVF_CMD_RESULT_ERR: + return -ERANGE; + case HCLGEVF_CMD_TIMEOUT: + return -ETIME; + case HCLGEVF_CMD_HILINK_ERR: + return -ENOLINK; + case HCLGEVF_CMD_QUEUE_ILLEGAL: + return -ENXIO; + case HCLGEVF_CMD_INVALID: + return -EBADR; + default: + return -EIO; + } +} + /* hclgevf_cmd_send - send command to command queue * @hw: pointer to the hw struct * @desc: prefilled descriptor for describing the command @@ -259,11 +291,7 @@ int hclgevf_cmd_send(struct hclgevf_hw *hw, struct hclgevf_desc *desc, int num) else retval = le16_to_cpu(desc[0].retval); - if ((enum hclgevf_cmd_return_status)retval == - HCLGEVF_CMD_EXEC_SUCCESS) - status = 0; - else - status = -EIO; + status = hclgevf_cmd_convert_err_code(retval); hw->cmq.last_status = (enum hclgevf_cmd_status)retval; ntc++; handle++; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h index 47030b42341f..127a434a56f3 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h @@ -46,9 +46,17 @@ struct hclgevf_cmq_ring { enum hclgevf_cmd_return_status { HCLGEVF_CMD_EXEC_SUCCESS = 0, - HCLGEVF_CMD_NO_AUTH = 1, - HCLGEVF_CMD_NOT_EXEC = 2, - HCLGEVF_CMD_QUEUE_FULL = 3, + HCLGEVF_CMD_NO_AUTH = 1, + HCLGEVF_CMD_NOT_SUPPORTED = 2, + HCLGEVF_CMD_QUEUE_FULL = 3, + HCLGEVF_CMD_NEXT_ERR = 4, + HCLGEVF_CMD_UNEXE_ERR = 5, + HCLGEVF_CMD_PARA_ERR = 6, + HCLGEVF_CMD_RESULT_ERR = 7, + HCLGEVF_CMD_TIMEOUT = 8, + HCLGEVF_CMD_HILINK_ERR = 9, + HCLGEVF_CMD_QUEUE_ILLEGAL = 10, + HCLGEVF_CMD_INVALID = 11, }; enum hclgevf_cmd_status { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 42006cccecbe..a13a0e101c3b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -994,6 +994,8 @@ static int hclgevf_bind_ring_to_vector(struct hnae3_handle *handle, bool en, u8 type; req = (struct hclge_mbx_vf_to_pf_cmd *)desc.data; + type = en ? HCLGE_MBX_MAP_RING_TO_VECTOR : + HCLGE_MBX_UNMAP_RING_TO_VECTOR; for (node = ring_chain; node; node = node->next) { int idx_offset = HCLGE_MBX_RING_MAP_BASIC_MSG_NUM + @@ -1003,9 +1005,6 @@ static int hclgevf_bind_ring_to_vector(struct hnae3_handle *handle, bool en, hclgevf_cmd_setup_basic_desc(&desc, HCLGEVF_OPC_MBX_VF_TO_PF, false); - type = en ? - HCLGE_MBX_MAP_RING_TO_VECTOR : - HCLGE_MBX_UNMAP_RING_TO_VECTOR; req->msg[0] = type; req->msg[1] = vector_id; } @@ -2589,6 +2588,12 @@ static int hclgevf_reset_hdev(struct hclgevf_dev *hdev) return ret; } + if (pdev->revision >= 0x21) { + ret = hclgevf_set_promisc_mode(hdev, true); + if (ret) + return ret; + } + dev_info(&hdev->pdev->dev, "Reset done\n"); return 0; @@ -2668,9 +2673,11 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev) * firmware makes sure broadcast packets can be accepted. * For revision 0x21, default to enable broadcast promisc mode. */ - ret = hclgevf_set_promisc_mode(hdev, true); - if (ret) - goto err_config; + if (pdev->revision >= 0x21) { + ret = hclgevf_set_promisc_mode(hdev, true); + if (ret) + goto err_config; + } /* Initialize RSS for this VF */ ret = hclgevf_rss_init_hw(hdev); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c index 30f2e9352cf3..f60b80bd605e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c @@ -102,7 +102,8 @@ int hclgevf_send_mbx_msg(struct hclgevf_dev *hdev, u16 code, u16 subcode, ~HCLGE_MBX_NEED_RESP_BIT; req->msg[0] = code; req->msg[1] = subcode; - memcpy(&req->msg[2], msg_data, msg_len); + if (msg_data) + memcpy(&req->msg[2], msg_data, msg_len); /* synchronous send */ if (need_resp) { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index 73a20f01ad4c..60ec48fe4144 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -117,11 +117,19 @@ static void hinic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { struct hinic_dev *nic_dev = netdev_priv(netdev); + u8 mgmt_ver[HINIC_MGMT_VERSION_MAX_LEN] = {0}; struct hinic_hwdev *hwdev = nic_dev->hwdev; struct hinic_hwif *hwif = hwdev->hwif; + int err; strlcpy(info->driver, HINIC_DRV_NAME, sizeof(info->driver)); strlcpy(info->bus_info, pci_name(hwif->pdev), sizeof(info->bus_info)); + + err = hinic_get_mgmt_version(nic_dev, mgmt_ver); + if (err) + return; + + snprintf(info->fw_version, sizeof(info->fw_version), "%s", mgmt_ver); } static void hinic_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 984c98f33258..b069045de416 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -77,6 +77,8 @@ enum hinic_port_cmd { HINIC_PORT_CMD_FWCTXT_INIT = 69, + HINIC_PORT_CMD_GET_MGMT_VERSION = 88, + HINIC_PORT_CMD_SET_FUNC_STATE = 93, HINIC_PORT_CMD_GET_GLOBAL_QPN = 102, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 1bbeb91be808..1e389a004e50 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -1038,3 +1038,33 @@ out: return err; } + +int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_version_info up_ver = {0}; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size; + int err; + + if (!hwdev) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_MGMT_VERSION, + &up_ver, sizeof(up_ver), &up_ver, + &out_size); + if (err || !out_size || up_ver.status) { + dev_err(&pdev->dev, + "Failed to get mgmt version, err: %d, status: 0x%x, out size: 0x%x\n", + err, up_ver.status, out_size); + return -EINVAL; + } + + snprintf(mgmt_ver, HINIC_MGMT_VERSION_MAX_LEN, "%s", up_ver.ver); + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 1bc47c7a5c00..44772fd47fc1 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -16,6 +16,18 @@ #define HINIC_RSS_KEY_SIZE 40 #define HINIC_RSS_INDIR_SIZE 256 #define HINIC_PORT_STATS_VERSION 0 +#define HINIC_FW_VERSION_NAME 16 +#define HINIC_COMPILE_TIME_LEN 20 +#define HINIC_MGMT_VERSION_MAX_LEN 32 + +struct hinic_version_info { + u8 status; + u8 version; + u8 rsvd[6]; + + u8 ver[HINIC_FW_VERSION_NAME]; + u8 time[HINIC_COMPILE_TIME_LEN]; +}; enum hinic_rx_mode { HINIC_RX_MODE_UC = BIT(0), @@ -571,4 +583,6 @@ int hinic_get_vport_stats(struct hinic_dev *nic_dev, int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en); +int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver); + #endif diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c index b195fb5d61f4..35478cba2aa5 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c @@ -548,6 +548,8 @@ void mvpp2_cls_c2_read(struct mvpp2 *priv, int index, static int mvpp2_cls_ethtool_flow_to_type(int flow_type) { switch (flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) { + case ETHER_FLOW: + return MVPP22_FLOW_ETHERNET; case TCP_V4_FLOW: return MVPP22_FLOW_TCP4; case TCP_V6_FLOW: @@ -1373,6 +1375,10 @@ int mvpp2_ethtool_cls_rule_ins(struct mvpp2_port *port, efs->rule.flow = ethtool_rule->rule; efs->rule.flow_type = mvpp2_cls_ethtool_flow_to_type(info->fs.flow_type); + if (efs->rule.flow_type < 0) { + ret = efs->rule.flow_type; + goto clean_rule; + } ret = mvpp2_cls_rfs_parse_rule(&efs->rule); if (ret) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index 7845aa5bf6be..37fef8cd25e3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -97,26 +97,60 @@ config MLX5_CORE_IPOIB ---help--- MLX5 IPoIB offloads & acceleration support. +config MLX5_FPGA_IPSEC + bool "Mellanox Technologies IPsec Innova support" + depends on MLX5_CORE + depends on MLX5_FPGA + default n + help + Build IPsec support for the Innova family of network cards by Mellanox + Technologies. Innova network cards are comprised of a ConnectX chip + and an FPGA chip on one board. If you select this option, the + mlx5_core driver will include the Innova FPGA core and allow building + sandbox-specific client drivers. + config MLX5_EN_IPSEC bool "IPSec XFRM cryptography-offload accelaration" - depends on MLX5_ACCEL depends on MLX5_CORE_EN depends on XFRM_OFFLOAD depends on INET_ESP_OFFLOAD || INET6_ESP_OFFLOAD + depends on MLX5_FPGA_IPSEC default n - ---help--- + help Build support for IPsec cryptography-offload accelaration in the NIC. Note: Support for hardware with this capability needs to be selected for this option to become available. -config MLX5_EN_TLS - bool "TLS cryptography-offload accelaration" +config MLX5_FPGA_TLS + bool "Mellanox Technologies TLS Innova support" + depends on TLS_DEVICE + depends on TLS=y || MLX5_CORE=m + depends on MLX5_FPGA + default n + help + Build TLS support for the Innova family of network cards by Mellanox + Technologies. Innova network cards are comprised of a ConnectX chip + and an FPGA chip on one board. If you select this option, the + mlx5_core driver will include the Innova FPGA core and allow building + sandbox-specific client drivers. + +config MLX5_TLS + bool "Mellanox Technologies TLS Connect-X support" depends on MLX5_CORE_EN depends on TLS_DEVICE depends on TLS=y || MLX5_CORE=m - depends on MLX5_ACCEL + select MLX5_ACCEL default n - ---help--- - Build support for TLS cryptography-offload accelaration in the NIC. - Note: Support for hardware with this capability needs to be selected - for this option to become available. + help + Build TLS support for the Connect-X family of network cards by Mellanox + Technologies. + +config MLX5_EN_TLS + bool "TLS cryptography-offload accelaration" + depends on MLX5_CORE_EN + depends on MLX5_FPGA_TLS || MLX5_TLS + default y + help + Build support for TLS cryptography-offload accelaration in the NIC. + Note: Support for hardware with this capability needs to be selected + for this option to become available. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 8456b19d79cd..57d2cc666fe3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -53,12 +53,14 @@ mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib # # Accelerations & FPGA # -mlx5_core-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o accel/tls.o +mlx5_core-$(CONFIG_MLX5_FPGA_IPSEC) += fpga/ipsec.o +mlx5_core-$(CONFIG_MLX5_FPGA_TLS) += fpga/tls.o +mlx5_core-$(CONFIG_MLX5_ACCEL) += lib/crypto.o accel/tls.o accel/ipsec.o -mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ - fpga/ipsec.o fpga/tls.o +mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ en_accel/ipsec_stats.o -mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o +mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/tls.o en_accel/tls_rxtx.o en_accel/tls_stats.o \ + en_accel/ktls.o en_accel/ktls_tx.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c index 9f1b1939716a..eddc34e4a762 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c @@ -31,6 +31,8 @@ * */ +#ifdef CONFIG_MLX5_FPGA_IPSEC + #include <linux/mlx5/device.h> #include "accel/ipsec.h" @@ -74,6 +76,11 @@ int mlx5_accel_ipsec_init(struct mlx5_core_dev *mdev) return mlx5_fpga_ipsec_init(mdev); } +void mlx5_accel_ipsec_build_fs_cmds(void) +{ + mlx5_fpga_ipsec_build_fs_cmds(); +} + void mlx5_accel_ipsec_cleanup(struct mlx5_core_dev *mdev) { mlx5_fpga_ipsec_cleanup(mdev); @@ -107,3 +114,5 @@ int mlx5_accel_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm, return mlx5_fpga_esp_modify_xfrm(xfrm, attrs); } EXPORT_SYMBOL_GPL(mlx5_accel_esp_modify_xfrm); + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h index 024dbd22a89b..530e428d46ab 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h @@ -37,7 +37,7 @@ #include <linux/mlx5/driver.h> #include <linux/mlx5/accel.h> -#ifdef CONFIG_MLX5_ACCEL +#ifdef CONFIG_MLX5_FPGA_IPSEC #define MLX5_IPSEC_DEV(mdev) (mlx5_accel_ipsec_device_caps(mdev) & \ MLX5_ACCEL_IPSEC_CAP_DEVICE) @@ -54,6 +54,7 @@ void *mlx5_accel_esp_create_hw_context(struct mlx5_core_dev *mdev, void mlx5_accel_esp_free_hw_context(void *context); int mlx5_accel_ipsec_init(struct mlx5_core_dev *mdev); +void mlx5_accel_ipsec_build_fs_cmds(void); void mlx5_accel_ipsec_cleanup(struct mlx5_core_dev *mdev); #else @@ -79,6 +80,10 @@ static inline int mlx5_accel_ipsec_init(struct mlx5_core_dev *mdev) return 0; } +static inline void mlx5_accel_ipsec_build_fs_cmds(void) +{ +} + static inline void mlx5_accel_ipsec_cleanup(struct mlx5_core_dev *mdev) { } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/accel/tls.c b/drivers/net/ethernet/mellanox/mlx5/core/accel/tls.c index da7bd26368f9..cab708af3422 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/accel/tls.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/accel/tls.c @@ -35,6 +35,9 @@ #include "accel/tls.h" #include "mlx5_core.h" +#include "lib/mlx5.h" + +#ifdef CONFIG_MLX5_FPGA_TLS #include "fpga/tls.h" int mlx5_accel_tls_add_flow(struct mlx5_core_dev *mdev, void *flow, @@ -61,7 +64,8 @@ int mlx5_accel_tls_resync_rx(struct mlx5_core_dev *mdev, u32 handle, u32 seq, bool mlx5_accel_is_tls_device(struct mlx5_core_dev *mdev) { - return mlx5_fpga_is_tls_device(mdev); + return mlx5_fpga_is_tls_device(mdev) || + mlx5_accel_is_ktls_device(mdev); } u32 mlx5_accel_tls_device_caps(struct mlx5_core_dev *mdev) @@ -78,3 +82,42 @@ void mlx5_accel_tls_cleanup(struct mlx5_core_dev *mdev) { mlx5_fpga_tls_cleanup(mdev); } +#endif + +#ifdef CONFIG_MLX5_TLS +int mlx5_ktls_create_key(struct mlx5_core_dev *mdev, + struct tls_crypto_info *crypto_info, + u32 *p_key_id) +{ + u32 sz_bytes; + void *key; + + switch (crypto_info->cipher_type) { + case TLS_CIPHER_AES_GCM_128: { + struct tls12_crypto_info_aes_gcm_128 *info = + (struct tls12_crypto_info_aes_gcm_128 *)crypto_info; + + key = info->key; + sz_bytes = sizeof(info->key); + break; + } + case TLS_CIPHER_AES_GCM_256: { + struct tls12_crypto_info_aes_gcm_256 *info = + (struct tls12_crypto_info_aes_gcm_256 *)crypto_info; + + key = info->key; + sz_bytes = sizeof(info->key); + break; + } + default: + return -EINVAL; + } + + return mlx5_create_encryption_key(mdev, key, sz_bytes, p_key_id); +} + +void mlx5_ktls_destroy_key(struct mlx5_core_dev *mdev, u32 key_id) +{ + mlx5_destroy_encryption_key(mdev, key_id); +} +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/accel/tls.h b/drivers/net/ethernet/mellanox/mlx5/core/accel/tls.h index def4093ebfae..879321b21616 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/accel/tls.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/accel/tls.h @@ -37,8 +37,51 @@ #include <linux/mlx5/driver.h> #include <linux/tls.h> -#ifdef CONFIG_MLX5_ACCEL +#ifdef CONFIG_MLX5_TLS +int mlx5_ktls_create_key(struct mlx5_core_dev *mdev, + struct tls_crypto_info *crypto_info, + u32 *p_key_id); +void mlx5_ktls_destroy_key(struct mlx5_core_dev *mdev, u32 key_id); +static inline bool mlx5_accel_is_ktls_device(struct mlx5_core_dev *mdev) +{ + if (!MLX5_CAP_GEN(mdev, tls)) + return false; + + if (!MLX5_CAP_GEN(mdev, log_max_dek)) + return false; + + return MLX5_CAP_TLS(mdev, tls_1_2_aes_gcm_128); +} + +static inline bool mlx5e_ktls_type_check(struct mlx5_core_dev *mdev, + struct tls_crypto_info *crypto_info) +{ + switch (crypto_info->cipher_type) { + case TLS_CIPHER_AES_GCM_128: + if (crypto_info->version == TLS_1_2_VERSION) + return MLX5_CAP_TLS(mdev, tls_1_2_aes_gcm_128); + break; + } + + return false; +} +#else +static inline int +mlx5_ktls_create_key(struct mlx5_core_dev *mdev, + struct tls_crypto_info *crypto_info, + u32 *p_key_id) { return -ENOTSUPP; } +static inline void +mlx5_ktls_destroy_key(struct mlx5_core_dev *mdev, u32 key_id) {} + +static inline bool +mlx5_accel_is_ktls_device(struct mlx5_core_dev *mdev) { return false; } +static inline bool +mlx5e_ktls_type_check(struct mlx5_core_dev *mdev, + struct tls_crypto_info *crypto_info) { return false; } +#endif + +#ifdef CONFIG_MLX5_FPGA_TLS enum { MLX5_ACCEL_TLS_TX = BIT(0), MLX5_ACCEL_TLS_RX = BIT(1), @@ -84,11 +127,13 @@ static inline void mlx5_accel_tls_del_flow(struct mlx5_core_dev *mdev, u32 swid, bool direction_sx) { } static inline int mlx5_accel_tls_resync_rx(struct mlx5_core_dev *mdev, u32 handle, u32 seq, u64 rcd_sn) { return 0; } -static inline bool mlx5_accel_is_tls_device(struct mlx5_core_dev *mdev) { return false; } +static inline bool mlx5_accel_is_tls_device(struct mlx5_core_dev *mdev) +{ + return mlx5_accel_is_ktls_device(mdev); +} static inline u32 mlx5_accel_tls_device_caps(struct mlx5_core_dev *mdev) { return 0; } static inline int mlx5_accel_tls_init(struct mlx5_core_dev *mdev) { return 0; } static inline void mlx5_accel_tls_cleanup(struct mlx5_core_dev *mdev) { } - #endif #endif /* __MLX5_ACCEL_TLS_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c index 713a17ee3751..818edc63e428 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c @@ -58,7 +58,7 @@ void mlx5_cq_tasklet_cb(unsigned long data) list_for_each_entry_safe(mcq, temp, &ctx->process_list, tasklet_ctx.list) { list_del_init(&mcq->tasklet_ctx.list); - mcq->tasklet_ctx.comp(mcq); + mcq->tasklet_ctx.comp(mcq, NULL); mlx5_cq_put(mcq); if (time_after(jiffies, end)) break; @@ -68,7 +68,8 @@ void mlx5_cq_tasklet_cb(unsigned long data) tasklet_schedule(&ctx->task); } -static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq) +static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq, + struct mlx5_eqe *eqe) { unsigned long flags; struct mlx5_eq_tasklet *tasklet_ctx = cq->tasklet_ctx.priv; @@ -87,11 +88,10 @@ static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq) } int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, - u32 *in, int inlen) + u32 *in, int inlen, u32 *out, int outlen) { int eqn = MLX5_GET(cqc, MLX5_ADDR_OF(create_cq_in, in, cq_context), c_eqn); u32 dout[MLX5_ST_SZ_DW(destroy_cq_out)]; - u32 out[MLX5_ST_SZ_DW(create_cq_out)]; u32 din[MLX5_ST_SZ_DW(destroy_cq_in)]; struct mlx5_eq_comp *eq; int err; @@ -100,9 +100,9 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, if (IS_ERR(eq)) return PTR_ERR(eq); - memset(out, 0, sizeof(out)); + memset(out, 0, outlen); MLX5_SET(create_cq_in, in, opcode, MLX5_CMD_OP_CREATE_CQ); - err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); + err = mlx5_cmd_exec(dev, in, inlen, out, outlen); if (err) return err; @@ -158,13 +158,8 @@ int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq) u32 in[MLX5_ST_SZ_DW(destroy_cq_in)] = {0}; int err; - err = mlx5_eq_del_cq(mlx5_get_async_eq(dev), cq); - if (err) - return err; - - err = mlx5_eq_del_cq(&cq->eq->core, cq); - if (err) - return err; + mlx5_eq_del_cq(mlx5_get_async_eq(dev), cq); + mlx5_eq_del_cq(&cq->eq->core, cq); MLX5_SET(destroy_cq_in, in, opcode, MLX5_CMD_OP_DESTROY_CQ); MLX5_SET(destroy_cq_in, in, cqn, cq->cqn); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c index f6b1da99e6c2..5bb6a26ea267 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c @@ -311,13 +311,20 @@ static u32 mlx5_gen_pci_id(struct mlx5_core_dev *dev) /* Must be called with intf_mutex held */ struct mlx5_core_dev *mlx5_get_next_phys_dev(struct mlx5_core_dev *dev) { - u32 pci_id = mlx5_gen_pci_id(dev); struct mlx5_core_dev *res = NULL; struct mlx5_core_dev *tmp_dev; struct mlx5_priv *priv; + u32 pci_id; + if (!mlx5_core_is_pf(dev)) + return NULL; + + pci_id = mlx5_gen_pci_id(dev); list_for_each_entry(priv, &mlx5_dev_list, dev_list) { tmp_dev = container_of(priv, struct mlx5_core_dev, priv); + if (!mlx5_core_is_pf(tmp_dev)) + continue; + if ((dev != tmp_dev) && (mlx5_gen_pci_id(tmp_dev) == pci_id)) { res = tmp_dev; break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index 1533c657220b..a400f4430c28 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -25,6 +25,65 @@ static int mlx5_devlink_flash_update(struct devlink *devlink, return mlx5_firmware_flash(dev, fw, extack); } +static u8 mlx5_fw_ver_major(u32 version) +{ + return (version >> 24) & 0xff; +} + +static u8 mlx5_fw_ver_minor(u32 version) +{ + return (version >> 16) & 0xff; +} + +static u16 mlx5_fw_ver_subminor(u32 version) +{ + return version & 0xffff; +} + +#define DEVLINK_FW_STRING_LEN 32 + +static int +mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + char version_str[DEVLINK_FW_STRING_LEN]; + u32 running_fw, stored_fw; + int err; + + err = devlink_info_driver_name_put(req, DRIVER_NAME); + if (err) + return err; + + err = devlink_info_version_fixed_put(req, "fw.psid", dev->board_id); + if (err) + return err; + + err = mlx5_fw_version_query(dev, &running_fw, &stored_fw); + if (err) + return err; + + snprintf(version_str, sizeof(version_str), "%d.%d.%04d", + mlx5_fw_ver_major(running_fw), mlx5_fw_ver_minor(running_fw), + mlx5_fw_ver_subminor(running_fw)); + err = devlink_info_version_running_put(req, "fw.version", version_str); + if (err) + return err; + + /* no pending version, return running (stored) version */ + if (stored_fw == 0) + stored_fw = running_fw; + + snprintf(version_str, sizeof(version_str), "%d.%d.%04d", + mlx5_fw_ver_major(stored_fw), mlx5_fw_ver_minor(stored_fw), + mlx5_fw_ver_subminor(stored_fw)); + err = devlink_info_version_stored_put(req, "fw.version", version_str); + if (err) + return err; + + return 0; +} + static const struct devlink_ops mlx5_devlink_ops = { #ifdef CONFIG_MLX5_ESWITCH .eswitch_mode_set = mlx5_devlink_eswitch_mode_set, @@ -35,6 +94,7 @@ static const struct devlink_ops mlx5_devlink_ops = { .eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get, #endif .flash_update = mlx5_devlink_flash_update, + .info_get = mlx5_devlink_info_get, }; struct devlink *mlx5_devlink_alloc(void) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index f0d77eb66acf..263558875f20 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -209,7 +209,10 @@ struct mlx5e_umr_wqe { struct mlx5_wqe_ctrl_seg ctrl; struct mlx5_wqe_umr_ctrl_seg uctrl; struct mlx5_mkey_seg mkc; - struct mlx5_mtt inline_mtts[0]; + union { + struct mlx5_mtt inline_mtts[0]; + u8 tls_static_params_ctx[0]; + }; }; extern const char mlx5e_self_tests[][ETH_GSTRING_LEN]; @@ -333,6 +336,9 @@ struct mlx5e_tx_wqe_info { u32 num_bytes; u8 num_wqebbs; u8 num_dma; +#ifdef CONFIG_MLX5_EN_TLS + skb_frag_t *resync_dump_frag; +#endif }; enum mlx5e_dma_map_type { @@ -390,6 +396,7 @@ struct mlx5e_txqsq { void __iomem *uar_map; struct netdev_queue *txq; u32 sqn; + u16 stop_room; u8 min_inline_mode; struct device *pdev; __be32 mkey_be; @@ -549,12 +556,6 @@ struct mlx5e_icosq { struct mlx5e_channel *channel; } ____cacheline_aligned_in_smp; -static inline bool -mlx5e_wqc_has_room_for(struct mlx5_wq_cyc *wq, u16 cc, u16 pc, u16 n) -{ - return (mlx5_wq_cyc_ctr2ix(wq, cc - pc) >= n) || (cc == pc); -} - struct mlx5e_wqe_frag_info { struct mlx5e_dma_info *di; u32 offset; @@ -880,7 +881,7 @@ netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, struct mlx5e_tx_wqe *wqe, u16 pi, bool xmit_more); void mlx5e_trigger_irq(struct mlx5e_icosq *sq); -void mlx5e_completion_event(struct mlx5_core_cq *mcq); +void mlx5e_completion_event(struct mlx5_core_cq *mcq, struct mlx5_eqe *eqe); void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event); int mlx5e_napi_poll(struct napi_struct *napi, int budget); bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget); @@ -1023,102 +1024,6 @@ static inline bool mlx5_tx_swp_supported(struct mlx5_core_dev *mdev) MLX5_CAP_ETH(mdev, swp_csum) && MLX5_CAP_ETH(mdev, swp_lso); } -struct mlx5e_swp_spec { - __be16 l3_proto; - u8 l4_proto; - u8 is_tun; - __be16 tun_l3_proto; - u8 tun_l4_proto; -}; - -static inline void -mlx5e_set_eseg_swp(struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg, - struct mlx5e_swp_spec *swp_spec) -{ - /* SWP offsets are in 2-bytes words */ - eseg->swp_outer_l3_offset = skb_network_offset(skb) / 2; - if (swp_spec->l3_proto == htons(ETH_P_IPV6)) - eseg->swp_flags |= MLX5_ETH_WQE_SWP_OUTER_L3_IPV6; - if (swp_spec->l4_proto) { - eseg->swp_outer_l4_offset = skb_transport_offset(skb) / 2; - if (swp_spec->l4_proto == IPPROTO_UDP) - eseg->swp_flags |= MLX5_ETH_WQE_SWP_OUTER_L4_UDP; - } - - if (swp_spec->is_tun) { - eseg->swp_inner_l3_offset = skb_inner_network_offset(skb) / 2; - if (swp_spec->tun_l3_proto == htons(ETH_P_IPV6)) - eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L3_IPV6; - } else { /* typically for ipsec when xfrm mode != XFRM_MODE_TUNNEL */ - eseg->swp_inner_l3_offset = skb_network_offset(skb) / 2; - if (swp_spec->l3_proto == htons(ETH_P_IPV6)) - eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L3_IPV6; - } - switch (swp_spec->tun_l4_proto) { - case IPPROTO_UDP: - eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L4_UDP; - /* fall through */ - case IPPROTO_TCP: - eseg->swp_inner_l4_offset = skb_inner_transport_offset(skb) / 2; - break; - } -} - -static inline void mlx5e_sq_fetch_wqe(struct mlx5e_txqsq *sq, - struct mlx5e_tx_wqe **wqe, - u16 *pi) -{ - struct mlx5_wq_cyc *wq = &sq->wq; - - *pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); - *wqe = mlx5_wq_cyc_get_wqe(wq, *pi); - memset(*wqe, 0, sizeof(**wqe)); -} - -static inline -struct mlx5e_tx_wqe *mlx5e_post_nop(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc) -{ - u16 pi = mlx5_wq_cyc_ctr2ix(wq, *pc); - struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); - struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; - - memset(cseg, 0, sizeof(*cseg)); - - cseg->opmod_idx_opcode = cpu_to_be32((*pc << 8) | MLX5_OPCODE_NOP); - cseg->qpn_ds = cpu_to_be32((sqn << 8) | 0x01); - - (*pc)++; - - return wqe; -} - -static inline -void mlx5e_notify_hw(struct mlx5_wq_cyc *wq, u16 pc, - void __iomem *uar_map, - struct mlx5_wqe_ctrl_seg *ctrl) -{ - ctrl->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; - /* ensure wqe is visible to device before updating doorbell record */ - dma_wmb(); - - *wq->db = cpu_to_be32(pc); - - /* ensure doorbell record is visible to device before ringing the - * doorbell - */ - wmb(); - - mlx5_write64((__be32 *)ctrl, uar_map); -} - -static inline void mlx5e_cq_arm(struct mlx5e_cq *cq) -{ - struct mlx5_core_cq *mcq; - - mcq = &cq->mcq; - mlx5_cq_arm(mcq, MLX5_CQ_DB_REQ_NOT, mcq->uar->map, cq->wq.cc); -} - extern const struct ethtool_ops mlx5e_ethtool_ops; #ifdef CONFIG_MLX5_CORE_EN_DCB extern const struct dcbnl_rtnl_ops mlx5e_dcbnl_ops; @@ -1154,8 +1059,7 @@ int mlx5e_create_direct_tirs(struct mlx5e_priv *priv, struct mlx5e_tir *tirs); void mlx5e_destroy_direct_tirs(struct mlx5e_priv *priv, struct mlx5e_tir *tirs); void mlx5e_destroy_rqt(struct mlx5e_priv *priv, struct mlx5e_rqt *rqt); -int mlx5e_create_tis(struct mlx5_core_dev *mdev, int tc, - u32 underlay_qpn, u32 *tisn); +int mlx5e_create_tis(struct mlx5_core_dev *mdev, void *in, u32 *tisn); void mlx5e_destroy_tis(struct mlx5_core_dev *mdev, u32 tisn); int mlx5e_create_tises(struct mlx5e_priv *priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h new file mode 100644 index 000000000000..ddfe19adb3d9 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2019 Mellanox Technologies. */ + +#ifndef __MLX5_EN_TXRX_H___ +#define __MLX5_EN_TXRX_H___ + +#include "en.h" + +#define MLX5E_SQ_NOPS_ROOM MLX5_SEND_WQE_MAX_WQEBBS +#define MLX5E_SQ_STOP_ROOM (MLX5_SEND_WQE_MAX_WQEBBS +\ + MLX5E_SQ_NOPS_ROOM) + +#ifndef CONFIG_MLX5_EN_TLS +#define MLX5E_SQ_TLS_ROOM (0) +#else +/* TLS offload requires additional stop_room for: + * - a resync SKB. + * kTLS offload requires additional stop_room for: + * - static params WQE, + * - progress params WQE, and + * - resync DUMP per frag. + */ +#define MLX5E_SQ_TLS_ROOM \ + (MLX5_SEND_WQE_MAX_WQEBBS + \ + MLX5E_KTLS_STATIC_WQEBBS + MLX5E_KTLS_PROGRESS_WQEBBS + \ + MAX_SKB_FRAGS * MLX5E_KTLS_MAX_DUMP_WQEBBS) +#endif + +#define INL_HDR_START_SZ (sizeof(((struct mlx5_wqe_eth_seg *)NULL)->inline_hdr.start)) + +static inline bool +mlx5e_wqc_has_room_for(struct mlx5_wq_cyc *wq, u16 cc, u16 pc, u16 n) +{ + return (mlx5_wq_cyc_ctr2ix(wq, cc - pc) >= n) || (cc == pc); +} + +static inline void * +mlx5e_sq_fetch_wqe(struct mlx5e_txqsq *sq, size_t size, u16 *pi) +{ + struct mlx5_wq_cyc *wq = &sq->wq; + void *wqe; + + *pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + wqe = mlx5_wq_cyc_get_wqe(wq, *pi); + memset(wqe, 0, size); + + return wqe; +} + +static inline struct mlx5e_tx_wqe * +mlx5e_post_nop(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc) +{ + u16 pi = mlx5_wq_cyc_ctr2ix(wq, *pc); + struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + + memset(cseg, 0, sizeof(*cseg)); + + cseg->opmod_idx_opcode = cpu_to_be32((*pc << 8) | MLX5_OPCODE_NOP); + cseg->qpn_ds = cpu_to_be32((sqn << 8) | 0x01); + + (*pc)++; + + return wqe; +} + +static inline struct mlx5e_tx_wqe * +mlx5e_post_nop_fence(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc) +{ + u16 pi = mlx5_wq_cyc_ctr2ix(wq, *pc); + struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + + memset(cseg, 0, sizeof(*cseg)); + + cseg->opmod_idx_opcode = cpu_to_be32((*pc << 8) | MLX5_OPCODE_NOP); + cseg->qpn_ds = cpu_to_be32((sqn << 8) | 0x01); + cseg->fm_ce_se = MLX5_FENCE_MODE_INITIATOR_SMALL; + + (*pc)++; + + return wqe; +} + +static inline void +mlx5e_fill_sq_frag_edge(struct mlx5e_txqsq *sq, struct mlx5_wq_cyc *wq, + u16 pi, u16 nnops) +{ + struct mlx5e_tx_wqe_info *edge_wi, *wi = &sq->db.wqe_info[pi]; + + edge_wi = wi + nnops; + + /* fill sq frag edge with nops to avoid wqe wrapping two pages */ + for (; wi < edge_wi; wi++) { + wi->skb = NULL; + wi->num_wqebbs = 1; + mlx5e_post_nop(wq, sq->sqn, &sq->pc); + } + sq->stats->nop += nnops; +} + +static inline void +mlx5e_notify_hw(struct mlx5_wq_cyc *wq, u16 pc, void __iomem *uar_map, + struct mlx5_wqe_ctrl_seg *ctrl) +{ + ctrl->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; + /* ensure wqe is visible to device before updating doorbell record */ + dma_wmb(); + + *wq->db = cpu_to_be32(pc); + + /* ensure doorbell record is visible to device before ringing the + * doorbell + */ + wmb(); + + mlx5_write64((__be32 *)ctrl, uar_map); +} + +static inline bool mlx5e_transport_inline_tx_wqe(struct mlx5e_tx_wqe *wqe) +{ + return !!wqe->ctrl.tisn; +} + +static inline void mlx5e_cq_arm(struct mlx5e_cq *cq) +{ + struct mlx5_core_cq *mcq; + + mcq = &cq->mcq; + mlx5_cq_arm(mcq, MLX5_CQ_DB_REQ_NOT, mcq->uar->map, cq->wq.cc); +} + +static inline struct mlx5e_sq_dma * +mlx5e_dma_get(struct mlx5e_txqsq *sq, u32 i) +{ + return &sq->db.dma_fifo[i & sq->dma_fifo_mask]; +} + +static inline void +mlx5e_dma_push(struct mlx5e_txqsq *sq, dma_addr_t addr, u32 size, + enum mlx5e_dma_map_type map_type) +{ + struct mlx5e_sq_dma *dma = mlx5e_dma_get(sq, sq->dma_fifo_pc++); + + dma->addr = addr; + dma->size = size; + dma->type = map_type; +} + +static inline void +mlx5e_tx_dma_unmap(struct device *pdev, struct mlx5e_sq_dma *dma) +{ + switch (dma->type) { + case MLX5E_DMA_MAP_SINGLE: + dma_unmap_single(pdev, dma->addr, dma->size, DMA_TO_DEVICE); + break; + case MLX5E_DMA_MAP_PAGE: + dma_unmap_page(pdev, dma->addr, dma->size, DMA_TO_DEVICE); + break; + default: + WARN_ONCE(true, "mlx5e_tx_dma_unmap unknown DMA type!\n"); + } +} + +/* SW parser related functions */ + +struct mlx5e_swp_spec { + __be16 l3_proto; + u8 l4_proto; + u8 is_tun; + __be16 tun_l3_proto; + u8 tun_l4_proto; +}; + +static inline void +mlx5e_set_eseg_swp(struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg, + struct mlx5e_swp_spec *swp_spec) +{ + /* SWP offsets are in 2-bytes words */ + eseg->swp_outer_l3_offset = skb_network_offset(skb) / 2; + if (swp_spec->l3_proto == htons(ETH_P_IPV6)) + eseg->swp_flags |= MLX5_ETH_WQE_SWP_OUTER_L3_IPV6; + if (swp_spec->l4_proto) { + eseg->swp_outer_l4_offset = skb_transport_offset(skb) / 2; + if (swp_spec->l4_proto == IPPROTO_UDP) + eseg->swp_flags |= MLX5_ETH_WQE_SWP_OUTER_L4_UDP; + } + + if (swp_spec->is_tun) { + eseg->swp_inner_l3_offset = skb_inner_network_offset(skb) / 2; + if (swp_spec->tun_l3_proto == htons(ETH_P_IPV6)) + eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L3_IPV6; + } else { /* typically for ipsec when xfrm mode != XFRM_MODE_TUNNEL */ + eseg->swp_inner_l3_offset = skb_network_offset(skb) / 2; + if (swp_spec->l3_proto == htons(ETH_P_IPV6)) + eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L3_IPV6; + } + switch (swp_spec->tun_l4_proto) { + case IPPROTO_UDP: + eseg->swp_flags |= MLX5_ETH_WQE_SWP_INNER_L4_UDP; + /* fall through */ + case IPPROTO_TCP: + eseg->swp_inner_l4_offset = skb_inner_transport_offset(skb) / 2; + break; + } +} + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h index 2d934c8d3807..b90923932668 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h @@ -33,6 +33,7 @@ #define __MLX5_EN_XDP_H__ #include "en.h" +#include "en/txrx.h" #define MLX5E_XDP_MIN_INLINE (ETH_HLEN + VLAN_HLEN) #define MLX5E_XDP_TX_EMPTY_DS_COUNT \ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h index 6da7c88742dc..3022463f2284 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h @@ -39,6 +39,7 @@ #include "en_accel/ipsec_rxtx.h" #include "en_accel/tls_rxtx.h" #include "en.h" +#include "en/txrx.h" #if IS_ENABLED(CONFIG_GENEVE) static inline bool mlx5_geneve_tx_allowed(struct mlx5_core_dev *mdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h index ca47c0540904..db84500b024f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h @@ -39,6 +39,7 @@ #include <linux/skbuff.h> #include <net/xfrm.h> #include "en.h" +#include "en/txrx.h" struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb, u32 *cqe_bcnt); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c new file mode 100644 index 000000000000..d2ff74d52720 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +// Copyright (c) 2019 Mellanox Technologies. + +#include "en.h" +#include "en_accel/ktls.h" + +static int mlx5e_ktls_create_tis(struct mlx5_core_dev *mdev, u32 *tisn) +{ + u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {}; + void *tisc; + + tisc = MLX5_ADDR_OF(create_tis_in, in, ctx); + + MLX5_SET(tisc, tisc, tls_en, 1); + + return mlx5e_create_tis(mdev, in, tisn); +} + +static int mlx5e_ktls_add(struct net_device *netdev, struct sock *sk, + enum tls_offload_ctx_dir direction, + struct tls_crypto_info *crypto_info, + u32 start_offload_tcp_sn) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_ktls_offload_context_tx *tx_priv; + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct mlx5_core_dev *mdev = priv->mdev; + int err; + + if (WARN_ON(direction != TLS_OFFLOAD_CTX_DIR_TX)) + return -EINVAL; + + if (WARN_ON(!mlx5e_ktls_type_check(mdev, crypto_info))) + return -EOPNOTSUPP; + + tx_priv = kvzalloc(sizeof(*tx_priv), GFP_KERNEL); + if (!tx_priv) + return -ENOMEM; + + tx_priv->expected_seq = start_offload_tcp_sn; + tx_priv->crypto_info = crypto_info; + mlx5e_set_ktls_tx_priv_ctx(tls_ctx, tx_priv); + + /* tc and underlay_qpn values are not in use for tls tis */ + err = mlx5e_ktls_create_tis(mdev, &tx_priv->tisn); + if (err) + goto create_tis_fail; + + err = mlx5_ktls_create_key(mdev, crypto_info, &tx_priv->key_id); + if (err) + goto encryption_key_create_fail; + + mlx5e_ktls_tx_offload_set_pending(tx_priv); + + return 0; + +encryption_key_create_fail: + mlx5e_destroy_tis(priv->mdev, tx_priv->tisn); +create_tis_fail: + kvfree(tx_priv); + return err; +} + +static void mlx5e_ktls_del(struct net_device *netdev, + struct tls_context *tls_ctx, + enum tls_offload_ctx_dir direction) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_ktls_offload_context_tx *tx_priv = + mlx5e_get_ktls_tx_priv_ctx(tls_ctx); + + mlx5_ktls_destroy_key(priv->mdev, tx_priv->key_id); + mlx5e_destroy_tis(priv->mdev, tx_priv->tisn); + kvfree(tx_priv); +} + +static const struct tlsdev_ops mlx5e_ktls_ops = { + .tls_dev_add = mlx5e_ktls_add, + .tls_dev_del = mlx5e_ktls_del, +}; + +void mlx5e_ktls_build_netdev(struct mlx5e_priv *priv) +{ + struct net_device *netdev = priv->netdev; + + if (!mlx5_accel_is_ktls_device(priv->mdev)) + return; + + netdev->hw_features |= NETIF_F_HW_TLS_TX; + netdev->features |= NETIF_F_HW_TLS_TX; + + netdev->tlsdev_ops = &mlx5e_ktls_ops; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h new file mode 100644 index 000000000000..407da83474ef --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* Copyright (c) 2019 Mellanox Technologies. */ + +#ifndef __MLX5E_KTLS_H__ +#define __MLX5E_KTLS_H__ + +#include "en.h" + +#ifdef CONFIG_MLX5_EN_TLS +#include <net/tls.h> +#include "accel/tls.h" + +#define MLX5E_KTLS_STATIC_UMR_WQE_SZ \ + (sizeof(struct mlx5e_umr_wqe) + MLX5_ST_SZ_BYTES(tls_static_params)) +#define MLX5E_KTLS_STATIC_WQEBBS \ + (DIV_ROUND_UP(MLX5E_KTLS_STATIC_UMR_WQE_SZ, MLX5_SEND_WQE_BB)) + +#define MLX5E_KTLS_PROGRESS_WQE_SZ \ + (sizeof(struct mlx5e_tx_wqe) + MLX5_ST_SZ_BYTES(tls_progress_params)) +#define MLX5E_KTLS_PROGRESS_WQEBBS \ + (DIV_ROUND_UP(MLX5E_KTLS_PROGRESS_WQE_SZ, MLX5_SEND_WQE_BB)) +#define MLX5E_KTLS_MAX_DUMP_WQEBBS 2 + +enum { + MLX5E_TLS_PROGRESS_PARAMS_AUTH_STATE_NO_OFFLOAD = 0, + MLX5E_TLS_PROGRESS_PARAMS_AUTH_STATE_OFFLOAD = 1, + MLX5E_TLS_PROGRESS_PARAMS_AUTH_STATE_AUTHENTICATION = 2, +}; + +enum { + MLX5E_TLS_PROGRESS_PARAMS_RECORD_TRACKER_STATE_START = 0, + MLX5E_TLS_PROGRESS_PARAMS_RECORD_TRACKER_STATE_SEARCHING = 1, + MLX5E_TLS_PROGRESS_PARAMS_RECORD_TRACKER_STATE_TRACKING = 2, +}; + +struct mlx5e_ktls_offload_context_tx { + struct tls_offload_context_tx *tx_ctx; + struct tls_crypto_info *crypto_info; + u32 expected_seq; + u32 tisn; + u32 key_id; + bool ctx_post_pending; +}; + +struct mlx5e_ktls_offload_context_tx_shadow { + struct tls_offload_context_tx tx_ctx; + struct mlx5e_ktls_offload_context_tx *priv_tx; +}; + +static inline void +mlx5e_set_ktls_tx_priv_ctx(struct tls_context *tls_ctx, + struct mlx5e_ktls_offload_context_tx *priv_tx) +{ + struct tls_offload_context_tx *tx_ctx = tls_offload_ctx_tx(tls_ctx); + struct mlx5e_ktls_offload_context_tx_shadow *shadow; + + BUILD_BUG_ON(sizeof(*shadow) > TLS_OFFLOAD_CONTEXT_SIZE_TX); + + shadow = (struct mlx5e_ktls_offload_context_tx_shadow *)tx_ctx; + + shadow->priv_tx = priv_tx; + priv_tx->tx_ctx = tx_ctx; +} + +static inline struct mlx5e_ktls_offload_context_tx * +mlx5e_get_ktls_tx_priv_ctx(struct tls_context *tls_ctx) +{ + struct tls_offload_context_tx *tx_ctx = tls_offload_ctx_tx(tls_ctx); + struct mlx5e_ktls_offload_context_tx_shadow *shadow; + + BUILD_BUG_ON(sizeof(*shadow) > TLS_OFFLOAD_CONTEXT_SIZE_TX); + + shadow = (struct mlx5e_ktls_offload_context_tx_shadow *)tx_ctx; + + return shadow->priv_tx; +} + +void mlx5e_ktls_build_netdev(struct mlx5e_priv *priv); +void mlx5e_ktls_tx_offload_set_pending(struct mlx5e_ktls_offload_context_tx *priv_tx); + +struct sk_buff *mlx5e_ktls_handle_tx_skb(struct net_device *netdev, + struct mlx5e_txqsq *sq, + struct sk_buff *skb, + struct mlx5e_tx_wqe **wqe, u16 *pi); +void mlx5e_ktls_tx_handle_resync_dump_comp(struct mlx5e_txqsq *sq, + struct mlx5e_tx_wqe_info *wi, + struct mlx5e_sq_dma *dma); + +#else + +static inline void mlx5e_ktls_build_netdev(struct mlx5e_priv *priv) +{ +} + +#endif + +#endif /* __MLX5E_TLS_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c new file mode 100644 index 000000000000..3f5f4317a22b --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +// Copyright (c) 2019 Mellanox Technologies. + +#include <linux/tls.h> +#include "en.h" +#include "en/txrx.h" +#include "en_accel/ktls.h" + +enum { + MLX5E_STATIC_PARAMS_CONTEXT_TLS_1_2 = 0x2, +}; + +enum { + MLX5E_ENCRYPTION_STANDARD_TLS = 0x1, +}; + +#define EXTRACT_INFO_FIELDS do { \ + salt = info->salt; \ + rec_seq = info->rec_seq; \ + salt_sz = sizeof(info->salt); \ + rec_seq_sz = sizeof(info->rec_seq); \ +} while (0) + +static void +fill_static_params_ctx(void *ctx, struct mlx5e_ktls_offload_context_tx *priv_tx) +{ + struct tls_crypto_info *crypto_info = priv_tx->crypto_info; + char *initial_rn, *gcm_iv; + u16 salt_sz, rec_seq_sz; + char *salt, *rec_seq; + u8 tls_version; + + switch (crypto_info->cipher_type) { + case TLS_CIPHER_AES_GCM_128: { + struct tls12_crypto_info_aes_gcm_128 *info = + (struct tls12_crypto_info_aes_gcm_128 *)crypto_info; + + EXTRACT_INFO_FIELDS; + break; + } + default: + WARN_ON(1); + return; + } + + gcm_iv = MLX5_ADDR_OF(tls_static_params, ctx, gcm_iv); + initial_rn = MLX5_ADDR_OF(tls_static_params, ctx, initial_record_number); + + memcpy(gcm_iv, salt, salt_sz); + memcpy(initial_rn, rec_seq, rec_seq_sz); + + tls_version = MLX5E_STATIC_PARAMS_CONTEXT_TLS_1_2; + + MLX5_SET(tls_static_params, ctx, tls_version, tls_version); + MLX5_SET(tls_static_params, ctx, const_1, 1); + MLX5_SET(tls_static_params, ctx, const_2, 2); + MLX5_SET(tls_static_params, ctx, encryption_standard, + MLX5E_ENCRYPTION_STANDARD_TLS); + MLX5_SET(tls_static_params, ctx, dek_index, priv_tx->key_id); +} + +static void +build_static_params(struct mlx5e_umr_wqe *wqe, u16 pc, u32 sqn, + struct mlx5e_ktls_offload_context_tx *priv_tx, + bool fence) +{ + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + struct mlx5_wqe_umr_ctrl_seg *ucseg = &wqe->uctrl; + +#define STATIC_PARAMS_DS_CNT \ + DIV_ROUND_UP(MLX5E_KTLS_STATIC_UMR_WQE_SZ, MLX5_SEND_WQE_DS) + + cseg->opmod_idx_opcode = cpu_to_be32((pc << 8) | MLX5_OPCODE_UMR | + (MLX5_OPC_MOD_TLS_TIS_STATIC_PARAMS << 24)); + cseg->qpn_ds = cpu_to_be32((sqn << MLX5_WQE_CTRL_QPN_SHIFT) | + STATIC_PARAMS_DS_CNT); + cseg->fm_ce_se = fence ? MLX5_FENCE_MODE_INITIATOR_SMALL : 0; + cseg->imm = cpu_to_be32(priv_tx->tisn); + + ucseg->flags = MLX5_UMR_INLINE; + ucseg->bsf_octowords = cpu_to_be16(MLX5_ST_SZ_BYTES(tls_static_params) / 16); + + fill_static_params_ctx(wqe->tls_static_params_ctx, priv_tx); +} + +static void +fill_progress_params_ctx(void *ctx, struct mlx5e_ktls_offload_context_tx *priv_tx) +{ + MLX5_SET(tls_progress_params, ctx, pd, priv_tx->tisn); + MLX5_SET(tls_progress_params, ctx, record_tracker_state, + MLX5E_TLS_PROGRESS_PARAMS_RECORD_TRACKER_STATE_START); + MLX5_SET(tls_progress_params, ctx, auth_state, + MLX5E_TLS_PROGRESS_PARAMS_AUTH_STATE_NO_OFFLOAD); +} + +static void +build_progress_params(struct mlx5e_tx_wqe *wqe, u16 pc, u32 sqn, + struct mlx5e_ktls_offload_context_tx *priv_tx, + bool fence) +{ + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + +#define PROGRESS_PARAMS_DS_CNT \ + DIV_ROUND_UP(MLX5E_KTLS_PROGRESS_WQE_SZ, MLX5_SEND_WQE_DS) + + cseg->opmod_idx_opcode = + cpu_to_be32((pc << 8) | MLX5_OPCODE_SET_PSV | + (MLX5_OPC_MOD_TLS_TIS_PROGRESS_PARAMS << 24)); + cseg->qpn_ds = cpu_to_be32((sqn << MLX5_WQE_CTRL_QPN_SHIFT) | + PROGRESS_PARAMS_DS_CNT); + cseg->fm_ce_se = fence ? MLX5_FENCE_MODE_INITIATOR_SMALL : 0; + + fill_progress_params_ctx(wqe->data, priv_tx); +} + +static void tx_fill_wi(struct mlx5e_txqsq *sq, + u16 pi, u8 num_wqebbs, + skb_frag_t *resync_dump_frag) +{ + struct mlx5e_tx_wqe_info *wi = &sq->db.wqe_info[pi]; + + wi->skb = NULL; + wi->num_wqebbs = num_wqebbs; + wi->resync_dump_frag = resync_dump_frag; +} + +void mlx5e_ktls_tx_offload_set_pending(struct mlx5e_ktls_offload_context_tx *priv_tx) +{ + priv_tx->ctx_post_pending = true; +} + +static bool +mlx5e_ktls_tx_offload_test_and_clear_pending(struct mlx5e_ktls_offload_context_tx *priv_tx) +{ + bool ret = priv_tx->ctx_post_pending; + + priv_tx->ctx_post_pending = false; + + return ret; +} + +static void +post_static_params(struct mlx5e_txqsq *sq, + struct mlx5e_ktls_offload_context_tx *priv_tx, + bool fence) +{ + struct mlx5e_umr_wqe *umr_wqe; + u16 pi; + + umr_wqe = mlx5e_sq_fetch_wqe(sq, MLX5E_KTLS_STATIC_UMR_WQE_SZ, &pi); + build_static_params(umr_wqe, sq->pc, sq->sqn, priv_tx, fence); + tx_fill_wi(sq, pi, MLX5E_KTLS_STATIC_WQEBBS, NULL); + sq->pc += MLX5E_KTLS_STATIC_WQEBBS; +} + +static void +post_progress_params(struct mlx5e_txqsq *sq, + struct mlx5e_ktls_offload_context_tx *priv_tx, + bool fence) +{ + struct mlx5e_tx_wqe *wqe; + u16 pi; + + wqe = mlx5e_sq_fetch_wqe(sq, MLX5E_KTLS_PROGRESS_WQE_SZ, &pi); + build_progress_params(wqe, sq->pc, sq->sqn, priv_tx, fence); + tx_fill_wi(sq, pi, MLX5E_KTLS_PROGRESS_WQEBBS, NULL); + sq->pc += MLX5E_KTLS_PROGRESS_WQEBBS; +} + +static void +mlx5e_ktls_tx_post_param_wqes(struct mlx5e_txqsq *sq, + struct mlx5e_ktls_offload_context_tx *priv_tx, + bool skip_static_post, bool fence_first_post) +{ + bool progress_fence = skip_static_post || !fence_first_post; + + if (!skip_static_post) + post_static_params(sq, priv_tx, fence_first_post); + + post_progress_params(sq, priv_tx, progress_fence); +} + +struct tx_sync_info { + u64 rcd_sn; + s32 sync_len; + int nr_frags; + skb_frag_t *frags[MAX_SKB_FRAGS]; +}; + +static bool tx_sync_info_get(struct mlx5e_ktls_offload_context_tx *priv_tx, + u32 tcp_seq, struct tx_sync_info *info) +{ + struct tls_offload_context_tx *tx_ctx = priv_tx->tx_ctx; + struct tls_record_info *record; + int remaining, i = 0; + unsigned long flags; + bool ret = true; + + spin_lock_irqsave(&tx_ctx->lock, flags); + record = tls_get_record(tx_ctx, tcp_seq, &info->rcd_sn); + + if (unlikely(!record)) { + ret = false; + goto out; + } + + if (unlikely(tcp_seq < tls_record_start_seq(record))) { + if (!tls_record_is_start_marker(record)) + ret = false; + goto out; + } + + info->sync_len = tcp_seq - tls_record_start_seq(record); + remaining = info->sync_len; + while (remaining > 0) { + skb_frag_t *frag = &record->frags[i]; + + __skb_frag_ref(frag); + remaining -= skb_frag_size(frag); + info->frags[i++] = frag; + } + /* reduce the part which will be sent with the original SKB */ + if (remaining < 0) + skb_frag_size_add(info->frags[i - 1], remaining); + info->nr_frags = i; +out: + spin_unlock_irqrestore(&tx_ctx->lock, flags); + return ret; +} + +static void +tx_post_resync_params(struct mlx5e_txqsq *sq, + struct mlx5e_ktls_offload_context_tx *priv_tx, + u64 rcd_sn) +{ + struct tls_crypto_info *crypto_info = priv_tx->crypto_info; + __be64 rn_be = cpu_to_be64(rcd_sn); + bool skip_static_post; + u16 rec_seq_sz; + char *rec_seq; + + switch (crypto_info->cipher_type) { + case TLS_CIPHER_AES_GCM_128: { + struct tls12_crypto_info_aes_gcm_128 *info = + (struct tls12_crypto_info_aes_gcm_128 *)crypto_info; + + rec_seq = info->rec_seq; + rec_seq_sz = sizeof(info->rec_seq); + break; + } + default: + WARN_ON(1); + } + + skip_static_post = !memcmp(rec_seq, &rn_be, rec_seq_sz); + if (!skip_static_post) + memcpy(rec_seq, &rn_be, rec_seq_sz); + + mlx5e_ktls_tx_post_param_wqes(sq, priv_tx, skip_static_post, true); +} + +static int +tx_post_resync_dump(struct mlx5e_txqsq *sq, struct sk_buff *skb, + skb_frag_t *frag, u32 tisn, bool first) +{ + struct mlx5_wqe_ctrl_seg *cseg; + struct mlx5_wqe_eth_seg *eseg; + struct mlx5_wqe_data_seg *dseg; + struct mlx5e_tx_wqe *wqe; + dma_addr_t dma_addr = 0; + u16 ds_cnt, ds_cnt_inl; + u8 num_wqebbs; + u16 pi, ihs; + int fsz; + + ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS; + ihs = eth_get_headlen(skb->dev, skb->data, skb_headlen(skb)); + ds_cnt_inl = DIV_ROUND_UP(ihs - INL_HDR_START_SZ, MLX5_SEND_WQE_DS); + ds_cnt += ds_cnt_inl; + ds_cnt += 1; /* one frag */ + + wqe = mlx5e_sq_fetch_wqe(sq, sizeof(*wqe), &pi); + + num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS); + + cseg = &wqe->ctrl; + eseg = &wqe->eth; + dseg = wqe->data; + + cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_DUMP); + cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); + cseg->imm = cpu_to_be32(tisn); + cseg->fm_ce_se = first ? MLX5_FENCE_MODE_INITIATOR_SMALL : 0; + + eseg->inline_hdr.sz = cpu_to_be16(ihs); + memcpy(eseg->inline_hdr.start, skb->data, ihs); + dseg += ds_cnt_inl; + + fsz = skb_frag_size(frag); + dma_addr = skb_frag_dma_map(sq->pdev, frag, 0, fsz, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(sq->pdev, dma_addr))) + return -ENOMEM; + + dseg->addr = cpu_to_be64(dma_addr); + dseg->lkey = sq->mkey_be; + dseg->byte_count = cpu_to_be32(fsz); + mlx5e_dma_push(sq, dma_addr, fsz, MLX5E_DMA_MAP_PAGE); + + tx_fill_wi(sq, pi, num_wqebbs, frag); + sq->pc += num_wqebbs; + + WARN(num_wqebbs > MLX5E_KTLS_MAX_DUMP_WQEBBS, + "unexpected DUMP num_wqebbs, %d > %d", + num_wqebbs, MLX5E_KTLS_MAX_DUMP_WQEBBS); + + return 0; +} + +void mlx5e_ktls_tx_handle_resync_dump_comp(struct mlx5e_txqsq *sq, + struct mlx5e_tx_wqe_info *wi, + struct mlx5e_sq_dma *dma) +{ + struct mlx5e_sq_stats *stats = sq->stats; + + mlx5e_tx_dma_unmap(sq->pdev, dma); + __skb_frag_unref(wi->resync_dump_frag); + stats->tls_dump_packets++; + stats->tls_dump_bytes += wi->num_bytes; +} + +static void tx_post_fence_nop(struct mlx5e_txqsq *sq) +{ + struct mlx5_wq_cyc *wq = &sq->wq; + u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + + tx_fill_wi(sq, pi, 1, NULL); + + mlx5e_post_nop_fence(wq, sq->sqn, &sq->pc); +} + +static struct sk_buff * +mlx5e_ktls_tx_handle_ooo(struct mlx5e_ktls_offload_context_tx *priv_tx, + struct mlx5e_txqsq *sq, + struct sk_buff *skb, + u32 seq) +{ + struct mlx5e_sq_stats *stats = sq->stats; + struct mlx5_wq_cyc *wq = &sq->wq; + struct tx_sync_info info = {}; + u16 contig_wqebbs_room, pi; + u8 num_wqebbs; + int i; + + if (!tx_sync_info_get(priv_tx, seq, &info)) { + /* We might get here if a retransmission reaches the driver + * after the relevant record is acked. + * It should be safe to drop the packet in this case + */ + stats->tls_drop_no_sync_data++; + goto err_out; + } + + if (unlikely(info.sync_len < 0)) { + u32 payload; + int headln; + + headln = skb_transport_offset(skb) + tcp_hdrlen(skb); + payload = skb->len - headln; + if (likely(payload <= -info.sync_len)) + return skb; + + stats->tls_drop_bypass_req++; + goto err_out; + } + + stats->tls_ooo++; + + num_wqebbs = MLX5E_KTLS_STATIC_WQEBBS + MLX5E_KTLS_PROGRESS_WQEBBS + + (info.nr_frags ? info.nr_frags * MLX5E_KTLS_MAX_DUMP_WQEBBS : 1); + pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); + contig_wqebbs_room = mlx5_wq_cyc_get_contig_wqebbs(wq, pi); + if (unlikely(contig_wqebbs_room < num_wqebbs)) + mlx5e_fill_sq_frag_edge(sq, wq, pi, contig_wqebbs_room); + + tx_post_resync_params(sq, priv_tx, info.rcd_sn); + + for (i = 0; i < info.nr_frags; i++) + if (tx_post_resync_dump(sq, skb, info.frags[i], + priv_tx->tisn, !i)) + goto err_out; + + /* If no dump WQE was sent, we need to have a fence NOP WQE before the + * actual data xmit. + */ + if (!info.nr_frags) + tx_post_fence_nop(sq); + + return skb; + +err_out: + dev_kfree_skb_any(skb); + return NULL; +} + +struct sk_buff *mlx5e_ktls_handle_tx_skb(struct net_device *netdev, + struct mlx5e_txqsq *sq, + struct sk_buff *skb, + struct mlx5e_tx_wqe **wqe, u16 *pi) +{ + struct mlx5e_ktls_offload_context_tx *priv_tx; + struct mlx5e_sq_stats *stats = sq->stats; + struct mlx5_wqe_ctrl_seg *cseg; + struct tls_context *tls_ctx; + int datalen; + u32 seq; + + if (!skb->sk || !tls_is_sk_tx_device_offloaded(skb->sk)) + goto out; + + datalen = skb->len - (skb_transport_offset(skb) + tcp_hdrlen(skb)); + if (!datalen) + goto out; + + tls_ctx = tls_get_ctx(skb->sk); + if (unlikely(tls_ctx->netdev != netdev)) + goto err_out; + + priv_tx = mlx5e_get_ktls_tx_priv_ctx(tls_ctx); + + if (unlikely(mlx5e_ktls_tx_offload_test_and_clear_pending(priv_tx))) { + mlx5e_ktls_tx_post_param_wqes(sq, priv_tx, false, false); + *wqe = mlx5e_sq_fetch_wqe(sq, sizeof(**wqe), pi); + stats->tls_ctx++; + } + + seq = ntohl(tcp_hdr(skb)->seq); + if (unlikely(priv_tx->expected_seq != seq)) { + skb = mlx5e_ktls_tx_handle_ooo(priv_tx, sq, skb, seq); + if (unlikely(!skb)) + goto out; + *wqe = mlx5e_sq_fetch_wqe(sq, sizeof(**wqe), pi); + } + + priv_tx->expected_seq = seq + datalen; + + cseg = &(*wqe)->ctrl; + cseg->imm = cpu_to_be32(priv_tx->tisn); + + stats->tls_encrypted_packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1; + stats->tls_encrypted_bytes += datalen; + +out: + return skb; + +err_out: + dev_kfree_skb_any(skb); + return NULL; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls.c index dc15c5c9e557..f8b93b62a7d2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls.c @@ -190,6 +190,11 @@ void mlx5e_tls_build_netdev(struct mlx5e_priv *priv) struct net_device *netdev = priv->netdev; u32 caps; + if (mlx5_accel_is_ktls_device(priv->mdev)) { + mlx5e_ktls_build_netdev(priv); + return; + } + if (!mlx5_accel_is_tls_device(priv->mdev)) return; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls.h index 3f5d72163b56..9015f3f7792d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls.h @@ -33,8 +33,10 @@ #ifndef __MLX5E_TLS_H__ #define __MLX5E_TLS_H__ -#ifdef CONFIG_MLX5_EN_TLS +#include "accel/tls.h" +#include "en_accel/ktls.h" +#ifdef CONFIG_MLX5_EN_TLS #include <net/tls.h> #include "en.h" @@ -94,7 +96,12 @@ int mlx5e_tls_get_stats(struct mlx5e_priv *priv, u64 *data); #else -static inline void mlx5e_tls_build_netdev(struct mlx5e_priv *priv) { } +static inline void mlx5e_tls_build_netdev(struct mlx5e_priv *priv) +{ + if (mlx5_accel_is_ktls_device(priv->mdev)) + mlx5e_ktls_build_netdev(priv); +} + static inline int mlx5e_tls_init(struct mlx5e_priv *priv) { return 0; } static inline void mlx5e_tls_cleanup(struct mlx5e_priv *priv) { } static inline int mlx5e_tls_get_count(struct mlx5e_priv *priv) { return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c index 439bf5953885..71384ad1a443 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c @@ -248,7 +248,7 @@ mlx5e_tls_handle_ooo(struct mlx5e_tls_offload_context_tx *context, mlx5e_tls_complete_sync_skb(skb, nskb, tcp_seq, headln, cpu_to_be64(info.rcd_sn)); mlx5e_sq_xmit(sq, nskb, *wqe, *pi, true); - mlx5e_sq_fetch_wqe(sq, wqe, pi); + *wqe = mlx5e_sq_fetch_wqe(sq, sizeof(**wqe), pi); return skb; err_out: @@ -269,6 +269,11 @@ struct sk_buff *mlx5e_tls_handle_tx_skb(struct net_device *netdev, int datalen; u32 skb_seq; + if (MLX5_CAP_GEN(sq->channel->mdev, tls)) { + skb = mlx5e_ktls_handle_tx_skb(netdev, sq, skb, wqe, pi); + goto out; + } + if (!skb->sk || !tls_is_sk_tx_device_offloaded(skb->sk)) goto out; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.h index 311667ec71b8..90bc1f2384c8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.h @@ -38,6 +38,7 @@ #include <linux/skbuff.h> #include "en.h" +#include "en/txrx.h" struct sk_buff *mlx5e_tls_handle_tx_skb(struct net_device *netdev, struct mlx5e_txqsq *sq, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 554672edf8c3..8dd31b5c740c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -680,7 +680,7 @@ static void mlx5e_dcbnl_getpermhwaddr(struct net_device *netdev, memset(perm_addr, 0xff, MAX_ADDR_LEN); - mlx5_query_nic_vport_mac_address(priv->mdev, 0, perm_addr); + mlx5_query_mac_address(priv->mdev, perm_addr); } static void mlx5e_dcbnl_setpgtccfgtx(struct net_device *netdev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 2f9093ba82aa..10efd69de7ef 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -41,6 +41,7 @@ #include <net/xdp_sock.h> #include "eswitch.h" #include "en.h" +#include "en/txrx.h" #include "en_tc.h" #include "en_rep.h" #include "en_accel/ipsec.h" @@ -62,6 +63,7 @@ #include "en/xsk/rx.h" #include "en/xsk/tx.h" + bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) { bool striding_rq_umr = MLX5_CAP_GEN(mdev, striding_rq) && @@ -575,8 +577,6 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, } err = xdp_rxq_info_reg_mem_model(&rq->xdp_rxq, MEM_TYPE_PAGE_POOL, rq->page_pool); - if (err) - page_pool_free(rq->page_pool); } if (err) goto err_free; @@ -644,6 +644,7 @@ err_rq_wq_destroy: if (rq->xdp_prog) bpf_prog_put(rq->xdp_prog); xdp_rxq_info_unreg(&rq->xdp_rxq); + page_pool_destroy(rq->page_pool); mlx5_wq_destroy(&rq->wq_ctrl); return err; @@ -678,6 +679,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq) } xdp_rxq_info_unreg(&rq->xdp_rxq); + page_pool_destroy(rq->page_pool); mlx5_wq_destroy(&rq->wq_ctrl); } @@ -1124,11 +1126,14 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, sq->uar_map = mdev->mlx5e_res.bfreg.map; sq->min_inline_mode = params->tx_min_inline_mode; sq->stats = &c->priv->channel_stats[c->ix].sq[tc]; + sq->stop_room = MLX5E_SQ_STOP_ROOM; INIT_WORK(&sq->recover_work, mlx5e_tx_err_cqe_work); if (MLX5_IPSEC_DEV(c->priv->mdev)) set_bit(MLX5E_SQ_STATE_IPSEC, &sq->state); - if (mlx5_accel_is_tls_device(c->priv->mdev)) + if (mlx5_accel_is_tls_device(c->priv->mdev)) { set_bit(MLX5E_SQ_STATE_TLS, &sq->state); + sq->stop_room += MLX5E_SQ_TLS_ROOM; + } param->wq.db_numa_node = cpu_to_node(c->cpu); err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, wq, &sq->wq_ctrl); @@ -1551,6 +1556,7 @@ static void mlx5e_free_cq(struct mlx5e_cq *cq) static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) { + u32 out[MLX5_ST_SZ_DW(create_cq_out)]; struct mlx5_core_dev *mdev = cq->mdev; struct mlx5_core_cq *mcq = &cq->mcq; @@ -1585,7 +1591,7 @@ static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) MLX5_ADAPTER_PAGE_SHIFT); MLX5_SET64(cqc, cqc, dbr_addr, cq->wq_ctrl.db.dma); - err = mlx5_core_create_cq(mdev, mcq, in, inlen); + err = mlx5_core_create_cq(mdev, mcq, in, inlen, out, sizeof(out)); kvfree(in); @@ -3144,20 +3150,19 @@ void mlx5e_close_drop_rq(struct mlx5e_rq *drop_rq) mlx5e_free_cq(&drop_rq->cq); } -int mlx5e_create_tis(struct mlx5_core_dev *mdev, int tc, - u32 underlay_qpn, u32 *tisn) +int mlx5e_create_tis(struct mlx5_core_dev *mdev, void *in, u32 *tisn) { - u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {0}; void *tisc = MLX5_ADDR_OF(create_tis_in, in, ctx); - MLX5_SET(tisc, tisc, prio, tc << 1); - MLX5_SET(tisc, tisc, underlay_qpn, underlay_qpn); MLX5_SET(tisc, tisc, transport_domain, mdev->mlx5e_res.td.tdn); + if (MLX5_GET(tisc, tisc, tls_en)) + MLX5_SET(tisc, tisc, pd, mdev->mlx5e_res.pdn); + if (mlx5_lag_is_lacp_owner(mdev)) MLX5_SET(tisc, tisc, strict_lag_tx_port_affinity, 1); - return mlx5_core_create_tis(mdev, in, sizeof(in), tisn); + return mlx5_core_create_tis(mdev, in, MLX5_ST_SZ_BYTES(create_tis_in), tisn); } void mlx5e_destroy_tis(struct mlx5_core_dev *mdev, u32 tisn) @@ -3171,7 +3176,14 @@ int mlx5e_create_tises(struct mlx5e_priv *priv) int tc; for (tc = 0; tc < priv->profile->max_tc; tc++) { - err = mlx5e_create_tis(priv->mdev, tc, 0, &priv->tisn[tc]); + u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {}; + void *tisc; + + tisc = MLX5_ADDR_OF(create_tis_in, in, ctx); + + MLX5_SET(tisc, tisc, prio, tc << 1); + + err = mlx5e_create_tis(priv->mdev, in, &priv->tisn[tc]); if (err) goto err_close_tises; } @@ -4790,7 +4802,7 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - mlx5_query_nic_vport_mac_address(priv->mdev, 0, netdev->dev_addr); + mlx5_query_mac_address(priv->mdev, netdev->dev_addr); if (is_zero_ether_addr(netdev->dev_addr) && !MLX5_CAP_GEN(priv->mdev, vport_group_manager)) { eth_hw_addr_random(netdev); @@ -5356,7 +5368,7 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) #ifdef CONFIG_MLX5_ESWITCH if (MLX5_ESWITCH_MANAGER(mdev) && - mlx5_eswitch_mode(mdev->priv.eswitch) == SRIOV_OFFLOADS) { + mlx5_eswitch_mode(mdev->priv.eswitch) == MLX5_ESWITCH_OFFLOADS) { mlx5e_rep_register_vport_reps(mdev); return mdev; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 6a013a8c1150..529f8e4b32c6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -398,7 +398,7 @@ static int mlx5e_rep_get_port_parent_id(struct net_device *dev, priv = netdev_priv(dev); esw = priv->mdev->priv.eswitch; - if (esw->mode == SRIOV_NONE) + if (esw->mode == MLX5_ESWITCH_NONE) return -EOPNOTSUPP; parent_id = mlx5_query_nic_system_image_guid(priv->mdev); @@ -414,7 +414,7 @@ static void mlx5e_sqs2vport_stop(struct mlx5_eswitch *esw, struct mlx5e_rep_sq *rep_sq, *tmp; struct mlx5e_rep_priv *rpriv; - if (esw->mode != SRIOV_OFFLOADS) + if (esw->mode != MLX5_ESWITCH_OFFLOADS) return; rpriv = mlx5e_rep_to_rep_priv(rep); @@ -435,7 +435,7 @@ static int mlx5e_sqs2vport_start(struct mlx5_eswitch *esw, int err; int i; - if (esw->mode != SRIOV_OFFLOADS) + if (esw->mode != MLX5_ESWITCH_OFFLOADS) return 0; rpriv = mlx5e_rep_to_rep_priv(rep); @@ -1392,7 +1392,7 @@ static void mlx5e_build_rep_netdev(struct net_device *netdev) SET_NETDEV_DEV(netdev, mdev->device); netdev->netdev_ops = &mlx5e_netdev_ops_uplink_rep; /* we want a persistent mac for the uplink rep */ - mlx5_query_nic_vport_mac_address(mdev, 0, netdev->dev_addr); + mlx5_query_mac_address(mdev, netdev->dev_addr); netdev->ethtool_ops = &mlx5e_uplink_rep_ethtool_ops; #ifdef CONFIG_MLX5_CORE_EN_DCB if (MLX5_CAP_GEN(mdev, qos)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 5f540db47cc9..539b4d3656da 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -48,8 +48,15 @@ static const struct counter_desc sw_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_nop) }, #ifdef CONFIG_MLX5_EN_TLS + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_encrypted_packets) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_encrypted_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_ctx) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_ooo) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_resync_bytes) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_drop_no_sync_data) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_drop_bypass_req) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_dump_packets) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_dump_bytes) }, #endif { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_lro_packets) }, @@ -271,8 +278,15 @@ static void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv) s->tx_csum_none += sq_stats->csum_none; s->tx_csum_partial += sq_stats->csum_partial; #ifdef CONFIG_MLX5_EN_TLS - s->tx_tls_ooo += sq_stats->tls_ooo; - s->tx_tls_resync_bytes += sq_stats->tls_resync_bytes; + s->tx_tls_encrypted_packets += sq_stats->tls_encrypted_packets; + s->tx_tls_encrypted_bytes += sq_stats->tls_encrypted_bytes; + s->tx_tls_ctx += sq_stats->tls_ctx; + s->tx_tls_ooo += sq_stats->tls_ooo; + s->tx_tls_resync_bytes += sq_stats->tls_resync_bytes; + s->tx_tls_drop_no_sync_data += sq_stats->tls_drop_no_sync_data; + s->tx_tls_drop_bypass_req += sq_stats->tls_drop_bypass_req; + s->tx_tls_dump_bytes += sq_stats->tls_dump_bytes; + s->tx_tls_dump_packets += sq_stats->tls_dump_packets; #endif s->tx_cqes += sq_stats->cqes; } @@ -1293,6 +1307,16 @@ static const struct counter_desc sq_stats_desc[] = { { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, csum_partial_inner) }, { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, added_vlan_packets) }, { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, nop) }, +#ifdef CONFIG_MLX5_EN_TLS + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tls_encrypted_packets) }, + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tls_encrypted_bytes) }, + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tls_ctx) }, + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tls_ooo) }, + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tls_drop_no_sync_data) }, + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tls_drop_bypass_req) }, + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tls_dump_packets) }, + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tls_dump_bytes) }, +#endif { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, csum_none) }, { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, stopped) }, { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, dropped) }, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index fb3ad7231e11..76ac111e14d0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -122,8 +122,15 @@ struct mlx5e_sw_stats { u64 ch_eq_rearm; #ifdef CONFIG_MLX5_EN_TLS + u64 tx_tls_encrypted_packets; + u64 tx_tls_encrypted_bytes; + u64 tx_tls_ctx; u64 tx_tls_ooo; u64 tx_tls_resync_bytes; + u64 tx_tls_drop_no_sync_data; + u64 tx_tls_drop_bypass_req; + u64 tx_tls_dump_packets; + u64 tx_tls_dump_bytes; #endif u64 rx_xsk_packets; @@ -256,8 +263,15 @@ struct mlx5e_sq_stats { u64 added_vlan_packets; u64 nop; #ifdef CONFIG_MLX5_EN_TLS + u64 tls_encrypted_packets; + u64 tls_encrypted_bytes; + u64 tls_ctx; u64 tls_ooo; u64 tls_resync_bytes; + u64 tls_drop_no_sync_data; + u64 tls_drop_bypass_req; + u64 tls_dump_packets; + u64 tls_dump_bytes; #endif /* less likely accessed in data path */ u64 csum_none; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index e6b199cd68ea..3ac9b1e423ee 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -3372,7 +3372,7 @@ mlx5e_tc_add_flow(struct mlx5e_priv *priv, if (!tc_can_offload_extack(priv->netdev, f->common.extack)) return -EOPNOTSUPP; - if (esw && esw->mode == SRIOV_OFFLOADS) + if (esw && esw->mode == MLX5_ESWITCH_OFFLOADS) err = mlx5e_add_fdb_flow(priv, f, flow_flags, filter_dev, flow); else diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 9048faa4bfcf..600e92cb629a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -35,55 +35,12 @@ #include <net/geneve.h> #include <net/dsfield.h> #include "en.h" +#include "en/txrx.h" #include "ipoib/ipoib.h" #include "en_accel/en_accel.h" +#include "en_accel/ktls.h" #include "lib/clock.h" -#define MLX5E_SQ_NOPS_ROOM MLX5_SEND_WQE_MAX_WQEBBS - -#ifndef CONFIG_MLX5_EN_TLS -#define MLX5E_SQ_STOP_ROOM (MLX5_SEND_WQE_MAX_WQEBBS +\ - MLX5E_SQ_NOPS_ROOM) -#else -/* TLS offload requires MLX5E_SQ_STOP_ROOM to have - * enough room for a resync SKB, a normal SKB and a NOP - */ -#define MLX5E_SQ_STOP_ROOM (2 * MLX5_SEND_WQE_MAX_WQEBBS +\ - MLX5E_SQ_NOPS_ROOM) -#endif - -static inline void mlx5e_tx_dma_unmap(struct device *pdev, - struct mlx5e_sq_dma *dma) -{ - switch (dma->type) { - case MLX5E_DMA_MAP_SINGLE: - dma_unmap_single(pdev, dma->addr, dma->size, DMA_TO_DEVICE); - break; - case MLX5E_DMA_MAP_PAGE: - dma_unmap_page(pdev, dma->addr, dma->size, DMA_TO_DEVICE); - break; - default: - WARN_ONCE(true, "mlx5e_tx_dma_unmap unknown DMA type!\n"); - } -} - -static inline struct mlx5e_sq_dma *mlx5e_dma_get(struct mlx5e_txqsq *sq, u32 i) -{ - return &sq->db.dma_fifo[i & sq->dma_fifo_mask]; -} - -static inline void mlx5e_dma_push(struct mlx5e_txqsq *sq, - dma_addr_t addr, - u32 size, - enum mlx5e_dma_map_type map_type) -{ - struct mlx5e_sq_dma *dma = mlx5e_dma_get(sq, sq->dma_fifo_pc++); - - dma->addr = addr; - dma->size = size; - dma->type = map_type; -} - static void mlx5e_dma_unmap_wqe_err(struct mlx5e_txqsq *sq, u8 num_dma) { int i; @@ -277,23 +234,6 @@ dma_unmap_wqe_err: return -ENOMEM; } -static inline void mlx5e_fill_sq_frag_edge(struct mlx5e_txqsq *sq, - struct mlx5_wq_cyc *wq, - u16 pi, u16 nnops) -{ - struct mlx5e_tx_wqe_info *edge_wi, *wi = &sq->db.wqe_info[pi]; - - edge_wi = wi + nnops; - - /* fill sq frag edge with nops to avoid wqe wrapping two pages */ - for (; wi < edge_wi; wi++) { - wi->skb = NULL; - wi->num_wqebbs = 1; - mlx5e_post_nop(wq, sq->sqn, &sq->pc); - } - sq->stats->nop += nnops; -} - static inline void mlx5e_txwqe_complete(struct mlx5e_txqsq *sq, struct sk_buff *skb, u8 opcode, u16 ds_cnt, u8 num_wqebbs, u32 num_bytes, u8 num_dma, @@ -315,7 +255,7 @@ mlx5e_txwqe_complete(struct mlx5e_txqsq *sq, struct sk_buff *skb, skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; sq->pc += wi->num_wqebbs; - if (unlikely(!mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, MLX5E_SQ_STOP_ROOM))) { + if (unlikely(!mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, sq->stop_room))) { netif_tx_stop_queue(sq->txq); sq->stats->stopped++; } @@ -326,8 +266,6 @@ mlx5e_txwqe_complete(struct mlx5e_txqsq *sq, struct sk_buff *skb, mlx5e_notify_hw(wq, sq->pc, sq->uar_map, cseg); } -#define INL_HDR_START_SZ (sizeof(((struct mlx5_wqe_eth_seg *)NULL)->inline_hdr.start)) - netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, struct mlx5e_tx_wqe *wqe, u16 pi, bool xmit_more) { @@ -354,9 +292,12 @@ netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, num_bytes = skb->len + (skb_shinfo(skb)->gso_segs - 1) * ihs; stats->packets += skb_shinfo(skb)->gso_segs; } else { + u8 mode = mlx5e_transport_inline_tx_wqe(wqe) ? + MLX5_INLINE_MODE_TCP_UDP : sq->min_inline_mode; + opcode = MLX5_OPCODE_SEND; mss = 0; - ihs = mlx5e_calc_min_inline(sq->min_inline_mode, skb); + ihs = mlx5e_calc_min_inline(mode, skb); num_bytes = max_t(unsigned int, skb->len, ETH_ZLEN); stats->packets++; } @@ -381,11 +322,17 @@ netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, #ifdef CONFIG_MLX5_EN_IPSEC struct mlx5_wqe_eth_seg cur_eth = wqe->eth; #endif +#ifdef CONFIG_MLX5_EN_TLS + struct mlx5_wqe_ctrl_seg cur_ctrl = wqe->ctrl; +#endif mlx5e_fill_sq_frag_edge(sq, wq, pi, contig_wqebbs_room); - mlx5e_sq_fetch_wqe(sq, &wqe, &pi); + wqe = mlx5e_sq_fetch_wqe(sq, sizeof(*wqe), &pi); #ifdef CONFIG_MLX5_EN_IPSEC wqe->eth = cur_eth; #endif +#ifdef CONFIG_MLX5_EN_TLS + wqe->ctrl = cur_ctrl; +#endif } /* fill wqe */ @@ -444,7 +391,7 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) u16 pi; sq = priv->txq2sq[skb_get_queue_mapping(skb)]; - mlx5e_sq_fetch_wqe(sq, &wqe, &pi); + wqe = mlx5e_sq_fetch_wqe(sq, sizeof(*wqe), &pi); /* might send skbs and update wqe and pi */ skb = mlx5e_accel_handle_tx(skb, sq, dev, &wqe, &pi); @@ -532,8 +479,16 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) wi = &sq->db.wqe_info[ci]; skb = wi->skb; - if (unlikely(!skb)) { /* nop */ - sqcc++; + if (unlikely(!skb)) { +#ifdef CONFIG_MLX5_EN_TLS + if (wi->resync_dump_frag) { + struct mlx5e_sq_dma *dma = + mlx5e_dma_get(sq, dma_fifo_cc++); + + mlx5e_ktls_tx_handle_resync_dump_comp(sq, wi, dma); + } +#endif + sqcc += wi->num_wqebbs; continue; } @@ -575,8 +530,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) netdev_tx_completed_queue(sq->txq, npkts, nbytes); if (netif_tx_queue_stopped(sq->txq) && - mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, - MLX5E_SQ_STOP_ROOM) && + mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, sq->stop_room) && !test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state)) { netif_tx_wake_queue(sq->txq); stats->wake++; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 3c98b7fe2923..c50b6f0769c8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -168,7 +168,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) return work_done; } -void mlx5e_completion_event(struct mlx5_core_cq *mcq) +void mlx5e_completion_event(struct mlx5_core_cq *mcq, struct mlx5_eqe *eqe) { struct mlx5e_cq *cq = container_of(mcq, struct mlx5e_cq, mcq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 58fff2f39b38..41f25ea2e8d9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -153,7 +153,7 @@ static int mlx5_eq_comp_int(struct notifier_block *nb, cq = mlx5_eq_cq_get(eq, cqn); if (likely(cq)) { ++cq->arm_sn; - cq->comp(cq); + cq->comp(cq, eqe); mlx5_cq_put(cq); } else { mlx5_core_warn(eq->dev, "Completion event for bogus CQ 0x%x\n", cqn); @@ -256,6 +256,7 @@ create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, int inlen; u32 *in; int err; + int i; /* Init CQ table */ memset(cq_table, 0, sizeof(*cq_table)); @@ -283,10 +284,12 @@ create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, mlx5_fill_page_array(&eq->buf, pas); MLX5_SET(create_eq_in, in, opcode, MLX5_CMD_OP_CREATE_EQ); - if (!param->mask && MLX5_CAP_GEN(dev, log_max_uctx)) + if (!param->mask[0] && MLX5_CAP_GEN(dev, log_max_uctx)) MLX5_SET(create_eq_in, in, uid, MLX5_SHARED_RESOURCE_UID); - MLX5_SET64(create_eq_in, in, event_bitmask, param->mask); + for (i = 0; i < 4; i++) + MLX5_ARRAY_SET64(create_eq_in, in, event_bitmask, i, + param->mask[i]); eqc = MLX5_ADDR_OF(create_eq_in, in, eq_context_entry); MLX5_SET(eqc, eqc, log_eq_size, ilog2(eq->nent)); @@ -389,7 +392,7 @@ int mlx5_eq_add_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq) return err; } -int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq) +void mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq) { struct mlx5_cq_table *table = &eq->cq_table; struct mlx5_core_cq *tmp; @@ -399,16 +402,14 @@ int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq) spin_unlock(&table->lock); if (!tmp) { - mlx5_core_warn(eq->dev, "cq 0x%x not found in eq 0x%x tree\n", eq->eqn, cq->cqn); - return -ENOENT; - } - - if (tmp != cq) { - mlx5_core_warn(eq->dev, "corruption on cqn 0x%x in eq 0x%x\n", eq->eqn, cq->cqn); - return -EINVAL; + mlx5_core_dbg(eq->dev, "cq 0x%x not found in eq 0x%x tree\n", + eq->eqn, cq->cqn); + return; } - return 0; + if (tmp != cq) + mlx5_core_dbg(eq->dev, "corruption on cqn 0x%x in eq 0x%x\n", + eq->eqn, cq->cqn); } int mlx5_eq_table_init(struct mlx5_core_dev *dev) @@ -502,14 +503,31 @@ static int cq_err_event_notifier(struct notifier_block *nb, return NOTIFY_OK; } - cq->event(cq, type); + if (cq->event) + cq->event(cq, type); mlx5_cq_put(cq); return NOTIFY_OK; } -static u64 gather_async_events_mask(struct mlx5_core_dev *dev) +static void gather_user_async_events(struct mlx5_core_dev *dev, u64 mask[4]) +{ + __be64 *user_unaffiliated_events; + __be64 *user_affiliated_events; + int i; + + user_affiliated_events = + MLX5_CAP_DEV_EVENT(dev, user_affiliated_events); + user_unaffiliated_events = + MLX5_CAP_DEV_EVENT(dev, user_unaffiliated_events); + + for (i = 0; i < 4; i++) + mask[i] |= be64_to_cpu(user_affiliated_events[i] | + user_unaffiliated_events[i]); +} + +static void gather_async_events_mask(struct mlx5_core_dev *dev, u64 mask[4]) { u64 async_event_mask = MLX5_ASYNC_EVENT_MASK; @@ -546,7 +564,10 @@ static u64 gather_async_events_mask(struct mlx5_core_dev *dev) async_event_mask |= (1ull << MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED); - return async_event_mask; + mask[0] = async_event_mask; + + if (MLX5_CAP_GEN(dev, event_cap)) + gather_user_async_events(dev, mask); } static int create_async_eqs(struct mlx5_core_dev *dev) @@ -561,9 +582,10 @@ static int create_async_eqs(struct mlx5_core_dev *dev) table->cmd_eq.irq_nb.notifier_call = mlx5_eq_async_int; param = (struct mlx5_eq_param) { .irq_index = 0, - .mask = 1ull << MLX5_EVENT_TYPE_CMD, .nent = MLX5_NUM_CMD_EQE, }; + + param.mask[0] = 1ull << MLX5_EVENT_TYPE_CMD; err = create_async_eq(dev, &table->cmd_eq.core, ¶m); if (err) { mlx5_core_warn(dev, "failed to create cmd EQ %d\n", err); @@ -579,9 +601,10 @@ static int create_async_eqs(struct mlx5_core_dev *dev) table->async_eq.irq_nb.notifier_call = mlx5_eq_async_int; param = (struct mlx5_eq_param) { .irq_index = 0, - .mask = gather_async_events_mask(dev), .nent = MLX5_NUM_ASYNC_EQE, }; + + gather_async_events_mask(dev, param.mask); err = create_async_eq(dev, &table->async_eq.core, ¶m); if (err) { mlx5_core_warn(dev, "failed to create async EQ %d\n", err); @@ -597,9 +620,10 @@ static int create_async_eqs(struct mlx5_core_dev *dev) table->pages_eq.irq_nb.notifier_call = mlx5_eq_async_int; param = (struct mlx5_eq_param) { .irq_index = 0, - .mask = 1 << MLX5_EVENT_TYPE_PAGE_REQUEST, .nent = /* TODO: sriov max_vf + */ 1, }; + + param.mask[0] = 1ull << MLX5_EVENT_TYPE_PAGE_REQUEST; err = create_async_eq(dev, &table->pages_eq.core, ¶m); if (err) { mlx5_core_warn(dev, "failed to create pages EQ %d\n", err); @@ -791,7 +815,6 @@ static int create_comp_eqs(struct mlx5_core_dev *dev) eq->irq_nb.notifier_call = mlx5_eq_comp_int; param = (struct mlx5_eq_param) { .irq_index = vecidx, - .mask = 0, .nent = nent, }; err = create_map_eq(dev, &eq->core, ¶m); @@ -927,6 +950,7 @@ int mlx5_eq_notifier_register(struct mlx5_core_dev *dev, struct mlx5_nb *nb) return atomic_notifier_chain_register(&eqt->nh[nb->event_type], &nb->nb); } +EXPORT_SYMBOL(mlx5_eq_notifier_register); int mlx5_eq_notifier_unregister(struct mlx5_core_dev *dev, struct mlx5_nb *nb) { @@ -937,3 +961,4 @@ int mlx5_eq_notifier_unregister(struct mlx5_core_dev *dev, struct mlx5_nb *nb) return atomic_notifier_chain_unregister(&eqt->nh[nb->event_type], &nb->nb); } +EXPORT_SYMBOL(mlx5_eq_notifier_unregister); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 67e76979bb42..7281f8d6cba6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -497,7 +497,7 @@ static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) fdb_add: /* SRIOV is enabled: Forward UC MAC to vport */ - if (esw->fdb_table.legacy.fdb && esw->mode == SRIOV_LEGACY) + if (esw->fdb_table.legacy.fdb && esw->mode == MLX5_ESWITCH_LEGACY) vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport); esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM fr(%p)\n", @@ -897,7 +897,7 @@ static void esw_vport_change_handle_locked(struct mlx5_vport *vport) struct mlx5_eswitch *esw = dev->priv.eswitch; u8 mac[ETH_ALEN]; - mlx5_query_nic_vport_mac_address(dev, vport->vport, mac); + mlx5_query_nic_vport_mac_address(dev, vport->vport, true, mac); esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n", vport->vport, mac); @@ -1553,6 +1553,7 @@ static void esw_apply_vport_conf(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { u16 vport_num = vport->vport; + int flags; if (esw->manager_vport == vport_num) return; @@ -1570,11 +1571,13 @@ static void esw_apply_vport_conf(struct mlx5_eswitch *esw, vport->info.node_guid); } + flags = (vport->info.vlan || vport->info.qos) ? + SET_VLAN_STRIP | SET_VLAN_INSERT : 0; modify_esw_vport_cvlan(esw->dev, vport_num, vport->info.vlan, vport->info.qos, - (vport->info.vlan || vport->info.qos)); + flags); /* Only legacy mode needs ACLs */ - if (esw->mode == SRIOV_LEGACY) { + if (esw->mode == MLX5_ESWITCH_LEGACY) { esw_vport_ingress_config(esw, vport); esw_vport_egress_config(esw, vport); } @@ -1626,7 +1629,7 @@ static void esw_enable_vport(struct mlx5_eswitch *esw, struct mlx5_vport *vport, esw_debug(esw->dev, "Enabling VPORT(%d)\n", vport_num); /* Create steering drop counters for ingress and egress ACLs */ - if (vport_num && esw->mode == SRIOV_LEGACY) + if (vport_num && esw->mode == MLX5_ESWITCH_LEGACY) esw_vport_create_drop_counters(vport); /* Restore old vport configuration */ @@ -1680,7 +1683,7 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, vport->enabled_events = 0; esw_vport_disable_qos(esw, vport); if (esw->manager_vport != vport_num && - esw->mode == SRIOV_LEGACY) { + esw->mode == MLX5_ESWITCH_LEGACY) { mlx5_modify_vport_admin_state(esw->dev, MLX5_VPORT_STATE_OP_MOD_ESW_VPORT, vport_num, 1, @@ -1712,59 +1715,91 @@ static int eswitch_vport_event(struct notifier_block *nb, return NOTIFY_OK; } -int mlx5_esw_query_functions(struct mlx5_core_dev *dev, u32 *out, int outlen) +/** + * mlx5_esw_query_functions - Returns raw output about functions state + * @dev: Pointer to device to query + * + * mlx5_esw_query_functions() allocates and returns functions changed + * raw output memory pointer from device on success. Otherwise returns ERR_PTR. + * Caller must free the memory using kvfree() when valid pointer is returned. + */ +const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev) { + int outlen = MLX5_ST_SZ_BYTES(query_esw_functions_out); u32 in[MLX5_ST_SZ_DW(query_esw_functions_in)] = {}; + u32 *out; + int err; + + out = kvzalloc(outlen, GFP_KERNEL); + if (!out) + return ERR_PTR(-ENOMEM); MLX5_SET(query_esw_functions_in, in, opcode, MLX5_CMD_OP_QUERY_ESW_FUNCTIONS); - return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen); + err = mlx5_cmd_exec(dev, in, sizeof(in), out, outlen); + if (!err) + return out; + + kvfree(out); + return ERR_PTR(err); +} + +static void mlx5_eswitch_event_handlers_register(struct mlx5_eswitch *esw) +{ + MLX5_NB_INIT(&esw->nb, eswitch_vport_event, NIC_VPORT_CHANGE); + mlx5_eq_notifier_register(esw->dev, &esw->nb); + + if (esw->mode == MLX5_ESWITCH_OFFLOADS && mlx5_eswitch_is_funcs_handler(esw->dev)) { + MLX5_NB_INIT(&esw->esw_funcs.nb, mlx5_esw_funcs_changed_handler, + ESW_FUNCTIONS_CHANGED); + mlx5_eq_notifier_register(esw->dev, &esw->esw_funcs.nb); + } +} + +static void mlx5_eswitch_event_handlers_unregister(struct mlx5_eswitch *esw) +{ + if (esw->mode == MLX5_ESWITCH_OFFLOADS && mlx5_eswitch_is_funcs_handler(esw->dev)) + mlx5_eq_notifier_unregister(esw->dev, &esw->esw_funcs.nb); + + mlx5_eq_notifier_unregister(esw->dev, &esw->nb); + + flush_workqueue(esw->work_queue); } /* Public E-Switch API */ #define ESW_ALLOWED(esw) ((esw) && MLX5_ESWITCH_MANAGER((esw)->dev)) -int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) +int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int mode) { struct mlx5_vport *vport; - int total_nvports = 0; int err; int i, enabled_events; if (!ESW_ALLOWED(esw) || !MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ft_support)) { - esw_warn(esw->dev, "E-Switch FDB is not supported, aborting ...\n"); + esw_warn(esw->dev, "FDB is not supported, aborting ...\n"); return -EOPNOTSUPP; } if (!MLX5_CAP_ESW_INGRESS_ACL(esw->dev, ft_support)) - esw_warn(esw->dev, "E-Switch ingress ACL is not supported by FW\n"); + esw_warn(esw->dev, "ingress ACL is not supported by FW\n"); if (!MLX5_CAP_ESW_EGRESS_ACL(esw->dev, ft_support)) - esw_warn(esw->dev, "E-Switch engress ACL is not supported by FW\n"); - - esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d) mode (%d)\n", nvfs, mode); - - if (mode == SRIOV_OFFLOADS) { - if (mlx5_core_is_ecpf_esw_manager(esw->dev)) - total_nvports = esw->total_vports; - else - total_nvports = nvfs + MLX5_SPECIAL_VPORTS(esw->dev); - } + esw_warn(esw->dev, "engress ACL is not supported by FW\n"); esw->mode = mode; mlx5_lag_update(esw->dev); - if (mode == SRIOV_LEGACY) { + if (mode == MLX5_ESWITCH_LEGACY) { err = esw_create_legacy_table(esw); if (err) goto abort; } else { mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH); mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB); - err = esw_offloads_init(esw, nvfs, total_nvports); + err = esw_offloads_init(esw); } if (err) @@ -1774,11 +1809,8 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) if (err) esw_warn(esw->dev, "Failed to create eswitch TSAR"); - /* Don't enable vport events when in SRIOV_OFFLOADS mode, since: - * 1. L2 table (MPFS) is programmed by PF/VF representors netdevs set_rx_mode - * 2. FDB/Eswitch is programmed by user space tools - */ - enabled_events = (mode == SRIOV_LEGACY) ? SRIOV_VPORT_EVENTS : 0; + enabled_events = (mode == MLX5_ESWITCH_LEGACY) ? SRIOV_VPORT_EVENTS : + UC_ADDR_CHANGE; /* Enable PF vport */ vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_PF); @@ -1791,22 +1823,21 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) } /* Enable VF vports */ - mlx5_esw_for_each_vf_vport(esw, i, vport, nvfs) + mlx5_esw_for_each_vf_vport(esw, i, vport, esw->esw_funcs.num_vfs) esw_enable_vport(esw, vport, enabled_events); - if (mode == SRIOV_LEGACY) { - MLX5_NB_INIT(&esw->nb, eswitch_vport_event, NIC_VPORT_CHANGE); - mlx5_eq_notifier_register(esw->dev, &esw->nb); - } + mlx5_eswitch_event_handlers_register(esw); + + esw_info(esw->dev, "Enable: mode(%s), nvfs(%d), active vports(%d)\n", + mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS", + esw->esw_funcs.num_vfs, esw->enabled_vports); - esw_info(esw->dev, "SRIOV enabled: active vports(%d)\n", - esw->enabled_vports); return 0; abort: - esw->mode = SRIOV_NONE; + esw->mode = MLX5_ESWITCH_NONE; - if (mode == SRIOV_OFFLOADS) { + if (mode == MLX5_ESWITCH_OFFLOADS) { mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB); mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH); } @@ -1814,23 +1845,22 @@ abort: return err; } -void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) +void mlx5_eswitch_disable(struct mlx5_eswitch *esw) { struct esw_mc_addr *mc_promisc; struct mlx5_vport *vport; int old_mode; int i; - if (!ESW_ALLOWED(esw) || esw->mode == SRIOV_NONE) + if (!ESW_ALLOWED(esw) || esw->mode == MLX5_ESWITCH_NONE) return; - esw_info(esw->dev, "disable SRIOV: active vports(%d) mode(%d)\n", - esw->enabled_vports, esw->mode); + esw_info(esw->dev, "Disable: mode(%s), nvfs(%d), active vports(%d)\n", + esw->mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS", + esw->esw_funcs.num_vfs, esw->enabled_vports); mc_promisc = &esw->mc_promisc; - - if (esw->mode == SRIOV_LEGACY) - mlx5_eq_notifier_unregister(esw->dev, &esw->nb); + mlx5_eswitch_event_handlers_unregister(esw); mlx5_esw_for_all_vports(esw, i, vport) esw_disable_vport(esw, vport); @@ -1840,17 +1870,17 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) esw_destroy_tsar(esw); - if (esw->mode == SRIOV_LEGACY) + if (esw->mode == MLX5_ESWITCH_LEGACY) esw_destroy_legacy_table(esw); - else if (esw->mode == SRIOV_OFFLOADS) + else if (esw->mode == MLX5_ESWITCH_OFFLOADS) esw_offloads_cleanup(esw); old_mode = esw->mode; - esw->mode = SRIOV_NONE; + esw->mode = MLX5_ESWITCH_NONE; mlx5_lag_update(esw->dev); - if (old_mode == SRIOV_OFFLOADS) { + if (old_mode == MLX5_ESWITCH_OFFLOADS) { mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_IB); mlx5_reload_interface(esw->dev, MLX5_INTERFACE_PROTOCOL_ETH); } @@ -1858,14 +1888,16 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) int mlx5_eswitch_init(struct mlx5_core_dev *dev) { - int total_vports = MLX5_TOTAL_VPORTS(dev); struct mlx5_eswitch *esw; struct mlx5_vport *vport; + int total_vports; int err, i; if (!MLX5_VPORT_MANAGER(dev)) return 0; + total_vports = mlx5_eswitch_get_total_vports(dev); + esw_info(dev, "Total vports %d, per vport: max uc(%d) max mc(%d)\n", total_vports, @@ -1878,6 +1910,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) esw->dev = dev; esw->manager_vport = mlx5_eswitch_manager_vport(dev); + esw->first_host_vport = mlx5_eswitch_first_host_vport_num(dev); esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq"); if (!esw->work_queue) { @@ -1911,7 +1944,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) } esw->enabled_vports = 0; - esw->mode = SRIOV_NONE; + esw->mode = MLX5_ESWITCH_NONE; esw->offloads.inline_mode = MLX5_INLINE_MODE_NONE; if (MLX5_CAP_ESW_FLOWTABLE_FDB(dev, reformat) && MLX5_CAP_ESW_FLOWTABLE_FDB(dev, decap)) @@ -1981,7 +2014,7 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, ether_addr_copy(evport->info.mac, mac); evport->info.node_guid = node_guid; - if (evport->enabled && esw->mode == SRIOV_LEGACY) + if (evport->enabled && esw->mode == MLX5_ESWITCH_LEGACY) err = esw_vport_ingress_config(esw, evport); unlock: @@ -2065,7 +2098,7 @@ int __mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, evport->info.vlan = vlan; evport->info.qos = qos; - if (evport->enabled && esw->mode == SRIOV_LEGACY) { + if (evport->enabled && esw->mode == MLX5_ESWITCH_LEGACY) { err = esw_vport_ingress_config(esw, evport); if (err) goto unlock; @@ -2107,7 +2140,7 @@ int mlx5_eswitch_set_vport_spoofchk(struct mlx5_eswitch *esw, mlx5_core_warn(esw->dev, "Spoofchk in set while MAC is invalid, vport(%d)\n", evport->vport); - if (evport->enabled && esw->mode == SRIOV_LEGACY) + if (evport->enabled && esw->mode == MLX5_ESWITCH_LEGACY) err = esw_vport_ingress_config(esw, evport); if (err) evport->info.spoofchk = pschk; @@ -2203,7 +2236,7 @@ int mlx5_eswitch_set_vepa(struct mlx5_eswitch *esw, u8 setting) return -EPERM; mutex_lock(&esw->state_lock); - if (esw->mode != SRIOV_LEGACY) { + if (esw->mode != MLX5_ESWITCH_LEGACY) { err = -EOPNOTSUPP; goto out; } @@ -2226,7 +2259,7 @@ int mlx5_eswitch_get_vepa(struct mlx5_eswitch *esw, u8 *setting) return -EPERM; mutex_lock(&esw->state_lock); - if (esw->mode != SRIOV_LEGACY) { + if (esw->mode != MLX5_ESWITCH_LEGACY) { err = -EOPNOTSUPP; goto out; } @@ -2369,7 +2402,7 @@ static int mlx5_eswitch_query_vport_drop_stats(struct mlx5_core_dev *dev, u64 bytes = 0; int err = 0; - if (!vport->enabled || esw->mode != SRIOV_LEGACY) + if (!vport->enabled || esw->mode != MLX5_ESWITCH_LEGACY) return 0; if (vport->egress.drop_counter) @@ -2479,7 +2512,7 @@ free_out: u8 mlx5_eswitch_mode(struct mlx5_eswitch *esw) { - return ESW_ALLOWED(esw) ? esw->mode : SRIOV_NONE; + return ESW_ALLOWED(esw) ? esw->mode : MLX5_ESWITCH_NONE; } EXPORT_SYMBOL_GPL(mlx5_eswitch_mode); @@ -2496,10 +2529,10 @@ EXPORT_SYMBOL(mlx5_eswitch_get_encap_mode); bool mlx5_esw_lag_prereq(struct mlx5_core_dev *dev0, struct mlx5_core_dev *dev1) { - if ((dev0->priv.eswitch->mode == SRIOV_NONE && - dev1->priv.eswitch->mode == SRIOV_NONE) || - (dev0->priv.eswitch->mode == SRIOV_OFFLOADS && - dev1->priv.eswitch->mode == SRIOV_OFFLOADS)) + if ((dev0->priv.eswitch->mode == MLX5_ESWITCH_NONE && + dev1->priv.eswitch->mode == MLX5_ESWITCH_NONE) || + (dev0->priv.eswitch->mode == MLX5_ESWITCH_OFFLOADS && + dev1->priv.eswitch->mode == MLX5_ESWITCH_OFFLOADS)) return true; return false; @@ -2508,6 +2541,26 @@ bool mlx5_esw_lag_prereq(struct mlx5_core_dev *dev0, struct mlx5_core_dev *dev1) bool mlx5_esw_multipath_prereq(struct mlx5_core_dev *dev0, struct mlx5_core_dev *dev1) { - return (dev0->priv.eswitch->mode == SRIOV_OFFLOADS && - dev1->priv.eswitch->mode == SRIOV_OFFLOADS); + return (dev0->priv.eswitch->mode == MLX5_ESWITCH_OFFLOADS && + dev1->priv.eswitch->mode == MLX5_ESWITCH_OFFLOADS); +} + +void mlx5_eswitch_update_num_of_vfs(struct mlx5_eswitch *esw, const int num_vfs) +{ + const u32 *out; + + WARN_ON_ONCE(esw->mode != MLX5_ESWITCH_NONE); + + if (!mlx5_core_is_ecpf_esw_manager(esw->dev)) { + esw->esw_funcs.num_vfs = num_vfs; + return; + } + + out = mlx5_esw_query_functions(esw->dev); + if (IS_ERR(out)) + return; + + esw->esw_funcs.num_vfs = MLX5_GET(query_esw_functions_out, out, + host_params_context.host_num_of_vfs); + kvfree(out); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index b6472cf166ec..a38e8a3c7c9a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -229,12 +229,12 @@ struct mlx5_eswitch { int mode; int nvports; u16 manager_vport; + u16 first_host_vport; struct mlx5_esw_functions esw_funcs; }; void esw_offloads_cleanup(struct mlx5_eswitch *esw); -int esw_offloads_init(struct mlx5_eswitch *esw, int vf_nvports, - int total_nvports); +int esw_offloads_init(struct mlx5_eswitch *esw); void esw_offloads_cleanup_reps(struct mlx5_eswitch *esw); int esw_offloads_init_reps(struct mlx5_eswitch *esw); void esw_vport_cleanup_ingress_rules(struct mlx5_eswitch *esw, @@ -255,8 +255,8 @@ void esw_vport_del_ingress_acl_modify_metadata(struct mlx5_eswitch *esw, /* E-Switch API */ int mlx5_eswitch_init(struct mlx5_core_dev *dev); void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw); -int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode); -void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw); +int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int mode); +void mlx5_eswitch_disable(struct mlx5_eswitch *esw); int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, u16 vport, u8 mac[ETH_ALEN]); int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, @@ -392,7 +392,7 @@ int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode); int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode, struct netlink_ext_ack *extack); int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode); -int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode); +int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, u8 *mode); int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, enum devlink_eswitch_encap_mode encap, struct netlink_ext_ack *extack); @@ -425,7 +425,7 @@ bool mlx5_esw_lag_prereq(struct mlx5_core_dev *dev0, bool mlx5_esw_multipath_prereq(struct mlx5_core_dev *dev0, struct mlx5_core_dev *dev1); -int mlx5_esw_query_functions(struct mlx5_core_dev *dev, u32 *out, int outlen); +const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev); #define MLX5_DEBUG_ESWITCH_MASK BIT(3) @@ -445,6 +445,12 @@ static inline u16 mlx5_eswitch_manager_vport(struct mlx5_core_dev *dev) MLX5_VPORT_ECPF : MLX5_VPORT_PF; } +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; +} + static inline bool mlx5_eswitch_is_funcs_handler(struct mlx5_core_dev *dev) { /* Ideally device should have the functions changed supported @@ -541,25 +547,48 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw); #define mlx5_esw_for_each_vf_vport_num_reverse(esw, vport, nvfs) \ for ((vport) = (nvfs); (vport) >= MLX5_VPORT_FIRST_VF; (vport)--) +/* Includes host PF (vport 0) if it's not esw manager. */ +#define mlx5_esw_for_each_host_func_rep(esw, i, rep, nvfs) \ + for ((i) = (esw)->first_host_vport; \ + (rep) = &(esw)->offloads.vport_reps[i], \ + (i) <= (nvfs); (i)++) + +#define mlx5_esw_for_each_host_func_rep_reverse(esw, i, rep, nvfs) \ + for ((i) = (nvfs); \ + (rep) = &(esw)->offloads.vport_reps[i], \ + (i) >= (esw)->first_host_vport; (i)--) + +#define mlx5_esw_for_each_host_func_vport(esw, vport, nvfs) \ + for ((vport) = (esw)->first_host_vport; \ + (vport) <= (nvfs); (vport)++) + +#define mlx5_esw_for_each_host_func_vport_reverse(esw, vport, nvfs) \ + for ((vport) = (nvfs); \ + (vport) >= (esw)->first_host_vport; (vport)--) + struct mlx5_vport *__must_check mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num); bool mlx5_eswitch_is_vf_vport(const struct mlx5_eswitch *esw, u16 vport_num); +void mlx5_eswitch_update_num_of_vfs(struct mlx5_eswitch *esw, const int num_vfs); +int mlx5_esw_funcs_changed_handler(struct notifier_block *nb, unsigned long type, void *data); + #else /* CONFIG_MLX5_ESWITCH */ /* eswitch API stubs */ static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } static inline void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) {} -static inline int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) { return 0; } -static inline void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) {} +static inline int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int mode) { return 0; } +static inline void mlx5_eswitch_disable(struct mlx5_eswitch *esw) {} static inline bool mlx5_esw_lag_prereq(struct mlx5_core_dev *dev0, struct mlx5_core_dev *dev1) { return true; } static inline bool mlx5_eswitch_is_funcs_handler(struct mlx5_core_dev *dev) { return false; } -static inline int -mlx5_esw_query_functions(struct mlx5_core_dev *dev, u32 *out, int outlen) +static inline const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev) { - return -EOPNOTSUPP; + return ERR_PTR(-EOPNOTSUPP); } +static inline void mlx5_eswitch_update_num_of_vfs(struct mlx5_eswitch *esw, const int num_vfs) {} + #define FDB_MAX_CHAIN 1 #define FDB_SLOW_PATH_CHAIN (FDB_MAX_CHAIN + 1) #define FDB_MAX_PRIO 1 diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index af08e06f73ae..8ed4497929b9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -147,7 +147,7 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, struct mlx5_flow_table *fdb; int j, i = 0; - if (esw->mode != SRIOV_OFFLOADS) + if (esw->mode != MLX5_ESWITCH_OFFLOADS) return ERR_PTR(-EOPNOTSUPP); flow_act.action = attr->action; @@ -357,11 +357,10 @@ mlx5_eswitch_del_fwd_rule(struct mlx5_eswitch *esw, static int esw_set_global_vlan_pop(struct mlx5_eswitch *esw, u8 val) { struct mlx5_eswitch_rep *rep; - int vf_vport, err = 0; + int i, err = 0; esw_debug(esw->dev, "%s applying global %s policy\n", __func__, val ? "pop" : "none"); - for (vf_vport = 1; vf_vport < esw->enabled_vports; vf_vport++) { - rep = &esw->offloads.vport_reps[vf_vport]; + mlx5_esw_for_each_host_func_rep(esw, i, rep, esw->esw_funcs.num_vfs) { if (atomic_read(&rep->rep_data[REP_ETH].state) != REP_LOADED) continue; @@ -1370,21 +1369,22 @@ out: static int esw_offloads_start(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack) { - int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs; + int err, err1; - if (esw->mode != SRIOV_LEGACY && + if (esw->mode != MLX5_ESWITCH_LEGACY && !mlx5_core_is_ecpf_esw_manager(esw->dev)) { NL_SET_ERR_MSG_MOD(extack, "Can't set offloads mode, SRIOV legacy not enabled"); return -EINVAL; } - mlx5_eswitch_disable_sriov(esw); - err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_OFFLOADS); + mlx5_eswitch_disable(esw); + mlx5_eswitch_update_num_of_vfs(esw, esw->dev->priv.sriov.num_vfs); + err = mlx5_eswitch_enable(esw, MLX5_ESWITCH_OFFLOADS); if (err) { NL_SET_ERR_MSG_MOD(extack, "Failed setting eswitch to offloads"); - err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY); + err1 = mlx5_eswitch_enable(esw, MLX5_ESWITCH_LEGACY); if (err1) { NL_SET_ERR_MSG_MOD(extack, "Failed setting eswitch back to legacy"); @@ -1392,7 +1392,6 @@ static int esw_offloads_start(struct mlx5_eswitch *esw, } if (esw->offloads.inline_mode == MLX5_INLINE_MODE_NONE) { if (mlx5_eswitch_inline_mode_get(esw, - num_vfs, &esw->offloads.inline_mode)) { esw->offloads.inline_mode = MLX5_INLINE_MODE_L2; NL_SET_ERR_MSG_MOD(extack, @@ -1409,11 +1408,11 @@ void esw_offloads_cleanup_reps(struct mlx5_eswitch *esw) int esw_offloads_init_reps(struct mlx5_eswitch *esw) { - int total_vports = MLX5_TOTAL_VPORTS(esw->dev); + int total_vports = esw->total_vports; struct mlx5_core_dev *dev = esw->dev; struct mlx5_eswitch_rep *rep; u8 hw_id[ETH_ALEN], rep_type; - int vport; + int vport_index; esw->offloads.vport_reps = kcalloc(total_vports, sizeof(struct mlx5_eswitch_rep), @@ -1421,10 +1420,11 @@ int esw_offloads_init_reps(struct mlx5_eswitch *esw) if (!esw->offloads.vport_reps) return -ENOMEM; - mlx5_query_nic_vport_mac_address(dev, 0, hw_id); + mlx5_query_mac_address(dev, hw_id); - mlx5_esw_for_all_reps(esw, vport, rep) { - rep->vport = mlx5_eswitch_index_to_vport_num(esw, vport); + mlx5_esw_for_all_reps(esw, vport_index, rep) { + rep->vport = mlx5_eswitch_index_to_vport_num(esw, vport_index); + rep->vport_index = vport_index; ether_addr_copy(rep->hw_id, hw_id); for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++) @@ -1479,21 +1479,20 @@ static void esw_offloads_unload_vf_reps(struct mlx5_eswitch *esw, int nvports) __unload_reps_vf_vport(esw, nvports, rep_type); } -static void __unload_reps_all_vport(struct mlx5_eswitch *esw, int nvports, - u8 rep_type) +static void __unload_reps_all_vport(struct mlx5_eswitch *esw, u8 rep_type) { - __unload_reps_vf_vport(esw, nvports, rep_type); + __unload_reps_vf_vport(esw, esw->esw_funcs.num_vfs, rep_type); /* Special vports must be the last to unload. */ __unload_reps_special_vport(esw, rep_type); } -static void esw_offloads_unload_all_reps(struct mlx5_eswitch *esw, int nvports) +static void esw_offloads_unload_all_reps(struct mlx5_eswitch *esw) { u8 rep_type = NUM_REP_TYPES; while (rep_type-- > 0) - __unload_reps_all_vport(esw, nvports, rep_type); + __unload_reps_all_vport(esw, rep_type); } static int __esw_offloads_load_rep(struct mlx5_eswitch *esw, @@ -1569,6 +1568,26 @@ err_vf: return err; } +static int __load_reps_all_vport(struct mlx5_eswitch *esw, u8 rep_type) +{ + int err; + + /* Special vports must be loaded first, uplink rep creates mdev resource. */ + err = __load_reps_special_vport(esw, rep_type); + if (err) + return err; + + err = __load_reps_vf_vport(esw, esw->esw_funcs.num_vfs, rep_type); + if (err) + goto err_vfs; + + return 0; + +err_vfs: + __unload_reps_special_vport(esw, rep_type); + return err; +} + static int esw_offloads_load_vf_reps(struct mlx5_eswitch *esw, int nvports) { u8 rep_type = 0; @@ -1588,13 +1607,13 @@ err_reps: return err; } -static int esw_offloads_load_special_vport(struct mlx5_eswitch *esw) +static int esw_offloads_load_all_reps(struct mlx5_eswitch *esw) { u8 rep_type = 0; int err; for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++) { - err = __load_reps_special_vport(esw, rep_type); + err = __load_reps_all_vport(esw, rep_type); if (err) goto err_reps; } @@ -1603,7 +1622,7 @@ static int esw_offloads_load_special_vport(struct mlx5_eswitch *esw) err_reps: while (rep_type-- > 0) - __unload_reps_special_vport(esw, rep_type); + __unload_reps_all_vport(esw, rep_type); return err; } @@ -1989,11 +2008,17 @@ static void esw_destroy_offloads_acl_tables(struct mlx5_eswitch *esw) esw->flags &= ~MLX5_ESWITCH_VPORT_MATCH_METADATA; } -static int esw_offloads_steering_init(struct mlx5_eswitch *esw, int vf_nvports, - int nvports) +static int esw_offloads_steering_init(struct mlx5_eswitch *esw) { + int num_vfs = esw->esw_funcs.num_vfs; + int total_vports; int err; + if (mlx5_core_is_ecpf_esw_manager(esw->dev)) + total_vports = esw->total_vports; + else + total_vports = num_vfs + MLX5_SPECIAL_VPORTS(esw->dev); + memset(&esw->fdb_table.offloads, 0, sizeof(struct offloads_fdb)); mutex_init(&esw->fdb_table.offloads.fdb_prio_lock); @@ -2001,15 +2026,15 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw, int vf_nvports, if (err) return err; - err = esw_create_offloads_fdb_tables(esw, nvports); + err = esw_create_offloads_fdb_tables(esw, total_vports); if (err) goto create_fdb_err; - err = esw_create_offloads_table(esw, nvports); + err = esw_create_offloads_table(esw, total_vports); if (err) goto create_ft_err; - err = esw_create_vport_rx_group(esw, nvports); + err = esw_create_vport_rx_group(esw, total_vports); if (err) goto create_fg_err; @@ -2035,56 +2060,53 @@ static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) esw_destroy_offloads_acl_tables(esw); } -static void esw_functions_changed_event_handler(struct work_struct *work) +static void +esw_vfs_changed_event_handler(struct mlx5_eswitch *esw, const u32 *out) { - u32 out[MLX5_ST_SZ_DW(query_esw_functions_out)] = {}; - struct mlx5_host_work *host_work; - struct mlx5_eswitch *esw; - u16 num_vfs = 0; - int err; + bool host_pf_disabled; + u16 new_num_vfs; - host_work = container_of(work, struct mlx5_host_work, work); - esw = host_work->esw; + 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); - err = mlx5_esw_query_functions(esw->dev, out, sizeof(out)); - num_vfs = MLX5_GET(query_esw_functions_out, out, - host_params_context.host_num_of_vfs); - if (err || num_vfs == esw->esw_funcs.num_vfs) - goto out; + if (new_num_vfs == esw->esw_funcs.num_vfs || host_pf_disabled) + return; /* Number of VFs can only change from "0 to x" or "x to 0". */ if (esw->esw_funcs.num_vfs > 0) { esw_offloads_unload_vf_reps(esw, esw->esw_funcs.num_vfs); } else { - err = esw_offloads_load_vf_reps(esw, num_vfs); + int err; + err = esw_offloads_load_vf_reps(esw, new_num_vfs); if (err) - goto out; + return; } - - esw->esw_funcs.num_vfs = num_vfs; - -out: - kfree(host_work); + esw->esw_funcs.num_vfs = new_num_vfs; } -static void esw_emulate_event_handler(struct work_struct *work) +static void esw_functions_changed_event_handler(struct work_struct *work) { - struct mlx5_host_work *host_work = - container_of(work, struct mlx5_host_work, work); - struct mlx5_eswitch *esw = host_work->esw; - int err; + struct mlx5_host_work *host_work; + struct mlx5_eswitch *esw; + const u32 *out; - if (esw->esw_funcs.num_vfs) { - err = esw_offloads_load_vf_reps(esw, esw->esw_funcs.num_vfs); - if (err) - esw_warn(esw->dev, "Load vf reps err=%d\n", err); - } + host_work = container_of(work, struct mlx5_host_work, work); + esw = host_work->esw; + + out = mlx5_esw_query_functions(esw->dev); + if (IS_ERR(out)) + goto out; + + esw_vfs_changed_event_handler(esw, out); + kvfree(out); +out: kfree(host_work); } -static int esw_functions_changed_event(struct notifier_block *nb, - unsigned long type, void *data) +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; @@ -2099,44 +2121,17 @@ static int esw_functions_changed_event(struct notifier_block *nb, host_work->esw = esw; - if (mlx5_eswitch_is_funcs_handler(esw->dev)) - INIT_WORK(&host_work->work, - esw_functions_changed_event_handler); - else - INIT_WORK(&host_work->work, esw_emulate_event_handler); + INIT_WORK(&host_work->work, esw_functions_changed_event_handler); queue_work(esw->work_queue, &host_work->work); return NOTIFY_OK; } -static void esw_functions_changed_event_init(struct mlx5_eswitch *esw, - u16 vf_nvports) -{ - if (mlx5_eswitch_is_funcs_handler(esw->dev)) { - esw->esw_funcs.num_vfs = 0; - MLX5_NB_INIT(&esw->esw_funcs.nb, esw_functions_changed_event, - ESW_FUNCTIONS_CHANGED); - mlx5_eq_notifier_register(esw->dev, &esw->esw_funcs.nb); - } else { - esw->esw_funcs.num_vfs = vf_nvports; - } -} - -static void esw_functions_changed_event_cleanup(struct mlx5_eswitch *esw) -{ - if (!mlx5_eswitch_is_funcs_handler(esw->dev)) - return; - - mlx5_eq_notifier_unregister(esw->dev, &esw->esw_funcs.nb); - flush_workqueue(esw->work_queue); -} - -int esw_offloads_init(struct mlx5_eswitch *esw, int vf_nvports, - int total_nvports) +int esw_offloads_init(struct mlx5_eswitch *esw) { int err; - err = esw_offloads_steering_init(esw, vf_nvports, total_nvports); + err = esw_offloads_steering_init(esw); if (err) return err; @@ -2146,31 +2141,15 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int vf_nvports, goto err_vport_metadata; } - /* Only load special vports reps. VF reps will be loaded in - * context of functions_changed event handler through real - * or emulated event. - */ - err = esw_offloads_load_special_vport(esw); + err = esw_offloads_load_all_reps(esw); if (err) goto err_reps; esw_offloads_devcom_init(esw); mutex_init(&esw->offloads.termtbl_mutex); - esw_functions_changed_event_init(esw, vf_nvports); - mlx5_rdma_enable_roce(esw->dev); - /* Call esw_functions_changed event to load VF reps: - * 1. HW does not support the event then emulate it - * Or - * 2. The event was already notified when num_vfs changed - * and eswitch was in legacy mode - */ - esw_functions_changed_event(&esw->esw_funcs.nb.nb, - MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED, - NULL); - return 0; err_reps: @@ -2184,13 +2163,13 @@ err_vport_metadata: static int esw_offloads_stop(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack) { - int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs; + int err, err1; - mlx5_eswitch_disable_sriov(esw); - err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY); + mlx5_eswitch_disable(esw); + err = mlx5_eswitch_enable(esw, MLX5_ESWITCH_LEGACY); if (err) { NL_SET_ERR_MSG_MOD(extack, "Failed setting eswitch to legacy"); - err1 = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_OFFLOADS); + err1 = mlx5_eswitch_enable(esw, MLX5_ESWITCH_OFFLOADS); if (err1) { NL_SET_ERR_MSG_MOD(extack, "Failed setting eswitch back to offloads"); @@ -2202,10 +2181,9 @@ static int esw_offloads_stop(struct mlx5_eswitch *esw, void esw_offloads_cleanup(struct mlx5_eswitch *esw) { - esw_functions_changed_event_cleanup(esw); mlx5_rdma_disable_roce(esw->dev); esw_offloads_devcom_cleanup(esw); - esw_offloads_unload_all_reps(esw, esw->esw_funcs.num_vfs); + esw_offloads_unload_all_reps(esw); if (mlx5_eswitch_vport_match_metadata_enabled(esw)) mlx5_eswitch_disable_passing_vport_metadata(esw); esw_offloads_steering_cleanup(esw); @@ -2215,10 +2193,10 @@ static int esw_mode_from_devlink(u16 mode, u16 *mlx5_mode) { switch (mode) { case DEVLINK_ESWITCH_MODE_LEGACY: - *mlx5_mode = SRIOV_LEGACY; + *mlx5_mode = MLX5_ESWITCH_LEGACY; break; case DEVLINK_ESWITCH_MODE_SWITCHDEV: - *mlx5_mode = SRIOV_OFFLOADS; + *mlx5_mode = MLX5_ESWITCH_OFFLOADS; break; default: return -EINVAL; @@ -2230,10 +2208,10 @@ static int esw_mode_from_devlink(u16 mode, u16 *mlx5_mode) static int esw_mode_to_devlink(u16 mlx5_mode, u16 *mode) { switch (mlx5_mode) { - case SRIOV_LEGACY: + case MLX5_ESWITCH_LEGACY: *mode = DEVLINK_ESWITCH_MODE_LEGACY; break; - case SRIOV_OFFLOADS: + case MLX5_ESWITCH_OFFLOADS: *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; break; default: @@ -2297,7 +2275,7 @@ static int mlx5_devlink_eswitch_check(struct devlink *devlink) if(!MLX5_ESWITCH_MANAGER(dev)) return -EPERM; - if (dev->priv.eswitch->mode == SRIOV_NONE && + if (dev->priv.eswitch->mode == MLX5_ESWITCH_NONE && !mlx5_core_is_ecpf_esw_manager(dev)) return -EOPNOTSUPP; @@ -2348,7 +2326,7 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode, { struct mlx5_core_dev *dev = devlink_priv(devlink); struct mlx5_eswitch *esw = dev->priv.eswitch; - int err, vport; + int err, vport, num_vport; u8 mlx5_mode; err = mlx5_devlink_eswitch_check(devlink); @@ -2377,7 +2355,7 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode, if (err) goto out; - for (vport = 1; vport < esw->enabled_vports; vport++) { + mlx5_esw_for_each_host_func_vport(esw, vport, esw->esw_funcs.num_vfs) { err = mlx5_modify_nic_vport_min_inline(dev, vport, mlx5_mode); if (err) { NL_SET_ERR_MSG_MOD(extack, @@ -2390,7 +2368,8 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode, return 0; revert_inline_mode: - while (--vport > 0) + num_vport = --vport; + mlx5_esw_for_each_host_func_vport_reverse(esw, vport, num_vport) mlx5_modify_nic_vport_min_inline(dev, vport, esw->offloads.inline_mode); @@ -2411,7 +2390,7 @@ int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode) return esw_inline_mode_to_devlink(esw->offloads.inline_mode, mode); } -int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode) +int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, u8 *mode) { u8 prev_mlx5_mode, mlx5_mode = MLX5_INLINE_MODE_L2; struct mlx5_core_dev *dev = esw->dev; @@ -2420,7 +2399,7 @@ int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode) if (!MLX5_CAP_GEN(dev, vport_group_manager)) return -EOPNOTSUPP; - if (esw->mode == SRIOV_NONE) + if (esw->mode == MLX5_ESWITCH_NONE) return -EOPNOTSUPP; switch (MLX5_CAP_ETH(dev, wqe_inline_mode)) { @@ -2435,9 +2414,10 @@ int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode) } query_vports: - for (vport = 1; vport <= nvfs; vport++) { + mlx5_query_nic_vport_min_inline(dev, esw->first_host_vport, &prev_mlx5_mode); + mlx5_esw_for_each_host_func_vport(esw, vport, esw->esw_funcs.num_vfs) { mlx5_query_nic_vport_min_inline(dev, vport, &mlx5_mode); - if (vport > 1 && prev_mlx5_mode != mlx5_mode) + if (prev_mlx5_mode != mlx5_mode) return -EINVAL; prev_mlx5_mode = mlx5_mode; } @@ -2467,7 +2447,7 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, if (encap && encap != DEVLINK_ESWITCH_ENCAP_MODE_BASIC) return -EOPNOTSUPP; - if (esw->mode == SRIOV_LEGACY) { + if (esw->mode == MLX5_ESWITCH_LEGACY) { esw->offloads.encap = encap; return 0; } @@ -2530,12 +2510,11 @@ EXPORT_SYMBOL(mlx5_eswitch_register_vport_reps); void mlx5_eswitch_unregister_vport_reps(struct mlx5_eswitch *esw, u8 rep_type) { - u16 max_vf = mlx5_core_max_vfs(esw->dev); struct mlx5_eswitch_rep *rep; int i; - if (esw->mode == SRIOV_OFFLOADS) - __unload_reps_all_vport(esw, max_vf, rep_type); + if (esw->mode == MLX5_ESWITCH_OFFLOADS) + __unload_reps_all_vport(esw, rep_type); mlx5_esw_for_all_reps(esw, i, rep) atomic_set(&rep->rep_data[rep_type].state, REP_UNREGISTERED); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c index ca2296a2f9ee..4c50efe4e7f1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c @@ -414,7 +414,8 @@ static void mlx5_fpga_conn_cq_tasklet(unsigned long data) mlx5_fpga_conn_cqes(conn, MLX5_FPGA_CQ_BUDGET); } -static void mlx5_fpga_conn_cq_complete(struct mlx5_core_cq *mcq) +static void mlx5_fpga_conn_cq_complete(struct mlx5_core_cq *mcq, + struct mlx5_eqe *eqe) { struct mlx5_fpga_conn *conn; @@ -429,6 +430,7 @@ static int mlx5_fpga_conn_create_cq(struct mlx5_fpga_conn *conn, int cq_size) struct mlx5_fpga_device *fdev = conn->fdev; struct mlx5_core_dev *mdev = fdev->mdev; u32 temp_cqc[MLX5_ST_SZ_DW(cqc)] = {0}; + u32 out[MLX5_ST_SZ_DW(create_cq_out)]; struct mlx5_wq_param wqp; struct mlx5_cqe64 *cqe; int inlen, err, eqn; @@ -476,7 +478,7 @@ static int mlx5_fpga_conn_create_cq(struct mlx5_fpga_conn *conn, int cq_size) pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas); mlx5_fill_page_frag_array(&conn->cq.wq_ctrl.buf, pas); - err = mlx5_core_create_cq(mdev, &conn->cq.mcq, in, inlen); + err = mlx5_core_create_cq(mdev, &conn->cq.mcq, in, inlen, out, sizeof(out)); kvfree(in); if (err) @@ -867,7 +869,7 @@ struct mlx5_fpga_conn *mlx5_fpga_conn_create(struct mlx5_fpga_device *fdev, conn->cb_arg = attr->cb_arg; remote_mac = MLX5_ADDR_OF(fpga_qpc, conn->fpga_qpc, remote_mac_47_32); - err = mlx5_query_nic_vport_mac_address(fdev->mdev, 0, remote_mac); + err = mlx5_query_mac_address(fdev->mdev, remote_mac); if (err) { mlx5_fpga_err(fdev, "Failed to query local MAC: %d\n", err); ret = ERR_PTR(err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h index 2b5e63b0d4d6..382985e65b48 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h @@ -37,8 +37,6 @@ #include "accel/ipsec.h" #include "fs_cmd.h" -#ifdef CONFIG_MLX5_FPGA - u32 mlx5_fpga_ipsec_device_caps(struct mlx5_core_dev *mdev); unsigned int mlx5_fpga_ipsec_counters_count(struct mlx5_core_dev *mdev); int mlx5_fpga_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters, @@ -66,77 +64,4 @@ int mlx5_fpga_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm, const struct mlx5_flow_cmds * mlx5_fs_cmd_get_default_ipsec_fpga_cmds(enum fs_flow_table_type type); -#else - -static inline u32 mlx5_fpga_ipsec_device_caps(struct mlx5_core_dev *mdev) -{ - return 0; -} - -static inline unsigned int -mlx5_fpga_ipsec_counters_count(struct mlx5_core_dev *mdev) -{ - return 0; -} - -static inline int mlx5_fpga_ipsec_counters_read(struct mlx5_core_dev *mdev, - u64 *counters) -{ - return 0; -} - -static inline void * -mlx5_fpga_ipsec_create_sa_ctx(struct mlx5_core_dev *mdev, - struct mlx5_accel_esp_xfrm *accel_xfrm, - const __be32 saddr[4], - const __be32 daddr[4], - const __be32 spi, bool is_ipv6) -{ - return NULL; -} - -static inline void mlx5_fpga_ipsec_delete_sa_ctx(void *context) -{ -} - -static inline int mlx5_fpga_ipsec_init(struct mlx5_core_dev *mdev) -{ - return 0; -} - -static inline void mlx5_fpga_ipsec_cleanup(struct mlx5_core_dev *mdev) -{ -} - -static inline void mlx5_fpga_ipsec_build_fs_cmds(void) -{ -} - -static inline struct mlx5_accel_esp_xfrm * -mlx5_fpga_esp_create_xfrm(struct mlx5_core_dev *mdev, - const struct mlx5_accel_esp_xfrm_attrs *attrs, - u32 flags) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void mlx5_fpga_esp_destroy_xfrm(struct mlx5_accel_esp_xfrm *xfrm) -{ -} - -static inline int -mlx5_fpga_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm, - const struct mlx5_accel_esp_xfrm_attrs *attrs) -{ - return -EOPNOTSUPP; -} - -static inline const struct mlx5_flow_cmds * -mlx5_fs_cmd_get_default_ipsec_fpga_cmds(enum fs_flow_table_type type) -{ - return mlx5_fs_cmd_get_default(type); -} - -#endif /* CONFIG_MLX5_FPGA */ - #endif /* __MLX5_FPGA_SADB_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index a68a51c5011a..3e99799bdb40 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -2092,7 +2092,7 @@ struct mlx5_flow_namespace *mlx5_get_flow_vport_acl_namespace(struct mlx5_core_d { struct mlx5_flow_steering *steering = dev->priv.steering; - if (!steering || vport >= MLX5_TOTAL_VPORTS(dev)) + if (!steering || vport >= mlx5_eswitch_get_total_vports(dev)) return NULL; switch (type) { @@ -2423,7 +2423,7 @@ static void cleanup_egress_acls_root_ns(struct mlx5_core_dev *dev) if (!steering->esw_egress_root_ns) return; - for (i = 0; i < MLX5_TOTAL_VPORTS(dev); i++) + for (i = 0; i < mlx5_eswitch_get_total_vports(dev); i++) cleanup_root_ns(steering->esw_egress_root_ns[i]); kfree(steering->esw_egress_root_ns); @@ -2438,7 +2438,7 @@ static void cleanup_ingress_acls_root_ns(struct mlx5_core_dev *dev) if (!steering->esw_ingress_root_ns) return; - for (i = 0; i < MLX5_TOTAL_VPORTS(dev); i++) + for (i = 0; i < mlx5_eswitch_get_total_vports(dev); i++) cleanup_root_ns(steering->esw_ingress_root_ns[i]); kfree(steering->esw_ingress_root_ns); @@ -2606,16 +2606,18 @@ static int init_ingress_acl_root_ns(struct mlx5_flow_steering *steering, int vpo static int init_egress_acls_root_ns(struct mlx5_core_dev *dev) { struct mlx5_flow_steering *steering = dev->priv.steering; + int total_vports = mlx5_eswitch_get_total_vports(dev); int err; int i; - steering->esw_egress_root_ns = kcalloc(MLX5_TOTAL_VPORTS(dev), - sizeof(*steering->esw_egress_root_ns), - GFP_KERNEL); + steering->esw_egress_root_ns = + kcalloc(total_vports, + sizeof(*steering->esw_egress_root_ns), + GFP_KERNEL); if (!steering->esw_egress_root_ns) return -ENOMEM; - for (i = 0; i < MLX5_TOTAL_VPORTS(dev); i++) { + for (i = 0; i < total_vports; i++) { err = init_egress_acl_root_ns(steering, i); if (err) goto cleanup_root_ns; @@ -2634,16 +2636,18 @@ cleanup_root_ns: static int init_ingress_acls_root_ns(struct mlx5_core_dev *dev) { struct mlx5_flow_steering *steering = dev->priv.steering; + int total_vports = mlx5_eswitch_get_total_vports(dev); int err; int i; - steering->esw_ingress_root_ns = kcalloc(MLX5_TOTAL_VPORTS(dev), - sizeof(*steering->esw_ingress_root_ns), - GFP_KERNEL); + steering->esw_ingress_root_ns = + kcalloc(total_vports, + sizeof(*steering->esw_ingress_root_ns), + GFP_KERNEL); if (!steering->esw_ingress_root_ns) return -ENOMEM; - for (i = 0; i < MLX5_TOTAL_VPORTS(dev); i++) { + for (i = 0; i < total_vports; i++) { err = init_ingress_acl_root_ns(steering, i); if (err) goto cleanup_root_ns; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index e8fedb307b2c..a19790dee7b2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -37,6 +37,37 @@ #include "mlx5_core.h" #include "../../mlxfw/mlxfw.h" +enum { + MCQS_IDENTIFIER_BOOT_IMG = 0x1, + MCQS_IDENTIFIER_OEM_NVCONFIG = 0x4, + MCQS_IDENTIFIER_MLNX_NVCONFIG = 0x5, + MCQS_IDENTIFIER_CS_TOKEN = 0x6, + MCQS_IDENTIFIER_DBG_TOKEN = 0x7, + MCQS_IDENTIFIER_GEARBOX = 0xA, +}; + +enum { + MCQS_UPDATE_STATE_IDLE, + MCQS_UPDATE_STATE_IN_PROGRESS, + MCQS_UPDATE_STATE_APPLIED, + MCQS_UPDATE_STATE_ACTIVE, + MCQS_UPDATE_STATE_ACTIVE_PENDING_RESET, + MCQS_UPDATE_STATE_FAILED, + MCQS_UPDATE_STATE_CANCELED, + MCQS_UPDATE_STATE_BUSY, +}; + +enum { + MCQI_INFO_TYPE_CAPABILITIES = 0x0, + MCQI_INFO_TYPE_VERSION = 0x1, + MCQI_INFO_TYPE_ACTIVATION_METHOD = 0x5, +}; + +enum { + MCQI_FW_RUNNING_VERSION = 0, + MCQI_FW_STORED_VERSION = 1, +}; + static int mlx5_cmd_query_adapter(struct mlx5_core_dev *dev, u32 *out, int outlen) { @@ -202,6 +233,18 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev) return err; } + if (MLX5_CAP_GEN(dev, event_cap)) { + err = mlx5_core_get_caps(dev, MLX5_CAP_DEV_EVENT); + if (err) + return err; + } + + if (MLX5_CAP_GEN(dev, tls)) { + err = mlx5_core_get_caps(dev, MLX5_CAP_TLS); + if (err) + return err; + } + return 0; } @@ -392,33 +435,49 @@ static int mlx5_reg_mcda_set(struct mlx5_core_dev *dev, } static int mlx5_reg_mcqi_query(struct mlx5_core_dev *dev, - u16 component_index, - u32 *max_component_size, - u8 *log_mcda_word_size, - u16 *mcda_max_write_size) + u16 component_index, bool read_pending, + u8 info_type, u16 data_size, void *mcqi_data) { - u32 out[MLX5_ST_SZ_DW(mcqi_reg) + MLX5_ST_SZ_DW(mcqi_cap)]; - int offset = MLX5_ST_SZ_DW(mcqi_reg); - u32 in[MLX5_ST_SZ_DW(mcqi_reg)]; + u32 out[MLX5_ST_SZ_DW(mcqi_reg) + MLX5_UN_SZ_DW(mcqi_reg_data)] = {}; + u32 in[MLX5_ST_SZ_DW(mcqi_reg)] = {}; + void *data; int err; - memset(in, 0, sizeof(in)); - memset(out, 0, sizeof(out)); - MLX5_SET(mcqi_reg, in, component_index, component_index); - MLX5_SET(mcqi_reg, in, data_size, MLX5_ST_SZ_BYTES(mcqi_cap)); + MLX5_SET(mcqi_reg, in, read_pending_component, read_pending); + MLX5_SET(mcqi_reg, in, info_type, info_type); + MLX5_SET(mcqi_reg, in, data_size, data_size); err = mlx5_core_access_reg(dev, in, sizeof(in), out, - sizeof(out), MLX5_REG_MCQI, 0, 0); + MLX5_ST_SZ_BYTES(mcqi_reg) + data_size, + MLX5_REG_MCQI, 0, 0); if (err) - goto out; + return err; - *max_component_size = MLX5_GET(mcqi_cap, out + offset, max_component_size); - *log_mcda_word_size = MLX5_GET(mcqi_cap, out + offset, log_mcda_word_size); - *mcda_max_write_size = MLX5_GET(mcqi_cap, out + offset, mcda_max_write_size); + data = MLX5_ADDR_OF(mcqi_reg, out, data); + memcpy(mcqi_data, data, data_size); -out: - return err; + return 0; +} + +static int mlx5_reg_mcqi_caps_query(struct mlx5_core_dev *dev, u16 component_index, + u32 *max_component_size, u8 *log_mcda_word_size, + u16 *mcda_max_write_size) +{ + u32 mcqi_reg[MLX5_ST_SZ_DW(mcqi_cap)] = {}; + int err; + + err = mlx5_reg_mcqi_query(dev, component_index, 0, + MCQI_INFO_TYPE_CAPABILITIES, + MLX5_ST_SZ_BYTES(mcqi_cap), mcqi_reg); + if (err) + return err; + + *max_component_size = MLX5_GET(mcqi_cap, mcqi_reg, max_component_size); + *log_mcda_word_size = MLX5_GET(mcqi_cap, mcqi_reg, log_mcda_word_size); + *mcda_max_write_size = MLX5_GET(mcqi_cap, mcqi_reg, mcda_max_write_size); + + return 0; } struct mlx5_mlxfw_dev { @@ -434,8 +493,13 @@ static int mlx5_component_query(struct mlxfw_dev *mlxfw_dev, container_of(mlxfw_dev, struct mlx5_mlxfw_dev, mlxfw_dev); struct mlx5_core_dev *dev = mlx5_mlxfw_dev->mlx5_core_dev; - return mlx5_reg_mcqi_query(dev, component_index, p_max_size, - p_align_bits, p_max_write_size); + if (!MLX5_CAP_GEN(dev, mcam_reg) || !MLX5_CAP_MCAM_REG(dev, mcqi)) { + mlx5_core_warn(dev, "caps query isn't supported by running FW\n"); + return -EOPNOTSUPP; + } + + return mlx5_reg_mcqi_caps_query(dev, component_index, p_max_size, + p_align_bits, p_max_write_size); } static int mlx5_fsm_lock(struct mlxfw_dev *mlxfw_dev, u32 *fwhandle) @@ -575,3 +639,130 @@ int mlx5_firmware_flash(struct mlx5_core_dev *dev, return mlxfw_firmware_flash(&mlx5_mlxfw_dev.mlxfw_dev, firmware, extack); } + +static int mlx5_reg_mcqi_version_query(struct mlx5_core_dev *dev, + u16 component_index, bool read_pending, + u32 *mcqi_version_out) +{ + return mlx5_reg_mcqi_query(dev, component_index, read_pending, + MCQI_INFO_TYPE_VERSION, + MLX5_ST_SZ_BYTES(mcqi_version), + mcqi_version_out); +} + +static int mlx5_reg_mcqs_query(struct mlx5_core_dev *dev, u32 *out, + u16 component_index) +{ + u8 out_sz = MLX5_ST_SZ_BYTES(mcqs_reg); + u32 in[MLX5_ST_SZ_DW(mcqs_reg)] = {}; + int err; + + memset(out, 0, out_sz); + + MLX5_SET(mcqs_reg, in, component_index, component_index); + + err = mlx5_core_access_reg(dev, in, sizeof(in), out, + out_sz, MLX5_REG_MCQS, 0, 0); + return err; +} + +/* scans component index sequentially, to find the boot img index */ +static int mlx5_get_boot_img_component_index(struct mlx5_core_dev *dev) +{ + u32 out[MLX5_ST_SZ_DW(mcqs_reg)] = {}; + u16 identifier, component_idx = 0; + bool quit; + int err; + + do { + err = mlx5_reg_mcqs_query(dev, out, component_idx); + if (err) + return err; + + identifier = MLX5_GET(mcqs_reg, out, identifier); + quit = !!MLX5_GET(mcqs_reg, out, last_index_flag); + quit |= identifier == MCQS_IDENTIFIER_BOOT_IMG; + } while (!quit && ++component_idx); + + if (identifier != MCQS_IDENTIFIER_BOOT_IMG) { + mlx5_core_warn(dev, "mcqs: can't find boot_img component ix, last scanned idx %d\n", + component_idx); + return -EOPNOTSUPP; + } + + return component_idx; +} + +static int +mlx5_fw_image_pending(struct mlx5_core_dev *dev, + int component_index, + bool *pending_version_exists) +{ + u32 out[MLX5_ST_SZ_DW(mcqs_reg)]; + u8 component_update_state; + int err; + + err = mlx5_reg_mcqs_query(dev, out, component_index); + if (err) + return err; + + component_update_state = MLX5_GET(mcqs_reg, out, component_update_state); + + if (component_update_state == MCQS_UPDATE_STATE_IDLE) { + *pending_version_exists = false; + } else if (component_update_state == MCQS_UPDATE_STATE_ACTIVE_PENDING_RESET) { + *pending_version_exists = true; + } else { + mlx5_core_warn(dev, + "mcqs: can't read pending fw version while fw state is %d\n", + component_update_state); + return -ENODATA; + } + return 0; +} + +int mlx5_fw_version_query(struct mlx5_core_dev *dev, + u32 *running_ver, u32 *pending_ver) +{ + u32 reg_mcqi_version[MLX5_ST_SZ_DW(mcqi_version)] = {}; + bool pending_version_exists; + int component_index; + int err; + + if (!MLX5_CAP_GEN(dev, mcam_reg) || !MLX5_CAP_MCAM_REG(dev, mcqi) || + !MLX5_CAP_MCAM_REG(dev, mcqs)) { + mlx5_core_warn(dev, "fw query isn't supported by the FW\n"); + return -EOPNOTSUPP; + } + + component_index = mlx5_get_boot_img_component_index(dev); + if (component_index < 0) + return component_index; + + err = mlx5_reg_mcqi_version_query(dev, component_index, + MCQI_FW_RUNNING_VERSION, + reg_mcqi_version); + if (err) + return err; + + *running_ver = MLX5_GET(mcqi_version, reg_mcqi_version, version); + + err = mlx5_fw_image_pending(dev, component_index, &pending_version_exists); + if (err) + return err; + + if (!pending_version_exists) { + *pending_ver = 0; + return 0; + } + + err = mlx5_reg_mcqi_version_query(dev, component_index, + MCQI_FW_STORED_VERSION, + reg_mcqi_version); + if (err) + return err; + + *pending_ver = MLX5_GET(mcqi_version, reg_mcqi_version, version); + + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 00e66c3772cc..faf197d53743 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -258,6 +258,18 @@ void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp * mlx5_core_destroy_qp(mdev, qp); } +int mlx5i_create_tis(struct mlx5_core_dev *mdev, u32 underlay_qpn, u32 *tisn) +{ + u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {}; + void *tisc; + + tisc = MLX5_ADDR_OF(create_tis_in, in, ctx); + + MLX5_SET(tisc, tisc, underlay_qpn, underlay_qpn); + + return mlx5e_create_tis(mdev, in, tisn); +} + static int mlx5i_init_tx(struct mlx5e_priv *priv) { struct mlx5i_priv *ipriv = priv->ppriv; @@ -269,7 +281,7 @@ static int mlx5i_init_tx(struct mlx5e_priv *priv) return err; } - err = mlx5e_create_tis(priv->mdev, 0 /* tc */, ipriv->qp.qpn, &priv->tisn[0]); + err = mlx5i_create_tis(priv->mdev, ipriv->qp.qpn, &priv->tisn[0]); if (err) { mlx5_core_warn(priv->mdev, "create tis failed, %d\n", err); goto err_destroy_underlay_qp; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h index e19ba3fcd1b7..c87962cab921 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h @@ -59,6 +59,8 @@ struct mlx5i_priv { char *mlx5e_priv[0]; }; +int mlx5i_create_tis(struct mlx5_core_dev *mdev, u32 underlay_qpn, u32 *tisn); + /* Underlay QP create/destroy functions */ int mlx5i_create_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp); void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c index e05186ada721..6e56fa769d2e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c @@ -210,7 +210,7 @@ static int mlx5i_pkey_open(struct net_device *netdev) goto err_unint_underlay_qp; } - err = mlx5e_create_tis(mdev, 0 /* tc */, ipriv->qp.qpn, &epriv->tisn[0]); + err = mlx5i_create_tis(mdev, ipriv->qp.qpn, &epriv->tisn[0]); if (err) { mlx5_core_warn(mdev, "create child tis failed, %d\n", err); goto err_remove_rx_uderlay_qp; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag.c index 959605559858..c5ef2ff26465 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag.c @@ -305,8 +305,8 @@ static void mlx5_do_bond(struct mlx5_lag *ldev) !mlx5_sriov_is_enabled(dev1); #ifdef CONFIG_MLX5_ESWITCH - roce_lag &= dev0->priv.eswitch->mode == SRIOV_NONE && - dev1->priv.eswitch->mode == SRIOV_NONE; + roce_lag &= dev0->priv.eswitch->mode == MLX5_ESWITCH_NONE && + dev1->priv.eswitch->mode == MLX5_ESWITCH_NONE; #endif if (roce_lag) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c new file mode 100644 index 000000000000..ea9ee88491e5 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +// Copyright (c) 2019 Mellanox Technologies. + +#include "mlx5_core.h" + +int mlx5_create_encryption_key(struct mlx5_core_dev *mdev, + void *key, u32 sz_bytes, + u32 *p_key_id) +{ + u32 in[MLX5_ST_SZ_DW(create_encryption_key_in)] = {}; + u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; + u32 sz_bits = sz_bytes * BITS_PER_BYTE; + u8 general_obj_key_size; + u64 general_obj_types; + void *obj, *key_p; + int err; + + obj = MLX5_ADDR_OF(create_encryption_key_in, in, encryption_key_object); + key_p = MLX5_ADDR_OF(encryption_key_obj, obj, key); + + general_obj_types = MLX5_CAP_GEN_64(mdev, general_obj_types); + if (!(general_obj_types & + MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_ENCRYPTION_KEY)) + return -EINVAL; + + switch (sz_bits) { + case 128: + general_obj_key_size = + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_KEY_SIZE_128; + break; + case 256: + general_obj_key_size = + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_KEY_SIZE_256; + break; + default: + return -EINVAL; + } + + memcpy(key_p, key, sz_bytes); + + MLX5_SET(encryption_key_obj, obj, key_size, general_obj_key_size); + MLX5_SET(encryption_key_obj, obj, key_type, + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_DEK); + MLX5_SET(general_obj_in_cmd_hdr, in, opcode, + MLX5_CMD_OP_CREATE_GENERAL_OBJECT); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, + MLX5_GENERAL_OBJECT_TYPES_ENCRYPTION_KEY); + MLX5_SET(encryption_key_obj, obj, pd, mdev->mlx5e_res.pdn); + + err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); + if (!err) + *p_key_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); + + /* avoid leaking key on the stack */ + memzero_explicit(in, sizeof(in)); + + return err; +} + +void mlx5_destroy_encryption_key(struct mlx5_core_dev *mdev, u32 key_id) +{ + u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {}; + u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)]; + + MLX5_SET(general_obj_in_cmd_hdr, in, opcode, + MLX5_CMD_OP_DESTROY_GENERAL_OBJECT); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, + MLX5_GENERAL_OBJECT_TYPES_ENCRYPTION_KEY); + MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, key_id); + + mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h index 24bd991a727e..3dfab91ae5f2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h @@ -75,7 +75,7 @@ int mlx5_eq_table_create(struct mlx5_core_dev *dev); void mlx5_eq_table_destroy(struct mlx5_core_dev *dev); int mlx5_eq_add_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq); -int mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq); +void mlx5_eq_del_cq(struct mlx5_eq *eq, struct mlx5_core_cq *cq); struct mlx5_eq_comp *mlx5_eqn2comp_eq(struct mlx5_core_dev *dev, int eqn); struct mlx5_eq *mlx5_get_async_eq(struct mlx5_core_dev *dev); void mlx5_cq_tasklet_cb(unsigned long data); @@ -97,7 +97,4 @@ void mlx5_core_eq_free_irqs(struct mlx5_core_dev *dev); struct cpu_rmap *mlx5_eq_table_get_rmap(struct mlx5_core_dev *dev); #endif -int mlx5_eq_notifier_register(struct mlx5_core_dev *dev, struct mlx5_nb *nb); -int mlx5_eq_notifier_unregister(struct mlx5_core_dev *dev, struct mlx5_nb *nb); - #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h index d918e44491f4..b99d469e4e64 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h @@ -79,4 +79,9 @@ struct mlx5_pme_stats { void mlx5_get_pme_stats(struct mlx5_core_dev *dev, struct mlx5_pme_stats *stats); int mlx5_notifier_call_chain(struct mlx5_events *events, unsigned int event, void *data); +/* Crypto */ +int mlx5_create_encryption_key(struct mlx5_core_dev *mdev, + void *key, u32 sz_bytes, u32 *p_key_id); +void mlx5_destroy_encryption_key(struct mlx5_core_dev *mdev, u32 key_id); + #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 072b56fda27e..b15b27a497fc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -734,8 +734,7 @@ static int mlx5_pci_init(struct mlx5_core_dev *dev, struct pci_dev *pdev, struct mlx5_priv *priv = &dev->priv; int err = 0; - priv->pci_dev_data = id->driver_data; - + mutex_init(&dev->pci_status_mutex); pci_set_drvdata(dev->pdev, dev); dev->bar_addr = pci_resource_start(pdev, 0); @@ -1255,7 +1254,6 @@ static int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) INIT_LIST_HEAD(&priv->ctx_list); spin_lock_init(&priv->ctx_lock); - mutex_init(&dev->pci_status_mutex); mutex_init(&dev->intf_state_mutex); mutex_init(&priv->bfregs.reg_head.lock); @@ -1317,6 +1315,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *id) dev->device = &pdev->dev; dev->pdev = pdev; + dev->coredev_type = id->driver_data & MLX5_PCI_DEV_IS_VF ? + MLX5_COREDEV_VF : MLX5_COREDEV_PF; + err = mlx5_mdev_init(dev, prof_sel); if (err) goto mdev_init_err; @@ -1599,7 +1600,7 @@ static int __init init(void) get_random_bytes(&sw_owner_id, sizeof(sw_owner_id)); mlx5_core_verify_params(); - mlx5_fpga_ipsec_build_fs_cmds(); + mlx5_accel_ipsec_build_fs_cmds(); mlx5_register_debugfs(); err = pci_register_driver(&mlx5_core_driver); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 958769702823..471bbc48bc1f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -205,6 +205,8 @@ int mlx5_set_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 arm, u8 mode); int mlx5_firmware_flash(struct mlx5_core_dev *dev, const struct firmware *fw, struct netlink_ext_ack *extack); +int mlx5_fw_version_query(struct mlx5_core_dev *dev, + u32 *running_ver, u32 *stored_ver); void mlx5e_init(void); void mlx5e_cleanup(void); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/rdma.c b/drivers/net/ethernet/mellanox/mlx5/core/rdma.c index 401441aefbcb..17ce9dd56b13 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/rdma.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/rdma.c @@ -126,7 +126,7 @@ static void mlx5_rdma_make_default_gid(struct mlx5_core_dev *dev, union ib_gid * { u8 hw_id[ETH_ALEN]; - mlx5_query_nic_vport_mac_address(dev, 0, hw_id); + mlx5_query_mac_address(dev, hw_id); gid->global.subnet_prefix = cpu_to_be64(0xfe80000000000000LL); addrconf_addr_eui48(&gid->raw[8], hw_id); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index 2eecb831c499..61fcfd8b39b4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -74,17 +74,11 @@ static int mlx5_device_enable_sriov(struct mlx5_core_dev *dev, int num_vfs) int err; int vf; - if (sriov->enabled_vfs) { - mlx5_core_warn(dev, - "failed to enable SRIOV on device, already enabled with %d vfs\n", - sriov->enabled_vfs); - return -EBUSY; - } - if (!MLX5_ESWITCH_MANAGER(dev)) goto enable_vfs_hca; - err = mlx5_eswitch_enable_sriov(dev->priv.eswitch, num_vfs, SRIOV_LEGACY); + mlx5_eswitch_update_num_of_vfs(dev->priv.eswitch, num_vfs); + err = mlx5_eswitch_enable(dev->priv.eswitch, MLX5_ESWITCH_LEGACY); if (err) { mlx5_core_warn(dev, "failed to enable eswitch SRIOV (%d)\n", err); @@ -99,7 +93,6 @@ enable_vfs_hca: continue; } sriov->vfs_ctx[vf].enabled = 1; - sriov->enabled_vfs++; if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_IB) { err = sriov_restore_guids(dev, vf); if (err) { @@ -118,13 +111,11 @@ enable_vfs_hca: static void mlx5_device_disable_sriov(struct mlx5_core_dev *dev) { struct mlx5_core_sriov *sriov = &dev->priv.sriov; + int num_vfs = pci_num_vf(dev->pdev); int err; int vf; - if (!sriov->enabled_vfs) - goto out; - - for (vf = 0; vf < sriov->num_vfs; vf++) { + for (vf = num_vfs - 1; vf >= 0; vf--) { if (!sriov->vfs_ctx[vf].enabled) continue; err = mlx5_core_disable_hca(dev, vf + 1); @@ -133,12 +124,10 @@ static void mlx5_device_disable_sriov(struct mlx5_core_dev *dev) continue; } sriov->vfs_ctx[vf].enabled = 0; - sriov->enabled_vfs--; } -out: if (MLX5_ESWITCH_MANAGER(dev)) - mlx5_eswitch_disable_sriov(dev->priv.eswitch); + mlx5_eswitch_disable(dev->priv.eswitch); if (mlx5_wait_for_pages(dev, &dev->priv.vfs_pages)) mlx5_core_warn(dev, "timeout reclaiming VFs pages\n"); @@ -191,13 +180,11 @@ int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs) int mlx5_sriov_attach(struct mlx5_core_dev *dev) { - struct mlx5_core_sriov *sriov = &dev->priv.sriov; - - if (!mlx5_core_is_pf(dev) || !sriov->num_vfs) + if (!mlx5_core_is_pf(dev) || !pci_num_vf(dev->pdev)) return 0; /* If sriov VFs exist in PCI level, enable them in device level */ - return mlx5_device_enable_sriov(dev, sriov->num_vfs); + return mlx5_device_enable_sriov(dev, pci_num_vf(dev->pdev)); } void mlx5_sriov_detach(struct mlx5_core_dev *dev) @@ -210,22 +197,25 @@ void mlx5_sriov_detach(struct mlx5_core_dev *dev) static u16 mlx5_get_max_vfs(struct mlx5_core_dev *dev) { - u32 out[MLX5_ST_SZ_DW(query_esw_functions_out)] = {}; u16 host_total_vfs; - int err; + const u32 *out; if (mlx5_core_is_ecpf_esw_manager(dev)) { - err = mlx5_esw_query_functions(dev, out, sizeof(out)); - host_total_vfs = MLX5_GET(query_esw_functions_out, out, - host_params_context.host_total_vfs); + out = mlx5_esw_query_functions(dev); /* Old FW doesn't support getting total_vfs from esw func * but supports getting it from pci_sriov. */ - if (!err && host_total_vfs) + if (IS_ERR(out)) + goto done; + host_total_vfs = MLX5_GET(query_esw_functions_out, out, + host_params_context.host_total_vfs); + kvfree(out); + if (host_total_vfs) return host_total_vfs; } +done: return pci_sriov_get_totalvfs(dev->pdev); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 95cdc8cbcba4..c912d82ca64b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -34,6 +34,7 @@ #include <linux/etherdevice.h> #include <linux/mlx5/driver.h> #include <linux/mlx5/vport.h> +#include <linux/mlx5/eswitch.h> #include "mlx5_core.h" /* Mutex to hold while enabling or disabling RoCE */ @@ -155,11 +156,12 @@ int mlx5_modify_nic_vport_min_inline(struct mlx5_core_dev *mdev, } int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, - u16 vport, u8 *addr) + u16 vport, bool other, u8 *addr) { - u32 *out; int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out); + u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {}; u8 *out_addr; + u32 *out; int err; out = kvzalloc(outlen, GFP_KERNEL); @@ -169,7 +171,12 @@ int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, out_addr = MLX5_ADDR_OF(query_nic_vport_context_out, out, nic_vport_context.permanent_address); - err = mlx5_query_nic_vport_context(mdev, vport, out, outlen); + MLX5_SET(query_nic_vport_context_in, in, opcode, + MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT); + MLX5_SET(query_nic_vport_context_in, in, vport_number, vport); + MLX5_SET(query_nic_vport_context_in, in, other_vport, other); + + err = mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen); if (!err) ether_addr_copy(addr, &out_addr[2]); @@ -178,6 +185,12 @@ int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, } EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_address); +int mlx5_query_mac_address(struct mlx5_core_dev *mdev, u8 *addr) +{ + return mlx5_query_nic_vport_mac_address(mdev, 0, false, addr); +} +EXPORT_SYMBOL_GPL(mlx5_query_mac_address); + int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev, u16 vport, u8 *addr) { @@ -194,9 +207,7 @@ int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev, MLX5_SET(modify_nic_vport_context_in, in, field_select.permanent_address, 1); MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport); - - if (vport) - MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1); + MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1); nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in, nic_vport_context); @@ -291,9 +302,7 @@ int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT); MLX5_SET(query_nic_vport_context_in, in, allowed_list_type, list_type); MLX5_SET(query_nic_vport_context_in, in, vport_number, vport); - - if (vport) - MLX5_SET(query_nic_vport_context_in, in, other_vport, 1); + MLX5_SET(query_nic_vport_context_in, in, other_vport, 1); err = mlx5_cmd_exec(dev, in, sizeof(in), out, out_sz); if (err) @@ -483,7 +492,7 @@ int mlx5_modify_nic_vport_node_guid(struct mlx5_core_dev *mdev, MLX5_SET(modify_nic_vport_context_in, in, field_select.node_guid, 1); MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport); - MLX5_SET(modify_nic_vport_context_in, in, other_vport, !!vport); + MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1); nic_vport_context = MLX5_ADDR_OF(modify_nic_vport_context_in, in, nic_vport_context); @@ -1157,3 +1166,17 @@ u64 mlx5_query_nic_system_image_guid(struct mlx5_core_dev *mdev) return tmp; } EXPORT_SYMBOL_GPL(mlx5_query_nic_system_image_guid); + +/** + * mlx5_eswitch_get_total_vports - Get total vports of the eswitch + * + * @dev: Pointer to core device + * + * mlx5_eswitch_get_total_vports returns total number of vports for + * the eswitch. + */ +u16 mlx5_eswitch_get_total_vports(const struct mlx5_core_dev *dev) +{ + return MLX5_SPECIAL_VPORTS(dev) + mlx5_core_max_vfs(dev); +} +EXPORT_SYMBOL(mlx5_eswitch_get_total_vports); diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 76ff5b217c04..ead36702549a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -3515,6 +3515,18 @@ MLXSW_ITEM32(reg, qeec, next_element_index, 0x08, 0, 8); */ MLXSW_ITEM32(reg, qeec, mise, 0x0C, 31, 1); +/* reg_qeec_ptps + * PTP shaper + * 0: regular shaper mode + * 1: PTP oriented shaper + * Allowed only for hierarchy 0 + * Not supported for CPU port + * Note that ptps mode may affect the shaper rates of all hierarchies + * Supported only on Spectrum-1 + * Access: RW + */ +MLXSW_ITEM32(reg, qeec, ptps, 0x0C, 29, 1); + enum { MLXSW_REG_QEEC_BYTES_MODE, MLXSW_REG_QEEC_PACKETS_MODE, @@ -3601,6 +3613,16 @@ static inline void mlxsw_reg_qeec_pack(char *payload, u8 local_port, mlxsw_reg_qeec_next_element_index_set(payload, next_index); } +static inline void mlxsw_reg_qeec_ptps_pack(char *payload, u8 local_port, + bool ptps) +{ + MLXSW_REG_ZERO(qeec, payload); + mlxsw_reg_qeec_local_port_set(payload, local_port); + mlxsw_reg_qeec_element_hierarchy_set(payload, + MLXSW_REG_QEEC_HIERARCY_PORT); + mlxsw_reg_qeec_ptps_set(payload, ptps); +} + /* QRWE - QoS ReWrite Enable * ------------------------- * This register configures the rewrite enable per receive port. @@ -3814,6 +3836,112 @@ mlxsw_reg_qtctm_pack(char *payload, u8 local_port, bool mc) mlxsw_reg_qtctm_mc_set(payload, mc); } +/* QPSC - QoS PTP Shaper Configuration Register + * -------------------------------------------- + * The QPSC allows advanced configuration of the shapers when QEEC.ptps=1. + * Supported only on Spectrum-1. + */ +#define MLXSW_REG_QPSC_ID 0x401B +#define MLXSW_REG_QPSC_LEN 0x28 + +MLXSW_REG_DEFINE(qpsc, MLXSW_REG_QPSC_ID, MLXSW_REG_QPSC_LEN); + +enum mlxsw_reg_qpsc_port_speed { + MLXSW_REG_QPSC_PORT_SPEED_100M, + MLXSW_REG_QPSC_PORT_SPEED_1G, + MLXSW_REG_QPSC_PORT_SPEED_10G, + MLXSW_REG_QPSC_PORT_SPEED_25G, +}; + +/* reg_qpsc_port_speed + * Port speed. + * Access: Index + */ +MLXSW_ITEM32(reg, qpsc, port_speed, 0x00, 0, 4); + +/* reg_qpsc_shaper_time_exp + * The base-time-interval for updating the shapers tokens (for all hierarchies). + * shaper_update_rate = 2 ^ shaper_time_exp * (1 + shaper_time_mantissa) * 32nSec + * shaper_rate = 64bit * shaper_inc / shaper_update_rate + * Access: RW + */ +MLXSW_ITEM32(reg, qpsc, shaper_time_exp, 0x04, 16, 4); + +/* reg_qpsc_shaper_time_mantissa + * The base-time-interval for updating the shapers tokens (for all hierarchies). + * shaper_update_rate = 2 ^ shaper_time_exp * (1 + shaper_time_mantissa) * 32nSec + * shaper_rate = 64bit * shaper_inc / shaper_update_rate + * Access: RW + */ +MLXSW_ITEM32(reg, qpsc, shaper_time_mantissa, 0x04, 0, 5); + +/* reg_qpsc_shaper_inc + * Number of tokens added to shaper on each update. + * Units of 8B. + * Access: RW + */ +MLXSW_ITEM32(reg, qpsc, shaper_inc, 0x08, 0, 5); + +/* reg_qpsc_shaper_bs + * Max shaper Burst size. + * Burst size is 2 ^ max_shaper_bs * 512 [bits] + * Range is: 5..25 (from 2KB..2GB) + * Access: RW + */ +MLXSW_ITEM32(reg, qpsc, shaper_bs, 0x0C, 0, 6); + +/* reg_qpsc_ptsc_we + * Write enable to port_to_shaper_credits. + * Access: WO + */ +MLXSW_ITEM32(reg, qpsc, ptsc_we, 0x10, 31, 1); + +/* reg_qpsc_port_to_shaper_credits + * For split ports: range 1..57 + * For non-split ports: range 1..112 + * Written only when ptsc_we is set. + * Access: RW + */ +MLXSW_ITEM32(reg, qpsc, port_to_shaper_credits, 0x10, 0, 8); + +/* reg_qpsc_ing_timestamp_inc + * Ingress timestamp increment. + * 2's complement. + * The timestamp of MTPPTR at ingress will be incremented by this value. Global + * value for all ports. + * Same units as used by MTPPTR. + * Access: RW + */ +MLXSW_ITEM32(reg, qpsc, ing_timestamp_inc, 0x20, 0, 32); + +/* reg_qpsc_egr_timestamp_inc + * Egress timestamp increment. + * 2's complement. + * The timestamp of MTPPTR at egress will be incremented by this value. Global + * value for all ports. + * Same units as used by MTPPTR. + * Access: RW + */ +MLXSW_ITEM32(reg, qpsc, egr_timestamp_inc, 0x24, 0, 32); + +static inline void +mlxsw_reg_qpsc_pack(char *payload, enum mlxsw_reg_qpsc_port_speed port_speed, + u8 shaper_time_exp, u8 shaper_time_mantissa, u8 shaper_inc, + u8 shaper_bs, u8 port_to_shaper_credits, + int ing_timestamp_inc, int egr_timestamp_inc) +{ + MLXSW_REG_ZERO(qpsc, payload); + mlxsw_reg_qpsc_port_speed_set(payload, port_speed); + mlxsw_reg_qpsc_shaper_time_exp_set(payload, shaper_time_exp); + mlxsw_reg_qpsc_shaper_time_mantissa_set(payload, shaper_time_mantissa); + mlxsw_reg_qpsc_shaper_inc_set(payload, shaper_inc); + mlxsw_reg_qpsc_shaper_bs_set(payload, shaper_bs); + mlxsw_reg_qpsc_ptsc_we_set(payload, true); + mlxsw_reg_qpsc_port_to_shaper_credits_set(payload, port_to_shaper_credits); + mlxsw_reg_qpsc_ing_timestamp_inc_set(payload, ing_timestamp_inc); + mlxsw_reg_qpsc_egr_timestamp_inc_set(payload, egr_timestamp_inc); +} + /* PMLP - Ports Module to Local Port Register * ------------------------------------------ * Configures the assignment of modules to local ports. @@ -10374,6 +10502,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(qpdsm), MLXSW_REG(qpdpm), MLXSW_REG(qtctm), + MLXSW_REG(qpsc), MLXSW_REG(pmlp), MLXSW_REG(pmtu), MLXSW_REG(ptys), diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 755b14b82c8f..ce285fbeebd3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -171,6 +171,7 @@ struct mlxsw_sp_ptp_ops { struct hwtstamp_config *config); int (*hwtstamp_set)(struct mlxsw_sp_port *mlxsw_sp_port, struct hwtstamp_config *config); + void (*shaper_work)(struct work_struct *work); int (*get_ts_info)(struct mlxsw_sp *mlxsw_sp, struct ethtool_ts_info *info); }; @@ -2655,28 +2656,33 @@ mlxsw_sp1_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto, } } +static u32 +mlxsw_sp1_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto) +{ + int i; + + for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { + if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask) + return mlxsw_sp1_port_link_mode[i].speed; + } + + return SPEED_UNKNOWN; +} + static void mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, u32 ptys_eth_proto, struct ethtool_link_ksettings *cmd) { - u32 speed = SPEED_UNKNOWN; - u8 duplex = DUPLEX_UNKNOWN; - int i; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; if (!carrier_ok) - goto out; + return; - for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { - if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask) { - speed = mlxsw_sp1_port_link_mode[i].speed; - duplex = DUPLEX_FULL; - break; - } - } -out: - cmd->base.speed = speed; - cmd->base.duplex = duplex; + cmd->base.speed = mlxsw_sp1_from_ptys_speed(mlxsw_sp, ptys_eth_proto); + if (cmd->base.speed != SPEED_UNKNOWN) + cmd->base.duplex = DUPLEX_FULL; } static u32 @@ -2747,6 +2753,7 @@ static const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = { .from_ptys_supported_port = mlxsw_sp1_from_ptys_supported_port, .from_ptys_link = mlxsw_sp1_from_ptys_link, + .from_ptys_speed = mlxsw_sp1_from_ptys_speed, .from_ptys_speed_duplex = mlxsw_sp1_from_ptys_speed_duplex, .to_ptys_advert_link = mlxsw_sp1_to_ptys_advert_link, .to_ptys_speed = mlxsw_sp1_to_ptys_speed, @@ -2997,28 +3004,33 @@ mlxsw_sp2_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto, } } +static u32 +mlxsw_sp2_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto) +{ + int i; + + for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { + if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) + return mlxsw_sp2_port_link_mode[i].speed; + } + + return SPEED_UNKNOWN; +} + static void mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, u32 ptys_eth_proto, struct ethtool_link_ksettings *cmd) { - u32 speed = SPEED_UNKNOWN; - u8 duplex = DUPLEX_UNKNOWN; - int i; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; if (!carrier_ok) - goto out; + return; - for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { - if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) { - speed = mlxsw_sp2_port_link_mode[i].speed; - duplex = DUPLEX_FULL; - break; - } - } -out: - cmd->base.speed = speed; - cmd->base.duplex = duplex; + cmd->base.speed = mlxsw_sp2_from_ptys_speed(mlxsw_sp, ptys_eth_proto); + if (cmd->base.speed != SPEED_UNKNOWN) + cmd->base.duplex = DUPLEX_FULL; } static bool @@ -3129,6 +3141,7 @@ static const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = { .from_ptys_supported_port = mlxsw_sp2_from_ptys_supported_port, .from_ptys_link = mlxsw_sp2_from_ptys_link, + .from_ptys_speed = mlxsw_sp2_from_ptys_speed, .from_ptys_speed_duplex = mlxsw_sp2_from_ptys_speed_duplex, .to_ptys_advert_link = mlxsw_sp2_to_ptys_advert_link, .to_ptys_speed = mlxsw_sp2_to_ptys_speed, @@ -3457,8 +3470,9 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port) return err; } - /* Make sure the max shaper is disabled in all hierarchies that - * support it. + /* Make sure the max shaper is disabled in all hierarchies that support + * it. Note that this disables ptps (PTP shaper), but that is intended + * for the initial configuration. */ err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, MLXSW_REG_QEEC_HIERARCY_PORT, 0, 0, @@ -3703,6 +3717,9 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port, } mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan; + INIT_DELAYED_WORK(&mlxsw_sp_port->ptp.shaper_dw, + mlxsw_sp->ptp_ops->shaper_work); + mlxsw_sp->ports[local_port] = mlxsw_sp_port; err = register_netdev(dev); if (err) { @@ -3757,6 +3774,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw); + cancel_delayed_work_sync(&mlxsw_sp_port->ptp.shaper_dw); mlxsw_sp_port_ptp_clear(mlxsw_sp_port); mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp); unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ @@ -4042,6 +4060,7 @@ static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, if (status == MLXSW_PORT_OPER_STATUS_UP) { netdev_info(mlxsw_sp_port->dev, "link up\n"); netif_carrier_on(mlxsw_sp_port->dev); + mlxsw_core_schedule_dw(&mlxsw_sp_port->ptp.shaper_dw, 0); } else { netdev_info(mlxsw_sp_port->dev, "link down\n"); netif_carrier_off(mlxsw_sp_port->dev); @@ -4559,6 +4578,7 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp1_ptp_ops = { .transmitted = mlxsw_sp1_ptp_transmitted, .hwtstamp_get = mlxsw_sp1_ptp_hwtstamp_get, .hwtstamp_set = mlxsw_sp1_ptp_hwtstamp_set, + .shaper_work = mlxsw_sp1_ptp_shaper_work, .get_ts_info = mlxsw_sp1_ptp_get_ts_info, }; @@ -4571,6 +4591,7 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = { .transmitted = mlxsw_sp2_ptp_transmitted, .hwtstamp_get = mlxsw_sp2_ptp_hwtstamp_get, .hwtstamp_set = mlxsw_sp2_ptp_hwtstamp_set, + .shaper_work = mlxsw_sp2_ptp_shaper_work, .get_ts_info = mlxsw_sp2_ptp_get_ts_info, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 7f8427c1a997..abbb563db440 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -267,6 +267,7 @@ struct mlxsw_sp_port { struct mlxsw_sp_acl_block *ing_acl_block; struct mlxsw_sp_acl_block *eg_acl_block; struct { + struct delayed_work shaper_dw; struct hwtstamp_config hwtstamp_config; u16 ing_types; u16 egr_types; @@ -279,6 +280,7 @@ struct mlxsw_sp_port_type_speed_ops { struct ethtool_link_ksettings *cmd); void (*from_ptys_link)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto, unsigned long *mode); + u32 (*from_ptys_speed)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto); void (*from_ptys_speed_duplex)(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, u32 ptys_eth_proto, struct ethtool_link_ksettings *cmd); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c index 437023d67a3b..bd9c2bc2d5d6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c @@ -753,12 +753,101 @@ static int mlxsw_sp1_ptp_mtpppc_set(struct mlxsw_sp *mlxsw_sp, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpppc), mtpppc_pl); } +struct mlxsw_sp1_ptp_shaper_params { + u32 ethtool_speed; + enum mlxsw_reg_qpsc_port_speed port_speed; + u8 shaper_time_exp; + u8 shaper_time_mantissa; + u8 shaper_inc; + u8 shaper_bs; + u8 port_to_shaper_credits; + int ing_timestamp_inc; + int egr_timestamp_inc; +}; + +static const struct mlxsw_sp1_ptp_shaper_params +mlxsw_sp1_ptp_shaper_params[] = { + { + .ethtool_speed = SPEED_100, + .port_speed = MLXSW_REG_QPSC_PORT_SPEED_100M, + .shaper_time_exp = 4, + .shaper_time_mantissa = 12, + .shaper_inc = 9, + .shaper_bs = 1, + .port_to_shaper_credits = 1, + .ing_timestamp_inc = -313, + .egr_timestamp_inc = 313, + }, + { + .ethtool_speed = SPEED_1000, + .port_speed = MLXSW_REG_QPSC_PORT_SPEED_1G, + .shaper_time_exp = 0, + .shaper_time_mantissa = 12, + .shaper_inc = 6, + .shaper_bs = 0, + .port_to_shaper_credits = 1, + .ing_timestamp_inc = -35, + .egr_timestamp_inc = 35, + }, + { + .ethtool_speed = SPEED_10000, + .port_speed = MLXSW_REG_QPSC_PORT_SPEED_10G, + .shaper_time_exp = 0, + .shaper_time_mantissa = 2, + .shaper_inc = 14, + .shaper_bs = 1, + .port_to_shaper_credits = 1, + .ing_timestamp_inc = -11, + .egr_timestamp_inc = 11, + }, + { + .ethtool_speed = SPEED_25000, + .port_speed = MLXSW_REG_QPSC_PORT_SPEED_25G, + .shaper_time_exp = 0, + .shaper_time_mantissa = 0, + .shaper_inc = 11, + .shaper_bs = 1, + .port_to_shaper_credits = 1, + .ing_timestamp_inc = -14, + .egr_timestamp_inc = 14, + }, +}; + +#define MLXSW_SP1_PTP_SHAPER_PARAMS_LEN ARRAY_SIZE(mlxsw_sp1_ptp_shaper_params) + +static int mlxsw_sp1_ptp_shaper_params_set(struct mlxsw_sp *mlxsw_sp) +{ + const struct mlxsw_sp1_ptp_shaper_params *params; + char qpsc_pl[MLXSW_REG_QPSC_LEN]; + int i, err; + + for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) { + params = &mlxsw_sp1_ptp_shaper_params[i]; + mlxsw_reg_qpsc_pack(qpsc_pl, params->port_speed, + params->shaper_time_exp, + params->shaper_time_mantissa, + params->shaper_inc, params->shaper_bs, + params->port_to_shaper_credits, + params->ing_timestamp_inc, + params->egr_timestamp_inc); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpsc), qpsc_pl); + if (err) + return err; + } + + return 0; +} + struct mlxsw_sp_ptp_state *mlxsw_sp1_ptp_init(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_ptp_state *ptp_state; u16 message_type; int err; + err = mlxsw_sp1_ptp_shaper_params_set(mlxsw_sp); + if (err) + return ERR_PTR(err); + ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL); if (!ptp_state) return ERR_PTR(-ENOMEM); @@ -907,6 +996,71 @@ static int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port, ing_types, egr_types); } +static bool mlxsw_sp1_ptp_hwtstamp_enabled(struct mlxsw_sp_port *mlxsw_sp_port) +{ + return mlxsw_sp_port->ptp.ing_types || mlxsw_sp_port->ptp.egr_types; +} + +static int +mlxsw_sp1_ptp_port_shaper_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char qeec_pl[MLXSW_REG_QEEC_LEN]; + + mlxsw_reg_qeec_ptps_pack(qeec_pl, mlxsw_sp_port->local_port, enable); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl); +} + +static int mlxsw_sp1_ptp_port_shaper_check(struct mlxsw_sp_port *mlxsw_sp_port) +{ + const struct mlxsw_sp_port_type_speed_ops *port_type_speed_ops; + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ptys_pl[MLXSW_REG_PTYS_LEN]; + u32 eth_proto_oper, speed; + bool ptps = false; + int err, i; + + if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port)) + return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, false); + + port_type_speed_ops = mlxsw_sp->port_type_speed_ops; + port_type_speed_ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, + mlxsw_sp_port->local_port, 0, + false); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); + if (err) + return err; + port_type_speed_ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, NULL, NULL, + ð_proto_oper); + + speed = port_type_speed_ops->from_ptys_speed(mlxsw_sp, eth_proto_oper); + for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) { + if (mlxsw_sp1_ptp_shaper_params[i].ethtool_speed == speed) { + ptps = true; + break; + } + } + + return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, ptps); +} + +void mlxsw_sp1_ptp_shaper_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct mlxsw_sp_port *mlxsw_sp_port; + int err; + + mlxsw_sp_port = container_of(dwork, struct mlxsw_sp_port, + ptp.shaper_dw); + + if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port)) + return; + + err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port); + if (err) + netdev_err(mlxsw_sp_port->dev, "Failed to set up PTP shaper\n"); +} + int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, struct hwtstamp_config *config) { @@ -928,6 +1082,10 @@ int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_port->ptp.ing_types = ing_types; mlxsw_sp_port->ptp.egr_types = egr_types; + err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port); + if (err) + return err; + /* Notify the ioctl caller what we are actually timestamping. */ config->rx_filter = rx_filter; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h index b23abfc0bd76..72e55f6926b9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.h @@ -54,6 +54,8 @@ int mlxsw_sp1_ptp_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port, int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, struct hwtstamp_config *config); +void mlxsw_sp1_ptp_shaper_work(struct work_struct *work); + int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, struct ethtool_ts_info *info); @@ -113,6 +115,10 @@ mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, return -EOPNOTSUPP; } +static inline void mlxsw_sp1_ptp_shaper_work(struct work_struct *work) +{ +} + static inline int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, struct ethtool_ts_info *info) { @@ -167,6 +173,10 @@ mlxsw_sp2_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, return -EOPNOTSUPP; } +static inline void mlxsw_sp2_ptp_shaper_work(struct work_struct *work) +{ +} + static inline int mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, struct ethtool_ts_info *info) { diff --git a/drivers/net/ethernet/netronome/nfp/crypto/tls.c b/drivers/net/ethernet/netronome/nfp/crypto/tls.c index 3ee829d69c04..9f7ccb7da417 100644 --- a/drivers/net/ethernet/netronome/nfp/crypto/tls.c +++ b/drivers/net/ethernet/netronome/nfp/crypto/tls.c @@ -344,6 +344,7 @@ nfp_net_tls_add(struct net_device *netdev, struct sock *sk, if (!reply->handle[0] && !reply->handle[1]) { nn_dp_warn(&nn->dp, "FW returned NULL handle\n"); + err = -EINVAL; goto err_fw_remove; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 6bbd77ba56f2..0659756bf2bb 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -401,7 +401,7 @@ struct nfp_net_r_vector { struct { struct tasklet_struct tasklet; struct sk_buff_head queue; - struct spinlock lock; + spinlock_t lock; }; }; diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index bf5a7bca0298..be6660128b55 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -1042,7 +1042,6 @@ static int pasemi_mac_phy_init(struct net_device *dev) dn = pci_device_to_OF_node(mac->pdev); phy_dn = of_parse_phandle(dn, "phy-handle", 0); - of_node_put(phy_dn); mac->link = 0; mac->speed = 0; @@ -1051,6 +1050,7 @@ static int pasemi_mac_phy_init(struct net_device *dev) phydev = of_phy_connect(dev, phy_dn, &pasemi_adjust_link, 0, PHY_INTERFACE_MODE_SGMII); + of_node_put(phy_dn); if (!phydev) { printk(KERN_ERR "%s: Could not attach to phy\n", dev->name); return -ENODEV; diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 495d40c8f20e..efef5453b94f 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -157,7 +157,7 @@ static const struct { /* PCI-E devices. */ [RTL_GIGA_MAC_VER_07] = {"RTL8102e" }, [RTL_GIGA_MAC_VER_08] = {"RTL8102e" }, - [RTL_GIGA_MAC_VER_09] = {"RTL8102e" }, + [RTL_GIGA_MAC_VER_09] = {"RTL8102e/RTL8103e" }, [RTL_GIGA_MAC_VER_10] = {"RTL8101e" }, [RTL_GIGA_MAC_VER_11] = {"RTL8168b/8111b" }, [RTL_GIGA_MAC_VER_12] = {"RTL8168b/8111b" }, @@ -190,9 +190,9 @@ static const struct { [RTL_GIGA_MAC_VER_39] = {"RTL8106e", FIRMWARE_8106E_1}, [RTL_GIGA_MAC_VER_40] = {"RTL8168g/8111g", FIRMWARE_8168G_2}, [RTL_GIGA_MAC_VER_41] = {"RTL8168g/8111g" }, - [RTL_GIGA_MAC_VER_42] = {"RTL8168g/8111g", FIRMWARE_8168G_3}, - [RTL_GIGA_MAC_VER_43] = {"RTL8106e", FIRMWARE_8106E_2}, - [RTL_GIGA_MAC_VER_44] = {"RTL8411", FIRMWARE_8411_2 }, + [RTL_GIGA_MAC_VER_42] = {"RTL8168gu/8111gu", FIRMWARE_8168G_3}, + [RTL_GIGA_MAC_VER_43] = {"RTL8106eus", FIRMWARE_8106E_2}, + [RTL_GIGA_MAC_VER_44] = {"RTL8411b", FIRMWARE_8411_2 }, [RTL_GIGA_MAC_VER_45] = {"RTL8168h/8111h", FIRMWARE_8168H_1}, [RTL_GIGA_MAC_VER_46] = {"RTL8168h/8111h", FIRMWARE_8168H_2}, [RTL_GIGA_MAC_VER_47] = {"RTL8107e", FIRMWARE_8107E_1}, diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 53b726bfe945..ab58b837df47 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -3614,11 +3614,7 @@ static int efx_pci_probe(struct pci_dev *pci_dev, netif_warn(efx, probe, efx->net_dev, "failed to create MTDs (%d)\n", rc); - rc = pci_enable_pcie_error_reporting(pci_dev); - if (rc && rc != -EINVAL) - netif_notice(efx, probe, efx->net_dev, - "PCIE error reporting unavailable (%d).\n", - rc); + (void)pci_enable_pcie_error_reporting(pci_dev); if (efx->type->udp_tnl_push_ports) efx->type->udp_tnl_push_ports(efx); diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index 5544a722543f..d7307ab90d74 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -634,15 +634,14 @@ static void netsec_set_rx_de(struct netsec_priv *priv, static bool netsec_clean_tx_dring(struct netsec_priv *priv) { struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_TX]; - unsigned int pkts, bytes; struct netsec_de *entry; int tail = dring->tail; + unsigned int bytes; int cnt = 0; if (dring->is_xdp) spin_lock(&dring->lock); - pkts = 0; bytes = 0; entry = dring->vaddr + DESC_SZ * tail; @@ -727,6 +726,7 @@ static void *netsec_alloc_rx_data(struct netsec_priv *priv, { struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_RX]; + enum dma_data_direction dma_dir; struct page *page; page = page_pool_dev_alloc_pages(dring->page_pool); @@ -742,6 +742,8 @@ static void *netsec_alloc_rx_data(struct netsec_priv *priv, * cases and reserve enough space for headroom + skb_shared_info */ *desc_len = PAGE_SIZE - NETSEC_RX_BUF_NON_DATA; + dma_dir = page_pool_get_dma_dir(dring->page_pool); + dma_sync_single_for_device(priv->dev, *dma_handle, *desc_len, dma_dir); return page_address(page); } @@ -1210,15 +1212,11 @@ static void netsec_uninit_pkt_dring(struct netsec_priv *priv, int id) } } - /* Rx is currently using page_pool - * since the pool is created during netsec_setup_rx_dring(), we need to - * free the pool manually if the registration failed - */ + /* Rx is currently using page_pool */ if (id == NETSEC_RING_RX) { if (xdp_rxq_info_is_reg(&dring->xdp_rxq)) xdp_rxq_info_unreg(&dring->xdp_rxq); - else - page_pool_free(dring->page_pool); + page_pool_destroy(dring->page_pool); } memset(dring->desc, 0, sizeof(struct netsec_desc) * DESC_NUM); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 18cadf0b0d66..4304c1abc5d1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -24,11 +24,14 @@ #define MII_BUSY 0x00000001 #define MII_WRITE 0x00000002 +#define MII_DATA_MASK GENMASK(15, 0) /* GMAC4 defines */ #define MII_GMAC4_GOC_SHIFT 2 +#define MII_GMAC4_REG_ADDR_SHIFT 16 #define MII_GMAC4_WRITE (1 << MII_GMAC4_GOC_SHIFT) #define MII_GMAC4_READ (3 << MII_GMAC4_GOC_SHIFT) +#define MII_GMAC4_C45E BIT(1) /* XGMAC defines */ #define MII_XGMAC_SADDR BIT(18) @@ -155,22 +158,34 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) struct stmmac_priv *priv = netdev_priv(ndev); unsigned int mii_address = priv->hw->mii.addr; unsigned int mii_data = priv->hw->mii.data; - u32 v; - int data; u32 value = MII_BUSY; + int data = 0; + u32 v; value |= (phyaddr << priv->hw->mii.addr_shift) & priv->hw->mii.addr_mask; value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask; value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift) & priv->hw->mii.clk_csr_mask; - if (priv->plat->has_gmac4) + if (priv->plat->has_gmac4) { value |= MII_GMAC4_READ; + if (phyreg & MII_ADDR_C45) { + value |= MII_GMAC4_C45E; + value &= ~priv->hw->mii.reg_mask; + value |= ((phyreg >> MII_DEVADDR_C45_SHIFT) << + priv->hw->mii.reg_shift) & + priv->hw->mii.reg_mask; + + data |= (phyreg & MII_REGADDR_C45_MASK) << + MII_GMAC4_REG_ADDR_SHIFT; + } + } if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), 100, 10000)) return -EBUSY; + writel(data, priv->ioaddr + mii_data); writel(value, priv->ioaddr + mii_address); if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), @@ -178,7 +193,7 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) return -EBUSY; /* Read the data from the MII data register */ - data = (int)readl(priv->ioaddr + mii_data); + data = (int)readl(priv->ioaddr + mii_data) & MII_DATA_MASK; return data; } @@ -198,8 +213,9 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, struct stmmac_priv *priv = netdev_priv(ndev); unsigned int mii_address = priv->hw->mii.addr; unsigned int mii_data = priv->hw->mii.data; - u32 v; u32 value = MII_BUSY; + int data = phydata; + u32 v; value |= (phyaddr << priv->hw->mii.addr_shift) & priv->hw->mii.addr_mask; @@ -207,10 +223,21 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift) & priv->hw->mii.clk_csr_mask; - if (priv->plat->has_gmac4) + if (priv->plat->has_gmac4) { value |= MII_GMAC4_WRITE; - else + if (phyreg & MII_ADDR_C45) { + value |= MII_GMAC4_C45E; + value &= ~priv->hw->mii.reg_mask; + value |= ((phyreg >> MII_DEVADDR_C45_SHIFT) << + priv->hw->mii.reg_shift) & + priv->hw->mii.reg_mask; + + data |= (phyreg & MII_REGADDR_C45_MASK) << + MII_GMAC4_REG_ADDR_SHIFT; + } + } else { value |= MII_WRITE; + } /* Wait until any existing MII operation is complete */ if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), @@ -218,7 +245,7 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, return -EBUSY; /* Set the MII address register to write */ - writel(phydata, priv->ioaddr + mii_data); + writel(data, priv->ioaddr + mii_data); writel(value, priv->ioaddr + mii_address); /* Wait until any existing MII operation is complete */ diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index a800d3417411..834afca3a019 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -50,6 +50,7 @@ config TI_CPSW depends on ARCH_DAVINCI || ARCH_OMAP2PLUS || COMPILE_TEST select TI_DAVINCI_MDIO select MFD_SYSCON + select PAGE_POOL select REGMAP ---help--- This driver supports TI's CPSW Ethernet Switch. diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 32b7b3b74a6b..f16aefd8870b 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -31,6 +31,10 @@ #include <linux/if_vlan.h> #include <linux/kmemleak.h> #include <linux/sys_soc.h> +#include <net/page_pool.h> +#include <linux/bpf.h> +#include <linux/bpf_trace.h> +#include <linux/filter.h> #include <linux/pinctrl/consumer.h> #include <net/pkt_cls.h> @@ -60,6 +64,10 @@ static int descs_pool_size = CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT; module_param(descs_pool_size, int, 0444); MODULE_PARM_DESC(descs_pool_size, "Number of CPDMA CPPI descriptors in pool"); +/* The buf includes headroom compatible with both skb and xdpf */ +#define CPSW_HEADROOM_NA (max(XDP_PACKET_HEADROOM, NET_SKB_PAD) + NET_IP_ALIGN) +#define CPSW_HEADROOM ALIGN(CPSW_HEADROOM_NA, sizeof(long)) + #define for_each_slave(priv, func, arg...) \ do { \ struct cpsw_slave *slave; \ @@ -74,6 +82,11 @@ MODULE_PARM_DESC(descs_pool_size, "Number of CPDMA CPPI descriptors in pool"); (func)(slave++, ##arg); \ } while (0) +#define CPSW_XMETA_OFFSET ALIGN(sizeof(struct xdp_frame), sizeof(long)) + +#define CPSW_XDP_CONSUMED 1 +#define CPSW_XDP_PASS 0 + static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid); @@ -337,24 +350,58 @@ void cpsw_intr_disable(struct cpsw_common *cpsw) return; } +static int cpsw_is_xdpf_handle(void *handle) +{ + return (unsigned long)handle & BIT(0); +} + +static void *cpsw_xdpf_to_handle(struct xdp_frame *xdpf) +{ + return (void *)((unsigned long)xdpf | BIT(0)); +} + +static struct xdp_frame *cpsw_handle_to_xdpf(void *handle) +{ + return (struct xdp_frame *)((unsigned long)handle & ~BIT(0)); +} + +struct __aligned(sizeof(long)) cpsw_meta_xdp { + struct net_device *ndev; + int ch; +}; + void cpsw_tx_handler(void *token, int len, int status) { + struct cpsw_meta_xdp *xmeta; + struct xdp_frame *xdpf; + struct net_device *ndev; struct netdev_queue *txq; - struct sk_buff *skb = token; - struct net_device *ndev = skb->dev; - struct cpsw_common *cpsw = ndev_to_cpsw(ndev); + struct sk_buff *skb; + int ch; + + if (cpsw_is_xdpf_handle(token)) { + xdpf = cpsw_handle_to_xdpf(token); + xmeta = (void *)xdpf + CPSW_XMETA_OFFSET; + ndev = xmeta->ndev; + ch = xmeta->ch; + xdp_return_frame(xdpf); + } else { + skb = token; + ndev = skb->dev; + ch = skb_get_queue_mapping(skb); + cpts_tx_timestamp(ndev_to_cpsw(ndev)->cpts, skb); + dev_kfree_skb_any(skb); + } /* Check whether the queue is stopped due to stalled tx dma, if the * queue is stopped then start the queue as we have free desc for tx */ - txq = netdev_get_tx_queue(ndev, skb_get_queue_mapping(skb)); + txq = netdev_get_tx_queue(ndev, ch); if (unlikely(netif_tx_queue_stopped(txq))) netif_tx_wake_queue(txq); - cpts_tx_timestamp(cpsw->cpts, skb); ndev->stats.tx_packets++; ndev->stats.tx_bytes += len; - dev_kfree_skb_any(skb); } static void cpsw_rx_vlan_encap(struct sk_buff *skb) @@ -400,24 +447,252 @@ static void cpsw_rx_vlan_encap(struct sk_buff *skb) } } +static int cpsw_xdp_tx_frame(struct cpsw_priv *priv, struct xdp_frame *xdpf, + struct page *page) +{ + struct cpsw_common *cpsw = priv->cpsw; + struct cpsw_meta_xdp *xmeta; + struct cpdma_chan *txch; + dma_addr_t dma; + int ret, port; + + xmeta = (void *)xdpf + CPSW_XMETA_OFFSET; + xmeta->ndev = priv->ndev; + xmeta->ch = 0; + txch = cpsw->txv[0].ch; + + port = priv->emac_port + cpsw->data.dual_emac; + if (page) { + dma = page_pool_get_dma_addr(page); + dma += xdpf->headroom + sizeof(struct xdp_frame); + ret = cpdma_chan_submit_mapped(txch, cpsw_xdpf_to_handle(xdpf), + dma, xdpf->len, port); + } else { + if (sizeof(*xmeta) > xdpf->headroom) { + xdp_return_frame_rx_napi(xdpf); + return -EINVAL; + } + + ret = cpdma_chan_submit(txch, cpsw_xdpf_to_handle(xdpf), + xdpf->data, xdpf->len, port); + } + + if (ret) { + priv->ndev->stats.tx_dropped++; + xdp_return_frame_rx_napi(xdpf); + } + + return ret; +} + +static int cpsw_run_xdp(struct cpsw_priv *priv, int ch, struct xdp_buff *xdp, + struct page *page) +{ + struct cpsw_common *cpsw = priv->cpsw; + struct net_device *ndev = priv->ndev; + int ret = CPSW_XDP_CONSUMED; + struct xdp_frame *xdpf; + struct bpf_prog *prog; + u32 act; + + rcu_read_lock(); + + prog = READ_ONCE(priv->xdp_prog); + if (!prog) { + ret = CPSW_XDP_PASS; + goto out; + } + + act = bpf_prog_run_xdp(prog, xdp); + switch (act) { + case XDP_PASS: + ret = CPSW_XDP_PASS; + break; + case XDP_TX: + xdpf = convert_to_xdp_frame(xdp); + if (unlikely(!xdpf)) + goto drop; + + cpsw_xdp_tx_frame(priv, xdpf, page); + break; + case XDP_REDIRECT: + if (xdp_do_redirect(ndev, xdp, prog)) + goto drop; + + /* Have to flush here, per packet, instead of doing it in bulk + * at the end of the napi handler. The RX devices on this + * particular hardware is sharing a common queue, so the + * incoming device might change per packet. + */ + xdp_do_flush_map(); + break; + default: + bpf_warn_invalid_xdp_action(act); + /* fall through */ + case XDP_ABORTED: + trace_xdp_exception(ndev, prog, act); + /* fall through -- handle aborts by dropping packet */ + case XDP_DROP: + goto drop; + } +out: + rcu_read_unlock(); + return ret; +drop: + rcu_read_unlock(); + page_pool_recycle_direct(cpsw->page_pool[ch], page); + return ret; +} + +static unsigned int cpsw_rxbuf_total_len(unsigned int len) +{ + len += CPSW_HEADROOM; + len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + return SKB_DATA_ALIGN(len); +} + +static struct page_pool *cpsw_create_page_pool(struct cpsw_common *cpsw, + int size) +{ + struct page_pool_params pp_params; + struct page_pool *pool; + + pp_params.order = 0; + pp_params.flags = PP_FLAG_DMA_MAP; + pp_params.pool_size = size; + pp_params.nid = NUMA_NO_NODE; + pp_params.dma_dir = DMA_BIDIRECTIONAL; + pp_params.dev = cpsw->dev; + + pool = page_pool_create(&pp_params); + if (IS_ERR(pool)) + dev_err(cpsw->dev, "cannot create rx page pool\n"); + + return pool; +} + +static int cpsw_ndev_create_xdp_rxq(struct cpsw_priv *priv, int ch) +{ + struct cpsw_common *cpsw = priv->cpsw; + struct xdp_rxq_info *rxq; + struct page_pool *pool; + int ret; + + pool = cpsw->page_pool[ch]; + rxq = &priv->xdp_rxq[ch]; + + ret = xdp_rxq_info_reg(rxq, priv->ndev, ch); + if (ret) + return ret; + + ret = xdp_rxq_info_reg_mem_model(rxq, MEM_TYPE_PAGE_POOL, pool); + if (ret) + xdp_rxq_info_unreg(rxq); + + return ret; +} + +static void cpsw_ndev_destroy_xdp_rxq(struct cpsw_priv *priv, int ch) +{ + struct xdp_rxq_info *rxq = &priv->xdp_rxq[ch]; + + if (!xdp_rxq_info_is_reg(rxq)) + return; + + xdp_rxq_info_unreg(rxq); +} + +static int cpsw_create_rx_pool(struct cpsw_common *cpsw, int ch) +{ + struct page_pool *pool; + int ret = 0, pool_size; + + pool_size = cpdma_chan_get_rx_buf_num(cpsw->rxv[ch].ch); + pool = cpsw_create_page_pool(cpsw, pool_size); + if (IS_ERR(pool)) + ret = PTR_ERR(pool); + else + cpsw->page_pool[ch] = pool; + + return ret; +} + +void cpsw_destroy_xdp_rxqs(struct cpsw_common *cpsw) +{ + struct net_device *ndev; + int i, ch; + + for (ch = 0; ch < cpsw->rx_ch_num; ch++) { + for (i = 0; i < cpsw->data.slaves; i++) { + ndev = cpsw->slaves[i].ndev; + if (!ndev) + continue; + + cpsw_ndev_destroy_xdp_rxq(netdev_priv(ndev), ch); + } + + page_pool_destroy(cpsw->page_pool[ch]); + cpsw->page_pool[ch] = NULL; + } +} + +int cpsw_create_xdp_rxqs(struct cpsw_common *cpsw) +{ + struct net_device *ndev; + int i, ch, ret; + + for (ch = 0; ch < cpsw->rx_ch_num; ch++) { + ret = cpsw_create_rx_pool(cpsw, ch); + if (ret) + goto err_cleanup; + + /* using same page pool is allowed as no running rx handlers + * simultaneously for both ndevs + */ + for (i = 0; i < cpsw->data.slaves; i++) { + ndev = cpsw->slaves[i].ndev; + if (!ndev) + continue; + + ret = cpsw_ndev_create_xdp_rxq(netdev_priv(ndev), ch); + if (ret) + goto err_cleanup; + } + } + + return 0; + +err_cleanup: + cpsw_destroy_xdp_rxqs(cpsw); + + return ret; +} + static void cpsw_rx_handler(void *token, int len, int status) { - struct cpdma_chan *ch; - struct sk_buff *skb = token; - struct sk_buff *new_skb; - struct net_device *ndev = skb->dev; - int ret = 0, port; - struct cpsw_common *cpsw = ndev_to_cpsw(ndev); + struct page *new_page, *page = token; + void *pa = page_address(page); + struct cpsw_meta_xdp *xmeta = pa + CPSW_XMETA_OFFSET; + struct cpsw_common *cpsw = ndev_to_cpsw(xmeta->ndev); + int pkt_size = cpsw->rx_packet_max; + int ret = 0, port, ch = xmeta->ch; + int headroom = CPSW_HEADROOM; + struct net_device *ndev = xmeta->ndev; struct cpsw_priv *priv; + struct page_pool *pool; + struct sk_buff *skb; + struct xdp_buff xdp; + dma_addr_t dma; - if (cpsw->data.dual_emac) { + if (cpsw->data.dual_emac && status >= 0) { port = CPDMA_RX_SOURCE_PORT(status); - if (port) { + if (port) ndev = cpsw->slaves[--port].ndev; - skb->dev = ndev; - } } + priv = netdev_priv(ndev); + pool = cpsw->page_pool[ch]; if (unlikely(status < 0) || unlikely(!netif_running(ndev))) { /* In dual emac mode check for all interfaces */ if (cpsw->data.dual_emac && cpsw->usage_count && @@ -426,43 +701,87 @@ static void cpsw_rx_handler(void *token, int len, int status) * is already down and the other interface is up * and running, instead of freeing which results * in reducing of the number of rx descriptor in - * DMA engine, requeue skb back to cpdma. + * DMA engine, requeue page back to cpdma. */ - new_skb = skb; + new_page = page; goto requeue; } - /* the interface is going down, skbs are purged */ - dev_kfree_skb_any(skb); + /* the interface is going down, pages are purged */ + page_pool_recycle_direct(pool, page); return; } - new_skb = netdev_alloc_skb_ip_align(ndev, cpsw->rx_packet_max); - if (new_skb) { - skb_copy_queue_mapping(new_skb, skb); - skb_put(skb, len); - if (status & CPDMA_RX_VLAN_ENCAP) - cpsw_rx_vlan_encap(skb); - priv = netdev_priv(ndev); - if (priv->rx_ts_enabled) - cpts_rx_timestamp(cpsw->cpts, skb); - skb->protocol = eth_type_trans(skb, ndev); - netif_receive_skb(skb); - ndev->stats.rx_bytes += len; - ndev->stats.rx_packets++; - kmemleak_not_leak(new_skb); - } else { + new_page = page_pool_dev_alloc_pages(pool); + if (unlikely(!new_page)) { + new_page = page; ndev->stats.rx_dropped++; - new_skb = skb; + goto requeue; + } + + if (priv->xdp_prog) { + if (status & CPDMA_RX_VLAN_ENCAP) { + xdp.data = pa + CPSW_HEADROOM + + CPSW_RX_VLAN_ENCAP_HDR_SIZE; + xdp.data_end = xdp.data + len - + CPSW_RX_VLAN_ENCAP_HDR_SIZE; + } else { + xdp.data = pa + CPSW_HEADROOM; + xdp.data_end = xdp.data + len; + } + + xdp_set_data_meta_invalid(&xdp); + + xdp.data_hard_start = pa; + xdp.rxq = &priv->xdp_rxq[ch]; + + ret = cpsw_run_xdp(priv, ch, &xdp, page); + if (ret != CPSW_XDP_PASS) + goto requeue; + + /* XDP prog might have changed packet data and boundaries */ + len = xdp.data_end - xdp.data; + headroom = xdp.data - xdp.data_hard_start; + + /* XDP prog can modify vlan tag, so can't use encap header */ + status &= ~CPDMA_RX_VLAN_ENCAP; } + /* pass skb to netstack if no XDP prog or returned XDP_PASS */ + skb = build_skb(pa, cpsw_rxbuf_total_len(pkt_size)); + if (!skb) { + ndev->stats.rx_dropped++; + page_pool_recycle_direct(pool, page); + goto requeue; + } + + skb_reserve(skb, headroom); + skb_put(skb, len); + skb->dev = ndev; + if (status & CPDMA_RX_VLAN_ENCAP) + cpsw_rx_vlan_encap(skb); + if (priv->rx_ts_enabled) + cpts_rx_timestamp(cpsw->cpts, skb); + skb->protocol = eth_type_trans(skb, ndev); + + /* unmap page as no netstack skb page recycling */ + page_pool_release_page(pool, page); + netif_receive_skb(skb); + + ndev->stats.rx_bytes += len; + ndev->stats.rx_packets++; + requeue: - ch = cpsw->rxv[skb_get_queue_mapping(new_skb)].ch; - ret = cpdma_chan_submit(ch, new_skb, new_skb->data, - skb_tailroom(new_skb), 0); + xmeta = page_address(new_page) + CPSW_XMETA_OFFSET; + xmeta->ndev = ndev; + xmeta->ch = ch; + + dma = page_pool_get_dma_addr(new_page) + CPSW_HEADROOM; + ret = cpdma_chan_submit_mapped(cpsw->rxv[ch].ch, new_page, dma, + pkt_size, 0); if (ret < 0) { WARN_ON(ret == -ENOMEM); - dev_kfree_skb_any(new_skb); + page_pool_recycle_direct(pool, new_page); } } @@ -1032,33 +1351,39 @@ static void cpsw_init_host_port(struct cpsw_priv *priv) int cpsw_fill_rx_channels(struct cpsw_priv *priv) { struct cpsw_common *cpsw = priv->cpsw; - struct sk_buff *skb; + struct cpsw_meta_xdp *xmeta; + struct page_pool *pool; + struct page *page; int ch_buf_num; int ch, i, ret; + dma_addr_t dma; for (ch = 0; ch < cpsw->rx_ch_num; ch++) { + pool = cpsw->page_pool[ch]; ch_buf_num = cpdma_chan_get_rx_buf_num(cpsw->rxv[ch].ch); for (i = 0; i < ch_buf_num; i++) { - skb = __netdev_alloc_skb_ip_align(priv->ndev, - cpsw->rx_packet_max, - GFP_KERNEL); - if (!skb) { - cpsw_err(priv, ifup, "cannot allocate skb\n"); + page = page_pool_dev_alloc_pages(pool); + if (!page) { + cpsw_err(priv, ifup, "allocate rx page err\n"); return -ENOMEM; } - skb_set_queue_mapping(skb, ch); - ret = cpdma_chan_idle_submit(cpsw->rxv[ch].ch, skb, - skb->data, - skb_tailroom(skb), 0); + xmeta = page_address(page) + CPSW_XMETA_OFFSET; + xmeta->ndev = priv->ndev; + xmeta->ch = ch; + + dma = page_pool_get_dma_addr(page) + CPSW_HEADROOM; + ret = cpdma_chan_idle_submit_mapped(cpsw->rxv[ch].ch, + page, dma, + cpsw->rx_packet_max, + 0); if (ret < 0) { cpsw_err(priv, ifup, - "cannot submit skb to channel %d rx, error %d\n", + "cannot submit page to channel %d rx, error %d\n", ch, ret); - kfree_skb(skb); + page_pool_recycle_direct(pool, page); return ret; } - kmemleak_not_leak(skb); } cpsw_info(priv, ifup, "ch %d rx, submitted %d descriptors\n", @@ -1394,6 +1719,13 @@ static int cpsw_ndo_open(struct net_device *ndev) enable_irq(cpsw->irqs_table[0]); } + /* create rxqs for both infs in dual mac as they use same pool + * and must be destroyed together when no users. + */ + ret = cpsw_create_xdp_rxqs(cpsw); + if (ret < 0) + goto err_cleanup; + ret = cpsw_fill_rx_channels(priv); if (ret < 0) goto err_cleanup; @@ -1422,9 +1754,10 @@ static int cpsw_ndo_open(struct net_device *ndev) err_cleanup: if (!cpsw->usage_count) { cpdma_ctlr_stop(cpsw->dma); - for_each_slave(priv, cpsw_slave_stop, cpsw); + cpsw_destroy_xdp_rxqs(cpsw); } + for_each_slave(priv, cpsw_slave_stop, cpsw); pm_runtime_put_sync(cpsw->dev); netif_carrier_off(priv->ndev); return ret; @@ -1447,6 +1780,7 @@ static int cpsw_ndo_stop(struct net_device *ndev) cpsw_intr_disable(cpsw); cpdma_ctlr_stop(cpsw->dma); cpsw_ale_stop(cpsw->ale); + cpsw_destroy_xdp_rxqs(cpsw); } for_each_slave(priv, cpsw_slave_stop, cpsw); @@ -2004,6 +2338,64 @@ static int cpsw_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type, } } +static int cpsw_xdp_prog_setup(struct cpsw_priv *priv, struct netdev_bpf *bpf) +{ + struct bpf_prog *prog = bpf->prog; + + if (!priv->xdpi.prog && !prog) + return 0; + + if (!xdp_attachment_flags_ok(&priv->xdpi, bpf)) + return -EBUSY; + + WRITE_ONCE(priv->xdp_prog, prog); + + xdp_attachment_setup(&priv->xdpi, bpf); + + return 0; +} + +static int cpsw_ndo_bpf(struct net_device *ndev, struct netdev_bpf *bpf) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + + switch (bpf->command) { + case XDP_SETUP_PROG: + return cpsw_xdp_prog_setup(priv, bpf); + + case XDP_QUERY_PROG: + return xdp_attachment_query(&priv->xdpi, bpf); + + default: + return -EINVAL; + } +} + +static int cpsw_ndo_xdp_xmit(struct net_device *ndev, int n, + struct xdp_frame **frames, u32 flags) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct xdp_frame *xdpf; + int i, drops = 0; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + for (i = 0; i < n; i++) { + xdpf = frames[i]; + if (xdpf->len < CPSW_MIN_PACKET_SIZE) { + xdp_return_frame_rx_napi(xdpf); + drops++; + continue; + } + + if (cpsw_xdp_tx_frame(priv, xdpf, NULL)) + drops++; + } + + return n - drops; +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void cpsw_ndo_poll_controller(struct net_device *ndev) { @@ -2032,6 +2424,8 @@ static const struct net_device_ops cpsw_netdev_ops = { .ndo_vlan_rx_add_vid = cpsw_ndo_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = cpsw_ndo_vlan_rx_kill_vid, .ndo_setup_tc = cpsw_ndo_setup_tc, + .ndo_bpf = cpsw_ndo_bpf, + .ndo_xdp_xmit = cpsw_ndo_xdp_xmit, }; static void cpsw_get_drvinfo(struct net_device *ndev, diff --git a/drivers/net/ethernet/ti/cpsw_ethtool.c b/drivers/net/ethernet/ti/cpsw_ethtool.c index f60dc1dfc443..31248a6cc642 100644 --- a/drivers/net/ethernet/ti/cpsw_ethtool.c +++ b/drivers/net/ethernet/ti/cpsw_ethtool.c @@ -578,6 +578,18 @@ static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx, return 0; } +static void cpsw_fail(struct cpsw_common *cpsw) +{ + struct net_device *ndev; + int i; + + for (i = 0; i < cpsw->data.slaves; i++) { + ndev = cpsw->slaves[i].ndev; + if (ndev) + dev_close(ndev); + } +} + int cpsw_set_channels_common(struct net_device *ndev, struct ethtool_channels *chs, cpdma_handler_fn rx_handler) @@ -585,7 +597,7 @@ int cpsw_set_channels_common(struct net_device *ndev, struct cpsw_priv *priv = netdev_priv(ndev); struct cpsw_common *cpsw = priv->cpsw; struct net_device *sl_ndev; - int i, ret; + int i, new_pools, ret; ret = cpsw_check_ch_settings(cpsw, chs); if (ret < 0) @@ -593,6 +605,8 @@ int cpsw_set_channels_common(struct net_device *ndev, cpsw_suspend_data_pass(ndev); + new_pools = (chs->rx_count != cpsw->rx_ch_num) && cpsw->usage_count; + ret = cpsw_update_channels_res(priv, chs->rx_count, 1, rx_handler); if (ret) goto err; @@ -620,15 +634,21 @@ int cpsw_set_channels_common(struct net_device *ndev, } } - if (cpsw->usage_count) - cpsw_split_res(cpsw); + cpsw_split_res(cpsw); + + if (new_pools) { + cpsw_destroy_xdp_rxqs(cpsw); + ret = cpsw_create_xdp_rxqs(cpsw); + if (ret) + goto err; + } ret = cpsw_resume_data_pass(ndev); if (!ret) return 0; err: dev_err(priv->dev, "cannot update channels number, closing device\n"); - dev_close(ndev); + cpsw_fail(cpsw); return ret; } @@ -648,9 +668,8 @@ void cpsw_get_ringparam(struct net_device *ndev, int cpsw_set_ringparam(struct net_device *ndev, struct ethtool_ringparam *ering) { - struct cpsw_priv *priv = netdev_priv(ndev); - struct cpsw_common *cpsw = priv->cpsw; - int ret; + struct cpsw_common *cpsw = ndev_to_cpsw(ndev); + int descs_num, ret; /* ignore ering->tx_pending - only rx_pending adjustment is supported */ @@ -659,22 +678,34 @@ int cpsw_set_ringparam(struct net_device *ndev, ering->rx_pending > (cpsw->descs_pool_size - CPSW_MAX_QUEUES)) return -EINVAL; - if (ering->rx_pending == cpdma_get_num_rx_descs(cpsw->dma)) + descs_num = cpdma_get_num_rx_descs(cpsw->dma); + if (ering->rx_pending == descs_num) return 0; cpsw_suspend_data_pass(ndev); - cpdma_set_num_rx_descs(cpsw->dma, ering->rx_pending); + ret = cpdma_set_num_rx_descs(cpsw->dma, ering->rx_pending); + if (ret) { + if (cpsw_resume_data_pass(ndev)) + goto err; + + return ret; + } - if (cpsw->usage_count) - cpdma_chan_split_pool(cpsw->dma); + if (cpsw->usage_count) { + cpsw_destroy_xdp_rxqs(cpsw); + ret = cpsw_create_xdp_rxqs(cpsw); + if (ret) + goto err; + } ret = cpsw_resume_data_pass(ndev); if (!ret) return 0; - +err: + cpdma_set_num_rx_descs(cpsw->dma, descs_num); dev_err(cpsw->dev, "cannot set ring params, closing device\n"); - dev_close(ndev); + cpsw_fail(cpsw); return ret; } diff --git a/drivers/net/ethernet/ti/cpsw_priv.h b/drivers/net/ethernet/ti/cpsw_priv.h index 04795b97ee71..086bf38a4736 100644 --- a/drivers/net/ethernet/ti/cpsw_priv.h +++ b/drivers/net/ethernet/ti/cpsw_priv.h @@ -346,6 +346,7 @@ struct cpsw_common { int rx_ch_num, tx_ch_num; int speed; int usage_count; + struct page_pool *page_pool[CPSW_MAX_QUEUES]; }; struct cpsw_priv { @@ -360,6 +361,10 @@ struct cpsw_priv { int shp_cfg_speed; int tx_ts_enabled; int rx_ts_enabled; + struct bpf_prog *xdp_prog; + struct xdp_rxq_info xdp_rxq[CPSW_MAX_QUEUES]; + struct xdp_attachment_info xdpi; + u32 emac_port; struct cpsw_common *cpsw; }; @@ -391,6 +396,8 @@ int cpsw_fill_rx_channels(struct cpsw_priv *priv); void cpsw_intr_enable(struct cpsw_common *cpsw); void cpsw_intr_disable(struct cpsw_common *cpsw); void cpsw_tx_handler(void *token, int len, int status); +int cpsw_create_xdp_rxqs(struct cpsw_common *cpsw); +void cpsw_destroy_xdp_rxqs(struct cpsw_common *cpsw); /* ethtool */ u32 cpsw_get_msglevel(struct net_device *ndev); diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 5cf1758d425b..0ca2a1a254de 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -139,6 +139,7 @@ struct submit_info { int directed; void *token; void *data; + int flags; int len; }; @@ -184,6 +185,8 @@ static struct cpdma_control_info controls[] = { (directed << CPDMA_TO_PORT_SHIFT)); \ } while (0) +#define CPDMA_DMA_EXT_MAP BIT(16) + static void cpdma_desc_pool_destroy(struct cpdma_ctlr *ctlr) { struct cpdma_desc_pool *pool = ctlr->pool; @@ -1015,6 +1018,7 @@ static int cpdma_chan_submit_si(struct submit_info *si) struct cpdma_chan *chan = si->chan; struct cpdma_ctlr *ctlr = chan->ctlr; int len = si->len; + int swlen = len; struct cpdma_desc __iomem *desc; dma_addr_t buffer; u32 mode; @@ -1036,16 +1040,22 @@ static int cpdma_chan_submit_si(struct submit_info *si) chan->stats.runt_transmit_buff++; } - buffer = dma_map_single(ctlr->dev, si->data, len, chan->dir); - ret = dma_mapping_error(ctlr->dev, buffer); - if (ret) { - cpdma_desc_free(ctlr->pool, desc, 1); - return -EINVAL; - } - mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; cpdma_desc_to_port(chan, mode, si->directed); + if (si->flags & CPDMA_DMA_EXT_MAP) { + buffer = (dma_addr_t)si->data; + dma_sync_single_for_device(ctlr->dev, buffer, len, chan->dir); + swlen |= CPDMA_DMA_EXT_MAP; + } else { + buffer = dma_map_single(ctlr->dev, si->data, len, chan->dir); + ret = dma_mapping_error(ctlr->dev, buffer); + if (ret) { + cpdma_desc_free(ctlr->pool, desc, 1); + return -EINVAL; + } + } + /* Relaxed IO accessors can be used here as there is read barrier * at the end of write sequence. */ @@ -1055,7 +1065,7 @@ static int cpdma_chan_submit_si(struct submit_info *si) writel_relaxed(mode | len, &desc->hw_mode); writel_relaxed((uintptr_t)si->token, &desc->sw_token); writel_relaxed(buffer, &desc->sw_buffer); - writel_relaxed(len, &desc->sw_len); + writel_relaxed(swlen, &desc->sw_len); desc_read(desc, sw_len); __cpdma_chan_submit(chan, desc); @@ -1079,6 +1089,32 @@ int cpdma_chan_idle_submit(struct cpdma_chan *chan, void *token, void *data, si.data = data; si.len = len; si.directed = directed; + si.flags = 0; + + spin_lock_irqsave(&chan->lock, flags); + if (chan->state == CPDMA_STATE_TEARDOWN) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EINVAL; + } + + ret = cpdma_chan_submit_si(&si); + spin_unlock_irqrestore(&chan->lock, flags); + return ret; +} + +int cpdma_chan_idle_submit_mapped(struct cpdma_chan *chan, void *token, + dma_addr_t data, int len, int directed) +{ + struct submit_info si; + unsigned long flags; + int ret; + + si.chan = chan; + si.token = token; + si.data = (void *)data; + si.len = len; + si.directed = directed; + si.flags = CPDMA_DMA_EXT_MAP; spin_lock_irqsave(&chan->lock, flags); if (chan->state == CPDMA_STATE_TEARDOWN) { @@ -1103,6 +1139,32 @@ int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, si.data = data; si.len = len; si.directed = directed; + si.flags = 0; + + spin_lock_irqsave(&chan->lock, flags); + if (chan->state != CPDMA_STATE_ACTIVE) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EINVAL; + } + + ret = cpdma_chan_submit_si(&si); + spin_unlock_irqrestore(&chan->lock, flags); + return ret; +} + +int cpdma_chan_submit_mapped(struct cpdma_chan *chan, void *token, + dma_addr_t data, int len, int directed) +{ + struct submit_info si; + unsigned long flags; + int ret; + + si.chan = chan; + si.token = token; + si.data = (void *)data; + si.len = len; + si.directed = directed; + si.flags = CPDMA_DMA_EXT_MAP; spin_lock_irqsave(&chan->lock, flags); if (chan->state != CPDMA_STATE_ACTIVE) { @@ -1140,10 +1202,17 @@ static void __cpdma_chan_free(struct cpdma_chan *chan, uintptr_t token; token = desc_read(desc, sw_token); - buff_dma = desc_read(desc, sw_buffer); origlen = desc_read(desc, sw_len); - dma_unmap_single(ctlr->dev, buff_dma, origlen, chan->dir); + buff_dma = desc_read(desc, sw_buffer); + if (origlen & CPDMA_DMA_EXT_MAP) { + origlen &= ~CPDMA_DMA_EXT_MAP; + dma_sync_single_for_cpu(ctlr->dev, buff_dma, origlen, + chan->dir); + } else { + dma_unmap_single(ctlr->dev, buff_dma, origlen, chan->dir); + } + cpdma_desc_free(pool, desc, 1); (*chan->handler)((void *)token, outlen, status); } @@ -1354,8 +1423,23 @@ int cpdma_get_num_tx_descs(struct cpdma_ctlr *ctlr) return ctlr->num_tx_desc; } -void cpdma_set_num_rx_descs(struct cpdma_ctlr *ctlr, int num_rx_desc) +int cpdma_set_num_rx_descs(struct cpdma_ctlr *ctlr, int num_rx_desc) { + unsigned long flags; + int temp, ret; + + spin_lock_irqsave(&ctlr->lock, flags); + + temp = ctlr->num_rx_desc; ctlr->num_rx_desc = num_rx_desc; ctlr->num_tx_desc = ctlr->pool->num_desc - ctlr->num_rx_desc; + ret = cpdma_chan_split_pool(ctlr); + if (ret) { + ctlr->num_rx_desc = temp; + ctlr->num_tx_desc = ctlr->pool->num_desc - ctlr->num_rx_desc; + } + + spin_unlock_irqrestore(&ctlr->lock, flags); + + return ret; } diff --git a/drivers/net/ethernet/ti/davinci_cpdma.h b/drivers/net/ethernet/ti/davinci_cpdma.h index 9343c8c73c1b..d3cfe234d16a 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.h +++ b/drivers/net/ethernet/ti/davinci_cpdma.h @@ -77,8 +77,12 @@ int cpdma_chan_stop(struct cpdma_chan *chan); int cpdma_chan_get_stats(struct cpdma_chan *chan, struct cpdma_chan_stats *stats); +int cpdma_chan_submit_mapped(struct cpdma_chan *chan, void *token, + dma_addr_t data, int len, int directed); int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, int len, int directed); +int cpdma_chan_idle_submit_mapped(struct cpdma_chan *chan, void *token, + dma_addr_t data, int len, int directed); int cpdma_chan_idle_submit(struct cpdma_chan *chan, void *token, void *data, int len, int directed); int cpdma_chan_process(struct cpdma_chan *chan, int quota); @@ -112,8 +116,7 @@ enum cpdma_control { int cpdma_control_get(struct cpdma_ctlr *ctlr, int control); int cpdma_control_set(struct cpdma_ctlr *ctlr, int control, int value); int cpdma_get_num_rx_descs(struct cpdma_ctlr *ctlr); -void cpdma_set_num_rx_descs(struct cpdma_ctlr *ctlr, int num_rx_desc); +int cpdma_set_num_rx_descs(struct cpdma_ctlr *ctlr, int num_rx_desc); int cpdma_get_num_tx_descs(struct cpdma_ctlr *ctlr); -int cpdma_chan_split_pool(struct cpdma_ctlr *ctlr); #endif diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 561e28a5df04..4fc627fb4d11 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1802,7 +1802,6 @@ static int axienet_probe(struct platform_device *pdev) if (IS_ERR(lp->dma_regs)) { dev_err(&pdev->dev, "could not map DMA regs\n"); ret = PTR_ERR(lp->dma_regs); - of_node_put(np); goto free_netdev; } if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) { diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 61ef90329788..dc45d16e8d21 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2,7 +2,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) 2018-2019, The Linux Foundation. All rights reserved. */ #include <linux/module.h> @@ -26,10 +26,13 @@ #include "coredump.h" unsigned int ath10k_debug_mask; +EXPORT_SYMBOL(ath10k_debug_mask); + static unsigned int ath10k_cryptmode_param; static bool uart_print; static bool skip_otp; static bool rawmode; +static bool fw_diag_log; unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) | BIT(ATH10K_FW_CRASH_DUMP_CE_DATA); @@ -40,6 +43,7 @@ module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644); module_param(uart_print, bool, 0644); module_param(skip_otp, bool, 0644); module_param(rawmode, bool, 0644); +module_param(fw_diag_log, bool, 0644); module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444); MODULE_PARM_DESC(debug_mask, "Debugging mask"); @@ -48,6 +52,7 @@ MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software"); MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath"); MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file"); +MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging"); static const struct ath10k_hw_params ath10k_hw_params_list[] = { { @@ -83,6 +88,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = true, }, { .id = QCA988X_HW_2_0_VERSION, @@ -117,6 +123,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = true, }, { .id = QCA9887_HW_1_0_VERSION, @@ -152,6 +159,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = QCA6174_HW_3_2_VERSION, @@ -170,7 +178,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, - .hw_ops = &qca6174_ops, + .hw_ops = &qca6174_sdio_ops, .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, .decap_align_bytes = 4, @@ -179,6 +187,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .uart_pin_workaround = true, + .tx_stats_over_pktlog = false, }, { .id = QCA6174_HW_2_1_VERSION, @@ -213,6 +222,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = QCA6174_HW_2_1_VERSION, @@ -247,6 +257,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = QCA6174_HW_3_0_VERSION, @@ -281,6 +292,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = QCA6174_HW_3_2_VERSION, @@ -318,6 +330,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = true, + .tx_stats_over_pktlog = false, }, { .id = QCA99X0_HW_2_0_DEV_VERSION, @@ -358,6 +371,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = QCA9984_HW_1_0_DEV_VERSION, @@ -405,6 +419,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = QCA9888_HW_2_0_DEV_VERSION, @@ -449,6 +464,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = QCA9377_HW_1_0_DEV_VERSION, @@ -483,6 +499,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = QCA9377_HW_1_1_DEV_VERSION, @@ -519,6 +536,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = true, + .tx_stats_over_pktlog = false, }, { .id = QCA4019_HW_1_0_DEV_VERSION, @@ -560,6 +578,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = false, .hw_filter_reset_required = true, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, { .id = WCN3990_HW_1_0_DEV_VERSION, @@ -587,6 +606,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .rri_on_ddr = true, .hw_filter_reset_required = false, .fw_diag_ce_download = false, + .tx_stats_over_pktlog = false, }, }; @@ -612,6 +632,7 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_MGMT_TX_BY_REF] = "mgmt-tx-by-reference", [ATH10K_FW_FEATURE_NON_BMI] = "non-bmi", [ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL] = "single-chan-info-per-channel", + [ATH10K_FW_FEATURE_PEER_FIXED_RATE] = "peer-fixed-rate", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, @@ -2179,6 +2200,7 @@ static void ath10k_core_restart(struct work_struct *work) complete(&ar->offchan_tx_completed); complete(&ar->install_key_done); complete(&ar->vdev_setup_done); + complete(&ar->vdev_delete_done); complete(&ar->thermal.wmi_sync); complete(&ar->bss_survey_done); wake_up(&ar->htt.empty_tx_wq); @@ -2760,6 +2782,12 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, if (status) goto err_hif_stop; + status = ath10k_hif_set_target_log_mode(ar, fw_diag_log); + if (status && status != -EOPNOTSUPP) { + ath10k_warn(ar, "set traget log mode faileds: %d\n", status); + goto err_hif_stop; + } + return 0; err_hif_stop: @@ -3145,8 +3173,10 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_completion(&ar->install_key_done); init_completion(&ar->vdev_setup_done); + init_completion(&ar->vdev_delete_done); init_completion(&ar->thermal.wmi_sync); init_completion(&ar->bss_survey_done); + init_completion(&ar->peer_delete_done); INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 2d109c030ec4..4d7db07db6ba 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -2,7 +2,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) 2018-2019, The Linux Foundation. All rights reserved. */ #ifndef _CORE_H_ @@ -514,7 +514,8 @@ struct ath10k_sta { u32 peer_ps_state; }; -#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5 * HZ) +#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5 * HZ) +#define ATH10K_VDEV_DELETE_TIMEOUT_HZ (5 * HZ) enum ath10k_beacon_state { ATH10K_BEACON_SCHEDULED = 0, @@ -579,6 +580,10 @@ struct ath10k_vif { struct work_struct ap_csa_work; struct delayed_work connection_loss_work; struct cfg80211_bitrate_mask bitrate_mask; + + /* For setting VHT peer fixed rate, protected by conf_mutex */ + int vht_num_rates; + u8 vht_pfr; }; struct ath10k_vif_iter { @@ -640,6 +645,7 @@ struct ath10k_debug { u32 nf_cal_period; void *cal_data; u32 enable_extd_tx_stats; + u8 fw_dbglog_mode; }; enum ath10k_state { @@ -770,6 +776,9 @@ enum ath10k_fw_features { /* Firmware sends only one chan_info event per channel */ ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL = 20, + /* Firmware allows setting peer fixed rate */ + ATH10K_FW_FEATURE_PEER_FIXED_RATE = 21, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; @@ -1065,6 +1074,7 @@ struct ath10k { int last_wmi_vdev_start_status; struct completion vdev_setup_done; + struct completion vdev_delete_done; struct workqueue_struct *workqueue; /* Auxiliary workqueue */ @@ -1199,6 +1209,7 @@ struct ath10k { struct ath10k_radar_found_info last_radar_info; struct work_struct radar_confirmation_work; struct ath10k_bus_params bus_param; + struct completion peer_delete_done; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 740781fe80f8..bd2b5628f850 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -2664,8 +2664,8 @@ void ath10k_debug_unregister(struct ath10k *ar) #endif /* CONFIG_ATH10K_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG -void ath10k_dbg(struct ath10k *ar, enum ath10k_debug_mask mask, - const char *fmt, ...) +void __ath10k_dbg(struct ath10k *ar, enum ath10k_debug_mask mask, + const char *fmt, ...) { struct va_format vaf; va_list args; @@ -2682,7 +2682,7 @@ void ath10k_dbg(struct ath10k *ar, enum ath10k_debug_mask mask, va_end(args); } -EXPORT_SYMBOL(ath10k_dbg); +EXPORT_SYMBOL(__ath10k_dbg); void ath10k_dbg_dump(struct ath10k *ar, enum ath10k_debug_mask mask, @@ -2695,7 +2695,7 @@ void ath10k_dbg_dump(struct ath10k *ar, if (ath10k_debug_mask & mask) { if (msg) - ath10k_dbg(ar, mask, "%s\n", msg); + __ath10k_dbg(ar, mask, "%s\n", msg); for (ptr = buf; (ptr - buf) < len; ptr += 16) { linebuflen = 0; diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index db78e855a80f..82f7eb8583d9 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -71,6 +71,9 @@ struct ath10k_pktlog_hdr { /* FIXME: How to calculate the buffer size sanely? */ #define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024) +#define ATH10K_TX_POWER_MAX_VAL 70 +#define ATH10K_TX_POWER_MIN_VAL 0 + extern unsigned int ath10k_debug_mask; __printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...); @@ -240,18 +243,18 @@ void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, #endif /* CONFIG_MAC80211_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG -__printf(3, 4) void ath10k_dbg(struct ath10k *ar, - enum ath10k_debug_mask mask, - const char *fmt, ...); +__printf(3, 4) void __ath10k_dbg(struct ath10k *ar, + enum ath10k_debug_mask mask, + const char *fmt, ...); void ath10k_dbg_dump(struct ath10k *ar, enum ath10k_debug_mask mask, const char *msg, const char *prefix, const void *buf, size_t len); #else /* CONFIG_ATH10K_DEBUG */ -static inline int ath10k_dbg(struct ath10k *ar, - enum ath10k_debug_mask dbg_mask, - const char *fmt, ...) +static inline int __ath10k_dbg(struct ath10k *ar, + enum ath10k_debug_mask dbg_mask, + const char *fmt, ...) { return 0; } @@ -263,4 +266,14 @@ static inline void ath10k_dbg_dump(struct ath10k *ar, { } #endif /* CONFIG_ATH10K_DEBUG */ + +/* Avoid calling __ath10k_dbg() if debug_mask is not set and tracing + * disabled. + */ +#define ath10k_dbg(ar, dbg_mask, fmt, ...) \ +do { \ + if ((ath10k_debug_mask & dbg_mask) || \ + trace_ath10k_log_dbg_enabled()) \ + __ath10k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \ +} while (0) #endif /* _DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index fe5417962f40..496ee34a4d78 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -12,6 +12,12 @@ #include "bmi.h" #include "debug.h" +/* Types of fw logging mode */ +enum ath_dbg_mode { + ATH10K_ENABLE_FW_LOG_DIAG, + ATH10K_ENABLE_FW_LOG_CE, +}; + struct ath10k_hif_sg_item { u16 transfer_id; void *transfer_context; /* NULL = tx completion callback not called */ @@ -88,6 +94,7 @@ struct ath10k_hif_ops { int (*get_target_info)(struct ath10k *ar, struct bmi_target_info *target_info); + int (*set_target_log_mode)(struct ath10k *ar, u8 fw_log_mode); }; static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id, @@ -230,4 +237,12 @@ static inline int ath10k_hif_get_target_info(struct ath10k *ar, return ar->hif.ops->get_target_info(ar, tgt_info); } +static inline int ath10k_hif_set_target_log_mode(struct ath10k *ar, + u8 fw_log_mode) +{ + if (!ar->hif.ops->set_target_log_mode) + return -EOPNOTSUPP; + + return ar->hif.ops->set_target_log_mode(ar, fw_log_mode); +} #endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index d235ff3098e8..7b75200ceae5 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -257,7 +257,7 @@ int ath10k_htt_setup(struct ath10k_htt *htt) return status; } - status = htt->tx_ops->htt_h2t_aggr_cfg_msg(htt, + status = ath10k_htt_h2t_aggr_cfg_msg(htt, htt->max_num_ampdu, htt->max_num_amsdu); if (status) { diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 19cd0cb5875f..30c080094af1 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -2086,6 +2086,19 @@ static inline void ath10k_htt_free_txbuff(struct ath10k_htt *htt) htt->tx_ops->htt_free_txbuff(htt); } +static inline int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, + u8 max_subfrms_ampdu, + u8 max_subfrms_amsdu) + +{ + if (!htt->tx_ops->htt_h2t_aggr_cfg_msg) + return -EOPNOTSUPP; + + return htt->tx_ops->htt_h2t_aggr_cfg_msg(htt, + max_subfrms_ampdu, + max_subfrms_amsdu); +} + struct ath10k_htt_rx_ops { size_t (*htt_get_rx_ring_size)(struct ath10k_htt *htt); void (*htt_config_paddrs_ring)(struct ath10k_htt *htt, void *vaddr); @@ -2246,9 +2259,6 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u32 mask, u32 reset_mask, u64 cookie); -int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, - u8 max_subfrms_ampdu, - u8 max_subfrms_amsdu); void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_tx_fetch_resp(struct ath10k *ar, __le32 token, diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 0c18c58de71d..83a7fb68fd24 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2158,13 +2158,14 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, int num_mpdu_ranges; size_t tot_hdr_len; struct ieee80211_channel *ch; + bool pn_invalid; peer_id = __le16_to_cpu(rx->hdr.peer_id); spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find_by_id(ar, peer_id); spin_unlock_bh(&ar->data_lock); - if (!peer) + if (!peer && peer_id != HTT_INVALID_PEERID) ath10k_warn(ar, "Got RX ind from invalid peer: %u\n", peer_id); num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1), @@ -2183,15 +2184,21 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, num_mpdu_ranges); if (mpdu_ranges->mpdu_range_status != - HTT_RX_IND_MPDU_STATUS_OK) { + HTT_RX_IND_MPDU_STATUS_OK && + mpdu_ranges->mpdu_range_status != + HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) { ath10k_warn(ar, "MPDU range status: %d\n", mpdu_ranges->mpdu_range_status); goto err; } - if (check_pn_type == HTT_RX_PN_CHECK && - ath10k_htt_rx_pn_check_replay_hl(ar, peer, rx)) - goto err; + if (check_pn_type == HTT_RX_PN_CHECK) { + spin_lock_bh(&ar->data_lock); + pn_invalid = ath10k_htt_rx_pn_check_replay_hl(ar, peer, rx); + spin_unlock_bh(&ar->data_lock); + if (pn_invalid) + goto err; + } /* Strip off all headers before the MAC header before delivery to * mac80211 @@ -2253,6 +2260,9 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, rx_status->flag &= ~RX_FLAG_IV_STRIPPED & ~RX_FLAG_MMIC_STRIPPED; + if (mpdu_ranges->mpdu_range_status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + ieee80211_rx_ni(ar->hw, skb); /* We have delivered the skb to the upper layers (mac80211) so we diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 1f391c739e40..2ef717f18795 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -978,9 +978,9 @@ static int ath10k_htt_send_rx_ring_cfg_hl(struct ath10k_htt *htt) return 0; } -int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, - u8 max_subfrms_ampdu, - u8 max_subfrms_amsdu) +static int ath10k_htt_h2t_aggr_cfg_msg_32(struct ath10k_htt *htt, + u8 max_subfrms_ampdu, + u8 max_subfrms_amsdu) { struct ath10k *ar = htt->ar; struct htt_aggr_conf *aggr_conf; @@ -1747,7 +1747,7 @@ static const struct ath10k_htt_tx_ops htt_tx_ops_32 = { .htt_tx = ath10k_htt_tx_32, .htt_alloc_txbuff = ath10k_htt_tx_alloc_cont_txbuf_32, .htt_free_txbuff = ath10k_htt_tx_free_cont_txbuf_32, - .htt_h2t_aggr_cfg_msg = ath10k_htt_h2t_aggr_cfg_msg, + .htt_h2t_aggr_cfg_msg = ath10k_htt_h2t_aggr_cfg_msg_32, }; static const struct ath10k_htt_tx_ops htt_tx_ops_64 = { @@ -1765,6 +1765,7 @@ static const struct ath10k_htt_tx_ops htt_tx_ops_hl = { .htt_send_rx_ring_cfg = ath10k_htt_send_rx_ring_cfg_hl, .htt_send_frag_desc_bank_cfg = ath10k_htt_send_frag_desc_bank_cfg_32, .htt_tx = ath10k_htt_tx_hl, + .htt_h2t_aggr_cfg_msg = ath10k_htt_h2t_aggr_cfg_msg_32, }; void ath10k_htt_set_tx_ops(struct ath10k_htt *htt) diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index ad082b7d7643..c415e971735b 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -158,7 +158,7 @@ const struct ath10k_hw_values qca6174_values = { }; const struct ath10k_hw_values qca99x0_values = { - .rtc_state_val_on = 5, + .rtc_state_val_on = 7, .ce_count = 12, .msi_assign_ce_max = 12, .num_target_ce_config_wlan = 10, @@ -1153,6 +1153,10 @@ const struct ath10k_hw_ops qca6174_ops = { .is_rssi_enable = ath10k_htt_tx_rssi_enable, }; +const struct ath10k_hw_ops qca6174_sdio_ops = { + .enable_pll_clk = ath10k_hw_qca6174_enable_pll_clock, +}; + const struct ath10k_hw_ops wcn3990_ops = { .tx_data_rssi_pad_bytes = ath10k_get_htt_tx_data_rssi_pad, .is_rssi_enable = ath10k_htt_tx_rssi_enable_wcn3990, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 248cfb4e53c3..2ae57c1de7b5 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -152,6 +152,8 @@ enum qca9377_chip_id_rev { #define ATH10K_FW_UTF_FILE "utf.bin" #define ATH10K_FW_UTF_API2_FILE "utf-2.bin" +#define ATH10K_FW_UTF_FILE_BASE "utf" + /* includes also the null byte */ #define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" #define ATH10K_BOARD_MAGIC "QCA-ATH10K-BOARD" @@ -612,6 +614,9 @@ struct ath10k_hw_params { * firmware bug */ bool uart_pin_workaround; + + /* tx stats support over pktlog */ + bool tx_stats_over_pktlog; }; struct htt_rx_desc; @@ -631,6 +636,7 @@ struct ath10k_hw_ops { extern const struct ath10k_hw_ops qca988x_ops; extern const struct ath10k_hw_ops qca99x0_ops; extern const struct ath10k_hw_ops qca6174_ops; +extern const struct ath10k_hw_ops qca6174_sdio_ops; extern const struct ath10k_hw_ops wcn3990_ops; extern const struct ath10k_hw_clk_params qca6174_clk[]; @@ -1101,6 +1107,7 @@ ath10k_is_rssi_enable(struct ath10k_hw_params *hw, #define MBOX_CPU_INT_STATUS_ENABLE_ADDRESS 0x00000819 #define MBOX_CPU_INT_STATUS_ENABLE_BIT_LSB 0 #define MBOX_CPU_INT_STATUS_ENABLE_BIT_MASK 0x000000ff +#define MBOX_CPU_STATUS_ENABLE_ASSERT_MASK 0x00000001 #define MBOX_ERROR_STATUS_ENABLE_ADDRESS 0x0000081a #define MBOX_ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB 1 #define MBOX_ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK 0x00000002 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b500fd427595..e43a566eef77 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -693,6 +693,26 @@ ath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw, *def = &conf->def; } +static void ath10k_wait_for_peer_delete_done(struct ath10k *ar, u32 vdev_id, + const u8 *addr) +{ + unsigned long time_left; + int ret; + + if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) { + ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr); + if (ret) { + ath10k_warn(ar, "failed wait for peer deleted"); + return; + } + + time_left = wait_for_completion_timeout(&ar->peer_delete_done, + 5 * HZ); + if (!time_left) + ath10k_warn(ar, "Timeout in receiving peer delete response\n"); + } +} + static int ath10k_peer_create(struct ath10k *ar, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -737,7 +757,7 @@ static int ath10k_peer_create(struct ath10k *ar, spin_unlock_bh(&ar->data_lock); ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n", addr, vdev_id); - ath10k_wmi_peer_delete(ar, vdev_id, addr); + ath10k_wait_for_peer_delete_done(ar, vdev_id, addr); return -ENOENT; } @@ -819,6 +839,18 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) if (ret) return ret; + if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) { + unsigned long time_left; + + time_left = wait_for_completion_timeout + (&ar->peer_delete_done, 5 * HZ); + + if (!time_left) { + ath10k_warn(ar, "Timeout in receiving peer delete response\n"); + return -ETIMEDOUT; + } + } + ar->num_peers--; return 0; @@ -1011,6 +1043,7 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) arg.channel.max_antenna_gain = channel->max_antenna_gain * 2; reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); ret = ath10k_wmi_vdev_start(ar, &arg); if (ret) { @@ -1060,6 +1093,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar) ar->monitor_vdev_id, ret); reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) @@ -1401,6 +1435,7 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif) lockdep_assert_held(&ar->conf_mutex); reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id); if (ret) { @@ -1437,6 +1472,7 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif, lockdep_assert_held(&ar->conf_mutex); reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); arg.vdev_id = arvif->vdev_id; arg.dtim_period = arvif->dtim_period; @@ -5419,8 +5455,11 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, err_peer_delete: if (arvif->vdev_type == WMI_VDEV_TYPE_AP || - arvif->vdev_type == WMI_VDEV_TYPE_IBSS) + arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr); + ath10k_wait_for_peer_delete_done(ar, arvif->vdev_id, + vif->addr); + } err_vdev_delete: ath10k_wmi_vdev_delete(ar, arvif->vdev_id); @@ -5455,6 +5494,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = (void *)vif->drv_priv; struct ath10k_peer *peer; + unsigned long time_left; int ret; int i; @@ -5485,6 +5525,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to submit AP/IBSS self-peer removal on vdev %i: %d\n", arvif->vdev_id, ret); + ath10k_wait_for_peer_delete_done(ar, arvif->vdev_id, + vif->addr); kfree(arvif->u.ap.noa_data); } @@ -5496,6 +5538,15 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n", arvif->vdev_id, ret); + if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) { + time_left = wait_for_completion_timeout(&ar->vdev_delete_done, + ATH10K_VDEV_DELETE_TIMEOUT_HZ); + if (time_left == 0) { + ath10k_warn(ar, "Timeout in receiving vdev delete response\n"); + goto out; + } + } + /* Some firmware revisions don't notify host about self-peer removal * until after associated vdev is deleted. */ @@ -5546,6 +5597,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, ath10k_mac_txq_unref(ar, vif->txq); +out: mutex_unlock(&ar->conf_mutex); } @@ -6358,6 +6410,41 @@ static void ath10k_mac_dec_num_stations(struct ath10k_vif *arvif, ar->num_stations--; } +static int ath10k_sta_set_txpwr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = (void *)vif->drv_priv; + int ret = 0; + s16 txpwr; + + if (sta->txpwr.type == NL80211_TX_POWER_AUTOMATIC) { + txpwr = 0; + } else { + txpwr = sta->txpwr.power; + if (!txpwr) + return -EINVAL; + } + + if (txpwr > ATH10K_TX_POWER_MAX_VAL || txpwr < ATH10K_TX_POWER_MIN_VAL) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_USE_FIXED_PWR, txpwr); + if (ret) { + ath10k_warn(ar, "failed to set tx power for station ret: %d\n", + ret); + goto out; + } + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + static int ath10k_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -7107,18 +7194,23 @@ exit: static bool ath10k_mac_bitrate_mask_has_single_rate(struct ath10k *ar, enum nl80211_band band, - const struct cfg80211_bitrate_mask *mask) + const struct cfg80211_bitrate_mask *mask, + int *vht_num_rates) { int num_rates = 0; - int i; + int i, tmp; num_rates += hweight32(mask->control[band].legacy); for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++) num_rates += hweight8(mask->control[band].ht_mcs[i]); - for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) - num_rates += hweight16(mask->control[band].vht_mcs[i]); + *vht_num_rates = 0; + for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) { + tmp = hweight16(mask->control[band].vht_mcs[i]); + num_rates += tmp; + *vht_num_rates += tmp; + } return num_rates == 1; } @@ -7176,7 +7268,7 @@ static int ath10k_mac_bitrate_mask_get_single_rate(struct ath10k *ar, enum nl80211_band band, const struct cfg80211_bitrate_mask *mask, - u8 *rate, u8 *nss) + u8 *rate, u8 *nss, bool vht_only) { int rate_idx; int i; @@ -7184,6 +7276,9 @@ ath10k_mac_bitrate_mask_get_single_rate(struct ath10k *ar, u8 preamble; u8 hw_rate; + if (vht_only) + goto next; + if (hweight32(mask->control[band].legacy) == 1) { rate_idx = ffs(mask->control[band].legacy) - 1; @@ -7217,6 +7312,7 @@ ath10k_mac_bitrate_mask_get_single_rate(struct ath10k *ar, } } +next: for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) { if (hweight16(mask->control[band].vht_mcs[i]) == 1) { *nss = i + 1; @@ -7278,7 +7374,8 @@ static int ath10k_mac_set_fixed_rate_params(struct ath10k_vif *arvif, static bool ath10k_mac_can_set_bitrate_mask(struct ath10k *ar, enum nl80211_band band, - const struct cfg80211_bitrate_mask *mask) + const struct cfg80211_bitrate_mask *mask, + bool allow_pfr) { int i; u16 vht_mcs; @@ -7297,7 +7394,8 @@ ath10k_mac_can_set_bitrate_mask(struct ath10k *ar, case BIT(10) - 1: break; default: - ath10k_warn(ar, "refusing bitrate mask with missing 0-7 VHT MCS rates\n"); + if (!allow_pfr) + ath10k_warn(ar, "refusing bitrate mask with missing 0-7 VHT MCS rates\n"); return false; } } @@ -7305,6 +7403,26 @@ ath10k_mac_can_set_bitrate_mask(struct ath10k *ar, return true; } +static bool ath10k_mac_set_vht_bitrate_mask_fixup(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta) +{ + int err; + u8 rate = arvif->vht_pfr; + + /* skip non vht and multiple rate peers */ + if (!sta->vht_cap.vht_supported || arvif->vht_num_rates != 1) + return false; + + err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_PARAM_FIXED_RATE, rate); + if (err) + ath10k_warn(ar, "failed to eanble STA %pM peer fixed rate: %d\n", + sta->addr, err); + + return true; +} + static void ath10k_mac_set_bitrate_mask_iter(void *data, struct ieee80211_sta *sta) { @@ -7315,6 +7433,9 @@ static void ath10k_mac_set_bitrate_mask_iter(void *data, if (arsta->arvif != arvif) return; + if (ath10k_mac_set_vht_bitrate_mask_fixup(ar, arvif, sta)) + return; + spin_lock_bh(&ar->data_lock); arsta->changed |= IEEE80211_RC_SUPP_RATES_CHANGED; spin_unlock_bh(&ar->data_lock); @@ -7322,6 +7443,26 @@ static void ath10k_mac_set_bitrate_mask_iter(void *data, ieee80211_queue_work(ar->hw, &arsta->update_wk); } +static void ath10k_mac_clr_bitrate_mask_iter(void *data, + struct ieee80211_sta *sta) +{ + struct ath10k_vif *arvif = data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arvif->ar; + int err; + + /* clear vht peers only */ + if (arsta->arvif != arvif || !sta->vht_cap.vht_supported) + return; + + err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_PARAM_FIXED_RATE, + WMI_FIXED_RATE_NONE); + if (err) + ath10k_warn(ar, "failed to clear STA %pM peer fixed rate: %d\n", + sta->addr, err); +} + static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask) @@ -7338,6 +7479,9 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, u8 ldpc; int single_nss; int ret; + int vht_num_rates, allow_pfr; + u8 vht_pfr; + bool update_bitrate_mask = true; if (ath10k_mac_vif_chan(vif, &def)) return -EPERM; @@ -7351,9 +7495,21 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, if (sgi == NL80211_TXRATE_FORCE_LGI) return -EINVAL; - if (ath10k_mac_bitrate_mask_has_single_rate(ar, band, mask)) { + allow_pfr = test_bit(ATH10K_FW_FEATURE_PEER_FIXED_RATE, + ar->normal_mode_fw.fw_file.fw_features); + if (allow_pfr) { + mutex_lock(&ar->conf_mutex); + ieee80211_iterate_stations_atomic(ar->hw, + ath10k_mac_clr_bitrate_mask_iter, + arvif); + mutex_unlock(&ar->conf_mutex); + } + + if (ath10k_mac_bitrate_mask_has_single_rate(ar, band, mask, + &vht_num_rates)) { ret = ath10k_mac_bitrate_mask_get_single_rate(ar, band, mask, - &rate, &nss); + &rate, &nss, + false); if (ret) { ath10k_warn(ar, "failed to get single rate for vdev %i: %d\n", arvif->vdev_id, ret); @@ -7369,12 +7525,30 @@ static int ath10k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw, max(ath10k_mac_max_ht_nss(ht_mcs_mask), ath10k_mac_max_vht_nss(vht_mcs_mask))); - if (!ath10k_mac_can_set_bitrate_mask(ar, band, mask)) - return -EINVAL; + if (!ath10k_mac_can_set_bitrate_mask(ar, band, mask, + allow_pfr)) { + u8 vht_nss; + + if (!allow_pfr || vht_num_rates != 1) + return -EINVAL; + + /* Reach here, firmware supports peer fixed rate and has + * single vht rate, and don't update vif birate_mask, as + * the rate only for specific peer. + */ + ath10k_mac_bitrate_mask_get_single_rate(ar, band, mask, + &vht_pfr, + &vht_nss, + true); + update_bitrate_mask = false; + } mutex_lock(&ar->conf_mutex); - arvif->bitrate_mask = *mask; + if (update_bitrate_mask) + arvif->bitrate_mask = *mask; + arvif->vht_num_rates = vht_num_rates; + arvif->vht_pfr = vht_pfr; ieee80211_iterate_stations_atomic(ar->hw, ath10k_mac_set_bitrate_mask_iter, arvif); @@ -7877,7 +8051,8 @@ ath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, arvif->vdev_id, ret); } - if (ath10k_peer_stats_enabled(ar)) { + if (ath10k_peer_stats_enabled(ar) && + ar->hw_params.tx_stats_over_pktlog) { ar->pktlog_filter |= ATH10K_PKTLOG_PEER_STATS; ret = ath10k_wmi_pdev_pktlog_enable(ar, ar->pktlog_filter); @@ -8015,6 +8190,7 @@ static const struct ieee80211_ops ath10k_ops = { .set_key = ath10k_set_key, .set_default_unicast_key = ath10k_set_default_unicast_key, .sta_state = ath10k_sta_state, + .sta_set_txpwr = ath10k_sta_set_txpwr, .conf_tx = ath10k_conf_tx, .remain_on_channel = ath10k_remain_on_channel, .cancel_remain_on_channel = ath10k_cancel_remain_on_channel, @@ -8703,6 +8879,9 @@ int ath10k_mac_register(struct ath10k *ar) wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER); + if (test_bit(WMI_SERVICE_TX_PWR_PER_PEER, ar->wmi.svc_map)) + wiphy_ext_feature_set(ar->hw->wiphy, + NL80211_EXT_FEATURE_STA_TX_PWR); /* * on LL hardware queues are managed entirely by the FW * so we only advertise to mac we can do the queues thing diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 2bd6cbad19e4..a0b4d265c6eb 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -909,7 +909,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, /* Host buffer address in CE space */ u32 ce_data; dma_addr_t ce_data_base = 0; - void *data_buf = NULL; + void *data_buf; int i; mutex_lock(&ar_pci->ce_diag_mutex); @@ -923,10 +923,8 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, */ alloc_nbytes = min_t(unsigned int, nbytes, DIAG_TRANSFER_LIMIT); - data_buf = (unsigned char *)dma_alloc_coherent(ar->dev, alloc_nbytes, - &ce_data_base, - GFP_ATOMIC); - + data_buf = dma_alloc_coherent(ar->dev, alloc_nbytes, &ce_data_base, + GFP_ATOMIC); if (!data_buf) { ret = -ENOMEM; goto done; @@ -1054,7 +1052,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, u32 *buf; unsigned int completed_nbytes, alloc_nbytes, remaining_bytes; struct ath10k_ce_pipe *ce_diag; - void *data_buf = NULL; + void *data_buf; dma_addr_t ce_data_base = 0; int i; @@ -1069,10 +1067,8 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, */ alloc_nbytes = min_t(unsigned int, nbytes, DIAG_TRANSFER_LIMIT); - data_buf = (unsigned char *)dma_alloc_coherent(ar->dev, - alloc_nbytes, - &ce_data_base, - GFP_ATOMIC); + data_buf = dma_alloc_coherent(ar->dev, alloc_nbytes, &ce_data_base, + GFP_ATOMIC); if (!data_buf) { ret = -ENOMEM; goto done; @@ -2059,6 +2055,11 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n"); + ath10k_pci_irq_disable(ar); + ath10k_pci_irq_sync(ar); + napi_synchronize(&ar->napi); + napi_disable(&ar->napi); + /* Most likely the device has HTT Rx ring configured. The only way to * prevent the device from accessing (and possible corrupting) host * memory is to reset the chip now. @@ -2072,10 +2073,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) */ ath10k_pci_safe_chip_reset(ar); - ath10k_pci_irq_disable(ar); - ath10k_pci_irq_sync(ar); - napi_synchronize(&ar->napi); - napi_disable(&ar->napi); ath10k_pci_flush(ar); spin_lock_irqsave(&ar_pci->ps_lock, flags); diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 2e678780df5d..3b63b6257c43 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -620,6 +620,51 @@ out: return ret; } +int ath10k_qmi_set_fw_log_mode(struct ath10k *ar, u8 fw_log_mode) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + struct wlfw_ini_resp_msg_v01 resp = {}; + struct ath10k_qmi *qmi = ar_snoc->qmi; + struct wlfw_ini_req_msg_v01 req = {}; + struct qmi_txn txn; + int ret; + + req.enablefwlog_valid = 1; + req.enablefwlog = fw_log_mode; + + ret = qmi_txn_init(&qmi->qmi_hdl, &txn, wlfw_ini_resp_msg_v01_ei, + &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&qmi->qmi_hdl, NULL, &txn, + QMI_WLFW_INI_REQ_V01, + WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN, + wlfw_ini_req_msg_v01_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + ath10k_err(ar, "fail to send fw log reqest: %d\n", ret); + goto out; + } + + ret = qmi_txn_wait(&txn, ATH10K_QMI_TIMEOUT * HZ); + if (ret < 0) + goto out; + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath10k_err(ar, "fw log request rejectedr: %d\n", + resp.resp.error); + ret = -EINVAL; + goto out; + } + ath10k_dbg(ar, ATH10K_DBG_QMI, "qmi fw log request completed, mode: %d\n", + fw_log_mode); + return 0; + +out: + return ret; +} + static int ath10k_qmi_ind_register_send_sync_msg(struct ath10k_qmi *qmi) { @@ -1003,6 +1048,7 @@ int ath10k_qmi_deinit(struct ath10k *ar) qmi_handle_release(&qmi->qmi_hdl); cancel_work_sync(&qmi->event_work); destroy_workqueue(qmi->event_wq); + kfree(qmi); ar_snoc->qmi = NULL; return 0; diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h index e4aa20445666..40aafb875ed0 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.h +++ b/drivers/net/wireless/ath/ath10k/qmi.h @@ -114,5 +114,6 @@ int ath10k_qmi_wlan_disable(struct ath10k *ar); int ath10k_qmi_register_service_notifier(struct notifier_block *nb); int ath10k_qmi_init(struct ath10k *ar, u32 msa_size); int ath10k_qmi_deinit(struct ath10k *ar); +int ath10k_qmi_set_fw_log_mode(struct ath10k *ar, u8 fw_log_mode); #endif /* ATH10K_QMI_H */ diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 9bbd5b54b8ca..8ed4fbd8d6c3 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -607,6 +607,10 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, full_len, last_in_bundle, last_in_bundle); + if (ret) { + ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret); + goto err; + } } ar_sdio->n_rx_pkts = i; @@ -855,6 +859,10 @@ static int ath10k_sdio_mbox_proc_cpu_intr(struct ath10k *ar) out: mutex_unlock(&irq_data->mtx); + if (cpu_int_status & MBOX_CPU_STATUS_ENABLE_ASSERT_MASK) { + ath10k_err(ar, "firmware crashed!\n"); + queue_work(ar->workqueue, &ar->restart_work); + } return ret; } @@ -1500,8 +1508,10 @@ static int ath10k_sdio_hif_enable_intrs(struct ath10k *ar) regs->int_status_en |= FIELD_PREP(MBOX_INT_STATUS_ENABLE_MBOX_DATA_MASK, 1); - /* Set up the CPU Interrupt status Register */ - regs->cpu_int_status_en = 0; + /* Set up the CPU Interrupt Status Register, enable CPU sourced interrupt #0 + * #0 is used for report assertion from target + */ + regs->cpu_int_status_en = FIELD_PREP(MBOX_CPU_STATUS_ENABLE_ASSERT_MASK, 1); /* Set up the Error Interrupt status Register */ regs->err_int_status_en = @@ -2087,6 +2097,9 @@ static void ath10k_sdio_remove(struct sdio_func *func) ath10k_core_unregister(ar); ath10k_core_destroy(ar); + + flush_workqueue(ar_sdio->workqueue); + destroy_workqueue(ar_sdio->workqueue); } static const struct sdio_device_id ath10k_sdio_devices[] = { diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index ca1186ec4129..b491361e6ed4 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1050,6 +1050,19 @@ err_wlan_enable: return ret; } +static int ath10k_snoc_hif_set_target_log_mode(struct ath10k *ar, + u8 fw_log_mode) +{ + u8 fw_dbg_mode; + + if (fw_log_mode) + fw_dbg_mode = ATH10K_ENABLE_FW_LOG_CE; + else + fw_dbg_mode = ATH10K_ENABLE_FW_LOG_DIAG; + + return ath10k_qmi_set_fw_log_mode(ar, fw_dbg_mode); +} + #ifdef CONFIG_PM static int ath10k_snoc_hif_suspend(struct ath10k *ar) { @@ -1103,6 +1116,8 @@ static const struct ath10k_hif_ops ath10k_snoc_hif_ops = { .send_complete_check = ath10k_snoc_hif_send_complete_check, .get_free_queue_number = ath10k_snoc_hif_get_free_queue_number, .get_target_info = ath10k_snoc_hif_get_target_info, + .set_target_log_mode = ath10k_snoc_hif_set_target_log_mode, + #ifdef CONFIG_PM .suspend = ath10k_snoc_hif_suspend, .resume = ath10k_snoc_hif_resume, diff --git a/drivers/net/wireless/ath/ath10k/swap.c b/drivers/net/wireless/ath/ath10k/swap.c index 4dddeee684b4..7198a386f2fb 100644 --- a/drivers/net/wireless/ath/ath10k/swap.c +++ b/drivers/net/wireless/ath/ath10k/swap.c @@ -106,10 +106,8 @@ ath10k_swap_code_seg_alloc(struct ath10k *ar, size_t swap_bin_len) virt_addr = dma_alloc_coherent(ar->dev, swap_bin_len, &paddr, GFP_KERNEL); - if (!virt_addr) { - ath10k_err(ar, "failed to allocate dma coherent memory\n"); + if (!virt_addr) return NULL; - } seg_info->seg_hw_info.bus_addr[0] = __cpu_to_le32(paddr); seg_info->seg_hw_info.size = __cpu_to_le32(swap_bin_len); diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index a29cfb9c72c2..1bffe3fbea3f 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -174,8 +174,23 @@ static int ath10k_tm_fetch_firmware(struct ath10k *ar) { struct ath10k_fw_components *utf_mode_fw; int ret; + char fw_name[100]; + int fw_api2 = 2; + + switch (ar->hif.bus) { + case ATH10K_BUS_SDIO: + case ATH10K_BUS_USB: + scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin", + ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus), + fw_api2); + break; + default: + scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin", + ATH10K_FW_UTF_FILE_BASE, fw_api2); + break; + } - ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_UTF_API2_FILE, + ret = ath10k_core_fetch_firmware_api_n(ar, fw_name, &ar->testmode.utf_mode_fw.fw_file); if (ret == 0) { ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2"); diff --git a/drivers/net/wireless/ath/ath10k/trace.c b/drivers/net/wireless/ath/ath10k/trace.c index 3ecdff17f64e..c7d4c97e6079 100644 --- a/drivers/net/wireless/ath/ath10k/trace.c +++ b/drivers/net/wireless/ath/ath10k/trace.c @@ -7,3 +7,4 @@ #define CREATE_TRACE_POINTS #include "trace.h" +EXPORT_SYMBOL(__tracepoint_ath10k_log_dbg); diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index ba977bbe6291..ab916459d237 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -29,7 +29,11 @@ static inline u32 ath10k_frm_hdr_len(const void *buf, size_t len) #if !defined(CONFIG_ATH10K_TRACING) #undef TRACE_EVENT #define TRACE_EVENT(name, proto, ...) \ -static inline void trace_ ## name(proto) {} +static inline void trace_ ## name(proto) {} \ +static inline bool trace_##name##_enabled(void) \ +{ \ + return false; \ +} #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(...) #undef DEFINE_EVENT diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index 2fb0fb40e86d..e1420f67f776 100644 --- a/drivers/net/wireless/ath/ath10k/usb.c +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -1016,7 +1016,7 @@ static int ath10k_usb_probe(struct usb_interface *interface, } /* TODO: remove this once USB support is fully implemented */ - ath10k_warn(ar, "WARNING: ath10k USB support is incomplete, don't expect anything to work!\n"); + ath10k_warn(ar, "Warning: ath10k USB support is incomplete, don't expect anything to work!\n"); return 0; diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 34e187486c63..2985bb17decd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -2,7 +2,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) 2018-2019, The Linux Foundation. All rights reserved. */ #include "core.h" #include "debug.h" @@ -212,6 +212,13 @@ static int ath10k_wmi_tlv_event_bcn_tx_status(struct ath10k *ar, return 0; } +static void ath10k_wmi_tlv_event_vdev_delete_resp(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_DELETE_RESP_EVENTID\n"); + complete(&ar->vdev_delete_done); +} + static int ath10k_wmi_tlv_event_diag_data(struct ath10k *ar, struct sk_buff *skb) { @@ -458,6 +465,24 @@ static void ath10k_wmi_event_tdls_peer(struct ath10k *ar, struct sk_buff *skb) kfree(tb); } +static int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar, + struct sk_buff *skb) +{ + struct wmi_peer_delete_resp_ev_arg *arg; + struct wmi_tlv *tlv_hdr; + + tlv_hdr = (struct wmi_tlv *)skb->data; + arg = (struct wmi_peer_delete_resp_ev_arg *)tlv_hdr->value; + + ath10k_dbg(ar, ATH10K_DBG_WMI, "vdev id %d", arg->vdev_id); + ath10k_dbg(ar, ATH10K_DBG_WMI, "peer mac addr %pM", &arg->peer_addr); + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer delete response\n"); + + complete(&ar->peer_delete_done); + + return 0; +} + /***********/ /* TLV ops */ /***********/ @@ -514,6 +539,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_TLV_VDEV_STOPPED_EVENTID: ath10k_wmi_event_vdev_stopped(ar, skb); break; + case WMI_TLV_VDEV_DELETE_RESP_EVENTID: + ath10k_wmi_tlv_event_vdev_delete_resp(ar, skb); + break; case WMI_TLV_PEER_STA_KICKOUT_EVENTID: ath10k_wmi_event_peer_sta_kickout(ar, skb); break; @@ -607,6 +635,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_TLV_TDLS_PEER_EVENTID: ath10k_wmi_event_tdls_peer(ar, skb); break; + case WMI_TLV_PEER_DELETE_RESP_EVENTID: + ath10k_wmi_tlv_event_peer_delete_resp(ar, skb); + break; case WMI_TLV_MGMT_TX_COMPLETION_EVENTID: ath10k_wmi_event_mgmt_tx_compl(ar, skb); break; diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index bc3198b08844..d691f06e58f2 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -2,7 +2,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) 2018-2019, The Linux Foundation. All rights reserved. */ #ifndef _WMI_TLV_H #define _WMI_TLV_H @@ -301,11 +301,15 @@ enum wmi_tlv_event_id { WMI_TLV_VDEV_STOPPED_EVENTID, WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID, WMI_TLV_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID, + WMI_TLV_VDEV_TSF_REPORT_EVENTID, + WMI_TLV_VDEV_DELETE_RESP_EVENTID, WMI_TLV_PEER_STA_KICKOUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PEER), WMI_TLV_PEER_INFO_EVENTID, WMI_TLV_PEER_TX_FAIL_CNT_THR_EVENTID, WMI_TLV_PEER_ESTIMATED_LINKSPEED_EVENTID, WMI_TLV_PEER_STATE_EVENTID, + WMI_TLV_PEER_ASSOC_CONF_EVENTID, + WMI_TLV_PEER_DELETE_RESP_EVENTID, WMI_TLV_MGMT_RX_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MGMT), WMI_TLV_HOST_SWBA_EVENTID, WMI_TLV_TBTTOFFSET_UPDATE_EVENTID, @@ -1569,6 +1573,8 @@ wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len) WMI_SERVICE_MGMT_TX_WMI, len); SVCMAP(WMI_TLV_SERVICE_MESH_11S, WMI_SERVICE_MESH_11S, len); + SVCMAP(WMI_TLV_SERVICE_SYNC_DELETE_CMDS, + WMI_SERVICE_SYNC_DELETE_CMDS, len); } static inline void diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 12f57f9adbba..838768c98adc 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2,7 +2,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) 2018-2019, The Linux Foundation. All rights reserved. */ #ifndef _WMI_H_ @@ -200,6 +200,8 @@ enum wmi_service { WMI_SERVICE_RTT_RESPONDER_ROLE, WMI_SERVICE_PER_PACKET_SW_ENCRYPT, WMI_SERVICE_REPORT_AIRTIME, + WMI_SERVICE_SYNC_DELETE_CMDS, + WMI_SERVICE_TX_PWR_PER_PEER, /* Remember to add the new value to wmi_service_name()! */ @@ -367,6 +369,7 @@ enum wmi_10_4_service { WMI_10_4_SERVICE_RTT_RESPONDER_ROLE, WMI_10_4_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT, WMI_10_4_SERVICE_REPORT_AIRTIME, + WMI_10_4_SERVICE_TX_PWR_PER_PEER, }; static inline char *wmi_service_name(enum wmi_service service_id) @@ -491,6 +494,8 @@ static inline char *wmi_service_name(enum wmi_service service_id) SVCSTR(WMI_SERVICE_RTT_RESPONDER_ROLE); SVCSTR(WMI_SERVICE_PER_PACKET_SW_ENCRYPT); SVCSTR(WMI_SERVICE_REPORT_AIRTIME); + SVCSTR(WMI_SERVICE_SYNC_DELETE_CMDS); + SVCSTR(WMI_SERVICE_TX_PWR_PER_PEER); case WMI_SERVICE_MAX: return NULL; @@ -818,6 +823,8 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out, WMI_SERVICE_PER_PACKET_SW_ENCRYPT, len); SVCMAP(WMI_10_4_SERVICE_REPORT_AIRTIME, WMI_SERVICE_REPORT_AIRTIME, len); + SVCMAP(WMI_10_4_SERVICE_TX_PWR_PER_PEER, + WMI_SERVICE_TX_PWR_PER_PEER, len); } #undef SVCMAP @@ -6260,6 +6267,8 @@ enum wmi_peer_param { WMI_PEER_CHAN_WIDTH = 0x4, WMI_PEER_NSS = 0x5, WMI_PEER_USE_4ADDR = 0x6, + WMI_PEER_USE_FIXED_PWR = 0x8, + WMI_PEER_PARAM_FIXED_RATE = 0x9, WMI_PEER_DEBUG = 0xa, WMI_PEER_PHYMODE = 0xd, WMI_PEER_DUMMY_VAR = 0xff, /* dummy parameter for STA PS workaround */ @@ -6757,6 +6766,11 @@ struct wmi_tlv_mgmt_tx_bundle_compl_ev_arg { const __le32 *ack_rssi; }; +struct wmi_peer_delete_resp_ev_arg { + __le32 vdev_id; + struct wmi_mac_addr peer_addr; +}; + struct wmi_mgmt_rx_ev_arg { __le32 channel; __le32 snr; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 4e97f7f3b2a3..06e660858766 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -815,6 +815,7 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_hdr *hdr; bool discard_current = sc->rx.discard_next; + bool is_phyerr; /* * Discard corrupt descriptors which are marked in @@ -827,8 +828,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, /* * Discard zero-length packets and packets smaller than an ACK + * which are not PHY_ERROR (short radar pulses have a length of 3) */ - if (rx_stats->rs_datalen < 10) { + is_phyerr = rx_stats->rs_status & ATH9K_RXERR_PHY; + if (!rx_stats->rs_datalen || + (rx_stats->rs_datalen < 10 && !is_phyerr)) { RX_STAT_INC(sc, rx_len_err); goto corrupt; } diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 7f1bdea742b8..40a8054f8aa6 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1387,13 +1387,8 @@ static int carl9170_op_conf_tx(struct ieee80211_hw *hw, int ret; mutex_lock(&ar->mutex); - if (queue < ar->hw->queues) { - memcpy(&ar->edcf[ar9170_qmap[queue]], param, sizeof(*param)); - ret = carl9170_set_qos(ar); - } else { - ret = -EINVAL; - } - + memcpy(&ar->edcf[ar9170_qmap[queue]], param, sizeof(*param)); + ret = carl9170_set_qos(ar); mutex_unlock(&ar->mutex); return ret; } diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index e7c3f3b8457d..99f1897a775d 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -128,6 +128,8 @@ static const struct usb_device_id carl9170_usb_ids[] = { }; MODULE_DEVICE_TABLE(usb, carl9170_usb_ids); +static struct usb_driver carl9170_driver; + static void carl9170_usb_submit_data_urb(struct ar9170 *ar) { struct urb *urb; @@ -966,32 +968,28 @@ err_out: static void carl9170_usb_firmware_failed(struct ar9170 *ar) { - struct device *parent = ar->udev->dev.parent; - struct usb_device *udev; - - /* - * Store a copy of the usb_device pointer locally. - * This is because device_release_driver initiates - * carl9170_usb_disconnect, which in turn frees our - * driver context (ar). + /* Store a copies of the usb_interface and usb_device pointer locally. + * This is because release_driver initiates carl9170_usb_disconnect, + * which in turn frees our driver context (ar). */ - udev = ar->udev; + struct usb_interface *intf = ar->intf; + struct usb_device *udev = ar->udev; complete(&ar->fw_load_wait); + /* at this point 'ar' could be already freed. Don't use it anymore */ + ar = NULL; /* unbind anything failed */ - if (parent) - device_lock(parent); - - device_release_driver(&udev->dev); - if (parent) - device_unlock(parent); + usb_lock_device(udev); + usb_driver_release_interface(&carl9170_driver, intf); + usb_unlock_device(udev); - usb_put_dev(udev); + usb_put_intf(intf); } static void carl9170_usb_firmware_finish(struct ar9170 *ar) { + struct usb_interface *intf = ar->intf; int err; err = carl9170_parse_firmware(ar); @@ -1009,7 +1007,7 @@ static void carl9170_usb_firmware_finish(struct ar9170 *ar) goto err_unrx; complete(&ar->fw_load_wait); - usb_put_dev(ar->udev); + usb_put_intf(intf); return; err_unrx: @@ -1052,7 +1050,6 @@ static int carl9170_usb_probe(struct usb_interface *intf, return PTR_ERR(ar); udev = interface_to_usbdev(intf); - usb_get_dev(udev); ar->udev = udev; ar->intf = intf; ar->features = id->driver_info; @@ -1094,15 +1091,14 @@ static int carl9170_usb_probe(struct usb_interface *intf, atomic_set(&ar->rx_anch_urbs, 0); atomic_set(&ar->rx_pool_urbs, 0); - usb_get_dev(ar->udev); + usb_get_intf(intf); carl9170_set_state(ar, CARL9170_STOPPED); err = request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME, &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2); if (err) { - usb_put_dev(udev); - usb_put_dev(udev); + usb_put_intf(intf); carl9170_free(ar); } return err; @@ -1131,7 +1127,6 @@ static void carl9170_usb_disconnect(struct usb_interface *intf) carl9170_release_firmware(ar); carl9170_free(ar); - usb_put_dev(udev); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index ca77d7a48f5d..d436cc51dfd1 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -314,7 +314,8 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid, memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd), - WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20); + WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) return rc; @@ -380,8 +381,8 @@ static int wil_cfg80211_get_station(struct wiphy *wiphy, wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid, vif->mid); - if (cid < 0) - return cid; + if (!wil_cid_valid(wil, cid)) + return -ENOENT; rc = wil_cid_fill_sinfo(vif, cid, sinfo); @@ -417,7 +418,7 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy, int rc; int cid = wil_find_cid_by_idx(wil, vif->mid, idx); - if (cid < 0) + if (!wil_cid_valid(wil, cid)) return -ENOENT; ether_addr_copy(mac, wil->sta[cid].addr); @@ -643,6 +644,16 @@ out: return rc; } +static bool wil_is_safe_switch(enum nl80211_iftype from, + enum nl80211_iftype to) +{ + if (from == NL80211_IFTYPE_STATION && + to == NL80211_IFTYPE_P2P_CLIENT) + return true; + + return false; +} + static int wil_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype type, @@ -668,7 +679,8 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy, * because it can cause significant disruption */ if (!wil_has_other_active_ifaces(wil, ndev, true, false) && - netif_running(ndev) && !wil_is_recovery_blocked(wil)) { + netif_running(ndev) && !wil_is_recovery_blocked(wil) && + !wil_is_safe_switch(wdev->iftype, type)) { wil_dbg_misc(wil, "interface is up. resetting...\n"); mutex_lock(&wil->mutex); __wil_down(wil); diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 7a2c3fdeca8c..74834131cf7c 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -422,25 +422,18 @@ static int wil_debugfs_iomem_x32_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, wil_debugfs_iomem_x32_set, "0x%08llx\n"); -static struct dentry *wil_debugfs_create_iomem_x32(const char *name, - umode_t mode, - struct dentry *parent, - void *value, - struct wil6210_priv *wil) +static void wil_debugfs_create_iomem_x32(const char *name, umode_t mode, + struct dentry *parent, void *value, + struct wil6210_priv *wil) { - struct dentry *file; struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[ wil->dbg_data.iomem_data_count]; data->wil = wil; data->offset = value; - file = debugfs_create_file_unsafe(name, mode, parent, data, - &fops_iomem_x32); - if (!IS_ERR_OR_NULL(file)) - wil->dbg_data.iomem_data_count++; - - return file; + debugfs_create_file_unsafe(name, mode, parent, data, &fops_iomem_x32); + wil->dbg_data.iomem_data_count++; } static int wil_debugfs_ulong_set(void *data, u64 val) @@ -458,14 +451,6 @@ static int wil_debugfs_ulong_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get, wil_debugfs_ulong_set, "0x%llx\n"); -static struct dentry *wil_debugfs_create_ulong(const char *name, umode_t mode, - struct dentry *parent, - ulong *value) -{ - return debugfs_create_file_unsafe(name, mode, parent, value, - &wil_fops_ulong); -} - /** * wil6210_debugfs_init_offset - create set of debugfs files * @wil - driver's context, used for printing @@ -482,37 +467,30 @@ static void wil6210_debugfs_init_offset(struct wil6210_priv *wil, int i; for (i = 0; tbl[i].name; i++) { - struct dentry *f; - switch (tbl[i].type) { case doff_u32: - f = debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg, - base + tbl[i].off); + debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg, + base + tbl[i].off); break; case doff_x32: - f = debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg, - base + tbl[i].off); + debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg, + base + tbl[i].off); break; case doff_ulong: - f = wil_debugfs_create_ulong(tbl[i].name, tbl[i].mode, - dbg, base + tbl[i].off); + debugfs_create_file_unsafe(tbl[i].name, tbl[i].mode, + dbg, base + tbl[i].off, + &wil_fops_ulong); break; case doff_io32: - f = wil_debugfs_create_iomem_x32(tbl[i].name, - tbl[i].mode, dbg, - base + tbl[i].off, - wil); + wil_debugfs_create_iomem_x32(tbl[i].name, tbl[i].mode, + dbg, base + tbl[i].off, + wil); break; case doff_u8: - f = debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg, - base + tbl[i].off); + debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg, + base + tbl[i].off); break; - default: - f = ERR_PTR(-EINVAL); } - if (IS_ERR_OR_NULL(f)) - wil_err(wil, "Create file \"%s\": err %ld\n", - tbl[i].name, PTR_ERR(f)); } } @@ -527,19 +505,14 @@ static const struct dbg_off isr_off[] = { {}, }; -static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, - const char *name, - struct dentry *parent, u32 off) +static void wil6210_debugfs_create_ISR(struct wil6210_priv *wil, + const char *name, struct dentry *parent, + u32 off) { struct dentry *d = debugfs_create_dir(name, parent); - if (IS_ERR_OR_NULL(d)) - return -ENODEV; - wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr + off, isr_off); - - return 0; } static const struct dbg_off pseudo_isr_off[] = { @@ -549,18 +522,13 @@ static const struct dbg_off pseudo_isr_off[] = { {}, }; -static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, - struct dentry *parent) +static void wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, + struct dentry *parent) { struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); - if (IS_ERR_OR_NULL(d)) - return -ENODEV; - wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, pseudo_isr_off); - - return 0; } static const struct dbg_off lgc_itr_cnt_off[] = { @@ -608,13 +576,9 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, struct dentry *d, *dtx, *drx; d = debugfs_create_dir("ITR_CNT", parent); - if (IS_ERR_OR_NULL(d)) - return -ENODEV; dtx = debugfs_create_dir("TX", d); drx = debugfs_create_dir("RX", d); - if (IS_ERR_OR_NULL(dtx) || IS_ERR_OR_NULL(drx)) - return -ENODEV; wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, lgc_itr_cnt_off); @@ -777,6 +741,44 @@ static const struct file_operations fops_rxon = { .open = simple_open, }; +static ssize_t wil_write_file_rbufcap(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + int val; + int rc; + + rc = kstrtoint_from_user(buf, count, 0, &val); + if (rc) { + wil_err(wil, "Invalid argument\n"); + return rc; + } + /* input value: negative to disable, 0 to use system default, + * 1..ring size to set descriptor threshold + */ + wil_info(wil, "%s RBUFCAP, descriptors threshold - %d\n", + val < 0 ? "Disabling" : "Enabling", val); + + if (!wil->ring_rx.va || val > wil->ring_rx.size) { + wil_err(wil, "Invalid descriptors threshold, %d\n", val); + return -EINVAL; + } + + rc = wmi_rbufcap_cfg(wil, val < 0 ? 0 : 1, val < 0 ? 0 : val); + if (rc) { + wil_err(wil, "RBUFCAP config failed: %d\n", rc); + return rc; + } + + return count; +} + +static const struct file_operations fops_rbufcap = { + .write = wil_write_file_rbufcap, + .open = simple_open, +}; + /* block ack control, write: * - "add <ringid> <agg_size> <timeout>" to trigger ADDBA * - "del_tx <ringid> <reason>" to trigger DELBA for Tx side @@ -938,9 +940,8 @@ static ssize_t wil_read_pmccfg(struct file *file, char __user *user_buf, " - \"alloc <num descriptors> <descriptor_size>\" to allocate pmc\n" " - \"free\" to free memory allocated for pmc\n"; - sprintf(text, "Last command status: %d\n\n%s", - wil_pmc_last_cmd_status(wil), - help); + snprintf(text, sizeof(text), "Last command status: %d\n\n%s", + wil_pmc_last_cmd_status(wil), help); return simple_read_from_buffer(user_buf, count, ppos, text, strlen(text) + 1); @@ -1297,7 +1298,7 @@ static int bf_show(struct seq_file *s, void *data) rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_NOTIFY_REQ_DONE_EVENTID, &reply, - sizeof(reply), 20); + sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); /* if reply is all-0, ignore this CID */ if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt))) continue; @@ -1335,7 +1336,7 @@ static void print_temp(struct seq_file *s, const char *prefix, s32 t) { switch (t) { case 0: - case ~(u32)0: + case WMI_INVALID_TEMPERATURE: seq_printf(s, "%s N/A\n", prefix); break; default: @@ -1348,17 +1349,41 @@ static void print_temp(struct seq_file *s, const char *prefix, s32 t) static int temp_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; - s32 t_m, t_r; - int rc = wmi_get_temperature(wil, &t_m, &t_r); + int rc, i; - if (rc) { - seq_puts(s, "Failed\n"); - return 0; - } + if (test_bit(WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF, + wil->fw_capabilities)) { + struct wmi_temp_sense_all_done_event sense_all_evt; - print_temp(s, "T_mac =", t_m); - print_temp(s, "T_radio =", t_r); + wil_dbg_misc(wil, + "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is supported"); + rc = wmi_get_all_temperatures(wil, &sense_all_evt); + if (rc) { + seq_puts(s, "Failed\n"); + return 0; + } + print_temp(s, "T_mac =", + le32_to_cpu(sense_all_evt.baseband_t1000)); + seq_printf(s, "Connected RFs [0x%08x]\n", + sense_all_evt.rf_bitmap); + for (i = 0; i < WMI_MAX_XIF_PORTS_NUM; i++) { + seq_printf(s, "RF[%d] = ", i); + print_temp(s, "", + le32_to_cpu(sense_all_evt.rf_t1000[i])); + } + } else { + s32 t_m, t_r; + wil_dbg_misc(wil, + "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is not supported"); + rc = wmi_get_temperature(wil, &t_m, &t_r); + if (rc) { + seq_puts(s, "Failed\n"); + return 0; + } + print_temp(s, "T_mac =", t_m); + print_temp(s, "T_radio =", t_r); + } return 0; } DEFINE_SHOW_ATTRIBUTE(temp); @@ -2364,6 +2389,7 @@ static const struct { {"tx_latency", 0644, &fops_tx_latency}, {"link_stats", 0644, &fops_link_stats}, {"link_stats_global", 0644, &fops_link_stats_global}, + {"rbufcap", 0244, &fops_rbufcap}, }; static void wil6210_debugfs_init_files(struct wil6210_priv *wil, diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 3c30076efb64..173561fe593d 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -340,7 +340,7 @@ static void _wil6210_disconnect_complete(struct wil6210_vif *vif, wil_dbg_misc(wil, "Disconnect complete %pM, CID=%d, reason=%d\n", bssid, cid, reason_code); - if (cid >= 0) /* disconnect 1 peer */ + if (wil_cid_valid(wil, cid)) /* disconnect 1 peer */ wil_disconnect_cid_complete(vif, cid, reason_code); } else { /* all */ wil_dbg_misc(wil, "Disconnect complete all\n"); @@ -452,7 +452,7 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid, cid = wil_find_cid(wil, vif->mid, bssid); wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n", bssid, cid, reason_code); - if (cid >= 0) /* disconnect 1 peer */ + if (wil_cid_valid(wil, cid)) /* disconnect 1 peer */ wil_disconnect_cid(vif, cid, reason_code); } else { /* all */ wil_dbg_misc(wil, "Disconnect all\n"); @@ -1522,6 +1522,7 @@ int wil_ps_update(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile) static void wil_pre_fw_config(struct wil6210_priv *wil) { + wil_clear_fw_log_addr(wil); /* Mark FW as loaded from host */ wil_s(wil, RGF_USER_USAGE_6, 1); @@ -1579,6 +1580,20 @@ static int wil_restore_vifs(struct wil6210_priv *wil) } /* + * Clear FW and ucode log start addr to indicate FW log is not ready. The host + * driver clears the addresses before FW starts and FW initializes the address + * when it is ready to send logs. + */ +void wil_clear_fw_log_addr(struct wil6210_priv *wil) +{ + /* FW log addr */ + wil_w(wil, RGF_USER_USAGE_1, 0); + /* ucode log addr */ + wil_w(wil, RGF_USER_USAGE_2, 0); + wil_dbg_misc(wil, "Cleared FW and ucode log address"); +} + +/* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload * the firmware. diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 4e2d9221b8e6..9f5a914abc18 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -420,6 +420,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* rollback to bus_disable */ + wil_clear_fw_log_addr(wil); rc = wil_if_add(wil); if (rc) { wil_err(wil, "wil_if_add failed: %d\n", rc); diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 1c796640d6aa..784239bcb3a6 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -316,7 +316,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) u16 agg_timeout = le16_to_cpu(ba_timeout); u16 seq_ctrl = le16_to_cpu(ba_seq_ctrl); struct wil_sta_info *sta; - u16 agg_wsize = 0; + u16 agg_wsize; /* bit 0: A-MSDU supported * bit 1: policy (should be 0 for us) * bits 2..5: TID @@ -328,7 +328,6 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) test_bit(WMI_FW_CAPABILITY_AMSDU, wil->fw_capabilities) && wil->amsdu_en && (param_set & BIT(0)); int ba_policy = param_set & BIT(1); - u16 status = WLAN_STATUS_SUCCESS; u16 ssn = seq_ctrl >> 4; struct wil_tid_ampdu_rx *r; int rc = 0; @@ -355,27 +354,19 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) agg_amsdu ? "+" : "-", !!ba_policy, dialog_token, ssn); /* apply policies */ - if (ba_policy) { - wil_err(wil, "BACK requested unsupported ba_policy == 1\n"); - status = WLAN_STATUS_INVALID_QOS_PARAM; - } - if (status == WLAN_STATUS_SUCCESS) { - if (req_agg_wsize == 0) { - wil_dbg_misc(wil, "Suggest BACK wsize %d\n", - wil->max_agg_wsize); - agg_wsize = wil->max_agg_wsize; - } else { - agg_wsize = min_t(u16, - wil->max_agg_wsize, req_agg_wsize); - } + if (req_agg_wsize == 0) { + wil_dbg_misc(wil, "Suggest BACK wsize %d\n", + wil->max_agg_wsize); + agg_wsize = wil->max_agg_wsize; + } else { + agg_wsize = min_t(u16, wil->max_agg_wsize, req_agg_wsize); } rc = wil->txrx_ops.wmi_addba_rx_resp(wil, mid, cid, tid, dialog_token, - status, agg_amsdu, agg_wsize, - agg_timeout); - if (rc || (status != WLAN_STATUS_SUCCESS)) { - wil_err(wil, "do not apply ba, rc(%d), status(%d)\n", rc, - status); + WLAN_STATUS_SUCCESS, agg_amsdu, + agg_wsize, agg_timeout); + if (rc) { + wil_err(wil, "do not apply ba, rc(%d)\n", rc); goto out; } diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 8790e5effa28..eae00aafaa88 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1037,7 +1037,8 @@ static int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, if (!vif->privacy) txdata->dot1x_open = true; rc = wmi_call(wil, WMI_VRING_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), - WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) goto out_free; @@ -1125,7 +1126,8 @@ static int wil_tx_vring_modify(struct wil6210_vif *vif, int ring_id, int cid, cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); rc = wmi_call(wil, WMI_VRING_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), - WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) goto fail; @@ -1205,7 +1207,8 @@ int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) txdata->dot1x_open = true; rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), - WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) goto out_free; diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 6140db5960c3..dc040cd4ab06 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -26,6 +26,10 @@ #include "txrx.h" #include "trace.h" +/* Max number of entries (packets to complete) to update the hwtail of tx + * status ring. Should be power of 2 + */ +#define WIL_EDMA_TX_SRING_UPDATE_HW_TAIL 128 #define WIL_EDMA_MAX_DATA_OFFSET (2) /* RX buffer size must be aligned to 4 bytes */ #define WIL_EDMA_RX_BUF_LEN_DEFAULT (2048) @@ -1155,7 +1159,7 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, struct wil_net_stats *stats; struct wil_tx_enhanced_desc *_d; unsigned int ring_id; - unsigned int num_descs; + unsigned int num_descs, num_statuses = 0; int i; u8 dr_bit; /* Descriptor Ready bit */ struct wil_ring_tx_status msg; @@ -1276,6 +1280,11 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, } again: + num_statuses++; + if (num_statuses % WIL_EDMA_TX_SRING_UPDATE_HW_TAIL == 0) + /* update HW tail to allow HW to push new statuses */ + wil_w(wil, sring->hwtail, sring->swhead); + wil_sring_advance_swhead(sring); wil_get_next_tx_status_msg(sring, &msg); @@ -1286,8 +1295,9 @@ again: if (desc_cnt) wil_update_net_queues(wil, vif, NULL, false); - /* Update the HW tail ptr (RD ptr) */ - wil_w(wil, sring->hwtail, (sring->swhead - 1) % sring->size); + if (num_statuses % WIL_EDMA_TX_SRING_UPDATE_HW_TAIL != 0) + /* Update the HW tail ptr (RD ptr) */ + wil_w(wil, sring->hwtail, (sring->swhead - 1) % sring->size); return desc_cnt; } diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h index bb4ff28b73e5..e9e6ea9b16b9 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.h +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h @@ -24,7 +24,7 @@ #define WIL_SRING_SIZE_ORDER_MAX (WIL_RING_SIZE_ORDER_MAX) /* RX sring order should be bigger than RX ring order */ #define WIL_RX_SRING_SIZE_ORDER_DEFAULT (12) -#define WIL_TX_SRING_SIZE_ORDER_DEFAULT (12) +#define WIL_TX_SRING_SIZE_ORDER_DEFAULT (14) #define WIL_RX_BUFF_ARR_SIZE_DEFAULT (2600) #define WIL_DEFAULT_RX_STATUS_RING_ID 0 diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 44984031ce54..6f456b311a39 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -99,6 +99,7 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL_MAX_AMPDU_SIZE_128 (128 * 1024) /* FW/HW limit */ #define WIL_MAX_AGG_WSIZE_64 (64) /* FW/HW limit */ #define WIL6210_MAX_STATUS_RINGS (8) +#define WIL_WMI_CALL_GENERAL_TO_MS 100 /* Hardware offload block adds the following: * 26 bytes - 3-address QoS data header @@ -1252,6 +1253,9 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct wil_ring *vring); int wmi_update_ft_ies(struct wil6210_vif *vif, u16 ie_len, const void *ie); int wmi_rxon(struct wil6210_priv *wil, bool on); int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r); +int wmi_get_all_temperatures(struct wil6210_priv *wil, + struct wmi_temp_sense_all_done_event + *sense_all_evt); int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason, bool del_sta); int wmi_addba(struct wil6210_priv *wil, u8 mid, @@ -1406,6 +1410,7 @@ int wmi_stop_sched_scan(struct wil6210_priv *wil); int wmi_mgmt_tx(struct wil6210_vif *vif, const u8 *buf, size_t len); int wmi_mgmt_tx_ext(struct wil6210_vif *vif, const u8 *buf, size_t len, u8 channel, u16 duration_ms); +int wmi_rbufcap_cfg(struct wil6210_priv *wil, bool enable, u16 threshold); int reverse_memcmp(const void *cs, const void *ct, size_t count); @@ -1424,4 +1429,5 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, void update_supported_bands(struct wil6210_priv *wil); +void wil_clear_fw_log_addr(struct wil6210_priv *wil); #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 0a0818f1df28..475b1a233cc9 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -40,7 +40,6 @@ MODULE_PARM_DESC(led_id, " 60G device led enablement. Set the led ID (0-2) to enable"); #define WIL_WAIT_FOR_SUSPEND_RESUME_COMP 200 -#define WIL_WMI_CALL_GENERAL_TO_MS 100 #define WIL_WMI_PCP_STOP_TO_MS 5000 /** @@ -484,6 +483,10 @@ static const char *cmdid2name(u16 cmdid) return "WMI_FT_REASSOC_CMD"; case WMI_UPDATE_FT_IES_CMDID: return "WMI_UPDATE_FT_IES_CMD"; + case WMI_RBUFCAP_CFG_CMDID: + return "WMI_RBUFCAP_CFG_CMD"; + case WMI_TEMP_SENSE_ALL_CMDID: + return "WMI_TEMP_SENSE_ALL_CMDID"; default: return "Untracked CMD"; } @@ -628,6 +631,10 @@ static const char *eventid2name(u16 eventid) return "WMI_FT_AUTH_STATUS_EVENT"; case WMI_FT_REASSOC_STATUS_EVENTID: return "WMI_FT_REASSOC_STATUS_EVENT"; + case WMI_RBUFCAP_CFG_EVENTID: + return "WMI_RBUFCAP_CFG_EVENT"; + case WMI_TEMP_SENSE_ALL_DONE_EVENTID: + return "WMI_TEMP_SENSE_ALL_DONE_EVENTID"; default: return "Untracked EVENT"; } @@ -2051,7 +2058,8 @@ int wmi_echo(struct wil6210_priv *wil) }; return wmi_call(wil, WMI_ECHO_CMDID, vif->mid, &cmd, sizeof(cmd), - WMI_ECHO_RSP_EVENTID, NULL, 0, 50); + WMI_ECHO_RSP_EVENTID, NULL, 0, + WIL_WMI_CALL_GENERAL_TO_MS); } int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) @@ -2110,7 +2118,7 @@ int wmi_led_cfg(struct wil6210_priv *wil, bool enable) rc = wmi_call(wil, WMI_LED_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_LED_CFG_DONE_EVENTID, &reply, sizeof(reply), - 100); + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) goto out; @@ -2124,6 +2132,37 @@ out: return rc; } +int wmi_rbufcap_cfg(struct wil6210_priv *wil, bool enable, u16 threshold) +{ + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); + int rc; + + struct wmi_rbufcap_cfg_cmd cmd = { + .enable = enable, + .rx_desc_threshold = cpu_to_le16(threshold), + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_rbufcap_cfg_event evt; + } __packed reply = { + .evt = {.status = WMI_FW_STATUS_FAILURE}, + }; + + rc = wmi_call(wil, WMI_RBUFCAP_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), + WMI_RBUFCAP_CFG_EVENTID, &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) + return rc; + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "RBUFCAP_CFG failed. status %d\n", + reply.evt.status); + rc = -EINVAL; + } + + return rc; +} + int wmi_pcp_start(struct wil6210_vif *vif, int bi, u8 wmi_nettype, u8 chan, u8 hidden_ssid, u8 is_go) { @@ -2228,7 +2267,8 @@ int wmi_get_ssid(struct wil6210_vif *vif, u8 *ssid_len, void *ssid) memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_GET_SSID_CMDID, vif->mid, NULL, 0, - WMI_GET_SSID_EVENTID, &reply, sizeof(reply), 20); + WMI_GET_SSID_EVENTID, &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) return rc; @@ -2265,7 +2305,8 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel) memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, vif->mid, NULL, 0, - WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); + WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) return rc; @@ -2361,7 +2402,8 @@ int wmi_stop_discovery(struct wil6210_vif *vif) wil_dbg_wmi(wil, "sending WMI_DISCOVERY_STOP_CMDID\n"); rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, vif->mid, NULL, 0, - WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 100); + WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) wil_err(wil, "Failed to stop discovery\n"); @@ -2507,12 +2549,14 @@ int wmi_rxon(struct wil6210_priv *wil, bool on) if (on) { rc = wmi_call(wil, WMI_START_LISTEN_CMDID, vif->mid, NULL, 0, WMI_LISTEN_STARTED_EVENTID, - &reply, sizeof(reply), 100); + &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS)) rc = -EINVAL; } else { rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, vif->mid, NULL, 0, - WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20); + WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, + WIL_WMI_CALL_GENERAL_TO_MS); } return rc; @@ -2601,7 +2645,8 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_TEMP_SENSE_CMDID, vif->mid, &cmd, sizeof(cmd), - WMI_TEMP_SENSE_DONE_EVENTID, &reply, sizeof(reply), 100); + WMI_TEMP_SENSE_DONE_EVENTID, &reply, sizeof(reply), + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) return rc; @@ -2613,6 +2658,44 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) return 0; } +int wmi_get_all_temperatures(struct wil6210_priv *wil, + struct wmi_temp_sense_all_done_event + *sense_all_evt) +{ + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); + int rc; + struct wmi_temp_sense_all_cmd cmd = { + .measure_baseband_en = true, + .measure_rf_en = true, + .measure_mode = TEMPERATURE_MEASURE_NOW, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_temp_sense_all_done_event evt; + } __packed reply; + + if (!sense_all_evt) { + wil_err(wil, "Invalid sense_all_evt value\n"); + return -EINVAL; + } + + memset(&reply, 0, sizeof(reply)); + reply.evt.status = WMI_FW_STATUS_FAILURE; + rc = wmi_call(wil, WMI_TEMP_SENSE_ALL_CMDID, vif->mid, &cmd, + sizeof(cmd), WMI_TEMP_SENSE_ALL_DONE_EVENTID, + &reply, sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS); + if (rc) + return rc; + + if (reply.evt.status == WMI_FW_STATUS_FAILURE) { + wil_err(wil, "Failed geting TEMP_SENSE_ALL\n"); + return -EINVAL; + } + + memcpy(sense_all_evt, &reply.evt, sizeof(reply.evt)); + return 0; +} + int wmi_disconnect_sta(struct wil6210_vif *vif, const u8 *mac, u16 reason, bool del_sta) { @@ -2715,7 +2798,7 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, .dialog_token = token, .status_code = cpu_to_le16(status), /* bit 0: A-MSDU supported - * bit 1: policy (should be 0 for us) + * bit 1: policy (controlled by FW) * bits 2..5: TID * bits 6..15: buffer size */ @@ -2745,7 +2828,7 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, mid, &cmd, sizeof(cmd), WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply), - 100); + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) return rc; @@ -2769,7 +2852,7 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, .dialog_token = token, .status_code = cpu_to_le16(status), /* bit 0: A-MSDU supported - * bit 1: policy (should be 0 for us) + * bit 1: policy (controlled by FW) * bits 2..5: TID * bits 6..15: buffer size */ @@ -2827,7 +2910,7 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, rc = wmi_call(wil, WMI_PS_DEV_PROFILE_CFG_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_PS_DEV_PROFILE_CFG_EVENTID, &reply, sizeof(reply), - 100); + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) return rc; @@ -2864,7 +2947,7 @@ int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short) rc = wmi_call(wil, WMI_SET_MGMT_RETRY_LIMIT_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_SET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply), - 100); + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) return rc; @@ -2894,7 +2977,7 @@ int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short) memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_GET_MGMT_RETRY_LIMIT_CMDID, vif->mid, NULL, 0, WMI_GET_MGMT_RETRY_LIMIT_EVENTID, &reply, sizeof(reply), - 100); + WIL_WMI_CALL_GENERAL_TO_MS); if (rc) return rc; @@ -3220,7 +3303,18 @@ static void wmi_event_handle(struct wil6210_priv *wil, /* check if someone waits for this event */ if (wil->reply_id && wil->reply_id == id && wil->reply_mid == mid) { - WARN_ON(wil->reply_buf); + if (wil->reply_buf) { + /* event received while wmi_call is waiting + * with a buffer. Such event should be handled + * in wmi_recv_cmd function. Handling the event + * here means a previous wmi_call was timeout. + * Drop the event and do not handle it. + */ + wil_err(wil, + "Old event (%d, %s) while wmi_call is waiting. Drop it and Continue waiting\n", + id, eventid2name(id)); + return; + } wmi_evt_call_handler(vif, id, evt_data, len - sizeof(*wmi)); @@ -3800,6 +3894,7 @@ int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id) .ring_size = cpu_to_le16(ring->size), .ring_id = ring_id, }, + .max_msdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)), .status_ring_id = wil->tx_sring_idx, .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, }; diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index da46fc8d39cf..3e37229b36b5 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -35,6 +35,7 @@ #define WMI_PROX_RANGE_NUM (3) #define WMI_MAX_LOSS_DMG_BEACONS (20) #define MAX_NUM_OF_SECTORS (128) +#define WMI_INVALID_TEMPERATURE (0xFFFFFFFF) #define WMI_SCHED_MAX_ALLOCS_PER_CMD (4) #define WMI_RF_DTYPE_LENGTH (3) #define WMI_RF_ETYPE_LENGTH (3) @@ -64,6 +65,7 @@ #define WMI_QOS_MAX_WEIGHT 50 #define WMI_QOS_SET_VIF_PRIORITY (0xFF) #define WMI_QOS_DEFAULT_PRIORITY (WMI_QOS_NUM_OF_PRIORITY) +#define WMI_MAX_XIF_PORTS_NUM (8) /* Mailbox interface * used for commands and events @@ -105,6 +107,7 @@ enum wmi_fw_capability { WMI_FW_CAPABILITY_TX_REQ_EXT = 25, WMI_FW_CAPABILITY_CHANNEL_4 = 26, WMI_FW_CAPABILITY_IPA = 27, + WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF = 30, WMI_FW_CAPABILITY_MAX, }; @@ -296,6 +299,7 @@ enum wmi_command_id { WMI_SET_VRING_PRIORITY_WEIGHT_CMDID = 0xA10, WMI_SET_VRING_PRIORITY_CMDID = 0xA11, WMI_RBUFCAP_CFG_CMDID = 0xA12, + WMI_TEMP_SENSE_ALL_CMDID = 0xA13, WMI_SET_MAC_ADDRESS_CMDID = 0xF003, WMI_ABORT_SCAN_CMDID = 0xF007, WMI_SET_PROMISCUOUS_MODE_CMDID = 0xF041, @@ -1411,12 +1415,7 @@ struct wmi_rf_xpm_write_cmd { u8 data_bytes[0]; } __packed; -/* WMI_TEMP_SENSE_CMDID - * - * Measure MAC and radio temperatures - * - * Possible modes for temperature measurement - */ +/* Possible modes for temperature measurement */ enum wmi_temperature_measure_mode { TEMPERATURE_USE_OLD_VALUE = 0x01, TEMPERATURE_MEASURE_NOW = 0x02, @@ -1942,6 +1941,14 @@ struct wmi_set_ap_slot_size_cmd { __le32 slot_size; } __packed; +/* WMI_TEMP_SENSE_ALL_CMDID */ +struct wmi_temp_sense_all_cmd { + u8 measure_baseband_en; + u8 measure_rf_en; + u8 measure_mode; + u8 reserved; +} __packed; + /* WMI Events * List of Events (target to host) */ @@ -2101,6 +2108,7 @@ enum wmi_event_id { WMI_SET_VRING_PRIORITY_WEIGHT_EVENTID = 0x1A10, WMI_SET_VRING_PRIORITY_EVENTID = 0x1A11, WMI_RBUFCAP_CFG_EVENTID = 0x1A12, + WMI_TEMP_SENSE_ALL_DONE_EVENTID = 0x1A13, WMI_SET_CHANNEL_EVENTID = 0x9000, WMI_ASSOC_REQ_EVENTID = 0x9001, WMI_EAPOL_RX_EVENTID = 0x9002, @@ -2784,11 +2792,13 @@ struct wmi_fixed_scheduling_ul_config_event { */ struct wmi_temp_sense_done_event { /* Temperature times 1000 (actual temperature will be achieved by - * dividing the value by 1000) + * dividing the value by 1000). When temperature cannot be read from + * device return WMI_INVALID_TEMPERATURE */ __le32 baseband_t1000; /* Temperature times 1000 (actual temperature will be achieved by - * dividing the value by 1000) + * dividing the value by 1000). When temperature cannot be read from + * device return WMI_INVALID_TEMPERATURE */ __le32 rf_t1000; } __packed; @@ -4140,4 +4150,25 @@ struct wmi_rbufcap_cfg_event { u8 reserved[3]; } __packed; +/* WMI_TEMP_SENSE_ALL_DONE_EVENTID + * Measure MAC and all radio temperatures + */ +struct wmi_temp_sense_all_done_event { + /* enum wmi_fw_status */ + u8 status; + /* Bitmap of connected RFs */ + u8 rf_bitmap; + u8 reserved[2]; + /* Temperature times 1000 (actual temperature will be achieved by + * dividing the value by 1000). When temperature cannot be read from + * device return WMI_INVALID_TEMPERATURE + */ + __le32 rf_t1000[WMI_MAX_XIF_PORTS_NUM]; + /* Temperature times 1000 (actual temperature will be achieved by + * dividing the value by 1000). When temperature cannot be read from + * device return WMI_INVALID_TEMPERATURE + */ + __le32 baseband_t1000; +} __packed; + #endif /* __WILOCITY_WMI_H__ */ diff --git a/drivers/net/wireless/broadcom/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c index 806406aab43d..31bf71a80c26 100644 --- a/drivers/net/wireless/broadcom/b43/dma.c +++ b/drivers/net/wireless/broadcom/b43/dma.c @@ -797,7 +797,7 @@ static void free_all_descbuffers(struct b43_dmaring *ring) } } -static u64 supported_dma_mask(struct b43_wldev *dev) +static enum b43_dmatype b43_engine_type(struct b43_wldev *dev) { u32 tmp; u16 mmio_base; @@ -807,14 +807,14 @@ static u64 supported_dma_mask(struct b43_wldev *dev) case B43_BUS_BCMA: tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST); if (tmp & BCMA_IOST_DMA64) - return DMA_BIT_MASK(64); + return B43_DMA_64BIT; break; #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); if (tmp & SSB_TMSHIGH_DMA64) - return DMA_BIT_MASK(64); + return B43_DMA_64BIT; break; #endif } @@ -823,20 +823,7 @@ static u64 supported_dma_mask(struct b43_wldev *dev) b43_write32(dev, mmio_base + B43_DMA32_TXCTL, B43_DMA32_TXADDREXT_MASK); tmp = b43_read32(dev, mmio_base + B43_DMA32_TXCTL); if (tmp & B43_DMA32_TXADDREXT_MASK) - return DMA_BIT_MASK(32); - - return DMA_BIT_MASK(30); -} - -static enum b43_dmatype dma_mask_to_engine_type(u64 dmamask) -{ - if (dmamask == DMA_BIT_MASK(30)) - return B43_DMA_30BIT; - if (dmamask == DMA_BIT_MASK(32)) return B43_DMA_32BIT; - if (dmamask == DMA_BIT_MASK(64)) - return B43_DMA_64BIT; - B43_WARN_ON(1); return B43_DMA_30BIT; } @@ -1043,42 +1030,6 @@ void b43_dma_free(struct b43_wldev *dev) destroy_ring(dma, tx_ring_mcast); } -static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask) -{ - u64 orig_mask = mask; - bool fallback = false; - int err; - - /* Try to set the DMA mask. If it fails, try falling back to a - * lower mask, as we can always also support a lower one. */ - while (1) { - err = dma_set_mask_and_coherent(dev->dev->dma_dev, mask); - if (!err) - break; - if (mask == DMA_BIT_MASK(64)) { - mask = DMA_BIT_MASK(32); - fallback = true; - continue; - } - if (mask == DMA_BIT_MASK(32)) { - mask = DMA_BIT_MASK(30); - fallback = true; - continue; - } - b43err(dev->wl, "The machine/kernel does not support " - "the required %u-bit DMA mask\n", - (unsigned int)dma_mask_to_engine_type(orig_mask)); - return -EOPNOTSUPP; - } - if (fallback) { - b43info(dev->wl, "DMA mask fallback from %u-bit to %u-bit\n", - (unsigned int)dma_mask_to_engine_type(orig_mask), - (unsigned int)dma_mask_to_engine_type(mask)); - } - - return 0; -} - /* Some hardware with 64-bit DMA seems to be bugged and looks for translation * bit in low address word instead of high one. */ @@ -1101,15 +1052,15 @@ static bool b43_dma_translation_in_low_word(struct b43_wldev *dev, int b43_dma_init(struct b43_wldev *dev) { struct b43_dma *dma = &dev->dma; + enum b43_dmatype type = b43_engine_type(dev); int err; - u64 dmamask; - enum b43_dmatype type; - dmamask = supported_dma_mask(dev); - type = dma_mask_to_engine_type(dmamask); - err = b43_dma_set_mask(dev, dmamask); - if (err) + err = dma_set_mask_and_coherent(dev->dev->dma_dev, DMA_BIT_MASK(type)); + if (err) { + b43err(dev->wl, "The machine/kernel does not support " + "the required %u-bit DMA mask\n", type); return err; + } switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA @@ -1813,7 +1764,7 @@ void b43_dma_direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type; u16 mmio_base; - type = dma_mask_to_engine_type(supported_dma_mask(dev)); + type = b43_engine_type(dev); mmio_base = b43_dmacontroller_base(type, engine_index); direct_fifo_rx(dev, type, mmio_base, enable); diff --git a/drivers/net/wireless/broadcom/b43legacy/dma.c b/drivers/net/wireless/broadcom/b43legacy/dma.c index 1cc25f44dd9a..f7594e2a896e 100644 --- a/drivers/net/wireless/broadcom/b43legacy/dma.c +++ b/drivers/net/wireless/broadcom/b43legacy/dma.c @@ -603,7 +603,7 @@ static void free_all_descbuffers(struct b43legacy_dmaring *ring) } } -static u64 supported_dma_mask(struct b43legacy_wldev *dev) +static enum b43legacy_dmatype b43legacy_engine_type(struct b43legacy_wldev *dev) { u32 tmp; u16 mmio_base; @@ -615,18 +615,7 @@ static u64 supported_dma_mask(struct b43legacy_wldev *dev) tmp = b43legacy_read32(dev, mmio_base + B43legacy_DMA32_TXCTL); if (tmp & B43legacy_DMA32_TXADDREXT_MASK) - return DMA_BIT_MASK(32); - - return DMA_BIT_MASK(30); -} - -static enum b43legacy_dmatype dma_mask_to_engine_type(u64 dmamask) -{ - if (dmamask == DMA_BIT_MASK(30)) - return B43legacy_DMA_30BIT; - if (dmamask == DMA_BIT_MASK(32)) return B43legacy_DMA_32BIT; - B43legacy_WARN_ON(1); return B43legacy_DMA_30BIT; } @@ -784,54 +773,14 @@ void b43legacy_dma_free(struct b43legacy_wldev *dev) dma->tx_ring0 = NULL; } -static int b43legacy_dma_set_mask(struct b43legacy_wldev *dev, u64 mask) -{ - u64 orig_mask = mask; - bool fallback = false; - int err; - - /* Try to set the DMA mask. If it fails, try falling back to a - * lower mask, as we can always also support a lower one. */ - while (1) { - err = dma_set_mask_and_coherent(dev->dev->dma_dev, mask); - if (!err) - break; - if (mask == DMA_BIT_MASK(64)) { - mask = DMA_BIT_MASK(32); - fallback = true; - continue; - } - if (mask == DMA_BIT_MASK(32)) { - mask = DMA_BIT_MASK(30); - fallback = true; - continue; - } - b43legacyerr(dev->wl, "The machine/kernel does not support " - "the required %u-bit DMA mask\n", - (unsigned int)dma_mask_to_engine_type(orig_mask)); - return -EOPNOTSUPP; - } - if (fallback) { - b43legacyinfo(dev->wl, "DMA mask fallback from %u-bit to %u-" - "bit\n", - (unsigned int)dma_mask_to_engine_type(orig_mask), - (unsigned int)dma_mask_to_engine_type(mask)); - } - - return 0; -} - int b43legacy_dma_init(struct b43legacy_wldev *dev) { struct b43legacy_dma *dma = &dev->dma; struct b43legacy_dmaring *ring; + enum b43legacy_dmatype type = b43legacy_engine_type(dev); int err; - u64 dmamask; - enum b43legacy_dmatype type; - dmamask = supported_dma_mask(dev); - type = dma_mask_to_engine_type(dmamask); - err = b43legacy_dma_set_mask(dev, dmamask); + err = dma_set_mask_and_coherent(dev->dev->dma_dev, DMA_BIT_MASK(type)); if (err) { #ifdef CONFIG_B43LEGACY_PIO b43legacywarn(dev->wl, "DMA for this device not supported. " diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index 1fd6bf578474..eab94d2f46b1 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -1009,8 +1009,7 @@ int iwlagn_send_patterns(struct iwl_priv *priv, if (!wowlan->n_patterns) return 0; - cmd.len[0] = sizeof(*pattern_cmd) + - wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); + cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns); pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); if (!pattern_cmd) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index 405038ce98d6..7573af2d88ce 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -97,7 +97,7 @@ IWL_EXPORT_SYMBOL(iwl_acpi_get_object); union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev, union acpi_object *data, - int data_size) + int data_size, int *tbl_rev) { int i; union acpi_object *wifi_pkg; @@ -113,16 +113,19 @@ union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev, /* * We need at least two packages, one for the revision and one * for the data itself. Also check that the revision is valid - * (i.e. it is an integer set to 0). + * (i.e. it is an integer smaller than 2, as we currently support only + * 2 revisions). */ if (data->type != ACPI_TYPE_PACKAGE || data->package.count < 2 || data->package.elements[0].type != ACPI_TYPE_INTEGER || - data->package.elements[0].integer.value != 0) { + data->package.elements[0].integer.value > 1) { IWL_DEBUG_DEV_RADIO(dev, "Unsupported packages structure\n"); return ERR_PTR(-EINVAL); } + *tbl_rev = data->package.elements[0].integer.value; + /* loop through all the packages to find the one for WiFi */ for (i = 1; i < data->package.count; i++) { union acpi_object *domain; @@ -151,14 +154,15 @@ int iwl_acpi_get_mcc(struct device *dev, char *mcc) { union acpi_object *wifi_pkg, *data; u32 mcc_val; - int ret; + int ret, tbl_rev; data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg)) { + wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE, + &tbl_rev); + if (IS_ERR(wifi_pkg) || tbl_rev != 0) { ret = PTR_ERR(wifi_pkg); goto out_free; } @@ -185,6 +189,7 @@ u64 iwl_acpi_get_pwr_limit(struct device *dev) { union acpi_object *data, *wifi_pkg; u64 dflt_pwr_limit; + int tbl_rev; data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD); if (IS_ERR(data)) { @@ -193,8 +198,8 @@ u64 iwl_acpi_get_pwr_limit(struct device *dev) } wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, - ACPI_SPLC_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg) || + ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev); + if (IS_ERR(wifi_pkg) || tbl_rev != 0 || wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) { dflt_pwr_limit = 0; goto out_free; @@ -211,14 +216,15 @@ IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit); int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) { union acpi_object *wifi_pkg, *data; - int ret; + int ret, tbl_rev; data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg)) { + wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE, + &tbl_rev); + if (IS_ERR(wifi_pkg) || tbl_rev != 0) { ret = PTR_ERR(wifi_pkg); goto out_free; } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index f5704e16643f..991a23450999 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -97,7 +97,7 @@ void *iwl_acpi_get_object(struct device *dev, acpi_string method); union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev, union acpi_object *data, - int data_size); + int data_size, int *tbl_rev); /** * iwl_acpi_get_mcc - read MCC from ACPI, if available @@ -131,7 +131,8 @@ static inline void *iwl_acpi_get_object(struct device *dev, acpi_string method) static inline union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev, union acpi_object *data, - int data_size) + int data_size, + int *tbl_rev) { return ERR_PTR(-ENOENT); } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index f4202bc231a6..aaf3974a9a20 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -291,6 +291,28 @@ struct iwl_fw_ini_trigger_tlv { struct iwl_fw_ini_trigger trigger_config[]; } __packed; /* FW_TLV_DEBUG_TRIGGERS_API_S_VER_1 */ +#define IWL_FW_INI_MAX_IMG_NAME_LEN 32 +#define IWL_FW_INI_MAX_DBG_CFG_NAME_LEN 64 + +/** + * struct iwl_fw_ini_debug_info_tlv - (IWL_UCODE_TLV_TYPE_DEBUG_INFO) + * + * holds image name and debug configuration name + * + * @header: header + * @img_name_len: length of the image name string + * @img_name: image name string + * @dbg_cfg_name_len : length of the debug configuration name string + * @dbg_cfg_name: debug configuration name string + */ +struct iwl_fw_ini_debug_info_tlv { + struct iwl_fw_ini_header header; + __le32 img_name_len; + u8 img_name[IWL_FW_INI_MAX_IMG_NAME_LEN]; + __le32 dbg_cfg_name_len; + u8 dbg_cfg_name[IWL_FW_INI_MAX_DBG_CFG_NAME_LEN]; +} __packed; /* FW_DEBUG_TLV_INFO_API_S_VER_1 */ + /** * enum iwl_fw_ini_trigger_id * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h index 8d78b0e671c0..ec864c7b497f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h @@ -937,8 +937,13 @@ struct iwl_ftm_responder_stats { __le16 reserved; } __packed; /* TOF_RESPONDER_STATISTICS_NTFY_S_VER_2 */ -#define IWL_CSI_CHUNK_CTL_NUM_MASK 0x3 -#define IWL_CSI_CHUNK_CTL_IDX_MASK 0xc +#define IWL_CSI_MAX_EXPECTED_CHUNKS 16 + +#define IWL_CSI_CHUNK_CTL_NUM_MASK_VER_1 0x0003 +#define IWL_CSI_CHUNK_CTL_IDX_MASK_VER_1 0x000c + +#define IWL_CSI_CHUNK_CTL_NUM_MASK_VER_2 0x00ff +#define IWL_CSI_CHUNK_CTL_IDX_MASK_VER_2 0xff00 struct iwl_csi_chunk_notification { __le32 token; @@ -946,6 +951,6 @@ struct iwl_csi_chunk_notification { __le16 ctl; __le32 size; u8 data[]; -} __packed; /* CSI_CHUNKS_HDR_NTFY_API_S_VER_1 */ +} __packed; /* CSI_CHUNKS_HDR_NTFY_API_S_VER_1/VER_2 */ #endif /* __iwl_fw_api_location_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index 01f003c6cff9..f195db398bed 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -420,13 +420,25 @@ struct iwl_per_chain_offset_group { } __packed; /* PER_CHAIN_LIMIT_OFFSET_GROUP_S_VER_1 */ /** + * struct iwl_geo_tx_power_profile_cmd_v1 - struct for GEO_TX_POWER_LIMIT cmd. + * @ops: operations, value from &enum iwl_geo_per_chain_offset_operation + * @table: offset profile per band. + */ +struct iwl_geo_tx_power_profiles_cmd_v1 { + __le32 ops; + struct iwl_per_chain_offset_group table[IWL_NUM_GEO_PROFILES]; +} __packed; /* GEO_TX_POWER_LIMIT_VER_1 */ + +/** * struct iwl_geo_tx_power_profile_cmd - struct for GEO_TX_POWER_LIMIT cmd. * @ops: operations, value from &enum iwl_geo_per_chain_offset_operation * @table: offset profile per band. + * @table_revision: BIOS table revision. */ struct iwl_geo_tx_power_profiles_cmd { __le32 ops; struct iwl_per_chain_offset_group table[IWL_NUM_GEO_PROFILES]; + __le32 table_revision; } __packed; /* GEO_TX_POWER_LIMIT */ /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h index 1a67a2a439ab..c4960f045415 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h @@ -750,6 +750,21 @@ struct iwl_scan_req_umac { struct iwl_scan_umac_chan_param channel; u8 data[]; } v8; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_8 */ + struct { + u8 active_dwell[SCAN_TWO_LMACS]; + u8 adwell_default_hb_n_aps; + u8 adwell_default_lb_n_aps; + u8 adwell_default_n_aps_social; + u8 general_flags2; + __le16 adwell_max_budget; + __le32 max_out_time[SCAN_TWO_LMACS]; + __le32 suspend_time[SCAN_TWO_LMACS]; + __le32 scan_priority; + u8 passive_dwell[SCAN_TWO_LMACS]; + u8 num_of_fragments[SCAN_TWO_LMACS]; + struct iwl_scan_umac_chan_param channel; + u8 data[]; + } v9; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_9 */ }; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 33d7bc5500db..e411ac98290d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1059,7 +1059,7 @@ static int iwl_dump_ini_prph_iter(struct iwl_fw_runtime *fwrt, u32 addr = le32_to_cpu(reg->start_addr[idx]) + le32_to_cpu(reg->offset); int i; - range->start_addr = cpu_to_le64(addr); + range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->internal.range_data_size; for (i = 0; i < le32_to_cpu(reg->internal.range_data_size); i += 4) { prph_val = iwl_read_prph(fwrt->trans, addr + i); @@ -1080,7 +1080,7 @@ static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, u32 addr = le32_to_cpu(reg->start_addr[idx]) + le32_to_cpu(reg->offset); int i; - range->start_addr = cpu_to_le64(addr); + range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->internal.range_data_size; for (i = 0; i < le32_to_cpu(reg->internal.range_data_size); i += 4) *val++ = cpu_to_le32(iwl_trans_read32(fwrt->trans, addr + i)); @@ -1095,7 +1095,7 @@ static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_error_dump_range *range = range_ptr; u32 addr = le32_to_cpu(reg->start_addr[idx]) + le32_to_cpu(reg->offset); - range->start_addr = cpu_to_le64(addr); + range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = reg->internal.range_data_size; iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, le32_to_cpu(reg->internal.range_data_size)); @@ -1111,7 +1111,7 @@ iwl_dump_ini_paging_gen2_iter(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_error_dump_range *range = range_ptr; u32 page_size = fwrt->trans->init_dram.paging[idx].size; - range->start_addr = cpu_to_le64(idx); + range->page_num = cpu_to_le32(idx); range->range_data_size = cpu_to_le32(page_size); memcpy(range->data, fwrt->trans->init_dram.paging[idx].block, page_size); @@ -1131,7 +1131,7 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, dma_addr_t addr = fwrt->fw_paging_db[idx].fw_paging_phys; u32 page_size = fwrt->fw_paging_db[idx].fw_paging_size; - range->start_addr = cpu_to_le64(idx); + range->page_num = cpu_to_le32(idx); range->range_data_size = cpu_to_le32(page_size); dma_sync_single_for_cpu(fwrt->trans->dev, addr, page_size, DMA_BIDIRECTIONAL); @@ -1154,11 +1154,11 @@ iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt, if (start_addr == 0x5a5a5a5a) return -EBUSY; - range->start_addr = cpu_to_le64(start_addr); - range->range_data_size = cpu_to_le32(fwrt->trans->fw_mon[idx].size); + range->dram_base_addr = cpu_to_le64(start_addr); + range->range_data_size = cpu_to_le32(fwrt->trans->dbg.fw_mon[idx].size); - memcpy(range->data, fwrt->trans->fw_mon[idx].block, - fwrt->trans->fw_mon[idx].size); + memcpy(range->data, fwrt->trans->dbg.fw_mon[idx].block, + fwrt->trans->dbg.fw_mon[idx].size); return sizeof(*range) + le32_to_cpu(range->range_data_size); } @@ -1228,7 +1228,7 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_region_cfg *reg, void *range_ptr, int idx) { - struct iwl_fw_ini_fifo_error_dump_range *range = range_ptr; + struct iwl_fw_ini_error_dump_range *range = range_ptr; struct iwl_ini_txf_iter_data *iter; struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data; u32 offs = le32_to_cpu(reg->offset), addr; @@ -1246,8 +1246,8 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, iter = fwrt->dump.fifo_iter; - range->fifo_num = cpu_to_le32(iter->fifo); - range->num_of_registers = reg->fifos.num_of_registers; + range->fifo_hdr.fifo_num = cpu_to_le32(iter->fifo); + range->fifo_hdr.num_of_registers = reg->fifos.num_of_registers; 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); @@ -1336,7 +1336,7 @@ static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_region_cfg *reg, void *range_ptr, int idx) { - struct iwl_fw_ini_fifo_error_dump_range *range = range_ptr; + struct iwl_fw_ini_error_dump_range *range = range_ptr; struct iwl_ini_rxf_data rxf_data; struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data; u32 offs = le32_to_cpu(reg->offset), addr; @@ -1353,8 +1353,8 @@ static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, if (!iwl_trans_grab_nic_access(fwrt->trans, &flags)) return -EBUSY; - range->fifo_num = cpu_to_le32(rxf_data.fifo_num); - range->num_of_registers = reg->fifos.num_of_registers; + range->fifo_hdr.fifo_num = cpu_to_le32(rxf_data.fifo_num); + range->fifo_hdr.num_of_registers = reg->fifos.num_of_registers; range->range_data_size = cpu_to_le32(rxf_data.size + registers_size); /* @@ -1408,7 +1408,7 @@ static void *iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, { struct iwl_fw_ini_error_dump *dump = data; - dump->header.version = cpu_to_le32(IWL_INI_DUMP_MEM_VER); + dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER); return dump->ranges; } @@ -1433,7 +1433,7 @@ static void iwl_trans_release_nic_access(fwrt->trans, &flags); - data->header.version = cpu_to_le32(IWL_INI_DUMP_MONITOR_VER); + data->header.version = cpu_to_le32(IWL_INI_DUMP_VER); data->write_ptr = cpu_to_le32(write_ptr & write_ptr_msk); data->cycle_cnt = cpu_to_le32(cycle_cnt & cycle_cnt_msk); @@ -1490,17 +1490,6 @@ static void } -static void *iwl_dump_ini_fifo_fill_header(struct iwl_fw_runtime *fwrt, - struct iwl_fw_ini_region_cfg *reg, - void *data) -{ - struct iwl_fw_ini_fifo_error_dump *dump = data; - - dump->header.version = cpu_to_le32(IWL_INI_DUMP_FIFO_VER); - - return dump->ranges; -} - static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_region_cfg *reg) { @@ -1592,8 +1581,8 @@ static u32 iwl_dump_ini_mon_dram_get_size(struct iwl_fw_runtime *fwrt, u32 size = sizeof(struct iwl_fw_ini_monitor_dump) + sizeof(struct iwl_fw_ini_error_dump_range); - if (fwrt->trans->num_blocks) - size += fwrt->trans->fw_mon[0].size; + if (fwrt->trans->dbg.num_blocks) + size += fwrt->trans->dbg.fw_mon[0].size; return size; } @@ -1613,8 +1602,9 @@ static u32 iwl_dump_ini_txf_get_size(struct iwl_fw_runtime *fwrt, struct iwl_ini_txf_iter_data iter = { .init = true }; void *fifo_iter = fwrt->dump.fifo_iter; u32 size = 0; - u32 fifo_hdr = sizeof(struct iwl_fw_ini_fifo_error_dump_range) + - le32_to_cpu(reg->fifos.num_of_registers) * sizeof(__le32) * 2; + u32 fifo_hdr = sizeof(struct iwl_fw_ini_error_dump_range) + + le32_to_cpu(reg->fifos.num_of_registers) * + sizeof(struct iwl_fw_ini_error_dump_register); fwrt->dump.fifo_iter = &iter; while (iwl_ini_txf_iter(fwrt, reg)) { @@ -1624,7 +1614,7 @@ static u32 iwl_dump_ini_txf_get_size(struct iwl_fw_runtime *fwrt, } if (size) - size += sizeof(struct iwl_fw_ini_fifo_error_dump); + size += sizeof(struct iwl_fw_ini_error_dump); fwrt->dump.fifo_iter = fifo_iter; @@ -1635,9 +1625,10 @@ static u32 iwl_dump_ini_rxf_get_size(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_region_cfg *reg) { struct iwl_ini_rxf_data rx_data; - u32 size = sizeof(struct iwl_fw_ini_fifo_error_dump) + - sizeof(struct iwl_fw_ini_fifo_error_dump_range) + - le32_to_cpu(reg->fifos.num_of_registers) * sizeof(__le32) * 2; + u32 size = sizeof(struct iwl_fw_ini_error_dump) + + sizeof(struct iwl_fw_ini_error_dump_range) + + le32_to_cpu(reg->fifos.num_of_registers) * + sizeof(struct iwl_fw_ini_error_dump_register); if (reg->fifos.header_only) return size; @@ -1683,20 +1674,24 @@ iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_mem_ops *ops) { struct iwl_fw_ini_error_dump_header *header = (void *)(*data)->data; - u32 num_of_ranges, i, type = le32_to_cpu(reg->region_type); + u32 num_of_ranges, i, type = le32_to_cpu(reg->region_type), size; void *range; if (WARN_ON(!ops || !ops->get_num_of_ranges || !ops->get_size || !ops->fill_mem_hdr || !ops->fill_range)) return; + size = ops->get_size(fwrt, reg); + if (!size) + return; + IWL_DEBUG_FW(fwrt, "WRT: collecting region: id=%d, type=%d\n", le32_to_cpu(reg->region_id), type); num_of_ranges = ops->get_num_of_ranges(fwrt, reg); - (*data)->type = cpu_to_le32(type | INI_DUMP_BIT); - (*data)->len = cpu_to_le32(ops->get_size(fwrt, reg)); + (*data)->type = cpu_to_le32(type); + (*data)->len = cpu_to_le32(size); header->region_id = reg->region_id; header->num_of_ranges = cpu_to_le32(num_of_ranges); @@ -1709,7 +1704,7 @@ iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, IWL_ERR(fwrt, "WRT: failed to fill region header: id=%d, type=%d\n", le32_to_cpu(reg->region_id), type); - memset(*data, 0, le32_to_cpu((*data)->len)); + memset(*data, 0, size); return; } @@ -1720,7 +1715,7 @@ iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, IWL_ERR(fwrt, "WRT: failed to dump region: id=%d, type=%d\n", le32_to_cpu(reg->region_id), type); - memset(*data, 0, le32_to_cpu((*data)->len)); + memset(*data, 0, size); return; } range = range + range_size; @@ -1728,10 +1723,71 @@ iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, *data = iwl_fw_error_next_data(*data); } +static void iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_trigger *trigger, + struct iwl_fw_error_dump_data **data) +{ + struct iwl_fw_ini_dump_info *dump = (void *)(*data)->data; + u32 reg_ids_size = le32_to_cpu(trigger->num_regions) * sizeof(__le32); + + (*data)->type = cpu_to_le32(IWL_INI_DUMP_INFO_TYPE); + (*data)->len = cpu_to_le32(sizeof(*dump) + reg_ids_size); + + dump->version = cpu_to_le32(IWL_INI_DUMP_VER); + dump->trigger_id = trigger->trigger_id; + dump->is_external_cfg = + cpu_to_le32(fwrt->trans->dbg.external_ini_loaded); + + dump->ver_type = cpu_to_le32(fwrt->dump.fw_ver.type); + dump->ver_subtype = cpu_to_le32(fwrt->dump.fw_ver.subtype); + + dump->hw_step = cpu_to_le32(CSR_HW_REV_STEP(fwrt->trans->hw_rev)); + dump->hw_type = cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->hw_rev)); + + dump->rf_id_flavor = + cpu_to_le32(CSR_HW_RFID_FLAVOR(fwrt->trans->hw_rf_id)); + dump->rf_id_dash = cpu_to_le32(CSR_HW_RFID_DASH(fwrt->trans->hw_rf_id)); + dump->rf_id_step = cpu_to_le32(CSR_HW_RFID_STEP(fwrt->trans->hw_rf_id)); + dump->rf_id_type = cpu_to_le32(CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)); + + dump->lmac_major = cpu_to_le32(fwrt->dump.fw_ver.lmac_major); + dump->lmac_minor = cpu_to_le32(fwrt->dump.fw_ver.lmac_minor); + dump->umac_major = cpu_to_le32(fwrt->dump.fw_ver.umac_major); + dump->umac_minor = cpu_to_le32(fwrt->dump.fw_ver.umac_minor); + + dump->build_tag_len = cpu_to_le32(sizeof(dump->build_tag)); + memcpy(dump->build_tag, fwrt->fw->human_readable, + sizeof(dump->build_tag)); + + dump->img_name_len = cpu_to_le32(sizeof(dump->img_name)); + memcpy(dump->img_name, fwrt->dump.img_name, sizeof(dump->img_name)); + + dump->internal_dbg_cfg_name_len = + cpu_to_le32(sizeof(dump->internal_dbg_cfg_name)); + memcpy(dump->internal_dbg_cfg_name, fwrt->dump.internal_dbg_cfg_name, + sizeof(dump->internal_dbg_cfg_name)); + + dump->external_dbg_cfg_name_len = + cpu_to_le32(sizeof(dump->external_dbg_cfg_name)); + + /* dump info size is allocated in iwl_fw_ini_get_trigger_len. + * The driver allocates (sizeof(*dump) + reg_ids_size) so it is safe to + * use reg_ids_size + */ + memcpy(dump->external_dbg_cfg_name, fwrt->dump.external_dbg_cfg_name, + sizeof(dump->external_dbg_cfg_name)); + + dump->regions_num = trigger->num_regions; + memcpy(dump->region_ids, trigger->data, reg_ids_size); + + *data = iwl_fw_error_next_data(*data); +} + static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_trigger *trigger) { - int i, size = 0, hdr_len = sizeof(struct iwl_fw_error_dump_data); + int i, ret_size = 0, hdr_len = sizeof(struct iwl_fw_error_dump_data); + u32 size; if (!trigger || !trigger->num_regions) return 0; @@ -1763,32 +1819,40 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_REGION_CSR: case IWL_FW_INI_REGION_LMAC_ERROR_TABLE: case IWL_FW_INI_REGION_UMAC_ERROR_TABLE: - size += hdr_len + iwl_dump_ini_mem_get_size(fwrt, reg); + size = iwl_dump_ini_mem_get_size(fwrt, reg); + if (size) + ret_size += hdr_len + size; break; case IWL_FW_INI_REGION_TXF: - size += hdr_len + iwl_dump_ini_txf_get_size(fwrt, reg); + size = iwl_dump_ini_txf_get_size(fwrt, reg); + if (size) + ret_size += hdr_len + size; break; case IWL_FW_INI_REGION_RXF: - size += hdr_len + iwl_dump_ini_rxf_get_size(fwrt, reg); + size = iwl_dump_ini_rxf_get_size(fwrt, reg); + if (size) + ret_size += hdr_len + size; break; case IWL_FW_INI_REGION_PAGING: - size += hdr_len; - if (iwl_fw_dbg_is_paging_enabled(fwrt)) { - size += iwl_dump_ini_paging_get_size(fwrt, reg); - } else { - size += iwl_dump_ini_paging_gen2_get_size(fwrt, - reg); - } + if (iwl_fw_dbg_is_paging_enabled(fwrt)) + size = iwl_dump_ini_paging_get_size(fwrt, reg); + else + size = iwl_dump_ini_paging_gen2_get_size(fwrt, + reg); + if (size) + ret_size += hdr_len + size; break; case IWL_FW_INI_REGION_DRAM_BUFFER: - if (!fwrt->trans->num_blocks) + if (!fwrt->trans->dbg.num_blocks) break; - size += hdr_len + - iwl_dump_ini_mon_dram_get_size(fwrt, reg); + size = iwl_dump_ini_mon_dram_get_size(fwrt, reg); + if (size) + ret_size += hdr_len + size; break; case IWL_FW_INI_REGION_INTERNAL_BUFFER: - size += hdr_len + - iwl_dump_ini_mon_smem_get_size(fwrt, reg); + size = iwl_dump_ini_mon_smem_get_size(fwrt, reg); + if (size) + ret_size += hdr_len + size; break; case IWL_FW_INI_REGION_DRAM_IMR: /* Undefined yet */ @@ -1796,7 +1860,13 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, break; } } - return size; + + /* add dump info size */ + if (ret_size) + ret_size += hdr_len + sizeof(struct iwl_fw_ini_dump_info) + + (le32_to_cpu(trigger->num_regions) * sizeof(__le32)); + + return ret_size; } static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, @@ -1805,6 +1875,8 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, { int i, num = le32_to_cpu(trigger->num_regions); + iwl_dump_ini_info(fwrt, trigger, data); + for (i = 0; i < num; i++) { u32 reg_id = le32_to_cpu(trigger->data[i]); struct iwl_fw_ini_region_cfg *reg; @@ -1879,7 +1951,7 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, fwrt->dump.fifo_iter = &iter; ops.get_num_of_ranges = iwl_dump_ini_txf_ranges; ops.get_size = iwl_dump_ini_txf_get_size; - ops.fill_mem_hdr = iwl_dump_ini_fifo_fill_header; + ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; ops.fill_range = iwl_dump_ini_txf_iter; iwl_dump_ini_mem(fwrt, data, reg, &ops); fwrt->dump.fifo_iter = fifo_iter; @@ -1888,7 +1960,7 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_REGION_RXF: ops.get_num_of_ranges = iwl_dump_ini_rxf_ranges; ops.get_size = iwl_dump_ini_rxf_get_size; - ops.fill_mem_hdr = iwl_dump_ini_fifo_fill_header; + ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; ops.fill_range = iwl_dump_ini_rxf_iter; iwl_dump_ini_mem(fwrt, data, reg, &ops); break; @@ -1908,18 +1980,18 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, } static struct iwl_fw_error_dump_file * -iwl_fw_error_ini_dump_file(struct iwl_fw_runtime *fwrt) +iwl_fw_error_ini_dump_file(struct iwl_fw_runtime *fwrt, + enum iwl_fw_ini_trigger_id trig_id) { int size; struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_ini_trigger *trigger; - enum iwl_fw_ini_trigger_id id = fwrt->dump.ini_trig_id; - if (!iwl_fw_ini_trigger_on(fwrt, id)) + if (!iwl_fw_ini_trigger_on(fwrt, trig_id)) return NULL; - trigger = fwrt->dump.active_trigs[id].trig; + trigger = fwrt->dump.active_trigs[trig_id].trig; size = iwl_fw_ini_get_trigger_len(fwrt, trigger); if (!size) @@ -1931,7 +2003,7 @@ iwl_fw_error_ini_dump_file(struct iwl_fw_runtime *fwrt) if (!dump_file) return NULL; - dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); + dump_file->barker = cpu_to_le32(IWL_FW_INI_ERROR_DUMP_BARKER); dump_data = (void *)dump_file->data; dump_file->file_len = cpu_to_le32(size); @@ -1952,7 +2024,7 @@ static void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) if (!dump_file) goto out; - if (!fwrt->trans->ini_valid && fwrt->dump.monitor_only) + if (fwrt->dump.monitor_only) dump_mask &= IWL_FW_ERROR_DUMP_FW_MONITOR; fw_error_dump.trans_ptr = iwl_trans_dump_data(fwrt->trans, dump_mask); @@ -1984,16 +2056,16 @@ static void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) out: iwl_fw_free_dump_desc(fwrt); - clear_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status); } -static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt) +static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt, u8 wk_idx) { + enum iwl_fw_ini_trigger_id trig_id = fwrt->dump.wks[wk_idx].ini_trig_id; struct iwl_fw_error_dump_file *dump_file; struct scatterlist *sg_dump_data; u32 file_len; - dump_file = iwl_fw_error_ini_dump_file(fwrt); + dump_file = iwl_fw_error_ini_dump_file(fwrt, trig_id); if (!dump_file) goto out; @@ -2008,8 +2080,7 @@ static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt) } vfree(dump_file); out: - fwrt->dump.ini_trig_id = IWL_FW_TRIGGER_ID_INVALID; - clear_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status); + fwrt->dump.wks[wk_idx].ini_trig_id = IWL_FW_TRIGGER_ID_INVALID; } const struct iwl_fw_dump_desc iwl_dump_desc_assert = { @@ -2027,7 +2098,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, u32 trig_type = le32_to_cpu(desc->trig_desc.type); int ret; - if (fwrt->trans->ini_valid) { + if (fwrt->trans->dbg.ini_valid) { ret = iwl_fw_dbg_ini_collect(fwrt, trig_type); if (!ret) iwl_fw_free_dump_desc(fwrt); @@ -2035,7 +2106,10 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, return ret; } - if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) + /* use wks[0] since dump flow prior to ini does not need to support + * consecutive triggers collection + */ + if (test_and_set_bit(fwrt->dump.wks[0].idx, &fwrt->dump.active_wks)) return -EBUSY; if (WARN_ON(fwrt->dump.desc)) @@ -2047,7 +2121,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, fwrt->dump.desc = desc; fwrt->dump.monitor_only = monitor_only; - schedule_delayed_work(&fwrt->dump.wk, usecs_to_jiffies(delay)); + schedule_delayed_work(&fwrt->dump.wks[0].wk, usecs_to_jiffies(delay)); return 0; } @@ -2057,9 +2131,12 @@ int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt, enum iwl_fw_dbg_trigger trig_type) { int ret; - struct iwl_fw_dump_desc *iwl_dump_error_desc = - kmalloc(sizeof(*iwl_dump_error_desc), GFP_KERNEL); + struct iwl_fw_dump_desc *iwl_dump_error_desc; + + if (!test_bit(STATUS_DEVICE_ENABLED, &fwrt->trans->status)) + return -EIO; + iwl_dump_error_desc = kmalloc(sizeof(*iwl_dump_error_desc), GFP_KERNEL); if (!iwl_dump_error_desc) return -ENOMEM; @@ -2123,13 +2200,11 @@ int _iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, { struct iwl_fw_ini_active_triggers *active; u32 occur, delay; + unsigned long idx; if (WARN_ON(!iwl_fw_ini_trigger_on(fwrt, id))) return -EINVAL; - if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) - return -EBUSY; - if (!iwl_fw_ini_trigger_on(fwrt, id)) { IWL_WARN(fwrt, "WRT: Trigger %d is not active, aborting dump\n", id); @@ -2150,14 +2225,24 @@ int _iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, return 0; } - if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) + /* Check there is an available worker. + * ffz return value is undefined if no zero exists, + * so check against ~0UL first. + */ + if (fwrt->dump.active_wks == ~0UL) + return -EBUSY; + + idx = ffz(fwrt->dump.active_wks); + + if (idx >= IWL_FW_RUNTIME_DUMP_WK_NUM || + test_and_set_bit(fwrt->dump.wks[idx].idx, &fwrt->dump.active_wks)) return -EBUSY; - fwrt->dump.ini_trig_id = id; + fwrt->dump.wks[idx].ini_trig_id = id; IWL_WARN(fwrt, "WRT: collecting data: ini trigger %d fired.\n", id); - schedule_delayed_work(&fwrt->dump.wk, usecs_to_jiffies(delay)); + schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); return 0; } @@ -2191,9 +2276,6 @@ int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt, int ret, len = 0; char buf[64]; - if (fwrt->trans->ini_valid) - return 0; - if (fmt) { va_list ap; @@ -2270,56 +2352,57 @@ IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf); /* this function assumes dump_start was called beforehand and dump_end will be * called afterwards */ -void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt) +static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) { struct iwl_fw_dbg_params params = {0}; - if (!test_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) + if (!test_bit(wk_idx, &fwrt->dump.active_wks)) return; if (fwrt->ops && fwrt->ops->fw_running && !fwrt->ops->fw_running(fwrt->ops_ctx)) { IWL_ERR(fwrt, "Firmware not running - cannot dump error\n"); iwl_fw_free_dump_desc(fwrt); - clear_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status); - return; + goto out; } /* there's no point in fw dump if the bus is dead */ if (test_bit(STATUS_TRANS_DEAD, &fwrt->trans->status)) { IWL_ERR(fwrt, "Skip fw error dump since bus is dead\n"); - return; + goto out; } - iwl_fw_dbg_stop_recording(fwrt, ¶ms); + iwl_fw_dbg_stop_recording(fwrt->trans, ¶ms); IWL_DEBUG_FW_INFO(fwrt, "WRT: data collection start\n"); - if (fwrt->trans->ini_valid) - iwl_fw_error_ini_dump(fwrt); + if (fwrt->trans->dbg.ini_valid) + iwl_fw_error_ini_dump(fwrt, wk_idx); else iwl_fw_error_dump(fwrt); IWL_DEBUG_FW_INFO(fwrt, "WRT: data collection done\n"); - /* start recording again if the firmware is not crashed */ - if (!test_bit(STATUS_FW_ERROR, &fwrt->trans->status) && - fwrt->fw->dbg.dest_tlv) { - /* wait before we collect the data till the DBGC stop */ - udelay(500); - iwl_fw_dbg_restart_recording(fwrt, ¶ms); - } + iwl_fw_dbg_restart_recording(fwrt, ¶ms); + +out: + clear_bit(wk_idx, &fwrt->dump.active_wks); } -IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_sync); void iwl_fw_error_dump_wk(struct work_struct *work) { - struct iwl_fw_runtime *fwrt = - container_of(work, struct iwl_fw_runtime, dump.wk.work); + struct iwl_fw_runtime *fwrt; + typeof(fwrt->dump.wks[0]) *wks; + + wks = container_of(work, typeof(fwrt->dump.wks[0]), wk.work); + fwrt = container_of(wks, struct iwl_fw_runtime, dump.wks[wks->idx]); + /* assumes the op mode mutex is locked in dump_start since + * iwl_fw_dbg_collect_sync can't run in parallel + */ if (fwrt->ops && fwrt->ops->dump_start && fwrt->ops->dump_start(fwrt->ops_ctx)) return; - iwl_fw_dbg_collect_sync(fwrt); + iwl_fw_dbg_collect_sync(fwrt, wks->idx); if (fwrt->ops && fwrt->ops->dump_end) fwrt->ops->dump_end(fwrt->ops_ctx); @@ -2349,6 +2432,38 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt) } IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data); +static void iwl_fw_dbg_info_apply(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_debug_info_tlv *dbg_info, + bool ext, enum iwl_fw_ini_apply_point pnt) +{ + u32 img_name_len = le32_to_cpu(dbg_info->img_name_len); + u32 dbg_cfg_name_len = le32_to_cpu(dbg_info->dbg_cfg_name_len); + const char err_str[] = + "WRT: ext=%d. Invalid %s name length %d, expected %d\n"; + + if (img_name_len != IWL_FW_INI_MAX_IMG_NAME_LEN) { + IWL_WARN(fwrt, err_str, ext, "image", img_name_len, + IWL_FW_INI_MAX_IMG_NAME_LEN); + return; + } + + if (dbg_cfg_name_len != IWL_FW_INI_MAX_DBG_CFG_NAME_LEN) { + IWL_WARN(fwrt, err_str, ext, "debug cfg", dbg_cfg_name_len, + IWL_FW_INI_MAX_DBG_CFG_NAME_LEN); + return; + } + + if (ext) { + memcpy(fwrt->dump.external_dbg_cfg_name, dbg_info->dbg_cfg_name, + sizeof(fwrt->dump.external_dbg_cfg_name)); + } else { + memcpy(fwrt->dump.img_name, dbg_info->img_name, + sizeof(fwrt->dump.img_name)); + memcpy(fwrt->dump.internal_dbg_cfg_name, dbg_info->dbg_cfg_name, + sizeof(fwrt->dump.internal_dbg_cfg_name)); + } +} + static void iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt, u32 size) { @@ -2356,7 +2471,8 @@ iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt, u32 size) void *virtual_addr = NULL; dma_addr_t phys_addr; - if (WARN_ON_ONCE(trans->num_blocks == ARRAY_SIZE(trans->fw_mon))) + if (WARN_ON_ONCE(trans->dbg.num_blocks == + ARRAY_SIZE(trans->dbg.fw_mon))) return; virtual_addr = @@ -2370,12 +2486,12 @@ iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt, u32 size) IWL_DEBUG_FW(trans, "Allocated DRAM buffer[%d], size=0x%x\n", - trans->num_blocks, size); + trans->dbg.num_blocks, size); - trans->fw_mon[trans->num_blocks].block = virtual_addr; - trans->fw_mon[trans->num_blocks].physical = phys_addr; - trans->fw_mon[trans->num_blocks].size = size; - trans->num_blocks++; + trans->dbg.fw_mon[trans->dbg.num_blocks].block = virtual_addr; + trans->dbg.fw_mon[trans->dbg.num_blocks].physical = phys_addr; + trans->dbg.fw_mon[trans->dbg.num_blocks].size = size; + trans->dbg.num_blocks++; } static void iwl_fw_dbg_buffer_apply(struct iwl_fw_runtime *fwrt, @@ -2393,20 +2509,26 @@ static void iwl_fw_dbg_buffer_apply(struct iwl_fw_runtime *fwrt, .data[0] = &ldbg_cmd, .len[0] = sizeof(ldbg_cmd), }; - int block_idx = trans->num_blocks; + int block_idx = trans->dbg.num_blocks; u32 buf_location = le32_to_cpu(alloc->tlv.buffer_location); + if (fwrt->trans->dbg.ini_dest == IWL_FW_INI_LOCATION_INVALID) + fwrt->trans->dbg.ini_dest = buf_location; + + if (buf_location != fwrt->trans->dbg.ini_dest) { + WARN(fwrt, + "WRT: attempt to override buffer location on apply point %d\n", + pnt); + + return; + } + if (buf_location == IWL_FW_INI_LOCATION_SRAM_PATH) { - if (!WARN(pnt != IWL_FW_INI_APPLY_EARLY, - "WRT: Invalid apply point %d for SMEM buffer allocation, aborting\n", - pnt)) { - IWL_DEBUG_FW(trans, - "WRT: applying SMEM buffer destination\n"); - - /* set sram monitor by enabling bit 7 */ - iwl_set_bit(fwrt->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_MONITOR_SRAM); - } + IWL_DEBUG_FW(trans, "WRT: applying SMEM buffer destination\n"); + /* set sram monitor by enabling bit 7 */ + iwl_set_bit(fwrt->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_MONITOR_SRAM); + return; } @@ -2416,13 +2538,13 @@ static void iwl_fw_dbg_buffer_apply(struct iwl_fw_runtime *fwrt, if (!alloc->is_alloc) { iwl_fw_dbg_buffer_allocation(fwrt, le32_to_cpu(alloc->tlv.size)); - if (block_idx == trans->num_blocks) + if (block_idx == trans->dbg.num_blocks) return; alloc->is_alloc = 1; } /* First block is assigned via registers / context info */ - if (trans->num_blocks == 1) + if (trans->dbg.num_blocks == 1) return; IWL_DEBUG_FW(trans, @@ -2430,7 +2552,7 @@ static void iwl_fw_dbg_buffer_apply(struct iwl_fw_runtime *fwrt, cmd->num_frags = cpu_to_le32(1); cmd->fragments[0].address = - cpu_to_le64(trans->fw_mon[block_idx].physical); + cpu_to_le64(trans->dbg.fw_mon[block_idx].physical); cmd->fragments[0].size = alloc->tlv.size; cmd->allocation_id = alloc->tlv.allocation_id; cmd->buffer_location = alloc->tlv.buffer_location; @@ -2653,20 +2775,30 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, struct iwl_ucode_tlv *tlv = iter; void *ini_tlv = (void *)tlv->data; u32 type = le32_to_cpu(tlv->type); + const char invalid_ap_str[] = + "WRT: ext=%d. Invalid apply point %d for %s\n"; switch (type) { + case IWL_UCODE_TLV_TYPE_DEBUG_INFO: + iwl_fw_dbg_info_apply(fwrt, ini_tlv, ext, pnt); + break; case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION: { struct iwl_fw_ini_allocation_data *buf_alloc = ini_tlv; + if (pnt != IWL_FW_INI_APPLY_EARLY) { + IWL_ERR(fwrt, invalid_ap_str, ext, pnt, + "buffer allocation"); + goto next; + } + iwl_fw_dbg_buffer_apply(fwrt, ini_tlv, pnt); iter += sizeof(buf_alloc->is_alloc); break; } case IWL_UCODE_TLV_TYPE_HCMD: if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) { - IWL_ERR(fwrt, - "WRT: ext=%d. Invalid apply point %d for host command\n", - ext, pnt); + IWL_ERR(fwrt, invalid_ap_str, ext, pnt, + "host command"); goto next; } iwl_fw_dbg_send_hcmd(fwrt, tlv, ext); @@ -2690,34 +2822,51 @@ next: } } +static void iwl_fw_dbg_ini_reset_cfg(struct iwl_fw_runtime *fwrt) +{ + int i; + + for (i = 0; i < IWL_FW_INI_MAX_REGION_ID; i++) + fwrt->dump.active_regs[i] = NULL; + + /* disable the triggers, used in recovery flow */ + for (i = 0; i < IWL_FW_TRIGGER_ID_NUM; i++) + fwrt->dump.active_trigs[i].active = false; + + memset(fwrt->dump.img_name, 0, + sizeof(fwrt->dump.img_name)); + memset(fwrt->dump.internal_dbg_cfg_name, 0, + sizeof(fwrt->dump.internal_dbg_cfg_name)); + memset(fwrt->dump.external_dbg_cfg_name, 0, + sizeof(fwrt->dump.external_dbg_cfg_name)); + + fwrt->trans->dbg.ini_dest = IWL_FW_INI_LOCATION_INVALID; +} + void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, enum iwl_fw_ini_apply_point apply_point) { - void *data = &fwrt->trans->apply_points[apply_point]; - int i; + void *data = &fwrt->trans->dbg.apply_points[apply_point]; IWL_DEBUG_FW(fwrt, "WRT: enabling apply point %d\n", apply_point); - if (apply_point == IWL_FW_INI_APPLY_EARLY) { - for (i = 0; i < IWL_FW_INI_MAX_REGION_ID; i++) - fwrt->dump.active_regs[i] = NULL; - - /* disable the triggers, used in recovery flow */ - for (i = 0; i < IWL_FW_TRIGGER_ID_NUM; i++) - fwrt->dump.active_trigs[i].active = false; - } + if (apply_point == IWL_FW_INI_APPLY_EARLY) + iwl_fw_dbg_ini_reset_cfg(fwrt); _iwl_fw_dbg_apply_point(fwrt, data, apply_point, false); - data = &fwrt->trans->apply_points_ext[apply_point]; + data = &fwrt->trans->dbg.apply_points_ext[apply_point]; _iwl_fw_dbg_apply_point(fwrt, data, apply_point, true); } IWL_EXPORT_SYMBOL(iwl_fw_dbg_apply_point); void iwl_fwrt_stop_device(struct iwl_fw_runtime *fwrt) { + int i; + del_timer(&fwrt->dump.periodic_trig); - iwl_fw_dbg_collect_sync(fwrt); + for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) + iwl_fw_dbg_collect_sync(fwrt, i); iwl_trans_stop_device(fwrt->trans); } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index fd0ad220e961..a8459ac71b2c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -73,6 +73,7 @@ #include "error-dump.h" #include "api/commands.h" #include "api/dbg-tlv.h" +#include "api/alive.h" /** * struct iwl_fw_dump_desc - describes the dump @@ -201,7 +202,7 @@ _iwl_fw_dbg_trigger_on(struct iwl_fw_runtime *fwrt, { struct iwl_fw_dbg_trigger_tlv *trig; - if (fwrt->trans->ini_valid) + if (fwrt->trans->dbg.ini_valid) return NULL; if (!iwl_fw_dbg_trigger_enabled(fwrt->fw, id)) @@ -228,7 +229,7 @@ iwl_fw_ini_trigger_on(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_trigger *trig; u32 usec; - if (!fwrt->trans->ini_valid || id == IWL_FW_TRIGGER_ID_INVALID || + if (!fwrt->trans->dbg.ini_valid || id == IWL_FW_TRIGGER_ID_INVALID || id >= IWL_FW_TRIGGER_ID_NUM || !fwrt->dump.active_trigs[id].active) return false; @@ -262,23 +263,6 @@ _iwl_fw_dbg_trigger_simple_stop(struct iwl_fw_runtime *fwrt, iwl_fw_dbg_get_trigger((fwrt)->fw,\ (trig))) -static inline int -iwl_fw_dbg_start_stop_hcmd(struct iwl_fw_runtime *fwrt, bool start) -{ - struct iwl_ldbg_config_cmd cmd = { - .type = start ? cpu_to_le32(START_DEBUG_RECORDING) : - cpu_to_le32(STOP_DEBUG_RECORDING), - }; - struct iwl_host_cmd hcmd = { - .id = LDBG_CONFIG_CMD, - .flags = CMD_ASYNC, - .data[0] = &cmd, - .len[0] = sizeof(cmd), - }; - - return iwl_trans_send_cmd(fwrt->trans, &hcmd); -} - static inline void _iwl_fw_dbg_stop_recording(struct iwl_trans *trans, struct iwl_fw_dbg_params *params) @@ -294,21 +278,35 @@ _iwl_fw_dbg_stop_recording(struct iwl_trans *trans, } iwl_write_umac_prph(trans, DBGC_IN_SAMPLE, 0); - udelay(100); + /* wait for the DBGC to finish writing the internal buffer to DRAM to + * avoid halting the HW while writing + */ + usleep_range(700, 1000); iwl_write_umac_prph(trans, DBGC_OUT_CTRL, 0); #ifdef CONFIG_IWLWIFI_DEBUGFS - trans->dbg_rec_on = false; + trans->dbg.rec_on = false; #endif } static inline void -iwl_fw_dbg_stop_recording(struct iwl_fw_runtime *fwrt, +iwl_fw_dbg_stop_recording(struct iwl_trans *trans, struct iwl_fw_dbg_params *params) { - if (fwrt->trans->cfg->device_family < IWL_DEVICE_FAMILY_22560) - _iwl_fw_dbg_stop_recording(fwrt->trans, params); - else - iwl_fw_dbg_start_stop_hcmd(fwrt, false); + /* if the FW crashed or not debug monitor cfg was given, there is + * no point in stopping + */ + if (test_bit(STATUS_FW_ERROR, &trans->status) || + (!trans->dbg.dest_tlv && + trans->dbg.ini_dest == IWL_FW_INI_LOCATION_INVALID)) + return; + + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) { + IWL_ERR(trans, + "WRT: unsupported device family %d for debug stop recording\n", + trans->cfg->device_family); + return; + } + _iwl_fw_dbg_stop_recording(trans, params); } static inline void @@ -324,7 +322,6 @@ _iwl_fw_dbg_restart_recording(struct iwl_trans *trans, iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); } else { iwl_write_umac_prph(trans, DBGC_IN_SAMPLE, params->in_sample); - udelay(100); iwl_write_umac_prph(trans, DBGC_OUT_CTRL, params->out_ctrl); } } @@ -332,8 +329,10 @@ _iwl_fw_dbg_restart_recording(struct iwl_trans *trans, #ifdef CONFIG_IWLWIFI_DEBUGFS static inline void iwl_fw_set_dbg_rec_on(struct iwl_fw_runtime *fwrt) { - if (fwrt->fw->dbg.dest_tlv && fwrt->cur_fw_img == IWL_UCODE_REGULAR) - fwrt->trans->dbg_rec_on = true; + if (fwrt->cur_fw_img == IWL_UCODE_REGULAR && + (fwrt->fw->dbg.dest_tlv || + fwrt->trans->dbg.ini_dest != IWL_FW_INI_LOCATION_INVALID)) + fwrt->trans->dbg.rec_on = true; } #endif @@ -341,10 +340,21 @@ static inline void iwl_fw_dbg_restart_recording(struct iwl_fw_runtime *fwrt, struct iwl_fw_dbg_params *params) { - if (fwrt->trans->cfg->device_family < IWL_DEVICE_FAMILY_22560) - _iwl_fw_dbg_restart_recording(fwrt->trans, params); - else - iwl_fw_dbg_start_stop_hcmd(fwrt, true); + /* if the FW crashed or not debug monitor cfg was given, there is + * no point in restarting + */ + if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status) || + (!fwrt->trans->dbg.dest_tlv && + fwrt->trans->dbg.ini_dest == IWL_FW_INI_LOCATION_INVALID)) + return; + + if (fwrt->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) { + IWL_ERR(fwrt, + "WRT: unsupported device family %d for debug restart recording\n", + fwrt->trans->cfg->device_family); + return; + } + _iwl_fw_dbg_restart_recording(fwrt->trans, params); #ifdef CONFIG_IWLWIFI_DEBUGFS iwl_fw_set_dbg_rec_on(fwrt); #endif @@ -359,7 +369,7 @@ void iwl_fw_error_dump_wk(struct work_struct *work); static inline bool iwl_fw_dbg_type_on(struct iwl_fw_runtime *fwrt, u32 type) { - return (fwrt->fw->dbg.dump_mask & BIT(type) || fwrt->trans->ini_valid); + return (fwrt->fw->dbg.dump_mask & BIT(type)); } static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt) @@ -383,16 +393,26 @@ static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt) void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt); -static inline void iwl_fw_flush_dump(struct iwl_fw_runtime *fwrt) +static inline void iwl_fw_flush_dumps(struct iwl_fw_runtime *fwrt) { + int i; + del_timer(&fwrt->dump.periodic_trig); - flush_delayed_work(&fwrt->dump.wk); + for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) { + flush_delayed_work(&fwrt->dump.wks[i].wk); + fwrt->dump.wks[i].ini_trig_id = IWL_FW_TRIGGER_ID_INVALID; + } } -static inline void iwl_fw_cancel_dump(struct iwl_fw_runtime *fwrt) +static inline void iwl_fw_cancel_dumps(struct iwl_fw_runtime *fwrt) { + int i; + del_timer(&fwrt->dump.periodic_trig); - cancel_delayed_work_sync(&fwrt->dump.wk); + for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) { + cancel_delayed_work_sync(&fwrt->dump.wks[i].wk); + fwrt->dump.wks[i].ini_trig_id = IWL_FW_TRIGGER_ID_INVALID; + } } #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -431,7 +451,6 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {} #endif /* CONFIG_IWLWIFI_DEBUGFS */ -void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, enum iwl_fw_ini_apply_point apply_point); @@ -440,31 +459,28 @@ void iwl_fwrt_stop_device(struct iwl_fw_runtime *fwrt); static inline void iwl_fw_lmac1_set_alive_err_table(struct iwl_trans *trans, u32 lmac_error_event_table) { - if (!(trans->error_event_table_tlv_status & + if (!(trans->dbg.error_event_table_tlv_status & IWL_ERROR_EVENT_TABLE_LMAC1) || - WARN_ON(trans->lmac_error_event_table[0] != + WARN_ON(trans->dbg.lmac_error_event_table[0] != lmac_error_event_table)) - trans->lmac_error_event_table[0] = lmac_error_event_table; + trans->dbg.lmac_error_event_table[0] = lmac_error_event_table; } static inline void iwl_fw_umac_set_alive_err_table(struct iwl_trans *trans, u32 umac_error_event_table) { - if (!(trans->error_event_table_tlv_status & + if (!(trans->dbg.error_event_table_tlv_status & IWL_ERROR_EVENT_TABLE_UMAC) || - WARN_ON(trans->umac_error_event_table != + WARN_ON(trans->dbg.umac_error_event_table != umac_error_event_table)) - trans->umac_error_event_table = umac_error_event_table; + trans->dbg.umac_error_event_table = umac_error_event_table; } -/* This bit is used to differentiate the legacy dump from the ini dump */ -#define INI_DUMP_BIT BIT(31) - static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt) { - if (fwrt->trans->ini_valid && fwrt->trans->hw_error) { + if (fwrt->trans->dbg.ini_valid && fwrt->trans->dbg.hw_error) { _iwl_fw_dbg_ini_collect(fwrt, IWL_FW_TRIGGER_ID_FW_HW_ERROR); - fwrt->trans->hw_error = false; + fwrt->trans->dbg.hw_error = false; } else { iwl_fw_dbg_collect_desc(fwrt, &iwl_dump_desc_assert, false, 0); } @@ -473,4 +489,21 @@ static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt) void iwl_fw_dbg_periodic_trig_handler(struct timer_list *t); void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt); + +static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, + struct iwl_lmac_alive *lmac, + struct iwl_umac_alive *umac) +{ + if (lmac) { + fwrt->dump.fw_ver.type = lmac->ver_type; + fwrt->dump.fw_ver.subtype = lmac->ver_subtype; + fwrt->dump.fw_ver.lmac_major = le32_to_cpu(lmac->ucode_major); + fwrt->dump.fw_ver.lmac_minor = le32_to_cpu(lmac->ucode_minor); + } + + if (umac) { + fwrt->dump.fw_ver.umac_major = le32_to_cpu(umac->umac_major); + fwrt->dump.fw_ver.umac_minor = le32_to_cpu(umac->umac_minor); + } +} #endif /* __iwl_fw_dbg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index 0feff4c33e39..00a45ea85b69 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -67,6 +67,7 @@ #include <linux/types.h> #define IWL_FW_ERROR_DUMP_BARKER 0x14789632 +#define IWL_FW_INI_ERROR_DUMP_BARKER 0x14789633 /** * enum iwl_fw_error_dump_type - types of data in the dump file @@ -278,19 +279,42 @@ struct iwl_fw_error_dump_mem { u8 data[]; }; -#define IWL_INI_DUMP_MEM_VER 1 -#define IWL_INI_DUMP_MONITOR_VER 1 -#define IWL_INI_DUMP_FIFO_VER 1 +/* Dump version, used by the dump parser to differentiate between + * different dump formats + */ +#define IWL_INI_DUMP_VER 1 + +/* Use bit 31 as dump info type to avoid colliding with region types */ +#define IWL_INI_DUMP_INFO_TYPE BIT(31) + +/** + * struct iwl_fw_ini_fifo_hdr - fifo range header + * @fifo_num: the fifo number. In case of umac rx fifo, set BIT(31) to + * distinguish between lmac and umac rx fifos + * @num_of_registers: num of registers to dump, dword size each + */ +struct iwl_fw_ini_fifo_hdr { + __le32 fifo_num; + __le32 num_of_registers; +} __packed; /** * struct iwl_fw_ini_error_dump_range - range of memory * @range_data_size: the size of this range, in bytes - * @start_addr: the start address of this range + * @internal_base_addr - base address of internal memory range + * @dram_base_addr - base address of dram monitor range + * @page_num - page number of memory range + * @fifo_hdr - fifo header of memory range * @data: the actual memory */ struct iwl_fw_ini_error_dump_range { __le32 range_data_size; - __le64 start_addr; + union { + __le32 internal_base_addr; + __le64 dram_base_addr; + __le32 page_num; + struct iwl_fw_ini_fifo_hdr fifo_hdr; + }; __le32 data[]; } __packed; @@ -333,30 +357,63 @@ struct iwl_fw_ini_error_dump_register { __le32 data; } __packed; -/** - * struct iwl_fw_ini_fifo_error_dump_range - ini fifo range dump - * @fifo_num: the fifo num. In case of rxf and umac rxf, set BIT(31) to - * distinguish between lmac and umac - * @num_of_registers: num of registers to dump, dword size each - * @range_data_size: the size of the data - * @data: consist of - * num_of_registers * (register address + register value) + fifo data +/* struct iwl_fw_ini_dump_info - ini dump information + * @version: dump version + * @trigger_id: trigger id that caused the dump collection + * @trigger_reason: not supported yet + * @is_external_cfg: 1 if an external debug configuration was loaded + * and 0 otherwise + * @ver_type: FW version type + * @ver_subtype: FW version subype + * @hw_step: HW step + * @hw_type: HW type + * @rf_id_flavor: HW RF id flavor + * @rf_id_dash: HW RF id dash + * @rf_id_step: HW RF id step + * @rf_id_type: HW RF id type + * @lmac_major: lmac major version + * @lmac_minor: lmac minor version + * @umac_major: umac major version + * @umac_minor: umac minor version + * @build_tag_len: length of the build tag + * @build_tag: build tag string + * @img_name_len: length of the FW image name + * @img_name: FW image name + * @internal_dbg_cfg_name_len: length of the internal debug configuration name + * @internal_dbg_cfg_name: internal debug configuration name + * @external_dbg_cfg_name_len: length of the external debug configuration name + * @external_dbg_cfg_name: external debug configuration name + * @regions_num: number of region ids + * @region_ids: region ids the trigger configured to collect */ -struct iwl_fw_ini_fifo_error_dump_range { - __le32 fifo_num; - __le32 num_of_registers; - __le32 range_data_size; - __le32 data[]; -} __packed; +struct iwl_fw_ini_dump_info { + __le32 version; + __le32 trigger_id; + __le32 trigger_reason; + __le32 is_external_cfg; + __le32 ver_type; + __le32 ver_subtype; + __le32 hw_step; + __le32 hw_type; + __le32 rf_id_flavor; + __le32 rf_id_dash; + __le32 rf_id_step; + __le32 rf_id_type; + __le32 lmac_major; + __le32 lmac_minor; + __le32 umac_major; + __le32 umac_minor; + __le32 build_tag_len; + u8 build_tag[FW_VER_HUMAN_READABLE_SZ]; + __le32 img_name_len; + u8 img_name[IWL_FW_INI_MAX_IMG_NAME_LEN]; + __le32 internal_dbg_cfg_name_len; + u8 internal_dbg_cfg_name[IWL_FW_INI_MAX_DBG_CFG_NAME_LEN]; + __le32 external_dbg_cfg_name_len; + u8 external_dbg_cfg_name[IWL_FW_INI_MAX_DBG_CFG_NAME_LEN]; + __le32 regions_num; + __le32 region_ids[]; -/** - * struct iwl_fw_ini_fifo_error_dump - ini fifo region dump - * @header: the header of this region - * @ranges: the memory ranges of this region - */ -struct iwl_fw_ini_fifo_error_dump { - struct iwl_fw_ini_error_dump_header header; - struct iwl_fw_ini_fifo_error_dump_range ranges[]; } __packed; /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index de9243d30135..0c38e7392b61 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -151,12 +151,13 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_FW_RECOVERY_INFO = 57, IWL_UCODE_TLV_FW_FSEQ_VERSION = 60, - IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_INI_TLV_GROUP + 0x1, - IWL_UCODE_TLV_DEBUG_BASE = IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION, - IWL_UCODE_TLV_TYPE_HCMD = IWL_UCODE_INI_TLV_GROUP + 0x2, - IWL_UCODE_TLV_TYPE_REGIONS = IWL_UCODE_INI_TLV_GROUP + 0x3, - IWL_UCODE_TLV_TYPE_TRIGGERS = IWL_UCODE_INI_TLV_GROUP + 0x4, - IWL_UCODE_TLV_TYPE_DEBUG_FLOW = IWL_UCODE_INI_TLV_GROUP + 0x5, + IWL_UCODE_TLV_DEBUG_BASE = IWL_UCODE_INI_TLV_GROUP, + IWL_UCODE_TLV_TYPE_DEBUG_INFO = IWL_UCODE_TLV_DEBUG_BASE + 0, + IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_TLV_DEBUG_BASE + 1, + IWL_UCODE_TLV_TYPE_HCMD = IWL_UCODE_TLV_DEBUG_BASE + 2, + IWL_UCODE_TLV_TYPE_REGIONS = IWL_UCODE_TLV_DEBUG_BASE + 3, + IWL_UCODE_TLV_TYPE_TRIGGERS = IWL_UCODE_TLV_DEBUG_BASE + 4, + IWL_UCODE_TLV_TYPE_DEBUG_FLOW = IWL_UCODE_TLV_DEBUG_BASE + 5, IWL_UCODE_TLV_DEBUG_MAX = IWL_UCODE_TLV_TYPE_DEBUG_FLOW, /* TLVs 0x1000-0x2000 are for internal driver usage */ @@ -286,6 +287,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * SCAN_OFFLOAD_PROFILES_QUERY_RSP_S. * @IWL_UCODE_TLV_API_MBSSID_HE: This ucode supports v2 of * STA_CONTEXT_DOT11AX_API_S + * @IWL_UCODE_TLV_CAPA_SAR_TABLE_VER: This ucode supports different sar + * version tables. * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -318,6 +321,8 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_MBSSID_HE = (__force iwl_ucode_tlv_api_t)52, IWL_UCODE_TLV_API_WOWLAN_TCP_SYN_WAKE = (__force iwl_ucode_tlv_api_t)53, IWL_UCODE_TLV_API_FTM_RTT_ACCURACY = (__force iwl_ucode_tlv_api_t)54, + IWL_UCODE_TLV_API_SAR_TABLE_VER = (__force iwl_ucode_tlv_api_t)55, + IWL_UCODE_TLV_API_ADWELL_HB_DEF_N_AP = (__force iwl_ucode_tlv_api_t)57, NUM_IWL_UCODE_TLV_API #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c index 4435c0ce3013..c16d6e126e3c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/init.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c @@ -67,6 +67,8 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, const struct iwl_fw_runtime_ops *ops, void *ops_ctx, struct dentry *dbgfs_dir) { + int i; + memset(fwrt, 0, sizeof(*fwrt)); fwrt->trans = trans; fwrt->fw = fw; @@ -74,7 +76,10 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, fwrt->dump.conf = FW_DBG_INVALID; fwrt->ops = ops; fwrt->ops_ctx = ops_ctx; - INIT_DELAYED_WORK(&fwrt->dump.wk, iwl_fw_error_dump_wk); + for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) { + fwrt->dump.wks[i].idx = i; + INIT_DELAYED_WORK(&fwrt->dump.wks[i].wk, iwl_fw_error_dump_wk); + } iwl_fwrt_dbgfs_register(fwrt, dbgfs_dir); timer_setup(&fwrt->dump.periodic_trig, iwl_fw_dbg_periodic_trig_handler, 0); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index a6402a0b3854..406ef73992c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -89,9 +89,7 @@ struct iwl_fwrt_shared_mem_cfg { u32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; }; -enum iwl_fw_runtime_status { - IWL_FWRT_STATUS_DUMPING = 0, -}; +#define IWL_FW_RUNTIME_DUMP_WK_NUM 5 /** * struct iwl_fw_runtime - runtime data for firmware @@ -100,7 +98,6 @@ enum iwl_fw_runtime_status { * @dev: device pointer * @ops: user ops * @ops_ctx: user ops context - * @status: status flags * @fw_paging_db: paging database * @num_of_paging_blk: number of paging blocks * @num_of_pages_in_last_blk: number of pages in the last block @@ -117,8 +114,6 @@ struct iwl_fw_runtime { const struct iwl_fw_runtime_ops *ops; void *ops_ctx; - unsigned long status; - /* Paging */ struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS]; u16 num_of_paging_blk; @@ -133,7 +128,12 @@ struct iwl_fw_runtime { struct { const struct iwl_fw_dump_desc *desc; bool monitor_only; - struct delayed_work wk; + struct { + u8 idx; + enum iwl_fw_ini_trigger_id ini_trig_id; + struct delayed_work wk; + } wks[IWL_FW_RUNTIME_DUMP_WK_NUM]; + unsigned long active_wks; u8 conf; @@ -145,8 +145,20 @@ struct iwl_fw_runtime { u32 lmac_err_id[MAX_NUM_LMAC]; u32 umac_err_id; void *fifo_iter; - enum iwl_fw_ini_trigger_id ini_trig_id; struct timer_list periodic_trig; + + u8 img_name[IWL_FW_INI_MAX_IMG_NAME_LEN]; + u8 internal_dbg_cfg_name[IWL_FW_INI_MAX_DBG_CFG_NAME_LEN]; + u8 external_dbg_cfg_name[IWL_FW_INI_MAX_DBG_CFG_NAME_LEN]; + + struct { + u8 type; + u8 subtype; + u32 lmac_major; + u32 lmac_minor; + u32 umac_major; + u32 umac_minor; + } fw_ver; } dump; #ifdef CONFIG_IWLWIFI_DEBUGFS struct { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c index ff85d69c2a8c..557ee47bffd8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c @@ -8,7 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,7 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -134,6 +134,7 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt) .len = { 0, }, }; struct iwl_rx_packet *pkt; + int ret; if (fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) @@ -141,8 +142,13 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt) else cmd.id = SHARED_MEM_CFG; - if (WARN_ON(iwl_trans_send_cmd(fwrt->trans, &cmd))) + ret = iwl_trans_send_cmd(fwrt->trans, &cmd); + + if (ret) { + WARN(ret != -ERFKILL, + "Could not send the SMEM command: %d\n", ret); return; + } pkt = cmd.resp_pkt; if (fwrt->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index ba66f7fba064..fcaec410b3be 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -81,9 +81,9 @@ void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv, return; if (ext) - data = &trans->apply_points_ext[apply_point]; + data = &trans->dbg.apply_points_ext[apply_point]; else - data = &trans->apply_points[apply_point]; + data = &trans->dbg.apply_points[apply_point]; /* add room for is_alloc field in &iwl_fw_ini_allocation_data struct */ if (le32_to_cpu(tlv->type) == IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION) { @@ -172,14 +172,14 @@ void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data, } if (ext) { - trans->apply_points_ext[i].data = mem; - trans->apply_points_ext[i].size = size[i]; + trans->dbg.apply_points_ext[i].data = mem; + trans->dbg.apply_points_ext[i].size = size[i]; } else { - trans->apply_points[i].data = mem; - trans->apply_points[i].size = size[i]; + trans->dbg.apply_points[i].data = mem; + trans->dbg.apply_points[i].size = size[i]; } - trans->ini_valid = true; + trans->dbg.ini_valid = true; } } @@ -187,14 +187,14 @@ void iwl_fw_dbg_free(struct iwl_trans *trans) { int i; - for (i = 0; i < ARRAY_SIZE(trans->apply_points); i++) { - kfree(trans->apply_points[i].data); - trans->apply_points[i].size = 0; - trans->apply_points[i].offset = 0; + for (i = 0; i < ARRAY_SIZE(trans->dbg.apply_points); i++) { + kfree(trans->dbg.apply_points[i].data); + trans->dbg.apply_points[i].size = 0; + trans->dbg.apply_points[i].offset = 0; - kfree(trans->apply_points_ext[i].data); - trans->apply_points_ext[i].size = 0; - trans->apply_points_ext[i].offset = 0; + kfree(trans->dbg.apply_points_ext[i].data); + trans->dbg.apply_points_ext[i].size = 0; + trans->dbg.apply_points_ext[i].offset = 0; } } @@ -221,6 +221,7 @@ static int iwl_parse_fw_dbg_tlv(struct iwl_trans *trans, const u8 *data, data += sizeof(*tlv) + ALIGN(tlv_len, 4); switch (tlv_type) { + case IWL_UCODE_TLV_TYPE_DEBUG_INFO: case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION: case IWL_UCODE_TLV_TYPE_HCMD: case IWL_UCODE_TLV_TYPE_REGIONS: @@ -242,7 +243,7 @@ void iwl_load_fw_dbg_tlv(struct device *dev, struct iwl_trans *trans) const struct firmware *fw; int res; - if (trans->external_ini_loaded || !iwlwifi_mod_params.enable_ini) + if (trans->dbg.external_ini_loaded || !iwlwifi_mod_params.enable_ini) return; res = request_firmware(&fw, "iwl-dbg-tlv.ini", dev); @@ -252,6 +253,6 @@ void iwl_load_fw_dbg_tlv(struct device *dev, struct iwl_trans *trans) iwl_alloc_dbg_tlv(trans, fw->size, fw->data, true); iwl_parse_fw_dbg_tlv(trans, fw->data, fw->size); - trans->external_ini_loaded = true; + trans->dbg.external_ini_loaded = true; release_firmware(fw); } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index fba242284507..57d09049e615 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1105,6 +1105,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, le32_to_cpu(recov_info->buf_size); } break; + case IWL_UCODE_TLV_FW_FSEQ_VERSION: { + struct { + u8 version[32]; + u8 sha1[20]; + } *fseq_ver = (void *)tlv_data; + + if (tlv_len != sizeof(*fseq_ver)) + goto invalid_tlv_len; + IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %s\n", + fseq_ver->version); + } + break; case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: { struct iwl_umac_debug_addrs *dbg_ptrs = (void *)tlv_data; @@ -1114,10 +1126,10 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (drv->trans->cfg->device_family < IWL_DEVICE_FAMILY_22000) break; - drv->trans->umac_error_event_table = + drv->trans->dbg.umac_error_event_table = le32_to_cpu(dbg_ptrs->error_info_addr) & ~FW_ADDR_CACHE_CONTROL; - drv->trans->error_event_table_tlv_status |= + drv->trans->dbg.error_event_table_tlv_status |= IWL_ERROR_EVENT_TABLE_UMAC; break; } @@ -1130,13 +1142,14 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (drv->trans->cfg->device_family < IWL_DEVICE_FAMILY_22000) break; - drv->trans->lmac_error_event_table[0] = + drv->trans->dbg.lmac_error_event_table[0] = le32_to_cpu(dbg_ptrs->error_event_table_ptr) & ~FW_ADDR_CACHE_CONTROL; - drv->trans->error_event_table_tlv_status |= + drv->trans->dbg.error_event_table_tlv_status |= IWL_ERROR_EVENT_TABLE_LMAC1; break; } + case IWL_UCODE_TLV_TYPE_DEBUG_INFO: case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION: case IWL_UCODE_TLV_TYPE_HCMD: case IWL_UCODE_TLV_TYPE_REGIONS: @@ -1744,7 +1757,7 @@ IWL_EXPORT_SYMBOL(iwl_opmode_deregister); static int __init iwl_drv_init(void) { - int i; + int i, err; mutex_init(&iwlwifi_opmode_table_mtx); @@ -1759,7 +1772,17 @@ static int __init iwl_drv_init(void) iwl_dbgfs_root = debugfs_create_dir(DRV_NAME, NULL); #endif - return iwl_pci_register_driver(); + err = iwl_pci_register_driver(); + if (err) + goto cleanup_debugfs; + + return 0; + +cleanup_debugfs: +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove_recursive(iwl_dbgfs_root); +#endif + return err; } module_init(iwl_drv_init); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 1e4c9ef548cc..0f8aeb111b0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -722,6 +722,50 @@ struct iwl_self_init_dram { }; /** + * struct iwl_trans_debug - transport debug related data + * + * @n_dest_reg: num of reg_ops in %dbg_dest_tlv + * @rec_on: true iff there is a fw debug recording currently active + * @dest_tlv: points to the destination TLV for debug + * @conf_tlv: array of pointers to configuration TLVs for debug + * @trigger_tlv: array of pointers to triggers TLVs for debug + * @lmac_error_event_table: addrs of lmacs error tables + * @umac_error_event_table: addr of umac error table + * @error_event_table_tlv_status: bitmap that indicates what error table + * pointers was recevied via TLV. uses enum &iwl_error_event_table_status + * @external_ini_loaded: indicates if an external ini cfg was given + * @ini_valid: indicates if debug ini mode is on + * @num_blocks: number of blocks in fw_mon + * @fw_mon: address of the buffers for firmware monitor + * @hw_error: equals true if hw error interrupt was received from the FW + * @ini_dest: debug monitor destination uses &enum iwl_fw_ini_buffer_location + */ +struct iwl_trans_debug { + u8 n_dest_reg; + bool rec_on; + + const struct iwl_fw_dbg_dest_tlv_v1 *dest_tlv; + const struct iwl_fw_dbg_conf_tlv *conf_tlv[FW_DBG_CONF_MAX]; + struct iwl_fw_dbg_trigger_tlv * const *trigger_tlv; + + u32 lmac_error_event_table[2]; + u32 umac_error_event_table; + unsigned int error_event_table_tlv_status; + + bool external_ini_loaded; + bool ini_valid; + + struct iwl_apply_point_data apply_points[IWL_FW_INI_APPLY_NUM]; + struct iwl_apply_point_data apply_points_ext[IWL_FW_INI_APPLY_NUM]; + + int num_blocks; + struct iwl_dram_data fw_mon[IWL_FW_INI_APPLY_NUM]; + + bool hw_error; + enum iwl_fw_ini_buffer_location ini_dest; +}; + +/** * struct iwl_trans - transport common data * * @ops - pointer to iwl_trans_ops @@ -750,24 +794,12 @@ struct iwl_self_init_dram { * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the * start of the 802.11 header in the @rx_mpdu_cmd * @dflt_pwr_limit: default power limit fetched from the platform (ACPI) - * @dbg_dest_tlv: points to the destination TLV for debug - * @dbg_conf_tlv: array of pointers to configuration TLVs for debug - * @dbg_trigger_tlv: array of pointers to triggers TLVs for debug - * @dbg_n_dest_reg: num of reg_ops in %dbg_dest_tlv - * @num_blocks: number of blocks in fw_mon - * @fw_mon: address of the buffers for firmware monitor * @system_pm_mode: the system-wide power management mode in use. * This mode is set dynamically, depending on the WoWLAN values * configured from the userspace at runtime. * @runtime_pm_mode: the runtime power management mode in use. This * mode is set during the initialization phase and is not * supposed to change during runtime. - * @dbg_rec_on: true iff there is a fw debug recording currently active - * @lmac_error_event_table: addrs of lmacs error tables - * @umac_error_event_table: addr of umac error table - * @error_event_table_tlv_status: bitmap that indicates what error table - * pointers was recevied via TLV. use enum &iwl_error_event_table_status - * @hw_error: equals true if hw error interrupt was received from the FW */ struct iwl_trans { const struct iwl_trans_ops *ops; @@ -808,29 +840,12 @@ struct iwl_trans { struct lockdep_map sync_cmd_lockdep_map; #endif - struct iwl_apply_point_data apply_points[IWL_FW_INI_APPLY_NUM]; - struct iwl_apply_point_data apply_points_ext[IWL_FW_INI_APPLY_NUM]; - - bool external_ini_loaded; - bool ini_valid; - - const struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv; - const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; - struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv; - u8 dbg_n_dest_reg; - int num_blocks; - struct iwl_dram_data fw_mon[IWL_FW_INI_APPLY_NUM]; + struct iwl_trans_debug dbg; struct iwl_self_init_dram init_dram; enum iwl_plat_pm_mode system_pm_mode; enum iwl_plat_pm_mode runtime_pm_mode; bool suspending; - bool dbg_rec_on; - - u32 lmac_error_event_table[2]; - u32 umac_error_event_table; - unsigned int error_event_table_tlv_status; - bool hw_error; /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index dff14f1ec55f..915b172da57a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -152,5 +152,6 @@ #define IWL_MVM_FTM_INITIATOR_ALGO IWL_TOF_ALGO_TYPE_MAX_LIKE #define IWL_MVM_FTM_INITIATOR_DYNACK true #define IWL_MVM_D3_DEBUG false +#define IWL_MVM_USE_TWT false #endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index e7e68fb2bd29..cec40855a641 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -398,8 +398,7 @@ static int iwl_mvm_send_patterns_v1(struct iwl_mvm *mvm, if (!wowlan->n_patterns) return 0; - cmd.len[0] = sizeof(*pattern_cmd) + - wowlan->n_patterns * sizeof(struct iwl_wowlan_pattern_v1); + cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns); pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); if (!pattern_cmd) @@ -1079,11 +1078,12 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, #endif /* - * TODO: this is needed because the firmware is not stopping - * the recording automatically before entering D3. This can - * be removed once the FW starts doing that. + * Prior to 9000 device family the driver needs to stop the dbg + * recording before entering D3. In later devices the FW stops the + * recording automatically. */ - _iwl_fw_dbg_stop_recording(mvm->fwrt.trans, NULL); + if (mvm->trans->cfg->device_family < IWL_DEVICE_FAMILY_9000) + iwl_fw_dbg_stop_recording(mvm->trans, NULL); /* must be last -- this switches firmware state */ ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd); @@ -1986,7 +1986,7 @@ static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, static int iwl_mvm_check_rt_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - u32 base = mvm->trans->lmac_error_event_table[0]; + u32 base = mvm->trans->dbg.lmac_error_event_table[0]; struct error_table_start { /* cf. struct iwl_error_event_table */ u32 valid; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 5b1bb76c5d28..0c188a82cfc1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -467,6 +467,46 @@ static ssize_t iwl_dbgfs_rs_data_read(struct file *file, char __user *user_buf, return ret; } +static ssize_t iwl_dbgfs_amsdu_len_write(struct ieee80211_sta *sta, + char *buf, size_t count, + loff_t *ppos) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + int i; + u16 amsdu_len; + + if (kstrtou16(buf, 0, &amsdu_len)) + return -EINVAL; + + if (amsdu_len) { + mvmsta->orig_amsdu_len = sta->max_amsdu_len; + sta->max_amsdu_len = amsdu_len; + for (i = 0; i < ARRAY_SIZE(sta->max_tid_amsdu_len); i++) + sta->max_tid_amsdu_len[i] = amsdu_len; + } else { + sta->max_amsdu_len = mvmsta->orig_amsdu_len; + mvmsta->orig_amsdu_len = 0; + } + return count; +} + +static ssize_t iwl_dbgfs_amsdu_len_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + char buf[32]; + int pos; + + pos = scnprintf(buf, sizeof(buf), "current %d ", sta->max_amsdu_len); + pos += scnprintf(buf + pos, sizeof(buf) - pos, "stored %d\n", + mvmsta->orig_amsdu_len); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1356,24 +1396,6 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, return count; } -static ssize_t iwl_dbgfs_max_amsdu_len_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - unsigned int max_amsdu_len; - int ret; - - ret = kstrtouint(buf, 0, &max_amsdu_len); - if (ret) - return ret; - - if (max_amsdu_len > IEEE80211_MAX_MPDU_LEN_VHT_11454) - return -EINVAL; - mvm->max_amsdu_len = max_amsdu_len; - - return count; -} - #define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__) #ifdef CONFIG_IWLWIFI_BCAST_FILTERING static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file, @@ -1873,7 +1895,6 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64); -MVM_DEBUGFS_WRITE_FILE_OPS(max_amsdu_len, 8); MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl, (IWL_RSS_INDIRECTION_TABLE_SIZE * 2)); MVM_DEBUGFS_WRITE_FILE_OPS(inject_packet, 512); @@ -1891,6 +1912,8 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256); MVM_DEBUGFS_READ_FILE_OPS(sar_geo_profile); #endif +MVM_DEBUGFS_READ_WRITE_STA_FILE_OPS(amsdu_len, 16); + MVM_DEBUGFS_READ_WRITE_FILE_OPS(he_sniffer_params, 32); static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf, @@ -2032,8 +2055,10 @@ void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - if (iwl_mvm_has_tlc_offload(mvm)) + if (iwl_mvm_has_tlc_offload(mvm)) { MVM_DEBUGFS_ADD_STA_FILE(rs_data, dir, 0400); + } + MVM_DEBUGFS_ADD_STA_FILE(amsdu_len, dir, 0600); } void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) @@ -2069,7 +2094,6 @@ void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, 0600); MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, 0600); MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, 0200); - MVM_DEBUGFS_ADD_FILE(max_amsdu_len, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(indirection_tbl, mvm->debugfs_dir, 0200); MVM_DEBUGFS_ADD_FILE(inject_packet, mvm->debugfs_dir, 0200); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 153717587aeb..1d608e9e9101 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -238,7 +238,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, iwl_fw_lmac1_set_alive_err_table(mvm->trans, lmac_error_event_table); if (lmac2) - mvm->trans->lmac_error_event_table[1] = + mvm->trans->dbg.lmac_error_event_table[1] = le32_to_cpu(lmac2->dbg_ptrs.error_event_table_ptr); umac_error_event_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr); @@ -276,6 +276,8 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, le32_to_cpu(umac->umac_major), le32_to_cpu(umac->umac_minor)); + iwl_fwrt_update_fw_versions(&mvm->fwrt, lmac1, umac); + return true; } @@ -419,6 +421,8 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) lockdep_assert_held(&mvm->mutex); + mvm->rfkill_safe_init_done = false; + iwl_init_notification_wait(&mvm->notif_wait, &init_wait, init_complete, @@ -537,8 +541,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) lockdep_assert_held(&mvm->mutex); - if (WARN_ON_ONCE(mvm->rfkill_safe_init_done)) - return 0; + mvm->rfkill_safe_init_done = false; iwl_init_notification_wait(&mvm->notif_wait, &calib_wait, @@ -681,15 +684,15 @@ static int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm) { union acpi_object *wifi_pkg, *table, *data; bool enabled; - int ret; + int ret, tbl_rev; data = iwl_acpi_get_object(mvm->dev, ACPI_WRDS_METHOD); if (IS_ERR(data)) return PTR_ERR(data); wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, - ACPI_WRDS_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg)) { + ACPI_WRDS_WIFI_DATA_SIZE, &tbl_rev); + if (IS_ERR(wifi_pkg) || tbl_rev != 0) { ret = PTR_ERR(wifi_pkg); goto out_free; } @@ -718,15 +721,15 @@ static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) { union acpi_object *wifi_pkg, *data; bool enabled; - int i, n_profiles, ret; + int i, n_profiles, ret, tbl_rev; data = iwl_acpi_get_object(mvm->dev, ACPI_EWRD_METHOD); if (IS_ERR(data)) return PTR_ERR(data); wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, - ACPI_EWRD_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg)) { + ACPI_EWRD_WIFI_DATA_SIZE, &tbl_rev); + if (IS_ERR(wifi_pkg) || tbl_rev != 0) { ret = PTR_ERR(wifi_pkg); goto out_free; } @@ -777,7 +780,7 @@ out_free: static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm) { union acpi_object *wifi_pkg, *data; - int i, j, ret; + int i, j, ret, tbl_rev; int idx = 1; data = iwl_acpi_get_object(mvm->dev, ACPI_WGDS_METHOD); @@ -785,12 +788,13 @@ static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm) return PTR_ERR(data); wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, - ACPI_WGDS_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg)) { + ACPI_WGDS_WIFI_DATA_SIZE, &tbl_rev); + if (IS_ERR(wifi_pkg) || tbl_rev > 1) { ret = PTR_ERR(wifi_pkg); goto out_free; } + mvm->geo_rev = tbl_rev; for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) { for (j = 0; j < ACPI_GEO_TABLE_SIZE; j++) { union acpi_object *entry; @@ -858,6 +862,9 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) return -ENOENT; } + IWL_DEBUG_INFO(mvm, + "SAR EWRD: chain %d profile index %d\n", + i, profs[i]); IWL_DEBUG_RADIO(mvm, " Chain[%d]:\n", i); for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS; j++) { idx = (i * ACPI_SAR_NUM_SUB_BANDS) + j; @@ -877,15 +884,29 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) { struct iwl_geo_tx_power_profiles_resp *resp; int ret; + u16 len; + void *data; + struct iwl_geo_tx_power_profiles_cmd geo_cmd; + struct iwl_geo_tx_power_profiles_cmd_v1 geo_cmd_v1; + struct iwl_host_cmd cmd; + + if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_SAR_TABLE_VER)) { + geo_cmd.ops = + cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE); + len = sizeof(geo_cmd); + data = &geo_cmd; + } else { + geo_cmd_v1.ops = + cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE); + len = sizeof(geo_cmd_v1); + data = &geo_cmd_v1; + } - struct iwl_geo_tx_power_profiles_cmd geo_cmd = { - .ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE), - }; - struct iwl_host_cmd cmd = { + cmd = (struct iwl_host_cmd){ .id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT), - .len = { sizeof(geo_cmd), }, + .len = { len, }, .flags = CMD_WANT_SKB, - .data = { &geo_cmd }, + .data = { data }, }; ret = iwl_mvm_send_cmd(mvm, &cmd); @@ -955,6 +976,16 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) i, j, value[1], value[2], value[0]); } } + + cmd.table_revision = cpu_to_le32(mvm->geo_rev); + + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SAR_TABLE_VER)) { + return iwl_mvm_send_cmd_pdu(mvm, cmd_wide_id, 0, + sizeof(struct iwl_geo_tx_power_profiles_cmd_v1), + &cmd); + } + return iwl_mvm_send_cmd_pdu(mvm, cmd_wide_id, 0, sizeof(cmd), &cmd); } @@ -1108,10 +1139,13 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) iwl_fw_dbg_apply_point(&mvm->fwrt, IWL_FW_INI_APPLY_EARLY); + mvm->rfkill_safe_init_done = false; ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); if (ret) return ret; + mvm->rfkill_safe_init_done = true; + iwl_fw_dbg_apply_point(&mvm->fwrt, IWL_FW_INI_APPLY_AFTER_ALIVE); return iwl_init_paging(&mvm->fwrt, mvm->fwrt.cur_fw_img); @@ -1144,7 +1178,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); - if (!mvm->trans->ini_valid) { + if (!mvm->trans->dbg.ini_valid) { mvm->fwrt.dump.conf = FW_DBG_INVALID; /* if we have a destination, assume EARLY START */ if (mvm->fw->dbg.dest_tlv) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 53c217af13c8..cb22d447fcb8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -558,15 +558,16 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, for (i = 0; i < IEEE80211_NUM_ACS; i++) { u8 txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, i); + u8 ucode_ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); - cmd->ac[txf].cw_min = + cmd->ac[ucode_ac].cw_min = cpu_to_le16(mvmvif->queue_params[i].cw_min); - cmd->ac[txf].cw_max = + cmd->ac[ucode_ac].cw_max = cpu_to_le16(mvmvif->queue_params[i].cw_max); - cmd->ac[txf].edca_txop = + cmd->ac[ucode_ac].edca_txop = cpu_to_le16(mvmvif->queue_params[i].txop * 32); - cmd->ac[txf].aifsn = mvmvif->queue_params[i].aifs; - cmd->ac[txf].fifos_mask = BIT(txf); + cmd->ac[ucode_ac].aifsn = mvmvif->queue_params[i].aifs; + cmd->ac[ucode_ac].fifos_mask = BIT(txf); } if (vif->bss_conf.qos) @@ -678,7 +679,7 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) { cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX); - if (vif->bss_conf.twt_requester) + if (vif->bss_conf.twt_requester && IWL_MVM_USE_TWT) ctxt_sta->data_policy |= cpu_to_le32(TWT_SUPPORTED); } @@ -1081,9 +1082,6 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); } - if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) - cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX); - ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int); ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * vif->bss_conf.dtim_period); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index fdbabca0280e..55cd49ccbf0b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -207,6 +207,12 @@ static const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = { }, }; +static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); + void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) { if (!iwl_mvm_is_d0i3_supported(mvm)) @@ -1439,7 +1445,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) */ clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); - iwl_fw_cancel_dump(&mvm->fwrt); + iwl_fw_cancel_dumps(&mvm->fwrt); cancel_delayed_work_sync(&mvm->cs_tx_unblock_dwork); cancel_delayed_work_sync(&mvm->scan_timeout_dwork); iwl_fw_free_dump_desc(&mvm->fwrt); @@ -2365,22 +2371,23 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, /* Mark MU EDCA as enabled, unless none detected on some AC */ flags |= STA_CTXT_HE_MU_EDCA_CW; - for (i = 0; i < AC_NUM; i++) { + for (i = 0; i < IEEE80211_NUM_ACS; i++) { struct ieee80211_he_mu_edca_param_ac_rec *mu_edca = &mvmvif->queue_params[i].mu_edca_param_rec; + u8 ac = iwl_mvm_mac80211_ac_to_ucode_ac(i); if (!mvmvif->queue_params[i].mu_edca) { flags &= ~STA_CTXT_HE_MU_EDCA_CW; break; } - sta_ctxt_cmd.trig_based_txf[i].cwmin = + sta_ctxt_cmd.trig_based_txf[ac].cwmin = cpu_to_le16(mu_edca->ecw_min_max & 0xf); - sta_ctxt_cmd.trig_based_txf[i].cwmax = + sta_ctxt_cmd.trig_based_txf[ac].cwmax = cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4); - sta_ctxt_cmd.trig_based_txf[i].aifsn = + sta_ctxt_cmd.trig_based_txf[ac].aifsn = cpu_to_le16(mu_edca->aifsn); - sta_ctxt_cmd.trig_based_txf[i].mu_time = + sta_ctxt_cmd.trig_based_txf[ac].mu_time = cpu_to_le16(mu_edca->mu_edca_timer); } @@ -2636,7 +2643,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; + int ret, i; /* * iwl_mvm_mac_ctxt_add() might read directly from the device @@ -2710,6 +2717,20 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* must be set before quota calculations */ mvmvif->ap_ibss_active = true; + /* send all the early keys to the device now */ + for (i = 0; i < ARRAY_SIZE(mvmvif->ap_early_keys); i++) { + struct ieee80211_key_conf *key = mvmvif->ap_early_keys[i]; + + if (!key) + continue; + + mvmvif->ap_early_keys[i] = NULL; + + ret = iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key); + if (ret) + goto out_quota_failed; + } + if (vif->type == NL80211_IFTYPE_AP && !vif->p2p) { iwl_mvm_vif_set_low_latency(mvmvif, true, LOW_LATENCY_VIF_TYPE); @@ -3479,11 +3500,12 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_sta *mvmsta; struct iwl_mvm_key_pn *ptk_pn; int keyidx = key->keyidx; - int ret; + int ret, i; u8 key_offset; if (iwlwifi_mod_params.swcrypto) { @@ -3556,6 +3578,22 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, key->hw_key_idx = STA_KEY_IDX_INVALID; break; } + + if (!mvmvif->ap_ibss_active) { + for (i = 0; + i < ARRAY_SIZE(mvmvif->ap_early_keys); + i++) { + if (!mvmvif->ap_early_keys[i]) { + mvmvif->ap_early_keys[i] = key; + break; + } + } + + if (i >= ARRAY_SIZE(mvmvif->ap_early_keys)) + ret = -ENOSPC; + + break; + } } /* During FW restart, in order to restore the state as it was, @@ -3624,6 +3662,18 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, break; case DISABLE_KEY: + ret = -ENOENT; + for (i = 0; i < ARRAY_SIZE(mvmvif->ap_early_keys); i++) { + if (mvmvif->ap_early_keys[i] == key) { + mvmvif->ap_early_keys[i] = NULL; + ret = 0; + } + } + + /* found in pending list - don't do anything else */ + if (ret == 0) + break; + if (key->hw_key_idx == STA_KEY_IDX_INVALID) { ret = 0; break; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 02efcf2189c4..48c77af54e99 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -501,6 +501,9 @@ struct iwl_mvm_vif { netdev_features_t features; struct iwl_probe_resp_data __rcu *probe_resp_data; + + /* we can only have 2 GTK + 2 IGTK active at a time */ + struct ieee80211_key_conf *ap_early_keys[4]; }; static inline struct iwl_mvm_vif * @@ -1107,7 +1110,6 @@ struct iwl_mvm { u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */ /* Indicate if 32Khz external clock is valid */ u32 ext_clock_valid; - unsigned int max_amsdu_len; /* used for debugfs only */ struct ieee80211_vif __rcu *csa_vif; struct ieee80211_vif __rcu *csa_tx_blocked_vif; @@ -1181,6 +1183,7 @@ struct iwl_mvm { #ifdef CONFIG_ACPI struct iwl_mvm_sar_profile sar_profiles[ACPI_SAR_PROFILE_NUM]; struct iwl_mvm_geo_profile geo_profiles[ACPI_NUM_GEO_PROFILES]; + u32 geo_rev; #endif }; @@ -1307,6 +1310,12 @@ static inline bool iwl_mvm_is_adaptive_dwell_v2_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_API_ADAPTIVE_DWELL_V2); } +static inline bool iwl_mvm_is_adwell_hb_ap_num_supported(struct iwl_mvm *mvm) +{ + return fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_ADWELL_HB_DEF_N_AP); +} + static inline bool iwl_mvm_is_oce_supported(struct iwl_mvm *mvm) { /* OCE should never be enabled for LMAC scan FWs */ @@ -1532,6 +1541,7 @@ 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(int rate_idx); +u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac); void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); u8 first_antenna(u8 mask); u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 7bdbd010ae6b..719f793b3487 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -620,6 +620,7 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, enum iwl_mcc_source src; char mcc[3]; struct ieee80211_regdomain *regd; + u32 wgds_tbl_idx; lockdep_assert_held(&mvm->mutex); @@ -643,6 +644,14 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, if (IS_ERR_OR_NULL(regd)) return; + wgds_tbl_idx = iwl_mvm_get_sar_geo_profile(mvm); + if (wgds_tbl_idx < 0) + IWL_DEBUG_INFO(mvm, "SAR WGDS is disabled (%d)\n", + wgds_tbl_idx); + else + IWL_DEBUG_INFO(mvm, "SAR WGDS: geo profile %d is configured\n", + wgds_tbl_idx); + regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); kfree(regd); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index fad3bf563712..d7d6f3398f86 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -564,24 +564,24 @@ unlock: static int iwl_mvm_fwrt_dump_start(void *ctx) { struct iwl_mvm *mvm = ctx; - int ret; + int ret = 0; + + mutex_lock(&mvm->mutex); ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT); if (ret) - return ret; - - mutex_lock(&mvm->mutex); + mutex_unlock(&mvm->mutex); - return 0; + return ret; } static void iwl_mvm_fwrt_dump_end(void *ctx) { struct iwl_mvm *mvm = ctx; - mutex_unlock(&mvm->mutex); - iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT); + + mutex_unlock(&mvm->mutex); } static bool iwl_mvm_fwrt_fw_running(void *ctx) @@ -799,11 +799,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_trans_configure(mvm->trans, &trans_cfg); trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; - trans->dbg_dest_tlv = mvm->fw->dbg.dest_tlv; - trans->dbg_n_dest_reg = mvm->fw->dbg.n_dest_reg; - memcpy(trans->dbg_conf_tlv, mvm->fw->dbg.conf_tlv, - sizeof(trans->dbg_conf_tlv)); - trans->dbg_trigger_tlv = mvm->fw->dbg.trigger_tlv; + trans->dbg.dest_tlv = mvm->fw->dbg.dest_tlv; + trans->dbg.n_dest_reg = mvm->fw->dbg.n_dest_reg; + memcpy(trans->dbg.conf_tlv, mvm->fw->dbg.conf_tlv, + sizeof(trans->dbg.conf_tlv)); + trans->dbg.trigger_tlv = mvm->fw->dbg.trigger_tlv; trans->iml = mvm->fw->iml; trans->iml_len = mvm->fw->iml_len; @@ -880,7 +880,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, return op_mode; out_free: - iwl_fw_flush_dump(&mvm->fwrt); + iwl_fw_flush_dumps(&mvm->fwrt); iwl_fw_runtime_free(&mvm->fwrt); if (iwlmvm_mod_params.init_dbg) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index be62f499c595..08b67812e94e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -101,7 +101,7 @@ static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta) struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; u8 supp = 0; - if (he_cap && he_cap->has_he) + if (he_cap->has_he) return 0; if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) @@ -123,12 +123,12 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; struct ieee80211_sta_he_cap *he_cap = &sta->he_cap; - bool vht_ena = vht_cap && vht_cap->vht_supported; + bool vht_ena = vht_cap->vht_supported; u16 flags = 0; if (mvm->cfg->ht_params->stbc && (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1)) { - if (he_cap && he_cap->has_he) { + if (he_cap->has_he) { if (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; @@ -136,15 +136,14 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, if (he_cap->he_cap_elem.phy_cap_info[7] & IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ) flags |= IWL_TLC_MNG_CFG_FLAGS_HE_STBC_160MHZ_MSK; - } else if ((ht_cap && - (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) || + } else if ((ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) || (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))) flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; } if (mvm->cfg->ht_params->ldpc && - ((ht_cap && (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) || + ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; @@ -154,7 +153,7 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; - if (he_cap && he_cap->has_he && + if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[3] & IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK)) flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK; @@ -293,13 +292,13 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, cmd->mode = IWL_TLC_MNG_MODE_NON_HT; /* HT/VHT rates */ - if (he_cap && he_cap->has_he) { + if (he_cap->has_he) { cmd->mode = IWL_TLC_MNG_MODE_HE; rs_fw_he_set_enabled_rates(sta, sband, cmd); - } else if (vht_cap && vht_cap->vht_supported) { + } else if (vht_cap->vht_supported) { cmd->mode = IWL_TLC_MNG_MODE_VHT; rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd); - } else if (ht_cap && ht_cap->ht_supported) { + } else if (ht_cap->ht_supported) { cmd->mode = IWL_TLC_MNG_MODE_HT; cmd->ht_rates[0][0] = cpu_to_le16(ht_cap->mcs.rx_mask[0]); cmd->ht_rates[1][0] = cpu_to_le16(ht_cap->mcs.rx_mask[1]); @@ -344,7 +343,7 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, lq_sta->last_rate_n_flags); } - if (flags & IWL_TLC_NOTIF_FLAG_AMSDU) { + if (flags & IWL_TLC_NOTIF_FLAG_AMSDU && !mvmsta->orig_amsdu_len) { u16 size = le32_to_cpu(notif->amsdu_size); int i; @@ -381,7 +380,7 @@ static u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta) const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - if (vht_cap && vht_cap->vht_supported) { + if (vht_cap->vht_supported) { switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: return IEEE80211_MAX_MPDU_LEN_VHT_11454; @@ -391,7 +390,7 @@ static u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta) return IEEE80211_MAX_MPDU_LEN_VHT_3895; } - } else if (ht_cap && ht_cap->ht_supported) { + } else if (ht_cap->ht_supported) { if (ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU) /* * agg is offloaded so we need to assume that agg diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index d9ddf9ff6428..c284e6975b1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -83,8 +83,10 @@ #define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300 /* adaptive dwell max budget time [TU] for directed scan */ #define IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100 -/* adaptive dwell default APs number */ -#define IWL_SCAN_ADWELL_DEFAULT_N_APS 2 +/* adaptive dwell default high band APs number */ +#define IWL_SCAN_ADWELL_DEFAULT_HB_N_APS 8 +/* adaptive dwell default low band APs number */ +#define IWL_SCAN_ADWELL_DEFAULT_LB_N_APS 2 /* adaptive dwell default APs number in social channels (1, 6, 11) */ #define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10 @@ -1288,7 +1290,11 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, cmd->v7.adwell_default_n_aps_social = IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL; cmd->v7.adwell_default_n_aps = - IWL_SCAN_ADWELL_DEFAULT_N_APS; + IWL_SCAN_ADWELL_DEFAULT_LB_N_APS; + + if (iwl_mvm_is_adwell_hb_ap_num_supported(mvm)) + cmd->v9.adwell_default_hb_n_aps = + IWL_SCAN_ADWELL_DEFAULT_HB_N_APS; /* if custom max budget was configured with debugfs */ if (IWL_MVM_ADWELL_MAX_BUDGET) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index b4d4071b865d..4487cc3e07c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -386,6 +386,9 @@ struct iwl_mvm_rxq_dup_data { * @amsdu_enabled: bitmap of TX AMSDU allowed TIDs. * In case TLC offload is not active it is either 0xFFFF or 0. * @max_amsdu_len: max AMSDU length + * @orig_amsdu_len: used to save the original amsdu_len when it is changed via + * debugfs. If it's set to 0, it means that it is it's not set via + * debugfs. * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON) * @sleep_tx_count: the number of frames that we told the firmware to let out * even when that station is asleep. This is useful in case the queue @@ -434,6 +437,7 @@ struct iwl_mvm_sta { bool disable_tx; u16 amsdu_enabled; u16 max_amsdu_len; + u16 orig_amsdu_len; bool sleeping; u8 agg_tids; u8 sleep_tx_count; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 0c2aabc842f9..a3e5d88f1c07 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -726,6 +726,9 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) memcpy(&info, skb->cb, sizeof(info)); + if (WARN_ON_ONCE(skb->len > IEEE80211_MAX_DATA_LEN + hdrlen)) + return -1; + if (WARN_ON_ONCE(info.flags & IEEE80211_TX_CTL_AMPDU)) return -1; @@ -893,18 +896,15 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, unsigned int mss = skb_shinfo(skb)->gso_size; unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len; u16 snap_ip_tcp, pad; - unsigned int dbg_max_amsdu_len; netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG; u8 tid; snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) + tcp_hdrlen(skb); - dbg_max_amsdu_len = READ_ONCE(mvm->max_amsdu_len); - if (!mvmsta->max_amsdu_len || !ieee80211_is_data_qos(hdr->frame_control) || - (!mvmsta->amsdu_enabled && !dbg_max_amsdu_len)) + !mvmsta->amsdu_enabled) return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); /* @@ -936,10 +936,6 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, max_amsdu_len = iwl_mvm_max_amsdu_size(mvm, sta, tid); - if (unlikely(dbg_max_amsdu_len)) - max_amsdu_len = min_t(unsigned int, max_amsdu_len, - dbg_max_amsdu_len); - /* * Limit A-MSDU in A-MPDU to 4095 bytes when VHT is not * supported. This is a spec requirement (IEEE 802.11-2015 @@ -1063,7 +1059,9 @@ static int iwl_mvm_tx_pkt_queued(struct iwl_mvm *mvm, } /* - * Sets the fields in the Tx cmd that are crypto related + * Sets the fields in the Tx cmd that are crypto related. + * + * This function must be called with BHs disabled. */ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_tx_info *info, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index cc56ab88fb43..955a938b4002 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -238,6 +238,18 @@ u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx) return fw_rate_idx_to_plcp[rate_idx]; } +u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac) +{ + static const u8 mac80211_ac_to_ucode_ac[] = { + AC_VO, + AC_VI, + AC_BE, + AC_BK + }; + + return mac80211_ac_to_ucode_ac[ac]; +} + void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -457,10 +469,10 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) { struct iwl_trans *trans = mvm->trans; struct iwl_umac_error_event_table table; - u32 base = mvm->trans->umac_error_event_table; + u32 base = mvm->trans->dbg.umac_error_event_table; if (!mvm->support_umac_log && - !(mvm->trans->error_event_table_tlv_status & + !(mvm->trans->dbg.error_event_table_tlv_status & IWL_ERROR_EVENT_TABLE_UMAC)) return; @@ -496,7 +508,7 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u8 lmac_num) { struct iwl_trans *trans = mvm->trans; struct iwl_error_event_table table; - u32 val, base = mvm->trans->lmac_error_event_table[lmac_num]; + u32 val, base = mvm->trans->dbg.lmac_error_event_table[lmac_num]; if (mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) { if (!base) @@ -592,7 +604,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) iwl_mvm_dump_lmac_error_log(mvm, 0); - if (mvm->trans->lmac_error_event_table[1]) + if (mvm->trans->dbg.lmac_error_event_table[1]) iwl_mvm_dump_lmac_error_log(mvm, 1); iwl_mvm_dump_umac_error_log(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c index f496d1bcb643..5e86783d616b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c @@ -96,13 +96,13 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, cpu_to_le64(trans_pcie->rxq->bd_dma); /* Configure debug, for integration */ - if (!trans->ini_valid) + if (!trans->dbg.ini_valid) iwl_pcie_alloc_fw_monitor(trans, 0); - if (trans->num_blocks) { + if (trans->dbg.num_blocks) { prph_sc_ctrl->hwm_cfg.hwm_base_addr = - cpu_to_le64(trans->fw_mon[0].physical); + cpu_to_le64(trans->dbg.fw_mon[0].physical); prph_sc_ctrl->hwm_cfg.hwm_size = - cpu_to_le32(trans->fw_mon[0].size); + cpu_to_le32(trans->dbg.fw_mon[0].size); } /* allocate ucode sections in dram and set addresses */ @@ -169,7 +169,7 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, memcpy(iml_img, trans->iml, trans->iml_len); - iwl_enable_interrupts(trans); + iwl_enable_fw_load_int_ctx_info(trans); /* kick FW self load */ iwl_write64(trans, CSR_CTXT_INFO_ADDR, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index 8969b47bacf2..d38cefbb779e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -222,7 +222,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, trans_pcie->ctxt_info = ctxt_info; - iwl_enable_interrupts(trans); + iwl_enable_fw_load_int_ctx_info(trans); /* Configure debug, if exists */ if (iwl_pcie_dbg_on(trans)) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 85973dd57234..9f5d0fc839fe 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -874,6 +874,33 @@ static inline void iwl_enable_fw_load_int(struct iwl_trans *trans) } } +static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + IWL_DEBUG_ISR(trans, "Enabling ALIVE interrupt only\n"); + + if (!trans_pcie->msix_enabled) { + /* + * When we'll receive the ALIVE interrupt, the ISR will call + * iwl_enable_fw_load_int_ctx_info again to set the ALIVE + * interrupt (which is not really needed anymore) but also the + * RX interrupt which will allow us to receive the ALIVE + * notification (which is Rx) and continue the flow. + */ + trans_pcie->inta_mask = CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX; + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); + } else { + iwl_enable_hw_int_msk_msix(trans, + MSIX_HW_INT_CAUSES_REG_ALIVE); + /* + * Leave all the FH causes enabled to get the ALIVE + * notification. + */ + iwl_enable_fh_int_msk_msix(trans, trans_pcie->fh_init_mask); + } +} + static inline u16 iwl_pcie_get_cmd_index(const struct iwl_txq *q, u32 index) { return index & (q->n_window - 1); @@ -1018,7 +1045,7 @@ static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, static inline bool iwl_pcie_dbg_on(struct iwl_trans *trans) { - return (trans->dbg_dest_tlv || trans->ini_valid); + return (trans->dbg.dest_tlv || trans->dbg.ini_valid); } void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 31b3591f71d1..a2d709642b2a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1827,26 +1827,26 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) goto out; } - if (iwl_have_debug_level(IWL_DL_ISR)) { - /* NIC fires this, but we don't use it, redundant with WAKEUP */ - if (inta & CSR_INT_BIT_SCD) { - IWL_DEBUG_ISR(trans, - "Scheduler finished to transmit the frame/frames.\n"); - isr_stats->sch++; - } + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_SCD) { + IWL_DEBUG_ISR(trans, + "Scheduler finished to transmit the frame/frames.\n"); + isr_stats->sch++; + } - /* Alive notification via Rx interrupt will do the real work */ - if (inta & CSR_INT_BIT_ALIVE) { - IWL_DEBUG_ISR(trans, "Alive interrupt\n"); - isr_stats->alive++; - if (trans->cfg->gen2) { - /* - * We can restock, since firmware configured - * the RFH - */ - iwl_pcie_rxmq_restock(trans, trans_pcie->rxq); - } + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) { + IWL_DEBUG_ISR(trans, "Alive interrupt\n"); + isr_stats->alive++; + if (trans->cfg->gen2) { + /* + * We can restock, since firmware configured + * the RFH + */ + iwl_pcie_rxmq_restock(trans, trans_pcie->rxq); } + + handled |= CSR_INT_BIT_ALIVE; } /* Safely ignore these bits for debug checks below */ @@ -1965,6 +1965,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) /* Re-enable RF_KILL if it occurred */ else if (handled & CSR_INT_BIT_RF_KILL) iwl_enable_rfkill_int(trans); + /* Re-enable the ALIVE / Rx interrupt if it occurred */ + else if (handled & (CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX)) + iwl_enable_fw_load_int_ctx_info(trans); spin_unlock(&trans_pcie->irq_lock); out: @@ -2108,10 +2111,18 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) return IRQ_NONE; } - if (iwl_have_debug_level(IWL_DL_ISR)) - IWL_DEBUG_ISR(trans, "ISR inta_fh 0x%08x, enabled 0x%08x\n", - inta_fh, + if (iwl_have_debug_level(IWL_DL_ISR)) { + IWL_DEBUG_ISR(trans, + "ISR inta_fh 0x%08x, enabled (sw) 0x%08x (hw) 0x%08x\n", + inta_fh, trans_pcie->fh_mask, iwl_read32(trans, CSR_MSIX_FH_INT_MASK_AD)); + if (inta_fh & ~trans_pcie->fh_mask) + IWL_DEBUG_ISR(trans, + "We got a masked interrupt (0x%08x)\n", + inta_fh & ~trans_pcie->fh_mask); + } + + inta_fh &= trans_pcie->fh_mask; if ((trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_NON_RX) && inta_fh & MSIX_FH_INT_CAUSES_Q0) { @@ -2151,11 +2162,18 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) } /* After checking FH register check HW register */ - if (iwl_have_debug_level(IWL_DL_ISR)) + if (iwl_have_debug_level(IWL_DL_ISR)) { IWL_DEBUG_ISR(trans, - "ISR inta_hw 0x%08x, enabled 0x%08x\n", - inta_hw, + "ISR inta_hw 0x%08x, enabled (sw) 0x%08x (hw) 0x%08x\n", + inta_hw, trans_pcie->hw_mask, iwl_read32(trans, CSR_MSIX_HW_INT_MASK_AD)); + if (inta_hw & ~trans_pcie->hw_mask) + IWL_DEBUG_ISR(trans, + "We got a masked interrupt 0x%08x\n", + inta_hw & ~trans_pcie->hw_mask); + } + + inta_hw &= trans_pcie->hw_mask; /* Alive notification via Rx interrupt will do the real work */ if (inta_hw & MSIX_HW_INT_CAUSES_REG_ALIVE) { @@ -2212,7 +2230,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) "Hardware error detected. Restarting.\n"); isr_stats->hw++; - trans->hw_error = true; + trans->dbg.hw_error = true; iwl_pcie_irq_handle_error(trans); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 8507a7bdcfdd..8d17e68577fd 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -148,7 +148,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power) trans_pcie->is_down = true; /* Stop dbgc before stopping device */ - _iwl_fw_dbg_stop_recording(trans, NULL); + iwl_fw_dbg_stop_recording(trans, NULL); /* tell the device to stop sending interrupts */ iwl_disable_interrupts(trans); @@ -273,6 +273,15 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr) * paging memory cannot be freed included since FW will still use it */ iwl_pcie_ctxt_info_free(trans); + + /* + * Re-enable all the interrupts, including the RF-Kill one, now that + * the firmware is alive. + */ + iwl_enable_interrupts(trans); + mutex_lock(&trans_pcie->mutex); + iwl_pcie_check_hw_rf_kill(trans); + mutex_unlock(&trans_pcie->mutex); } int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index dfa1bed124aa..602c31b3992a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -90,8 +90,10 @@ void iwl_trans_pcie_dump_regs(struct iwl_trans *trans) { -#define PCI_DUMP_SIZE 64 -#define PREFIX_LEN 32 +#define PCI_DUMP_SIZE 352 +#define PCI_MEM_DUMP_SIZE 64 +#define PCI_PARENT_DUMP_SIZE 524 +#define PREFIX_LEN 32 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct pci_dev *pdev = trans_pcie->pci_dev; u32 i, pos, alloc_size, *ptr, *buf; @@ -102,11 +104,15 @@ void iwl_trans_pcie_dump_regs(struct iwl_trans *trans) /* Should be a multiple of 4 */ BUILD_BUG_ON(PCI_DUMP_SIZE > 4096 || PCI_DUMP_SIZE & 0x3); + BUILD_BUG_ON(PCI_MEM_DUMP_SIZE > 4096 || PCI_MEM_DUMP_SIZE & 0x3); + BUILD_BUG_ON(PCI_PARENT_DUMP_SIZE > 4096 || PCI_PARENT_DUMP_SIZE & 0x3); + /* Alloc a max size buffer */ - if (PCI_ERR_ROOT_ERR_SRC + 4 > PCI_DUMP_SIZE) - alloc_size = PCI_ERR_ROOT_ERR_SRC + 4 + PREFIX_LEN; - else - alloc_size = PCI_DUMP_SIZE + PREFIX_LEN; + alloc_size = PCI_ERR_ROOT_ERR_SRC + 4 + PREFIX_LEN; + alloc_size = max_t(u32, alloc_size, PCI_DUMP_SIZE + PREFIX_LEN); + alloc_size = max_t(u32, alloc_size, PCI_MEM_DUMP_SIZE + PREFIX_LEN); + alloc_size = max_t(u32, alloc_size, PCI_PARENT_DUMP_SIZE + PREFIX_LEN); + buf = kmalloc(alloc_size, GFP_ATOMIC); if (!buf) return; @@ -123,7 +129,7 @@ void iwl_trans_pcie_dump_regs(struct iwl_trans *trans) print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); IWL_ERR(trans, "iwlwifi device memory mapped registers:\n"); - for (i = 0, ptr = buf; i < PCI_DUMP_SIZE; i += 4, ptr++) + for (i = 0, ptr = buf; i < PCI_MEM_DUMP_SIZE; i += 4, ptr++) *ptr = iwl_read32(trans, i); print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); @@ -146,7 +152,7 @@ void iwl_trans_pcie_dump_regs(struct iwl_trans *trans) IWL_ERR(trans, "iwlwifi parent port (%s) config registers:\n", pci_name(pdev)); - for (i = 0, ptr = buf; i < PCI_DUMP_SIZE; i += 4, ptr++) + for (i = 0, ptr = buf; i < PCI_PARENT_DUMP_SIZE; i += 4, ptr++) if (pci_read_config_dword(pdev, i, ptr)) goto err_read; print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); @@ -188,14 +194,14 @@ static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans) { int i; - for (i = 0; i < trans->num_blocks; i++) { - dma_free_coherent(trans->dev, trans->fw_mon[i].size, - trans->fw_mon[i].block, - trans->fw_mon[i].physical); - trans->fw_mon[i].block = NULL; - trans->fw_mon[i].physical = 0; - trans->fw_mon[i].size = 0; - trans->num_blocks--; + for (i = 0; i < trans->dbg.num_blocks; i++) { + dma_free_coherent(trans->dev, trans->dbg.fw_mon[i].size, + trans->dbg.fw_mon[i].block, + trans->dbg.fw_mon[i].physical); + trans->dbg.fw_mon[i].block = NULL; + trans->dbg.fw_mon[i].physical = 0; + trans->dbg.fw_mon[i].size = 0; + trans->dbg.num_blocks--; } } @@ -230,10 +236,10 @@ static void iwl_pcie_alloc_fw_monitor_block(struct iwl_trans *trans, (unsigned long)BIT(power - 10), (unsigned long)BIT(max_power - 10)); - trans->fw_mon[trans->num_blocks].block = cpu_addr; - trans->fw_mon[trans->num_blocks].physical = phys; - trans->fw_mon[trans->num_blocks].size = size; - trans->num_blocks++; + trans->dbg.fw_mon[trans->dbg.num_blocks].block = cpu_addr; + trans->dbg.fw_mon[trans->dbg.num_blocks].physical = phys; + trans->dbg.fw_mon[trans->dbg.num_blocks].size = size; + trans->dbg.num_blocks++; } void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) @@ -254,7 +260,7 @@ void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) * This function allocats the default fw monitor. * The optional additional ones will be allocated in runtime */ - if (trans->num_blocks) + if (trans->dbg.num_blocks) return; iwl_pcie_alloc_fw_monitor_block(trans, max_power, 11); @@ -889,21 +895,21 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, void iwl_pcie_apply_destination(struct iwl_trans *trans) { - const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg_dest_tlv; + const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg.dest_tlv; int i; - if (trans->ini_valid) { - if (!trans->num_blocks) + if (trans->dbg.ini_valid) { + if (!trans->dbg.num_blocks) return; IWL_DEBUG_FW(trans, "WRT: applying DRAM buffer[0] destination\n"); iwl_write_umac_prph(trans, MON_BUFF_BASE_ADDR_VER2, - trans->fw_mon[0].physical >> + trans->dbg.fw_mon[0].physical >> MON_BUFF_SHIFT_VER2); iwl_write_umac_prph(trans, MON_BUFF_END_ADDR_VER2, - (trans->fw_mon[0].physical + - trans->fw_mon[0].size - 256) >> + (trans->dbg.fw_mon[0].physical + + trans->dbg.fw_mon[0].size - 256) >> MON_BUFF_SHIFT_VER2); return; } @@ -916,7 +922,7 @@ void iwl_pcie_apply_destination(struct iwl_trans *trans) else IWL_WARN(trans, "PCI should have external buffer debug\n"); - for (i = 0; i < trans->dbg_n_dest_reg; i++) { + for (i = 0; i < trans->dbg.n_dest_reg; i++) { u32 addr = le32_to_cpu(dest->reg_ops[i].addr); u32 val = le32_to_cpu(dest->reg_ops[i].val); @@ -955,18 +961,19 @@ void iwl_pcie_apply_destination(struct iwl_trans *trans) } monitor: - if (dest->monitor_mode == EXTERNAL_MODE && trans->fw_mon[0].size) { + if (dest->monitor_mode == EXTERNAL_MODE && trans->dbg.fw_mon[0].size) { iwl_write_prph(trans, le32_to_cpu(dest->base_reg), - trans->fw_mon[0].physical >> dest->base_shift); + trans->dbg.fw_mon[0].physical >> + dest->base_shift); if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) iwl_write_prph(trans, le32_to_cpu(dest->end_reg), - (trans->fw_mon[0].physical + - trans->fw_mon[0].size - 256) >> + (trans->dbg.fw_mon[0].physical + + trans->dbg.fw_mon[0].size - 256) >> dest->end_shift); else iwl_write_prph(trans, le32_to_cpu(dest->end_reg), - (trans->fw_mon[0].physical + - trans->fw_mon[0].size) >> + (trans->dbg.fw_mon[0].physical + + trans->dbg.fw_mon[0].size) >> dest->end_shift); } } @@ -1003,12 +1010,12 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { iwl_pcie_alloc_fw_monitor(trans, 0); - if (trans->fw_mon[0].size) { + if (trans->dbg.fw_mon[0].size) { iwl_write_prph(trans, MON_BUFF_BASE_ADDR, - trans->fw_mon[0].physical >> 4); + trans->dbg.fw_mon[0].physical >> 4); iwl_write_prph(trans, MON_BUFF_END_ADDR, - (trans->fw_mon[0].physical + - trans->fw_mon[0].size) >> 4); + (trans->dbg.fw_mon[0].physical + + trans->dbg.fw_mon[0].size) >> 4); } } else if (iwl_pcie_dbg_on(trans)) { iwl_pcie_apply_destination(trans); @@ -1236,7 +1243,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) trans_pcie->is_down = true; /* Stop dbgc before stopping device */ - _iwl_fw_dbg_stop_recording(trans, NULL); + iwl_fw_dbg_stop_recording(trans, NULL); /* tell the device to stop sending interrupts */ iwl_disable_interrupts(trans); @@ -2729,8 +2736,8 @@ static int iwl_dbgfs_monitor_data_open(struct inode *inode, struct iwl_trans *trans = inode->i_private; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (!trans->dbg_dest_tlv || - trans->dbg_dest_tlv->monitor_mode != EXTERNAL_MODE) { + if (!trans->dbg.dest_tlv || + trans->dbg.dest_tlv->monitor_mode != EXTERNAL_MODE) { IWL_ERR(trans, "Debug destination is not set to DRAM\n"); return -ENOENT; } @@ -2777,22 +2784,22 @@ static ssize_t iwl_dbgfs_monitor_data_read(struct file *file, { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - void *cpu_addr = (void *)trans->fw_mon[0].block, *curr_buf; + void *cpu_addr = (void *)trans->dbg.fw_mon[0].block, *curr_buf; struct cont_rec *data = &trans_pcie->fw_mon_data; u32 write_ptr_addr, wrap_cnt_addr, write_ptr, wrap_cnt; ssize_t size, bytes_copied = 0; bool b_full; - if (trans->dbg_dest_tlv) { + if (trans->dbg.dest_tlv) { write_ptr_addr = - le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg); - wrap_cnt_addr = le32_to_cpu(trans->dbg_dest_tlv->wrap_count); + le32_to_cpu(trans->dbg.dest_tlv->write_ptr_reg); + wrap_cnt_addr = le32_to_cpu(trans->dbg.dest_tlv->wrap_count); } else { write_ptr_addr = MON_BUFF_WRPTR; wrap_cnt_addr = MON_BUFF_CYCLE_CNT; } - if (unlikely(!trans->dbg_rec_on)) + if (unlikely(!trans->dbg.rec_on)) return 0; mutex_lock(&data->mutex); @@ -2816,7 +2823,7 @@ static ssize_t iwl_dbgfs_monitor_data_read(struct file *file, } else if (data->prev_wrap_cnt == wrap_cnt - 1 && write_ptr < data->prev_wr_ptr) { - size = trans->fw_mon[0].size - data->prev_wr_ptr; + size = trans->dbg.fw_mon[0].size - data->prev_wr_ptr; curr_buf = cpu_addr + data->prev_wr_ptr; b_full = iwl_write_to_user_buf(user_buf, count, curr_buf, &size, @@ -3035,14 +3042,10 @@ iwl_trans_pcie_dump_pointers(struct iwl_trans *trans, base_high = DBGC_CUR_DBGBUF_BASE_ADDR_MSB; write_ptr = DBGC_CUR_DBGBUF_STATUS; wrap_cnt = DBGC_DBGBUF_WRAP_AROUND; - } else if (trans->ini_valid) { - base = iwl_umac_prph(trans, MON_BUFF_BASE_ADDR_VER2); - write_ptr = iwl_umac_prph(trans, MON_BUFF_WRPTR_VER2); - wrap_cnt = iwl_umac_prph(trans, MON_BUFF_CYCLE_CNT_VER2); - } else if (trans->dbg_dest_tlv) { - write_ptr = le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg); - wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count); - base = le32_to_cpu(trans->dbg_dest_tlv->base_reg); + } else if (trans->dbg.dest_tlv) { + write_ptr = le32_to_cpu(trans->dbg.dest_tlv->write_ptr_reg); + wrap_cnt = le32_to_cpu(trans->dbg.dest_tlv->wrap_count); + base = le32_to_cpu(trans->dbg.dest_tlv->base_reg); } else { base = MON_BUFF_BASE_ADDR; write_ptr = MON_BUFF_WRPTR; @@ -3069,11 +3072,10 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, { u32 len = 0; - if ((trans->num_blocks && + if (trans->dbg.dest_tlv || + (trans->dbg.num_blocks && (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000 || - trans->cfg->device_family >= IWL_DEVICE_FAMILY_AX210 || - trans->ini_valid)) || - (trans->dbg_dest_tlv && !trans->ini_valid)) { + trans->cfg->device_family >= IWL_DEVICE_FAMILY_AX210))) { struct iwl_fw_error_dump_fw_mon *fw_mon_data; (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); @@ -3082,32 +3084,32 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, iwl_trans_pcie_dump_pointers(trans, fw_mon_data); len += sizeof(**data) + sizeof(*fw_mon_data); - if (trans->num_blocks) { + if (trans->dbg.num_blocks) { memcpy(fw_mon_data->data, - trans->fw_mon[0].block, - trans->fw_mon[0].size); + trans->dbg.fw_mon[0].block, + trans->dbg.fw_mon[0].size); - monitor_len = trans->fw_mon[0].size; - } else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) { + monitor_len = trans->dbg.fw_mon[0].size; + } else if (trans->dbg.dest_tlv->monitor_mode == SMEM_MODE) { u32 base = le32_to_cpu(fw_mon_data->fw_mon_base_ptr); /* * Update pointers to reflect actual values after * shifting */ - if (trans->dbg_dest_tlv->version) { + if (trans->dbg.dest_tlv->version) { base = (iwl_read_prph(trans, base) & IWL_LDBG_M2S_BUF_BA_MSK) << - trans->dbg_dest_tlv->base_shift; + trans->dbg.dest_tlv->base_shift; base *= IWL_M2S_UNIT_SIZE; base += trans->cfg->smem_offset; } else { base = iwl_read_prph(trans, base) << - trans->dbg_dest_tlv->base_shift; + trans->dbg.dest_tlv->base_shift; } iwl_trans_read_mem(trans, base, fw_mon_data->data, monitor_len / sizeof(u32)); - } else if (trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) { + } else if (trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) { monitor_len = iwl_trans_pci_dump_marbh_monitor(trans, fw_mon_data, @@ -3126,40 +3128,40 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, u32 *len) { - if (trans->num_blocks) { + if (trans->dbg.num_blocks) { *len += sizeof(struct iwl_fw_error_dump_data) + sizeof(struct iwl_fw_error_dump_fw_mon) + - trans->fw_mon[0].size; - return trans->fw_mon[0].size; - } else if (trans->dbg_dest_tlv) { + trans->dbg.fw_mon[0].size; + return trans->dbg.fw_mon[0].size; + } else if (trans->dbg.dest_tlv) { u32 base, end, cfg_reg, monitor_len; - if (trans->dbg_dest_tlv->version == 1) { - cfg_reg = le32_to_cpu(trans->dbg_dest_tlv->base_reg); + if (trans->dbg.dest_tlv->version == 1) { + cfg_reg = le32_to_cpu(trans->dbg.dest_tlv->base_reg); cfg_reg = iwl_read_prph(trans, cfg_reg); base = (cfg_reg & IWL_LDBG_M2S_BUF_BA_MSK) << - trans->dbg_dest_tlv->base_shift; + trans->dbg.dest_tlv->base_shift; base *= IWL_M2S_UNIT_SIZE; base += trans->cfg->smem_offset; monitor_len = (cfg_reg & IWL_LDBG_M2S_BUF_SIZE_MSK) >> - trans->dbg_dest_tlv->end_shift; + trans->dbg.dest_tlv->end_shift; monitor_len *= IWL_M2S_UNIT_SIZE; } else { - base = le32_to_cpu(trans->dbg_dest_tlv->base_reg); - end = le32_to_cpu(trans->dbg_dest_tlv->end_reg); + base = le32_to_cpu(trans->dbg.dest_tlv->base_reg); + end = le32_to_cpu(trans->dbg.dest_tlv->end_reg); base = iwl_read_prph(trans, base) << - trans->dbg_dest_tlv->base_shift; + trans->dbg.dest_tlv->base_shift; end = iwl_read_prph(trans, end) << - trans->dbg_dest_tlv->end_shift; + trans->dbg.dest_tlv->end_shift; /* Make "end" point to the actual end */ if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000 || - trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) - end += (1 << trans->dbg_dest_tlv->end_shift); + trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) + end += (1 << trans->dbg.dest_tlv->end_shift); monitor_len = end - base; } *len += sizeof(struct iwl_fw_error_dump_data) + @@ -3192,7 +3194,7 @@ static struct iwl_trans_dump_data len = sizeof(*dump_data); /* host commands */ - if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD)) + if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD) && cmdq) len += sizeof(*data) + cmdq->n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); @@ -3244,7 +3246,7 @@ static struct iwl_trans_dump_data len = 0; data = (void *)dump_data->data; - if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD)) { + if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD) && cmdq) { u16 tfd_size = trans_pcie->tfd_size; data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); @@ -3681,6 +3683,7 @@ void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long timeout = jiffies + IWL_TRANS_NMI_TIMEOUT; + bool interrupts_enabled = test_bit(STATUS_INT_ENABLED, &trans->status); u32 inta_addr, sw_err_bit; if (trans_pcie->msix_enabled) { @@ -3691,7 +3694,12 @@ void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans) sw_err_bit = CSR_INT_BIT_SW_ERR; } - iwl_disable_interrupts(trans); + /* if the interrupts were already disabled, there is no point in + * calling iwl_disable_interrupts + */ + if (interrupts_enabled) + iwl_disable_interrupts(trans); + iwl_force_nmi(trans); while (time_after(timeout, jiffies)) { u32 inta_hw = iwl_read32(trans, inta_addr); @@ -3705,6 +3713,13 @@ void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans) mdelay(1); } - iwl_enable_interrupts(trans); + + /* enable interrupts only if there were already enabled before this + * function to avoid a case were the driver enable interrupts before + * proper configurations were made + */ + if (interrupts_enabled) + iwl_enable_interrupts(trans); + iwl_trans_fw_error(trans); } diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c index 5d75c971004b..e435f801bc91 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.c +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -84,17 +84,15 @@ mwifiex_get_ba_status(struct mwifiex_private *priv, enum mwifiex_ba_status ba_status) { struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - unsigned long flags; - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { if (tx_ba_tsr_tbl->ba_status == ba_status) { - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, - flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); return tx_ba_tsr_tbl; } } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); return NULL; } @@ -516,13 +514,12 @@ void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv) { int i; struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; - unsigned long flags; - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); list_for_each_entry_safe(del_tbl_ptr, tmp_node, &priv->tx_ba_stream_tbl_ptr, list) mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); @@ -539,18 +536,16 @@ struct mwifiex_tx_ba_stream_tbl * mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra) { struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - unsigned long flags; - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) && tx_ba_tsr_tbl->tid == tid) { - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, - flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); return tx_ba_tsr_tbl; } } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); return NULL; } @@ -563,7 +558,6 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, { struct mwifiex_tx_ba_stream_tbl *new_node; struct mwifiex_ra_list_tbl *ra_list; - unsigned long flags; int tid_down; if (!mwifiex_get_ba_tbl(priv, tid, ra)) { @@ -584,9 +578,9 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, new_node->ba_status = ba_status; memcpy(new_node->ra, ra, ETH_ALEN); - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); } } @@ -599,7 +593,6 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) u32 tx_win_size = priv->add_ba_param.tx_win_size; static u8 dialog_tok; int ret; - unsigned long flags; u16 block_ack_param_set; mwifiex_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid); @@ -612,10 +605,10 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) { struct mwifiex_sta_node *sta_ptr; - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); if (!sta_ptr) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); mwifiex_dbg(priv->adapter, ERROR, "BA setup with unknown TDLS peer %pM!\n", peer_mac); @@ -623,7 +616,7 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) } if (sta_ptr->is_11ac_enabled) tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); } block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) | @@ -687,9 +680,8 @@ int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, void mwifiex_11n_delba(struct mwifiex_private *priv, int tid) { struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; - unsigned long flags; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { if (rx_reor_tbl_ptr->tid == tid) { dev_dbg(priv->adapter->dev, @@ -700,7 +692,7 @@ void mwifiex_11n_delba(struct mwifiex_private *priv, int tid) } } exit: - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); } /* @@ -729,9 +721,8 @@ int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf; struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr; int count = 0; - unsigned long flags; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid; @@ -750,7 +741,7 @@ int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED) break; } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); return count; } @@ -764,9 +755,8 @@ int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; int count = 0; - unsigned long flags; - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid; mwifiex_dbg(priv->adapter, DATA, "data: %s tid=%d\n", @@ -778,7 +768,7 @@ int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) break; } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); return count; } @@ -790,16 +780,15 @@ int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra) { struct mwifiex_tx_ba_stream_tbl *tbl, *tmp; - unsigned long flags; if (!ra) return; - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) if (!memcmp(tbl->ra, ra, ETH_ALEN)) mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); return; } diff --git a/drivers/net/wireless/marvell/mwifiex/11n.h b/drivers/net/wireless/marvell/mwifiex/11n.h index ea0fa68b9913..33268ce2cd82 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.h +++ b/drivers/net/wireless/marvell/mwifiex/11n.h @@ -147,11 +147,10 @@ mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, int tid; u8 ret = false; struct mwifiex_tx_ba_stream_tbl *tx_tbl; - unsigned long flags; tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; @@ -160,7 +159,7 @@ mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, ret = true; } } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); return ret; } diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c index 042a1d07f686..088612438530 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c @@ -155,7 +155,7 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *pra_list, - int ptrindex, unsigned long ra_list_flags) + int ptrindex) __releases(&priv->wmm.ra_list_spinlock) { struct mwifiex_adapter *adapter = priv->adapter; @@ -168,8 +168,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_src = skb_peek(&pra_list->skb_head); if (!skb_src) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); return 0; } @@ -177,8 +176,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size, GFP_ATOMIC); if (!skb_aggr) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); return -1; } @@ -208,17 +206,15 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, pra_list->total_pkt_count--; atomic_dec(&priv->wmm.tx_pkts_queued); aggr_num++; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); mwifiex_write_data_complete(adapter, skb_src, 0, 0); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); return -1; } @@ -232,7 +228,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, } while (skb_src); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); /* Last AMSDU packet does not need padding */ skb_trim(skb_aggr, skb_aggr->len - pad); @@ -265,10 +261,9 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, } switch (ret) { case -EBUSY: - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); mwifiex_write_data_complete(adapter, skb_aggr, 1, -1); return -1; } @@ -286,8 +281,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, atomic_inc(&priv->wmm.tx_pkts_queued); tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); break; case -1: diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.h b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h index 0cd2a3eb6c17..8279b159da7c 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.h +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h @@ -27,7 +27,7 @@ int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, struct sk_buff *skb); int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ptr, - int ptr_index, unsigned long flags) + int ptr_index) __releases(&priv->wmm.ra_list_spinlock); #endif /* !_MWIFIEX_11N_AGGR_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index 5380fba652cc..05a3c61ac603 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -76,7 +76,8 @@ static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, /* This function will process the rx packet and forward it to kernel/upper * layer. */ -static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) +static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, + struct sk_buff *payload) { int ret; @@ -109,27 +110,25 @@ mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl, int start_win) { + struct sk_buff_head list; + struct sk_buff *skb; int pkt_to_send, i; - void *rx_tmp_ptr; - unsigned long flags; + + __skb_queue_head_init(&list); + spin_lock_bh(&priv->rx_reorder_tbl_lock); pkt_to_send = (start_win > tbl->start_win) ? min((start_win - tbl->start_win), tbl->win_size) : tbl->win_size; for (i = 0; i < pkt_to_send; ++i) { - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - rx_tmp_ptr = NULL; if (tbl->rx_reorder_ptr[i]) { - rx_tmp_ptr = tbl->rx_reorder_ptr[i]; + skb = tbl->rx_reorder_ptr[i]; + __skb_queue_tail(&list, skb); tbl->rx_reorder_ptr[i] = NULL; } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - if (rx_tmp_ptr) - mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); } - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); /* * We don't have a circular buffer, hence use rotation to simulate * circular buffer @@ -140,7 +139,10 @@ mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, } tbl->start_win = start_win; - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + while ((skb = __skb_dequeue(&list))) + mwifiex_11n_dispatch_pkt(priv, skb); } /* @@ -155,24 +157,21 @@ static void mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl) { + struct sk_buff_head list; + struct sk_buff *skb; int i, j, xchg; - void *rx_tmp_ptr; - unsigned long flags; + + __skb_queue_head_init(&list); + spin_lock_bh(&priv->rx_reorder_tbl_lock); for (i = 0; i < tbl->win_size; ++i) { - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - if (!tbl->rx_reorder_ptr[i]) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); + if (!tbl->rx_reorder_ptr[i]) break; - } - rx_tmp_ptr = tbl->rx_reorder_ptr[i]; + skb = tbl->rx_reorder_ptr[i]; + __skb_queue_tail(&list, skb); tbl->rx_reorder_ptr[i] = NULL; - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); } - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); /* * We don't have a circular buffer, hence use rotation to simulate * circular buffer @@ -185,7 +184,11 @@ mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, } } tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + spin_unlock_bh(&priv->rx_reorder_tbl_lock); + + while ((skb = __skb_dequeue(&list))) + mwifiex_11n_dispatch_pkt(priv, skb); } /* @@ -198,19 +201,18 @@ static void mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl) { - unsigned long flags; int start_win; if (!tbl) return; - spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); + spin_lock_bh(&priv->adapter->rx_proc_lock); priv->adapter->rx_locked = true; if (priv->adapter->rx_processing) { - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + spin_unlock_bh(&priv->adapter->rx_proc_lock); flush_workqueue(priv->adapter->rx_workqueue); } else { - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + spin_unlock_bh(&priv->adapter->rx_proc_lock); } start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); @@ -219,16 +221,16 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, del_timer_sync(&tbl->timer_context.timer); tbl->timer_context.timer_is_set = false; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); list_del(&tbl->list); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); kfree(tbl->rx_reorder_ptr); kfree(tbl); - spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); + spin_lock_bh(&priv->adapter->rx_proc_lock); priv->adapter->rx_locked = false; - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + spin_unlock_bh(&priv->adapter->rx_proc_lock); } @@ -240,17 +242,15 @@ struct mwifiex_rx_reorder_tbl * mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) { struct mwifiex_rx_reorder_tbl *tbl; - unsigned long flags; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); return tbl; } } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); return NULL; } @@ -261,21 +261,19 @@ mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) { struct mwifiex_rx_reorder_tbl *tbl, *tmp; - unsigned long flags; if (!ta) return; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { if (!memcmp(tbl->ta, ta, ETH_ALEN)) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); mwifiex_del_rx_reorder_entry(priv, tbl); - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); } } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); return; } @@ -289,18 +287,16 @@ mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) { struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; struct mwifiex_private *priv = ctx->priv; - unsigned long flags; int i; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); return i; } } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); return -1; } @@ -348,7 +344,6 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, int i; struct mwifiex_rx_reorder_tbl *tbl, *new_node; u16 last_seq = 0; - unsigned long flags; struct mwifiex_sta_node *node; /* @@ -372,7 +367,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, new_node->init_win = seq_num; new_node->flags = 0; - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); if (mwifiex_queuing_ra_based(priv)) { if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { node = mwifiex_get_sta_entry(priv, ta); @@ -386,7 +381,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, else last_seq = priv->rx_seq[tid]; } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); mwifiex_dbg(priv->adapter, INFO, "info: last_seq=%d start_win=%d\n", @@ -418,9 +413,9 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, for (i = 0; i < win_size; ++i) new_node->rx_reorder_ptr[i] = NULL; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); } static void @@ -476,18 +471,17 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, u32 rx_win_size = priv->add_ba_param.rx_win_size; u8 tid; int win_size; - unsigned long flags; uint16_t block_ack_param_set; if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && priv->adapter->is_hw_11ac_capable && memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); sta_ptr = mwifiex_get_sta_entry(priv, cmd_addba_req->peer_mac_addr); if (!sta_ptr) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); mwifiex_dbg(priv->adapter, ERROR, "BA setup with unknown TDLS peer %pM!\n", cmd_addba_req->peer_mac_addr); @@ -495,7 +489,7 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, } if (sta_ptr->is_11ac_enabled) rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); } cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); @@ -682,7 +676,6 @@ mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, struct mwifiex_tx_ba_stream_tbl *ptx_tbl; struct mwifiex_ra_list_tbl *ra_list; u8 cleanup_rx_reorder_tbl; - unsigned long flags; int tid_down; if (type == TYPE_DELBA_RECEIVE) @@ -716,9 +709,9 @@ mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, ra_list->amsdu_in_ampdu = false; ra_list->ba_status = BA_SETUP_NONE; } - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + spin_lock_bh(&priv->tx_ba_stream_tbl_lock); mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + spin_unlock_bh(&priv->tx_ba_stream_tbl_lock); } } @@ -804,17 +797,16 @@ void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) { struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; - unsigned long flags; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); list_for_each_entry_safe(del_tbl_ptr, tmp_node, &priv->rx_reorder_tbl_ptr, list) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); } INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); mwifiex_reset_11n_rx_seq_num(priv); } @@ -826,7 +818,6 @@ void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) { struct mwifiex_private *priv; struct mwifiex_rx_reorder_tbl *tbl; - unsigned long lock_flags; int i; for (i = 0; i < adapter->priv_num; i++) { @@ -834,10 +825,10 @@ void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) if (!priv) continue; - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); + spin_lock_bh(&priv->rx_reorder_tbl_lock); list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) tbl->flags = flags; - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); + spin_unlock_bh(&priv->rx_reorder_tbl_lock); } return; diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 5a7cdb981789..d89684168500 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -876,13 +876,13 @@ static int mwifiex_deinit_priv_params(struct mwifiex_private *priv) spin_unlock_irqrestore(&adapter->main_proc_lock, flags); } - spin_lock_irqsave(&adapter->rx_proc_lock, flags); + spin_lock_bh(&adapter->rx_proc_lock); adapter->rx_locked = true; if (adapter->rx_processing) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); flush_workqueue(adapter->rx_workqueue); } else { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); } mwifiex_free_priv(priv); @@ -934,9 +934,9 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv, adapter->main_locked = false; spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - spin_lock_irqsave(&adapter->rx_proc_lock, flags); + spin_lock_bh(&adapter->rx_proc_lock); adapter->rx_locked = false; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); mwifiex_set_mac_address(priv, dev, false, NULL); @@ -1827,7 +1827,6 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct mwifiex_sta_node *sta_node; u8 deauth_mac[ETH_ALEN]; - unsigned long flags; if (!priv->bss_started && priv->wdev.cac_started) { mwifiex_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__); @@ -1845,11 +1844,11 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, eth_zero_addr(deauth_mac); - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); sta_node = mwifiex_get_sta_entry(priv, params->mac); if (sta_node) ether_addr_copy(deauth_mac, params->mac); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); if (is_valid_ether_addr(deauth_mac)) { if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, @@ -3852,15 +3851,14 @@ mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_chan_def *chandef) { struct mwifiex_sta_node *sta_ptr; - unsigned long flags; u16 chan; u8 second_chan_offset, band; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); sta_ptr = mwifiex_get_sta_entry(priv, addr); if (!sta_ptr) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", __func__, addr); return -ENOENT; @@ -3868,18 +3866,18 @@ mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev, if (!(sta_ptr->tdls_cap.extcap.ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); wiphy_err(wiphy, "%pM do not support tdls cs\n", addr); return -ENOENT; } if (sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); wiphy_err(wiphy, "channel switch is running, abort request\n"); return -EALREADY; } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); chan = chandef->chan->hw_value; second_chan_offset = mwifiex_get_sec_chan_offset(chan); @@ -3895,23 +3893,22 @@ mwifiex_cfg80211_tdls_cancel_chan_switch(struct wiphy *wiphy, const u8 *addr) { struct mwifiex_sta_node *sta_ptr; - unsigned long flags; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); sta_ptr = mwifiex_get_sta_entry(priv, addr); if (!sta_ptr) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", __func__, addr); } else if (!(sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || sta_ptr->tdls_status == TDLS_IN_BASE_CHAN || sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); wiphy_err(wiphy, "tdls chan switch not initialize by %pM\n", addr); } else { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); mwifiex_stop_tdls_cs(priv, addr); } } diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index 133b03d7daf9..e8788c35a453 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -61,19 +61,18 @@ static struct cmd_ctrl_node * mwifiex_get_cmd_node(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node; - unsigned long flags; - spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); + spin_lock_bh(&adapter->cmd_free_q_lock); if (list_empty(&adapter->cmd_free_q)) { mwifiex_dbg(adapter, ERROR, "GET_CMD_NODE: cmd node not available\n"); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); + spin_unlock_bh(&adapter->cmd_free_q_lock); return NULL; } cmd_node = list_first_entry(&adapter->cmd_free_q, struct cmd_ctrl_node, list); list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); + spin_unlock_bh(&adapter->cmd_free_q_lock); return cmd_node; } @@ -117,8 +116,6 @@ static void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node) { - unsigned long flags; - if (!cmd_node) return; @@ -128,9 +125,9 @@ mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, mwifiex_clean_cmd_node(adapter, cmd_node); /* Insert node into cmd_free_q */ - spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); + spin_lock_bh(&adapter->cmd_free_q_lock); list_add_tail(&cmd_node->list, &adapter->cmd_free_q); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); + spin_unlock_bh(&adapter->cmd_free_q_lock); } /* This function reuses a command node. */ @@ -183,7 +180,6 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, struct host_cmd_ds_command *host_cmd; uint16_t cmd_code; uint16_t cmd_size; - unsigned long flags; if (!adapter || !cmd_node) return -1; @@ -223,9 +219,9 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, cmd_node->priv->bss_num, cmd_node->priv->bss_type)); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->curr_cmd = cmd_node; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); /* Adjust skb length */ if (cmd_node->cmd_skb->len > cmd_size) @@ -276,9 +272,9 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, adapter->cmd_wait_q.status = -1; mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); adapter->dbg.num_cmd_host_to_card_failure++; return -1; @@ -697,7 +693,6 @@ mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, { struct host_cmd_ds_command *host_cmd = NULL; u16 command; - unsigned long flags; bool add_tail = true; host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); @@ -719,12 +714,12 @@ mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, } } - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + spin_lock_bh(&adapter->cmd_pending_q_lock); if (add_tail) list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); else list_add(&cmd_node->list, &adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + spin_unlock_bh(&adapter->cmd_pending_q_lock); atomic_inc(&adapter->cmd_pending); mwifiex_dbg(adapter, CMD, @@ -749,8 +744,6 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) struct cmd_ctrl_node *cmd_node; int ret = 0; struct host_cmd_ds_command *host_cmd; - unsigned long cmd_flags; - unsigned long cmd_pending_q_flags; /* Check if already in processing */ if (adapter->curr_cmd) { @@ -759,13 +752,12 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) return -1; } - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); /* Check if any command is pending */ - spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); + spin_lock_bh(&adapter->cmd_pending_q_lock); if (list_empty(&adapter->cmd_pending_q)) { - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); return 0; } cmd_node = list_first_entry(&adapter->cmd_pending_q, @@ -778,17 +770,15 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) mwifiex_dbg(adapter, ERROR, "%s: cannot send cmd in sleep state,\t" "this should not happen\n", __func__); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); return ret; } list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); + spin_unlock_bh(&adapter->cmd_pending_q_lock); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); /* Any command sent to the firmware when host is in sleep @@ -822,7 +812,6 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) uint16_t orig_cmdresp_no; uint16_t cmdresp_no; uint16_t cmdresp_result; - unsigned long flags; if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { resp = (struct host_cmd_ds_command *) adapter->upld_buf; @@ -891,9 +880,9 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) adapter->cmd_wait_q.status = -1; mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); return -1; } @@ -925,9 +914,9 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); } return ret; @@ -1033,17 +1022,16 @@ void mwifiex_cancel_pending_scan_cmd(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; - unsigned long flags; /* Cancel all pending scan command */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + spin_lock_bh(&adapter->scan_pending_q_lock); list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); cmd_node->wait_q_enabled = false; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_unlock_bh(&adapter->scan_pending_q_lock); } /* @@ -1057,9 +1045,8 @@ void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; - unsigned long flags, cmd_flags; - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); /* Cancel current cmd */ if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { adapter->cmd_wait_q.status = -1; @@ -1068,7 +1055,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) /* no recycle probably wait for response */ } /* Cancel all pending command */ - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + spin_lock_bh(&adapter->cmd_pending_q_lock); list_for_each_entry_safe(cmd_node, tmp_node, &adapter->cmd_pending_q, list) { list_del(&cmd_node->list); @@ -1077,8 +1064,8 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) adapter->cmd_wait_q.status = -1; mwifiex_recycle_cmd_node(adapter, cmd_node); } - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_unlock_bh(&adapter->cmd_pending_q_lock); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); mwifiex_cancel_scan(adapter); } @@ -1097,11 +1084,10 @@ static void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node = NULL; - unsigned long cmd_flags; if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); cmd_node = adapter->curr_cmd; /* setting curr_cmd to NULL is quite dangerous, because * mwifiex_process_cmdresp checks curr_cmd to be != NULL @@ -1112,7 +1098,7 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) * at that point */ adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); mwifiex_recycle_cmd_node(adapter, cmd_node); } diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index 673e89dff0b5..6c0e52eb8794 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -36,7 +36,6 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bss_prio_node *bss_prio; struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; - unsigned long flags; bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL); if (!bss_prio) @@ -45,9 +44,9 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) bss_prio->priv = priv; INIT_LIST_HEAD(&bss_prio->list); - spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); + spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock); list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head); - spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); + spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock); return 0; } @@ -344,11 +343,9 @@ void mwifiex_set_trans_start(struct net_device *dev) void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter) { - unsigned long dev_queue_flags; - - spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); + spin_lock_bh(&adapter->queue_lock); netif_tx_wake_all_queues(netdev); - spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); + spin_unlock_bh(&adapter->queue_lock); } /* @@ -357,11 +354,9 @@ void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, void mwifiex_stop_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter) { - unsigned long dev_queue_flags; - - spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); + spin_lock_bh(&adapter->queue_lock); netif_tx_stop_all_queues(netdev); - spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); + spin_unlock_bh(&adapter->queue_lock); } /* @@ -506,7 +501,6 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter) struct mwifiex_private *priv; u8 i, first_sta = true; int is_cmd_pend_q_empty; - unsigned long flags; adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; @@ -547,9 +541,9 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter) } } - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + spin_lock_bh(&adapter->cmd_pending_q_lock); is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + spin_unlock_bh(&adapter->cmd_pending_q_lock); if (!is_cmd_pend_q_empty) { /* Send the first command in queue and return */ if (mwifiex_main_process(adapter) != -1) @@ -574,7 +568,6 @@ static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) struct mwifiex_bss_prio_node *bssprio_node, *tmp_node; struct list_head *head; spinlock_t *lock; /* bss priority lock */ - unsigned long flags; for (i = 0; i < adapter->priv_num; ++i) { head = &adapter->bss_prio_tbl[i].bss_prio_head; @@ -586,7 +579,7 @@ static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) priv->bss_type, priv->bss_num, i, head); { - spin_lock_irqsave(lock, flags); + spin_lock_bh(lock); list_for_each_entry_safe(bssprio_node, tmp_node, head, list) { if (bssprio_node->priv == priv) { @@ -598,7 +591,7 @@ static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) kfree(bssprio_node); } } - spin_unlock_irqrestore(lock, flags); + spin_unlock_bh(lock); } } } @@ -630,7 +623,6 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; s32 i; - unsigned long flags; struct sk_buff *skb; /* mwifiex already shutdown */ @@ -665,7 +657,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) while ((skb = skb_dequeue(&adapter->tx_data_q))) mwifiex_write_data_complete(adapter, skb, 0, 0); - spin_lock_irqsave(&adapter->rx_proc_lock, flags); + spin_lock_bh(&adapter->rx_proc_lock); while ((skb = skb_dequeue(&adapter->rx_data_q))) { struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); @@ -678,7 +670,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) dev_kfree_skb_any(skb); } - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); mwifiex_adapter_cleanup(adapter); diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 4c14b4865367..a9657ae6d782 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -173,30 +173,27 @@ EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) { - unsigned long flags; - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); + spin_lock_bh(&adapter->rx_proc_lock); if (adapter->rx_processing) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); } else { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); queue_work(adapter->rx_workqueue, &adapter->rx_work); } } static int mwifiex_process_rx(struct mwifiex_adapter *adapter) { - unsigned long flags; struct sk_buff *skb; struct mwifiex_rxinfo *rx_info; - spin_lock_irqsave(&adapter->rx_proc_lock, flags); + spin_lock_bh(&adapter->rx_proc_lock); if (adapter->rx_processing || adapter->rx_locked) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); goto exit_rx_proc; } else { adapter->rx_processing = true; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); } /* Check for Rx data */ @@ -219,9 +216,9 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) mwifiex_handle_rx_packet(adapter, skb); } } - spin_lock_irqsave(&adapter->rx_proc_lock, flags); + spin_lock_bh(&adapter->rx_proc_lock); adapter->rx_processing = false; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + spin_unlock_bh(&adapter->rx_proc_lock); exit_rx_proc: return 0; @@ -825,13 +822,12 @@ mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, skb = skb_clone(skb, GFP_ATOMIC); if (skb) { - unsigned long flags; int id; - spin_lock_irqsave(&priv->ack_status_lock, flags); + spin_lock_bh(&priv->ack_status_lock); id = idr_alloc(&priv->ack_status_frames, orig_skb, 1, 0x10, GFP_ATOMIC); - spin_unlock_irqrestore(&priv->ack_status_lock, flags); + spin_unlock_bh(&priv->ack_status_lock); if (id >= 0) { tx_info = MWIFIEX_SKB_TXCB(skb); @@ -1354,12 +1350,11 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv, */ int is_command_pending(struct mwifiex_adapter *adapter) { - unsigned long flags; int is_cmd_pend_q_empty; - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + spin_lock_bh(&adapter->cmd_pending_q_lock); is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + spin_unlock_bh(&adapter->cmd_pending_q_lock); return !is_cmd_pend_q_empty; } diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index c269a0de9413..d870d4b2e03d 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -1500,7 +1500,6 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, u8 filtered_scan; u8 scan_current_chan_only; u8 max_chan_per_scan; - unsigned long flags; if (adapter->scan_processing) { mwifiex_dbg(adapter, WARN, @@ -1521,9 +1520,9 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, return -EFAULT; } - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->scan_processing = true; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), GFP_KERNEL); @@ -1551,13 +1550,12 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, /* Get scan command from scan_pending_q and put to cmd_pending_q */ if (!ret) { - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + spin_lock_bh(&adapter->scan_pending_q_lock); if (!list_empty(&adapter->scan_pending_q)) { cmd_node = list_first_entry(&adapter->scan_pending_q, struct cmd_ctrl_node, list); list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); + spin_unlock_bh(&adapter->scan_pending_q_lock); mwifiex_insert_cmd_to_pending_q(adapter, cmd_node); queue_work(adapter->workqueue, &adapter->main_work); @@ -1568,8 +1566,7 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, mwifiex_wait_queue_complete(adapter, cmd_node); } } else { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); + spin_unlock_bh(&adapter->scan_pending_q_lock); } } @@ -1577,9 +1574,9 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, kfree(scan_chan_list); done: if (ret) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); } return ret; } @@ -1715,7 +1712,6 @@ static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, { struct mwifiex_bssdescriptor *bss_desc; int ret; - unsigned long flags; /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); @@ -1730,7 +1726,7 @@ static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, if (ret) goto done; - spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); + spin_lock_bh(&priv->curr_bcn_buf_lock); /* Make a copy of current BSSID descriptor */ memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, sizeof(priv->curr_bss_params.bss_descriptor)); @@ -1739,7 +1735,7 @@ static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, * in mwifiex_save_curr_bcn() */ mwifiex_save_curr_bcn(priv); - spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); + spin_unlock_bh(&priv->curr_bcn_buf_lock); done: /* beacon_ie buffer was allocated in function @@ -1993,15 +1989,14 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) { struct mwifiex_adapter *adapter = priv->adapter; struct cmd_ctrl_node *cmd_node; - unsigned long flags; - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + spin_lock_bh(&adapter->scan_pending_q_lock); if (list_empty(&adapter->scan_pending_q)) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_unlock_bh(&adapter->scan_pending_q_lock); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); mwifiex_active_scan_req_for_passive_chan(priv); @@ -2025,13 +2020,13 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) } } else if ((priv->scan_aborting && !priv->scan_request) || priv->scan_block) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_unlock_bh(&adapter->scan_pending_q_lock); mwifiex_cancel_pending_scan_cmd(adapter); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); if (!adapter->active_scan_triggered) { if (priv->scan_request) { @@ -2057,7 +2052,7 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) cmd_node = list_first_entry(&adapter->scan_pending_q, struct cmd_ctrl_node, list); list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_unlock_bh(&adapter->scan_pending_q_lock); mwifiex_insert_cmd_to_pending_q(adapter, cmd_node); } @@ -2067,15 +2062,14 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) void mwifiex_cancel_scan(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; - unsigned long cmd_flags; int i; mwifiex_cancel_pending_scan_cmd(adapter); if (adapter->scan_processing) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (!priv) @@ -2557,7 +2551,6 @@ int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd_ptr; struct cmd_ctrl_node *cmd_node; - unsigned long cmd_flags, scan_flags; bool complete_scan = false; mwifiex_dbg(adapter, INFO, "info: EXT scan returns successfully\n"); @@ -2592,8 +2585,8 @@ int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, sizeof(struct mwifiex_ie_types_header)); } - spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_flags); - spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_flags); + spin_lock_bh(&adapter->cmd_pending_q_lock); + spin_lock_bh(&adapter->scan_pending_q_lock); if (list_empty(&adapter->scan_pending_q)) { complete_scan = true; list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { @@ -2607,8 +2600,8 @@ int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, } } } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_flags); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, cmd_flags); + spin_unlock_bh(&adapter->scan_pending_q_lock); + spin_unlock_bh(&adapter->cmd_pending_q_lock); if (complete_scan) mwifiex_complete_scan(priv); @@ -2780,13 +2773,12 @@ mwifiex_queue_scan_cmd(struct mwifiex_private *priv, struct cmd_ctrl_node *cmd_node) { struct mwifiex_adapter *adapter = priv->adapter; - unsigned long flags; cmd_node->wait_q_enabled = true; cmd_node->condition = &adapter->scan_wait_q_woken; - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + spin_lock_bh(&adapter->scan_pending_q_lock); list_add_tail(&cmd_node->list, &adapter->scan_pending_q); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_unlock_bh(&adapter->scan_pending_q_lock); } /* diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c index 24b33e20e7a9..20c206da0631 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c @@ -46,7 +46,6 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_ps_mode_enh *pm; - unsigned long flags; mwifiex_dbg(adapter, ERROR, "CMD_RESP: cmd %#x error, result=%#x\n", @@ -87,9 +86,9 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, /* Handling errors here */ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + spin_lock_bh(&adapter->mwifiex_cmd_lock); adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + spin_unlock_bh(&adapter->mwifiex_cmd_lock); } /* diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c index 8b3123cb84c8..5fdffb114913 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_event.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c @@ -345,7 +345,6 @@ static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv, { struct mwifiex_tx_pause_tlv *tp; struct mwifiex_sta_node *sta_ptr; - unsigned long flags; tp = (void *)tlv; mwifiex_dbg(priv->adapter, EVENT, @@ -361,14 +360,14 @@ static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv, } else if (is_multicast_ether_addr(tp->peermac)) { mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); } else { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { sta_ptr->tx_pause = tp->tx_pause; mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); } } @@ -378,7 +377,6 @@ static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv, struct mwifiex_tx_pause_tlv *tp; struct mwifiex_sta_node *sta_ptr; int status; - unsigned long flags; tp = (void *)tlv; mwifiex_dbg(priv->adapter, EVENT, @@ -397,7 +395,7 @@ static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv, status = mwifiex_get_tdls_link_status(priv, tp->peermac); if (mwifiex_is_tdls_link_setup(status)) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { sta_ptr->tx_pause = tp->tx_pause; @@ -405,7 +403,7 @@ static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv, tp->peermac, tp->tx_pause); } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); } } } diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 27779d7317fd..18e654dc34c6 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -33,12 +33,11 @@ static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, struct list_head *tid_list; struct sk_buff *skb, *tmp; struct mwifiex_txinfo *tx_info; - unsigned long flags; u32 tid; u8 tid_down; mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { if (!ether_addr_equal(mac, skb->data)) @@ -78,7 +77,7 @@ static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, atomic_inc(&priv->wmm.tx_pkts_queued); } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); return; } @@ -88,11 +87,10 @@ static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra_list; struct list_head *ra_list_head; struct sk_buff *skb, *tmp; - unsigned long flags; int i; mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); for (i = 0; i < MAX_NUM_TID; i++) { if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { @@ -111,7 +109,7 @@ static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, } } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); return; } @@ -1070,7 +1068,6 @@ mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct mwifiex_ds_tdls_oper tdls_oper; - unsigned long flags; memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); sta_ptr = mwifiex_get_sta_entry(priv, peer); @@ -1078,11 +1075,9 @@ mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) if (sta_ptr) { if (sta_ptr->is_11n_enabled) { mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } mwifiex_del_sta_entry(priv, peer); } @@ -1100,7 +1095,6 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) { struct mwifiex_sta_node *sta_ptr; struct ieee80211_mcs_info mcs; - unsigned long flags; int i; sta_ptr = mwifiex_get_sta_entry(priv, peer); @@ -1145,11 +1139,9 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) "tdls: enable link %pM failed\n", peer); if (sta_ptr) { mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); mwifiex_del_sta_entry(priv, peer); } mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); @@ -1194,7 +1186,6 @@ int mwifiex_get_tdls_list(struct mwifiex_private *priv, struct mwifiex_sta_node *sta_ptr; struct tdls_peer_info *peer = buf; int count = 0; - unsigned long flags; if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) return 0; @@ -1203,7 +1194,7 @@ int mwifiex_get_tdls_list(struct mwifiex_private *priv, if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) return 0; - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); list_for_each_entry(sta_ptr, &priv->sta_list, list) { if (mwifiex_is_tdls_link_setup(sta_ptr->tdls_status)) { ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr); @@ -1213,7 +1204,7 @@ int mwifiex_get_tdls_list(struct mwifiex_private *priv, break; } } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); return count; } @@ -1222,7 +1213,6 @@ void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) { struct mwifiex_sta_node *sta_ptr; struct mwifiex_ds_tdls_oper tdls_oper; - unsigned long flags; if (list_empty(&priv->sta_list)) return; @@ -1232,11 +1222,9 @@ void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) if (sta_ptr->is_11n_enabled) { mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, @@ -1256,12 +1244,11 @@ void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb) { struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; u8 mac[ETH_ALEN]; ether_addr_copy(mac, skb->data); - spin_lock_irqsave(&priv->auto_tdls_lock, flags); + spin_lock_bh(&priv->auto_tdls_lock); list_for_each_entry(peer, &priv->auto_tdls_list, list) { if (!memcmp(mac, peer->mac_addr, ETH_ALEN)) { if (peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && @@ -1290,7 +1277,7 @@ int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb) } } } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + spin_unlock_bh(&priv->auto_tdls_lock); return 0; } @@ -1298,33 +1285,31 @@ int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb) void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv) { struct mwifiex_auto_tdls_peer *peer, *tmp_node; - unsigned long flags; - spin_lock_irqsave(&priv->auto_tdls_lock, flags); + spin_lock_bh(&priv->auto_tdls_lock); list_for_each_entry_safe(peer, tmp_node, &priv->auto_tdls_list, list) { list_del(&peer->list); kfree(peer); } INIT_LIST_HEAD(&priv->auto_tdls_list); - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + spin_unlock_bh(&priv->auto_tdls_lock); priv->check_tdls_tx = false; } void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_auto_tdls_peer *tdls_peer; - unsigned long flags; if (!priv->adapter->auto_tdls) return; - spin_lock_irqsave(&priv->auto_tdls_lock, flags); + spin_lock_bh(&priv->auto_tdls_lock); list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { if (!memcmp(tdls_peer->mac_addr, mac, ETH_ALEN)) { tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; tdls_peer->rssi_jiffies = jiffies; - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + spin_unlock_bh(&priv->auto_tdls_lock); return; } } @@ -1341,19 +1326,18 @@ void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac) "Add auto TDLS peer= %pM to list\n", mac); } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + spin_unlock_bh(&priv->auto_tdls_lock); } void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, const u8 *mac, u8 link_status) { struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; if (!priv->adapter->auto_tdls) return; - spin_lock_irqsave(&priv->auto_tdls_lock, flags); + spin_lock_bh(&priv->auto_tdls_lock); list_for_each_entry(peer, &priv->auto_tdls_list, list) { if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { if ((link_status == TDLS_NOT_SETUP) && @@ -1366,19 +1350,18 @@ void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, break; } } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + spin_unlock_bh(&priv->auto_tdls_lock); } void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, u8 *mac, s8 snr, s8 nflr) { struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; if (!priv->adapter->auto_tdls) return; - spin_lock_irqsave(&priv->auto_tdls_lock, flags); + spin_lock_bh(&priv->auto_tdls_lock); list_for_each_entry(peer, &priv->auto_tdls_list, list) { if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { peer->rssi = nflr - snr; @@ -1386,14 +1369,13 @@ void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, break; } } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + spin_unlock_bh(&priv->auto_tdls_lock); } void mwifiex_check_auto_tdls(struct timer_list *t) { struct mwifiex_private *priv = from_timer(priv, t, auto_tdls_timer); struct mwifiex_auto_tdls_peer *tdls_peer; - unsigned long flags; u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; if (WARN_ON_ONCE(!priv || !priv->adapter)) { @@ -1413,7 +1395,7 @@ void mwifiex_check_auto_tdls(struct timer_list *t) priv->check_tdls_tx = false; - spin_lock_irqsave(&priv->auto_tdls_lock, flags); + spin_lock_bh(&priv->auto_tdls_lock); list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { if ((jiffies - tdls_peer->rssi_jiffies) > (MWIFIEX_AUTO_TDLS_IDLE_TIME * HZ)) { @@ -1448,7 +1430,7 @@ void mwifiex_check_auto_tdls(struct timer_list *t) tdls_peer->rssi); } } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + spin_unlock_bh(&priv->auto_tdls_lock); mod_timer(&priv->auto_tdls_timer, jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); diff --git a/drivers/net/wireless/marvell/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c index d848933466d9..e3c1446dd847 100644 --- a/drivers/net/wireless/marvell/mwifiex/txrx.c +++ b/drivers/net/wireless/marvell/mwifiex/txrx.c @@ -334,15 +334,14 @@ void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, { struct tx_status_event *tx_status = (void *)priv->adapter->event_body; struct sk_buff *ack_skb; - unsigned long flags; struct mwifiex_txinfo *tx_info; if (!tx_status->tx_token_id) return; - spin_lock_irqsave(&priv->ack_status_lock, flags); + spin_lock_bh(&priv->ack_status_lock); ack_skb = idr_remove(&priv->ack_status_frames, tx_status->tx_token_id); - spin_unlock_irqrestore(&priv->ack_status_lock, flags); + spin_unlock_bh(&priv->ack_status_lock); if (ack_skb) { tx_info = MWIFIEX_SKB_TXCB(ack_skb); diff --git a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c index 5ce85d5727e4..354b09c5e8dc 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c @@ -71,11 +71,10 @@ mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, */ static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) { - unsigned long flags; struct list_head *ra_list; int i; - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) { if (priv->del_list_idx == MAX_NUM_TID) @@ -87,7 +86,7 @@ static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) } } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } @@ -378,7 +377,6 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, struct rx_packet_hdr *rx_pkt_hdr; u16 rx_pkt_type; u8 ta[ETH_ALEN], pkt_type; - unsigned long flags; struct mwifiex_sta_node *node; uap_rx_pd = (struct uap_rxpd *)(skb->data); @@ -413,12 +411,12 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); node = mwifiex_get_sta_entry(priv, ta); if (node) node->rx_seq[uap_rx_pd->priority] = le16_to_cpu(uap_rx_pd->seq_num); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); } if (!priv->ap_11n_enabled || diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index d445acc4786b..c2365eeb7016 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -1128,10 +1128,9 @@ static void mwifiex_usb_tx_aggr_tmo(struct timer_list *t) from_timer(timer_context, t, hold_timer); struct mwifiex_adapter *adapter = timer_context->adapter; struct usb_tx_data_port *port = timer_context->port; - unsigned long flags; int err = 0; - spin_lock_irqsave(&port->tx_aggr_lock, flags); + spin_lock_bh(&port->tx_aggr_lock); err = mwifiex_usb_prepare_tx_aggr_skb(adapter, port, &skb_send); if (err) { mwifiex_dbg(adapter, ERROR, @@ -1158,7 +1157,7 @@ done: if (err == -1) mwifiex_write_data_complete(adapter, skb_send, 0, -1); unlock: - spin_unlock_irqrestore(&port->tx_aggr_lock, flags); + spin_unlock_bh(&port->tx_aggr_lock); } /* This function write a command/data packet to card. */ @@ -1169,7 +1168,6 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, struct usb_card_rec *card = adapter->card; struct urb_context *context = NULL; struct usb_tx_data_port *port = NULL; - unsigned long flags; int idx, ret; if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { @@ -1211,10 +1209,10 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, } if (adapter->bus_aggr.enable) { - spin_lock_irqsave(&port->tx_aggr_lock, flags); + spin_lock_bh(&port->tx_aggr_lock); ret = mwifiex_usb_aggr_tx_data(adapter, ep, skb, tx_param, port); - spin_unlock_irqrestore(&port->tx_aggr_lock, flags); + spin_unlock_bh(&port->tx_aggr_lock); return ret; } diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c index f9b71539d33e..3b0d31827681 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.c +++ b/drivers/net/wireless/marvell/mwifiex/util.c @@ -607,12 +607,11 @@ struct mwifiex_sta_node * mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *node; - unsigned long flags; if (!mac) return NULL; - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); node = mwifiex_get_sta_entry(priv, mac); if (node) goto done; @@ -625,7 +624,7 @@ mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) list_add_tail(&node->list, &priv->sta_list); done: - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); return node; } @@ -662,9 +661,8 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) { struct mwifiex_sta_node *node; - unsigned long flags; - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); node = mwifiex_get_sta_entry(priv, mac); if (node) { @@ -672,7 +670,7 @@ void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) kfree(node); } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); return; } @@ -680,9 +678,8 @@ void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) void mwifiex_del_all_sta_list(struct mwifiex_private *priv) { struct mwifiex_sta_node *node, *tmp; - unsigned long flags; - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { list_del(&node->list); @@ -690,7 +687,7 @@ void mwifiex_del_all_sta_list(struct mwifiex_private *priv) } INIT_LIST_HEAD(&priv->sta_list); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); return; } diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index 407b9932ca4d..0301bc33f554 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -138,7 +138,6 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) struct mwifiex_ra_list_tbl *ra_list; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_sta_node *node; - unsigned long flags; for (i = 0; i < MAX_NUM_TID; ++i) { @@ -163,7 +162,7 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) ra_list->is_11n_enabled = IS_11N_ENABLED(priv); } } else { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); + spin_lock_bh(&priv->sta_list_spinlock); node = mwifiex_get_sta_entry(priv, ra); if (node) ra_list->tx_paused = node->tx_pause; @@ -171,7 +170,7 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) mwifiex_is_sta_11n_enabled(priv, node); if (ra_list->is_11n_enabled) ra_list->max_amsdu = node->max_amsdu; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + spin_unlock_bh(&priv->sta_list_spinlock); } mwifiex_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n", @@ -583,11 +582,10 @@ static int mwifiex_free_ack_frame(int id, void *p, void *data) void mwifiex_clean_txrx(struct mwifiex_private *priv) { - unsigned long flags; struct sk_buff *skb, *tmp; mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); mwifiex_wmm_cleanup_queues(priv); mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); @@ -601,7 +599,7 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) if (priv->adapter->if_ops.clean_pcie_ring && !test_bit(MWIFIEX_SURPRISE_REMOVED, &priv->adapter->work_flags)) priv->adapter->if_ops.clean_pcie_ring(priv->adapter); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { skb_unlink(skb, &priv->tdls_txq); @@ -642,10 +640,9 @@ void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, { struct mwifiex_ra_list_tbl *ra_list; u32 pkt_cnt = 0, tx_pkts_queued; - unsigned long flags; int i; - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); for (i = 0; i < MAX_NUM_TID; ++i) { ra_list = mwifiex_wmm_get_ralist_node(priv, i, mac); @@ -671,7 +668,7 @@ void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } /* This function updates non-tdls peer ralist tx_pause while @@ -682,10 +679,9 @@ void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, { struct mwifiex_ra_list_tbl *ra_list; u32 pkt_cnt = 0, tx_pkts_queued; - unsigned long flags; int i; - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); for (i = 0; i < MAX_NUM_TID; ++i) { list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[i].ra_list, @@ -716,7 +712,7 @@ void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } /* @@ -748,10 +744,9 @@ void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; - unsigned long flags; int i; - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); for (i = 0; i < MAX_NUM_TID; ++i) { ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr); @@ -767,7 +762,7 @@ mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) list_del(&ra_list->list); kfree(ra_list); } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } /* @@ -818,7 +813,6 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, u32 tid; struct mwifiex_ra_list_tbl *ra_list; u8 ra[ETH_ALEN], tid_down; - unsigned long flags; struct list_head list_head; int tdls_status = TDLS_NOT_SETUP; struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; @@ -844,7 +838,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, tid = skb->priority; - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); tid_down = mwifiex_wmm_downgrade_tid(priv, tid); @@ -864,8 +858,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, break; case TDLS_SETUP_INPROGRESS: skb_queue_tail(&priv->tdls_txq, skb); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); return; default: list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list; @@ -881,7 +874,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, } if (!ra_list) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); mwifiex_write_data_complete(adapter, skb, 0, -1); return; } @@ -901,7 +894,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, else atomic_inc(&priv->wmm.tx_pkts_queued); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } /* @@ -1092,7 +1085,6 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, struct mwifiex_ra_list_tbl *ptr; struct mwifiex_tid_tbl *tid_ptr; atomic_t *hqp; - unsigned long flags_ra; int i, j; /* check the BSS with highest priority first */ @@ -1118,8 +1110,7 @@ try_again: hqp = &priv_tmp->wmm.highest_queued_prio; for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { - spin_lock_irqsave(&priv_tmp->wmm. - ra_list_spinlock, flags_ra); + spin_lock_bh(&priv_tmp->wmm.ra_list_spinlock); tid_ptr = &(priv_tmp)->wmm. tid_tbl_ptr[tos_to_tid[i]]; @@ -1134,9 +1125,7 @@ try_again: goto found; } - spin_unlock_irqrestore(&priv_tmp->wmm. - ra_list_spinlock, - flags_ra); + spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock); } if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) != 0) { @@ -1158,7 +1147,7 @@ found: /* holds ra_list_spinlock */ if (atomic_read(hqp) > i) atomic_set(hqp, i); - spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra); + spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock); *priv = priv_tmp; *tid = tos_to_tid[i]; @@ -1182,24 +1171,23 @@ void mwifiex_rotate_priolists(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; struct mwifiex_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid]; - unsigned long flags; - spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); + spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock); /* * dirty trick: we remove 'head' temporarily and reinsert it after * curr bss node. imagine list to stay fixed while head is moved */ list_move(&tbl[priv->bss_priority].bss_prio_head, &tbl[priv->bss_priority].bss_prio_cur->list); - spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); + spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); if (mwifiex_is_ralist_valid(priv, ra, tid)) { priv->wmm.packets_out[tid]++; /* same as above */ list_move(&tid_ptr->ra_list, &ra->list); } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } /* @@ -1236,8 +1224,7 @@ mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv, */ static void mwifiex_send_single_packet(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int ptr_index, - unsigned long ra_list_flags) + struct mwifiex_ra_list_tbl *ptr, int ptr_index) __releases(&priv->wmm.ra_list_spinlock) { struct sk_buff *skb, *skb_next; @@ -1246,8 +1233,7 @@ mwifiex_send_single_packet(struct mwifiex_private *priv, struct mwifiex_txinfo *tx_info; if (skb_queue_empty(&ptr->skb_head)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); mwifiex_dbg(adapter, DATA, "data: nothing to send\n"); return; } @@ -1265,18 +1251,17 @@ mwifiex_send_single_packet(struct mwifiex_private *priv, else skb_next = NULL; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); tx_param.next_pkt_len = ((skb_next) ? skb_next->len + sizeof(struct txpd) : 0); if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { /* Queue the packet back at the head */ - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); mwifiex_write_data_complete(adapter, skb, 0, -1); return; } @@ -1286,8 +1271,7 @@ mwifiex_send_single_packet(struct mwifiex_private *priv, ptr->total_pkt_count++; ptr->ba_pkt_count++; tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } else { mwifiex_rotate_priolists(priv, ptr, ptr_index); atomic_dec(&priv->wmm.tx_pkts_queued); @@ -1323,8 +1307,7 @@ mwifiex_is_ptr_processed(struct mwifiex_private *priv, */ static void mwifiex_send_processed_packet(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int ptr_index, - unsigned long ra_list_flags) + struct mwifiex_ra_list_tbl *ptr, int ptr_index) __releases(&priv->wmm.ra_list_spinlock) { struct mwifiex_tx_param tx_param; @@ -1334,8 +1317,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, struct mwifiex_txinfo *tx_info; if (skb_queue_empty(&ptr->skb_head)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); return; } @@ -1343,8 +1325,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, if (adapter->data_sent || adapter->tx_lock_flag) { ptr->total_pkt_count--; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); skb_queue_tail(&adapter->tx_data_q, skb); atomic_dec(&priv->wmm.tx_pkts_queued); atomic_inc(&adapter->tx_queued); @@ -1358,7 +1339,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, tx_info = MWIFIEX_SKB_TXCB(skb); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); tx_param.next_pkt_len = ((skb_next) ? skb_next->len + @@ -1374,11 +1355,10 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, switch (ret) { case -EBUSY: mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); mwifiex_write_data_complete(adapter, skb, 0, -1); return; } @@ -1386,8 +1366,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, skb_queue_tail(&ptr->skb_head, skb); tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); break; case -1: mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); @@ -1404,10 +1383,9 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, if (ret != -EBUSY) { mwifiex_rotate_priolists(priv, ptr, ptr_index); atomic_dec(&priv->wmm.tx_pkts_queued); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); ptr->total_pkt_count--; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); } } @@ -1423,7 +1401,6 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) int ptr_index = 0; u8 ra[ETH_ALEN]; int tid_del = 0, tid = 0; - unsigned long flags; ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); if (!ptr) @@ -1433,14 +1410,14 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) mwifiex_dbg(adapter, DATA, "data: tid=%d\n", tid); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + spin_lock_bh(&priv->wmm.ra_list_spinlock); if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + spin_unlock_bh(&priv->wmm.ra_list_spinlock); return -1; } if (mwifiex_is_ptr_processed(priv, ptr)) { - mwifiex_send_processed_packet(priv, ptr, ptr_index, flags); + mwifiex_send_processed_packet(priv, ptr, ptr_index); /* ra_list_spinlock has been freed in mwifiex_send_processed_packet() */ return 0; @@ -1455,12 +1432,12 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) mwifiex_is_amsdu_allowed(priv, tid) && mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) - mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); + mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index); /* ra_list_spinlock has been freed in * mwifiex_11n_aggregate_pkt() */ else - mwifiex_send_single_packet(priv, ptr, ptr_index, flags); + mwifiex_send_single_packet(priv, ptr, ptr_index); /* ra_list_spinlock has been freed in * mwifiex_send_single_packet() */ @@ -1481,11 +1458,11 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) if (mwifiex_is_amsdu_allowed(priv, tid) && mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) - mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); + mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index); /* ra_list_spinlock has been freed in mwifiex_11n_aggregate_pkt() */ else - mwifiex_send_single_packet(priv, ptr, ptr_index, flags); + mwifiex_send_single_packet(priv, ptr, ptr_index); /* ra_list_spinlock has been freed in mwifiex_send_single_packet() */ } diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 4381155375e1..d8f61e540bfd 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -588,6 +588,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev) { int i; + netif_napi_del(&dev->tx_napi); for (i = 0; i < ARRAY_SIZE(dev->q_tx); i++) mt76_dma_tx_cleanup(dev, i, true); diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 5b6a81ee457e..ec9efb79985f 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -766,10 +766,21 @@ int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, *dbm = DIV_ROUND_UP(dev->txpower_cur, 2); /* convert from per-chain power to combined - * output on 2x2 devices + * output power */ - if (n_chains > 1) + switch (n_chains) { + case 4: + *dbm += 6; + break; + case 3: + *dbm += 4; + break; + case 2: *dbm += 3; + break; + default: + break; + } return 0; } @@ -820,3 +831,50 @@ mt76_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) return 0; } EXPORT_SYMBOL_GPL(mt76_set_tim); + +void mt76_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); + u8 *hdr, *pn = status->iv; + + __skb_push(skb, 8); + memmove(skb->data, skb->data + 8, hdr_len); + hdr = skb->data + hdr_len; + + hdr[0] = pn[5]; + hdr[1] = pn[4]; + hdr[2] = 0; + hdr[3] = 0x20 | (key_id << 6); + hdr[4] = pn[3]; + hdr[5] = pn[2]; + hdr[6] = pn[1]; + hdr[7] = pn[0]; + + status->flag &= ~RX_FLAG_IV_STRIPPED; +} +EXPORT_SYMBOL_GPL(mt76_insert_ccmp_hdr); + +int mt76_get_rate(struct mt76_dev *dev, + struct ieee80211_supported_band *sband, + int idx, bool cck) +{ + int i, offset = 0, len = sband->n_bitrates; + + if (cck) { + if (sband == &dev->sband_5g.sband) + return 0; + + idx &= ~BIT(2); /* short preamble */ + } else if (sband == &dev->sband_2g.sband) { + offset = 4; + } + + for (i = offset; i < len; i++) { + if ((sband->bitrates[i].hw_value & GENMASK(7, 0)) == idx) + return i; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_get_rate); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 8ecbf81a906f..56bf93a8988e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -258,10 +258,11 @@ struct mt76_rx_tid { #define MT_TX_CB_TXS_DONE BIT(1) #define MT_TX_CB_TXS_FAILED BIT(2) -#define MT_PACKET_ID_MASK GENMASK(7, 0) +#define MT_PACKET_ID_MASK GENMASK(6, 0) #define MT_PACKET_ID_NO_ACK 0 #define MT_PACKET_ID_NO_SKB 1 #define MT_PACKET_ID_FIRST 2 +#define MT_PACKET_ID_HAS_RATE BIT(7) #define MT_TX_STATUS_SKB_TIMEOUT HZ @@ -381,7 +382,8 @@ enum mt76u_out_ep { __MT_EP_OUT_MAX, }; -#define MT_SG_MAX_SIZE 8 +#define MT_TX_SG_MAX_SIZE 8 +#define MT_RX_SG_MAX_SIZE 1 #define MT_NUM_TX_ENTRIES 256 #define MT_NUM_RX_ENTRIES 128 #define MCU_RESP_URB_SIZE 1024 @@ -393,9 +395,7 @@ struct mt76_usb { struct delayed_work stat_work; u8 out_ep[__MT_EP_OUT_MAX]; - u16 out_max_packet; u8 in_ep[__MT_EP_IN_MAX]; - u16 in_max_packet; bool sg_en; struct mt76u_mcu { @@ -452,6 +452,7 @@ struct mt76_dev { int tx_dma_idx[4]; struct tasklet_struct tx_tasklet; + struct napi_struct tx_napi; struct delayed_work mac_work; wait_queue_head_t tx_wait; @@ -483,6 +484,8 @@ struct mt76_dev { int txpower_conf; int txpower_cur; + enum nl80211_dfs_regions region; + u32 debugfs_reg; struct led_classdev led_cdev; @@ -688,6 +691,14 @@ static inline void mt76_insert_hdr_pad(struct sk_buff *skb) skb->data[len + 1] = 0; } +static inline bool mt76_is_skb_pktid(u8 pktid) +{ + if (pktid & MT_PACKET_ID_HAS_RATE) + return false; + + return pktid >= MT_PACKET_ID_FIRST; +} + void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb); void mt76_tx(struct mt76_dev *dev, struct ieee80211_sta *sta, struct mt76_wcid *wcid, struct sk_buff *skb); @@ -749,6 +760,10 @@ void mt76_csa_check(struct mt76_dev *dev); void mt76_csa_finish(struct mt76_dev *dev); int mt76_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); +void mt76_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id); +int mt76_get_rate(struct mt76_dev *dev, + struct ieee80211_supported_band *sband, + int idx, bool cck); /* internal */ void mt76_tx_free(struct mt76_dev *dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/core.c b/drivers/net/wireless/mediatek/mt76/mt7603/core.c index 37e5644b45ef..e7ee58e3379c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/core.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/core.c @@ -35,7 +35,7 @@ irqreturn_t mt7603_irq_handler(int irq, void *dev_instance) if (intr & MT_INT_TX_DONE_ALL) { mt7603_irq_disable(dev, MT_INT_TX_DONE_ALL); - tasklet_schedule(&dev->mt76.tx_tasklet); + napi_schedule(&dev->mt76.tx_napi); } if (intr & MT_INT_RX_DONE(0)) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c index f8b3b6ab6297..a1bc3103cbe9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c @@ -40,6 +40,35 @@ mt7603_radio_read(struct seq_file *s, void *data) return 0; } +static int +mt7603_edcca_set(void *data, u64 val) +{ + struct mt7603_dev *dev = data; + + mutex_lock(&dev->mt76.mutex); + + dev->ed_monitor_enabled = !!val; + dev->ed_monitor = dev->ed_monitor_enabled && + dev->mt76.region == NL80211_DFS_ETSI; + mt7603_init_edcca(dev); + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static int +mt7603_edcca_get(void *data, u64 *val) +{ + struct mt7603_dev *dev = data; + + *val = dev->ed_monitor_enabled; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_edcca, mt7603_edcca_get, + mt7603_edcca_set, "%lld\n"); + void mt7603_init_debugfs(struct mt7603_dev *dev) { struct dentry *dir; @@ -48,6 +77,7 @@ void mt7603_init_debugfs(struct mt7603_dev *dev) if (!dir) return; + debugfs_create_file("edcca", 0600, dir, dev, &fops_edcca); debugfs_create_u32("reset_test", 0600, dir, &dev->reset_test); debugfs_create_devm_seqfile(dev->mt76.dev, "reset", dir, mt7603_reset_read); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c index 27e2d9f90553..58dc511f93c5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c @@ -139,15 +139,30 @@ static void mt7603_tx_tasklet(unsigned long data) { struct mt7603_dev *dev = (struct mt7603_dev *)data; + + mt76_txq_schedule_all(&dev->mt76); +} + +static int mt7603_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt7603_dev *dev; int i; + dev = container_of(napi, struct mt7603_dev, mt76.tx_napi); dev->tx_dma_check = 0; + for (i = MT_TXQ_MCU; i >= 0; i--) mt76_queue_tx_cleanup(dev, i, false); - mt76_txq_schedule_all(&dev->mt76); + if (napi_complete_done(napi, 0)) + mt7603_irq_enable(dev, MT_INT_TX_DONE_ALL); - mt7603_irq_enable(dev, MT_INT_TX_DONE_ALL); + for (i = MT_TXQ_MCU; i >= 0; i--) + mt76_queue_tx_cleanup(dev, i, false); + + tasklet_schedule(&dev->mt76.tx_tasklet); + + return 0; } int mt7603_dma_init(struct mt7603_dev *dev) @@ -216,7 +231,15 @@ int mt7603_dma_init(struct mt7603_dev *dev) return ret; mt76_wr(dev, MT_DELAY_INT_CFG, 0); - return mt76_init_queues(dev); + ret = mt76_init_queues(dev); + if (ret) + return ret; + + netif_tx_napi_add(&dev->mt76.napi_dev, &dev->mt76.tx_napi, + mt7603_poll_tx, NAPI_POLL_WEIGHT); + napi_enable(&dev->mt76.tx_napi); + + return 0; } void mt7603_dma_cleanup(struct mt7603_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h index f27b99b7e359..b893facfba48 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h @@ -69,6 +69,8 @@ enum mt7603_eeprom_field { MT_EE_CP_FT_VERSION = 0x0f0, + MT_EE_TX_POWER_TSSI_OFF = 0x0f2, + MT_EE_XTAL_FREQ_OFFSET = 0x0f4, MT_EE_XTAL_TRIM_2_COMP = 0x0f5, MT_EE_XTAL_TRIM_3_COMP = 0x0f6, diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/init.c b/drivers/net/wireless/mediatek/mt76/mt7603/init.c index 78cdbb70e178..38834c7d0891 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/init.c @@ -227,11 +227,19 @@ mt7603_mac_init(struct mt7603_dev *dev) mt76_rmw_field(dev, MT_LPON_BTEIR, MT_LPON_BTEIR_MBSS_MODE, 2); mt76_rmw_field(dev, MT_WF_RMACDR, MT_WF_RMACDR_MBSSID_MASK, 2); - mt76_wr(dev, MT_AGG_ARUCR, FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 7)); + mt76_wr(dev, MT_AGG_ARUCR, + FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 7) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(2), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(3), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(4), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(5), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(6), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(7), 1)); + mt76_wr(dev, MT_AGG_ARDCR, - FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 0) | - FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), - max_t(int, 0, MT7603_RATE_RETRY - 2)) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), MT7603_RATE_RETRY - 1) | FIELD_PREP(MT_AGG_ARxCR_LIMIT(2), MT7603_RATE_RETRY - 1) | FIELD_PREP(MT_AGG_ARxCR_LIMIT(3), MT7603_RATE_RETRY - 1) | FIELD_PREP(MT_AGG_ARxCR_LIMIT(4), MT7603_RATE_RETRY - 1) | @@ -437,7 +445,9 @@ mt7603_regd_notifier(struct wiphy *wiphy, struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct mt7603_dev *dev = hw->priv; - dev->ed_monitor = request->dfs_region == NL80211_DFS_ETSI; + dev->mt76.region = request->dfs_region; + dev->ed_monitor = dev->ed_monitor_enabled && + dev->mt76.region == NL80211_DFS_ETSI; } static int @@ -463,9 +473,13 @@ mt7603_init_txpower(struct mt7603_dev *dev, u8 *eeprom = (u8 *)dev->mt76.eeprom.data; int target_power = eeprom[MT_EE_TX_POWER_0_START_2G + 2] & ~BIT(7); u8 *rate_power = &eeprom[MT_EE_TX_POWER_CCK]; + bool ext_pa = eeprom[MT_EE_NIC_CONF_0 + 1] & BIT(1); int max_offset, cur_offset; int i; + if (ext_pa && is_mt7603(dev)) + target_power = eeprom[MT_EE_TX_POWER_TSSI_OFF] & ~BIT(7); + if (target_power & BIT(6)) target_power = -(target_power & GENMASK(5, 0)); @@ -488,7 +502,7 @@ mt7603_init_txpower(struct mt7603_dev *dev, for (i = 0; i < sband->n_channels; i++) { chan = &sband->channels[i]; - chan->max_power = target_power; + chan->max_power = min_t(int, chan->max_reg_power, target_power); chan->orig_mpwr = target_power; } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c index 6d506e34c3ee..40db1cbc832d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c @@ -370,31 +370,6 @@ void mt7603_mac_tx_ba_reset(struct mt7603_dev *dev, int wcid, int tid, mt76_rmw(dev, addr + (15 * 4), tid_mask, tid_val); } -static int -mt7603_get_rate(struct mt7603_dev *dev, struct ieee80211_supported_band *sband, - int idx, bool cck) -{ - int offset = 0; - int len = sband->n_bitrates; - int i; - - if (cck) { - if (sband == &dev->mt76.sband_5g.sband) - return 0; - - idx &= ~BIT(2); /* short preamble */ - } else if (sband == &dev->mt76.sband_2g.sband) { - offset = 4; - } - - for (i = offset; i < len; i++) { - if ((sband->bitrates[i].hw_value & GENMASK(7, 0)) == idx) - return i; - } - - return 0; -} - static struct mt76_wcid * mt7603_rx_get_wcid(struct mt7603_dev *dev, u8 idx, bool unicast) { @@ -418,30 +393,6 @@ mt7603_rx_get_wcid(struct mt7603_dev *dev, u8 idx, bool unicast) return &sta->vif->sta.wcid; } -static void -mt7603_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id) -{ - struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; - int hdr_len = ieee80211_get_hdrlen_from_skb(skb); - u8 *pn = status->iv; - u8 *hdr; - - __skb_push(skb, 8); - memmove(skb->data, skb->data + 8, hdr_len); - hdr = skb->data + hdr_len; - - hdr[0] = pn[5]; - hdr[1] = pn[4]; - hdr[2] = 0; - hdr[3] = 0x20 | (key_id << 6); - hdr[4] = pn[3]; - hdr[5] = pn[2]; - hdr[6] = pn[1]; - hdr[7] = pn[0]; - - status->flag &= ~RX_FLAG_IV_STRIPPED; -} - int mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) { @@ -532,7 +483,7 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) cck = true; /* fall through */ case MT_PHY_TYPE_OFDM: - i = mt7603_get_rate(dev, sband, i, cck); + i = mt76_get_rate(&dev->mt76, sband, i, cck); break; case MT_PHY_TYPE_HT_GF: case MT_PHY_TYPE_HT: @@ -580,7 +531,7 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) if (insert_ccmp_hdr) { u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); - mt7603_insert_ccmp_hdr(skb, key_id); + mt76_insert_ccmp_hdr(skb, key_id); } hdr = (struct ieee80211_hdr *)skb->data; @@ -640,6 +591,7 @@ void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, struct ieee80211_tx_rate *probe_rate, struct ieee80211_tx_rate *rates) { + struct ieee80211_tx_rate *ref; int wcid = sta->wcid.idx; u32 addr = mt7603_wtbl2_addr(wcid); bool stbc = false; @@ -648,7 +600,8 @@ void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, u16 val[4]; u16 probe_val; u32 w9 = mt76_rr(dev, addr + 9 * 4); - int i; + bool rateset; + int i, k; if (!mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000)) return; @@ -656,6 +609,41 @@ void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, for (i = n_rates; i < 4; i++) rates[i] = rates[n_rates - 1]; + rateset = !(sta->rate_set_tsf & BIT(0)); + memcpy(sta->rateset[rateset].rates, rates, + sizeof(sta->rateset[rateset].rates)); + if (probe_rate) { + sta->rateset[rateset].probe_rate = *probe_rate; + ref = &sta->rateset[rateset].probe_rate; + } else { + sta->rateset[rateset].probe_rate.idx = -1; + ref = &sta->rateset[rateset].rates[0]; + } + + rates = sta->rateset[rateset].rates; + for (i = 0; i < ARRAY_SIZE(sta->rateset[rateset].rates); i++) { + /* + * We don't support switching between short and long GI + * within the rate set. For accurate tx status reporting, we + * need to make sure that flags match. + * For improved performance, avoid duplicate entries by + * decrementing the MCS index if necessary + */ + if ((ref->flags ^ rates[i].flags) & IEEE80211_TX_RC_SHORT_GI) + rates[i].flags ^= IEEE80211_TX_RC_SHORT_GI; + + for (k = 0; k < i; k++) { + if (rates[i].idx != rates[k].idx) + continue; + if ((rates[i].flags ^ rates[k].flags) & + IEEE80211_TX_RC_40_MHZ_WIDTH) + continue; + + rates[i].idx--; + } + + } + w9 &= MT_WTBL2_W9_SHORT_GI_20 | MT_WTBL2_W9_SHORT_GI_40 | MT_WTBL2_W9_SHORT_GI_80; @@ -699,19 +687,22 @@ void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, mt76_wr(dev, MT_WTBL_RIUCR1, FIELD_PREP(MT_WTBL_RIUCR1_RATE0, probe_val) | FIELD_PREP(MT_WTBL_RIUCR1_RATE1, val[0]) | - FIELD_PREP(MT_WTBL_RIUCR1_RATE2_LO, val[0])); + FIELD_PREP(MT_WTBL_RIUCR1_RATE2_LO, val[1])); mt76_wr(dev, MT_WTBL_RIUCR2, - FIELD_PREP(MT_WTBL_RIUCR2_RATE2_HI, val[0] >> 8) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE2_HI, val[1] >> 8) | FIELD_PREP(MT_WTBL_RIUCR2_RATE3, val[1]) | - FIELD_PREP(MT_WTBL_RIUCR2_RATE4, val[1]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE4, val[2]) | FIELD_PREP(MT_WTBL_RIUCR2_RATE5_LO, val[2])); mt76_wr(dev, MT_WTBL_RIUCR3, FIELD_PREP(MT_WTBL_RIUCR3_RATE5_HI, val[2] >> 4) | - FIELD_PREP(MT_WTBL_RIUCR3_RATE6, val[2]) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE6, val[3]) | FIELD_PREP(MT_WTBL_RIUCR3_RATE7, val[3])); + mt76_set(dev, MT_LPON_T0CR, MT_LPON_T0CR_MODE); /* TSF read */ + sta->rate_set_tsf = (mt76_rr(dev, MT_LPON_UTTR0) & ~BIT(0)) | rateset; + mt76_wr(dev, MT_WTBL_UPDATE, FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, wcid) | MT_WTBL_UPDATE_RATE_UPDATE | @@ -938,9 +929,9 @@ int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { spin_lock_bh(&dev->mt76.lock); - msta->rate_probe = true; mt7603_wtbl_set_rates(dev, msta, &info->control.rates[0], msta->rates); + msta->rate_probe = true; spin_unlock_bh(&dev->mt76.lock); } @@ -955,10 +946,12 @@ mt7603_fill_txs(struct mt7603_dev *dev, struct mt7603_sta *sta, struct ieee80211_tx_info *info, __le32 *txs_data) { struct ieee80211_supported_band *sband; - int final_idx = 0; + struct mt7603_rate_set *rs; + int first_idx = 0, last_idx; + u32 rate_set_tsf; u32 final_rate; u32 final_rate_flags; - bool final_mpdu; + bool rs_idx; bool ack_timeout; bool fixed_rate; bool probe; @@ -966,7 +959,6 @@ mt7603_fill_txs(struct mt7603_dev *dev, struct mt7603_sta *sta, bool cck = false; int count; u32 txs; - u8 pid; int idx; int i; @@ -974,10 +966,9 @@ mt7603_fill_txs(struct mt7603_dev *dev, struct mt7603_sta *sta, probe = !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); txs = le32_to_cpu(txs_data[4]); - final_mpdu = txs & MT_TXS4_ACKED_MPDU; ampdu = !fixed_rate && (txs & MT_TXS4_AMPDU); - pid = FIELD_GET(MT_TXS4_PID, txs); count = FIELD_GET(MT_TXS4_TX_COUNT, txs); + last_idx = FIELD_GET(MT_TXS4_LAST_TX_RATE, txs); txs = le32_to_cpu(txs_data[0]); final_rate = FIELD_GET(MT_TXS0_TX_RATE, txs); @@ -999,38 +990,57 @@ mt7603_fill_txs(struct mt7603_dev *dev, struct mt7603_sta *sta, if (ampdu || (info->flags & IEEE80211_TX_CTL_AMPDU)) info->flags |= IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_CTL_AMPDU; + first_idx = max_t(int, 0, last_idx - (count + 1) / MT7603_RATE_RETRY); + if (fixed_rate && !probe) { info->status.rates[0].count = count; + i = 0; goto out; } - for (i = 0, idx = 0; i < ARRAY_SIZE(info->status.rates); i++) { - int cur_count = min_t(int, count, 2 * MT7603_RATE_RETRY); + rate_set_tsf = READ_ONCE(sta->rate_set_tsf); + rs_idx = !((u32)(FIELD_GET(MT_TXS1_F0_TIMESTAMP, le32_to_cpu(txs_data[1])) - + rate_set_tsf) < 1000000); + rs_idx ^= rate_set_tsf & BIT(0); + rs = &sta->rateset[rs_idx]; - if (!i && probe) { - cur_count = 1; - } else { - info->status.rates[i] = sta->rates[idx]; - idx++; - } + if (!first_idx && rs->probe_rate.idx >= 0) { + info->status.rates[0] = rs->probe_rate; - if (i && info->status.rates[i].idx < 0) { - info->status.rates[i - 1].count += count; - break; + spin_lock_bh(&dev->mt76.lock); + if (sta->rate_probe) { + mt7603_wtbl_set_rates(dev, sta, NULL, + sta->rates); + sta->rate_probe = false; } + spin_unlock_bh(&dev->mt76.lock); + } else + info->status.rates[0] = rs->rates[first_idx / 2]; + info->status.rates[0].count = 0; - if (!count) { - info->status.rates[i].idx = -1; - break; - } + for (i = 0, idx = first_idx; count && idx <= last_idx; idx++) { + struct ieee80211_tx_rate *cur_rate; + int cur_count; - info->status.rates[i].count = cur_count; - final_idx = i; + cur_rate = &rs->rates[idx / 2]; + cur_count = min_t(int, MT7603_RATE_RETRY, count); count -= cur_count; + + if (idx && (cur_rate->idx != info->status.rates[i].idx || + cur_rate->flags != info->status.rates[i].flags)) { + i++; + if (i == ARRAY_SIZE(info->status.rates)) + break; + + info->status.rates[i] = *cur_rate; + info->status.rates[i].count = 0; + } + + info->status.rates[i].count += cur_count; } out: - final_rate_flags = info->status.rates[final_idx].flags; + final_rate_flags = info->status.rates[i].flags; switch (FIELD_GET(MT_TX_RATE_MODE, final_rate)) { case MT_PHY_TYPE_CCK: @@ -1042,7 +1052,8 @@ out: else sband = &dev->mt76.sband_2g.sband; final_rate &= GENMASK(5, 0); - final_rate = mt7603_get_rate(dev, sband, final_rate, cck); + final_rate = mt76_get_rate(&dev->mt76, sband, final_rate, + cck); final_rate_flags = 0; break; case MT_PHY_TYPE_HT_GF: @@ -1056,8 +1067,8 @@ out: return false; } - info->status.rates[final_idx].idx = final_rate; - info->status.rates[final_idx].flags = final_rate_flags; + info->status.rates[i].idx = final_rate; + info->status.rates[i].flags = final_rate_flags; return true; } @@ -1078,16 +1089,6 @@ mt7603_mac_add_txs_skb(struct mt7603_dev *dev, struct mt7603_sta *sta, int pid, if (skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { - spin_lock_bh(&dev->mt76.lock); - if (sta->rate_probe) { - mt7603_wtbl_set_rates(dev, sta, NULL, - sta->rates); - sta->rate_probe = false; - } - spin_unlock_bh(&dev->mt76.lock); - } - if (!mt7603_fill_txs(dev, sta, info, txs_data)) { ieee80211_tx_info_clear_status(info); info->status.rates[0].idx = -1; @@ -1282,6 +1283,7 @@ static void mt7603_mac_watchdog_reset(struct mt7603_dev *dev) tasklet_disable(&dev->mt76.pre_tbtt_tasklet); napi_disable(&dev->mt76.napi[0]); napi_disable(&dev->mt76.napi[1]); + napi_disable(&dev->mt76.tx_napi); mutex_lock(&dev->mt76.mutex); @@ -1326,7 +1328,8 @@ skip_dma_reset: mutex_unlock(&dev->mt76.mutex); tasklet_enable(&dev->mt76.tx_tasklet); - tasklet_schedule(&dev->mt76.tx_tasklet); + napi_enable(&dev->mt76.tx_napi); + napi_schedule(&dev->mt76.tx_napi); tasklet_enable(&dev->mt76.pre_tbtt_tasklet); mt7603_beacon_set_timer(dev, -1, beacon_int); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c index 0a0334dc40d5..e5d4cb6381a8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -103,8 +103,7 @@ mt7603_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mutex_unlock(&dev->mt76.mutex); } -static void -mt7603_init_edcca(struct mt7603_dev *dev) +void mt7603_init_edcca(struct mt7603_dev *dev) { /* Set lower signal level to -65dBm */ mt76_rmw_field(dev, MT_RXTD(8), MT_RXTD_8_LOWER_SIGNAL, 0x23); @@ -207,8 +206,11 @@ mt7603_config(struct ieee80211_hw *hw, u32 changed) int ret = 0; if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | - IEEE80211_CONF_CHANGE_POWER)) + IEEE80211_CONF_CHANGE_POWER)) { + ieee80211_stop_queues(hw); ret = mt7603_set_channel(dev, &hw->conf.chandef); + ieee80211_wake_queues(hw); + } if (changed & IEEE80211_CONF_CHANGE_MONITOR) { mutex_lock(&dev->mt76.mutex); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c index 6357b5658a32..343ddc5543c2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c @@ -346,7 +346,7 @@ int mt7603_mcu_set_eeprom(struct mt7603_dev *dev) }; struct req_data { - u16 addr; + __le16 addr; u8 val; u8 pad; } __packed; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h index fa64bbaab0d2..2c6f7b4cf0e9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h @@ -51,6 +51,11 @@ enum mt7603_bw { MT_BW_80, }; +struct mt7603_rate_set { + struct ieee80211_tx_rate probe_rate; + struct ieee80211_tx_rate rates[4]; +}; + struct mt7603_sta { struct mt76_wcid wcid; /* must be first */ @@ -58,7 +63,11 @@ struct mt7603_sta { struct sk_buff_head psq; - struct ieee80211_tx_rate rates[8]; + struct ieee80211_tx_rate rates[4]; + + struct mt7603_rate_set rateset[2]; + u32 rate_set_tsf; + u8 rate_count; u8 n_rates; @@ -117,8 +126,9 @@ struct mt7603_dev { u8 mac_work_count; u8 mcu_running; - u8 ed_monitor; + u8 ed_monitor_enabled; + u8 ed_monitor; s8 ed_trigger; u8 ed_strict_mode; u8 ed_strong_signal; @@ -241,4 +251,5 @@ void mt7603_update_channel(struct mt76_dev *mdev); void mt7603_edcca_set_strict(struct mt7603_dev *dev, bool val); void mt7603_cca_stats_reset(struct mt7603_dev *dev); +void mt7603_init_edcca(struct mt7603_dev *dev); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/regs.h b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h index 9d257d5c309d..eb9eefe8e125 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h @@ -480,6 +480,12 @@ enum { #define MT_LPON_BASE 0x24000 #define MT_LPON(n) (MT_LPON_BASE + (n)) +#define MT_LPON_T0CR MT_LPON(0x010) +#define MT_LPON_T0CR_MODE GENMASK(1, 0) + +#define MT_LPON_UTTR0 MT_LPON(0x018) +#define MT_LPON_UTTR1 MT_LPON(0x01c) + #define MT_LPON_BTEIR MT_LPON(0x020) #define MT_LPON_BTEIR_MBSS_MODE GENMASK(31, 29) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/dma.c b/drivers/net/wireless/mediatek/mt76/mt7615/dma.c index 3ec6582afd8f..6a70273d4a69 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/dma.c @@ -93,18 +93,33 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, static void mt7615_tx_tasklet(unsigned long data) { struct mt7615_dev *dev = (struct mt7615_dev *)data; + + mt76_txq_schedule_all(&dev->mt76); +} + +static int mt7615_poll_tx(struct napi_struct *napi, int budget) +{ static const u8 queue_map[] = { MT_TXQ_MCU, MT_TXQ_BE }; + struct mt7615_dev *dev; int i; + dev = container_of(napi, struct mt7615_dev, mt76.tx_napi); + for (i = 0; i < ARRAY_SIZE(queue_map); i++) mt76_queue_tx_cleanup(dev, queue_map[i], false); - mt76_txq_schedule_all(&dev->mt76); + if (napi_complete_done(napi, 0)) + mt7615_irq_enable(dev, MT_INT_TX_DONE_ALL); - mt7615_irq_enable(dev, MT_INT_TX_DONE_ALL); + for (i = 0; i < ARRAY_SIZE(queue_map); i++) + mt76_queue_tx_cleanup(dev, queue_map[i], false); + + tasklet_schedule(&dev->mt76.tx_tasklet); + + return 0; } int mt7615_dma_init(struct mt7615_dev *dev) @@ -178,6 +193,10 @@ int mt7615_dma_init(struct mt7615_dev *dev) if (ret < 0) return ret; + netif_tx_napi_add(&dev->mt76.napi_dev, &dev->mt76.tx_napi, + mt7615_poll_tx, NAPI_POLL_WEIGHT); + napi_enable(&dev->mt76.tx_napi); + mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY | MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 1000); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c index dd5ab46a4f66..dc94f52e6e8b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c @@ -42,13 +42,13 @@ static int mt7615_efuse_read(struct mt7615_dev *dev, u32 base, static int mt7615_efuse_init(struct mt7615_dev *dev) { - u32 base = mt7615_reg_map(dev, MT_EFUSE_BASE); - int len = MT7615_EEPROM_SIZE; - int ret, i; + u32 val, base = mt7615_reg_map(dev, MT_EFUSE_BASE); + int i, len = MT7615_EEPROM_SIZE; void *buf; - if (mt76_rr(dev, base + MT_EFUSE_BASE_CTRL) & MT_EFUSE_BASE_CTRL_EMPTY) - return -EINVAL; + val = mt76_rr(dev, base + MT_EFUSE_BASE_CTRL); + if (val & MT_EFUSE_BASE_CTRL_EMPTY) + return 0; dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, len, GFP_KERNEL); dev->mt76.otp.size = len; @@ -57,6 +57,8 @@ static int mt7615_efuse_init(struct mt7615_dev *dev) buf = dev->mt76.otp.data; for (i = 0; i + 16 <= len; i += 16) { + int ret; + ret = mt7615_efuse_read(dev, base, i, buf + i); if (ret) return ret; @@ -76,6 +78,82 @@ static int mt7615_eeprom_load(struct mt7615_dev *dev) return mt7615_efuse_init(dev); } +static int mt7615_check_eeprom(struct mt76_dev *dev) +{ + u16 val = get_unaligned_le16(dev->eeprom.data); + + switch (val) { + case 0x7615: + return 0; + default: + return -EINVAL; + } +} + +static void mt7615_eeprom_parse_hw_cap(struct mt7615_dev *dev) +{ + u8 val, *eeprom = dev->mt76.eeprom.data; + + val = FIELD_GET(MT_EE_NIC_WIFI_CONF_BAND_SEL, + eeprom[MT_EE_WIFI_CONF]); + switch (val) { + case MT_EE_5GHZ: + dev->mt76.cap.has_5ghz = true; + break; + case MT_EE_2GHZ: + dev->mt76.cap.has_2ghz = true; + break; + default: + dev->mt76.cap.has_2ghz = true; + dev->mt76.cap.has_5ghz = true; + break; + } +} + +int mt7615_eeprom_get_power_index(struct mt7615_dev *dev, + struct ieee80211_channel *chan, + u8 chain_idx) +{ + int index; + + if (chain_idx > 3) + return -EINVAL; + + /* TSSI disabled */ + if (mt7615_ext_pa_enabled(dev, chan->band)) { + if (chan->band == NL80211_BAND_2GHZ) + return MT_EE_EXT_PA_2G_TARGET_POWER; + else + return MT_EE_EXT_PA_5G_TARGET_POWER; + } + + /* TSSI enabled */ + if (chan->band == NL80211_BAND_2GHZ) { + index = MT_EE_TX0_2G_TARGET_POWER + chain_idx * 6; + } else { + int group = mt7615_get_channel_group(chan->hw_value); + + switch (chain_idx) { + case 1: + index = MT_EE_TX1_5G_G0_TARGET_POWER; + break; + case 2: + index = MT_EE_TX2_5G_G0_TARGET_POWER; + break; + case 3: + index = MT_EE_TX3_5G_G0_TARGET_POWER; + break; + case 0: + default: + index = MT_EE_TX0_5G_G0_TARGET_POWER; + break; + } + index += 5 * group; + } + + return index; +} + int mt7615_eeprom_init(struct mt7615_dev *dev) { int ret; @@ -84,11 +162,12 @@ int mt7615_eeprom_init(struct mt7615_dev *dev) if (ret < 0) return ret; - memcpy(dev->mt76.eeprom.data, dev->mt76.otp.data, MT7615_EEPROM_SIZE); - - dev->mt76.cap.has_2ghz = true; - dev->mt76.cap.has_5ghz = true; + ret = mt7615_check_eeprom(&dev->mt76); + if (ret && dev->mt76.otp.data) + memcpy(dev->mt76.eeprom.data, dev->mt76.otp.data, + MT7615_EEPROM_SIZE); + mt7615_eeprom_parse_hw_cap(dev); memcpy(dev->mt76.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, ETH_ALEN); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h index a4cf16688171..f4a4280768d2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h @@ -11,8 +11,69 @@ enum mt7615_eeprom_field { MT_EE_VERSION = 0x002, MT_EE_MAC_ADDR = 0x004, MT_EE_NIC_CONF_0 = 0x034, + MT_EE_NIC_CONF_1 = 0x036, + MT_EE_WIFI_CONF = 0x03e, + MT_EE_TX0_2G_TARGET_POWER = 0x058, + MT_EE_TX0_5G_G0_TARGET_POWER = 0x070, + MT_EE_TX1_5G_G0_TARGET_POWER = 0x098, + MT_EE_EXT_PA_2G_TARGET_POWER = 0x0f2, + MT_EE_EXT_PA_5G_TARGET_POWER = 0x0f3, + MT_EE_TX2_5G_G0_TARGET_POWER = 0x142, + MT_EE_TX3_5G_G0_TARGET_POWER = 0x16a, __MT_EE_MAX = 0x3bf }; +#define MT_EE_NIC_CONF_TSSI_2G BIT(5) +#define MT_EE_NIC_CONF_TSSI_5G BIT(6) + +#define MT_EE_NIC_WIFI_CONF_BAND_SEL GENMASK(5, 4) +enum mt7615_eeprom_band { + MT_EE_DUAL_BAND, + MT_EE_5GHZ, + MT_EE_2GHZ, + MT_EE_DBDC, +}; + +enum mt7615_channel_group { + MT_CH_5G_JAPAN, + MT_CH_5G_UNII_1, + MT_CH_5G_UNII_2A, + MT_CH_5G_UNII_2B, + MT_CH_5G_UNII_2E_1, + MT_CH_5G_UNII_2E_2, + MT_CH_5G_UNII_2E_3, + MT_CH_5G_UNII_3, + __MT_CH_MAX +}; + +static inline enum mt7615_channel_group +mt7615_get_channel_group(int channel) +{ + if (channel >= 184 && channel <= 196) + return MT_CH_5G_JAPAN; + if (channel <= 48) + return MT_CH_5G_UNII_1; + if (channel <= 64) + return MT_CH_5G_UNII_2A; + if (channel <= 114) + return MT_CH_5G_UNII_2E_1; + if (channel <= 144) + return MT_CH_5G_UNII_2E_2; + if (channel <= 161) + return MT_CH_5G_UNII_2E_3; + return MT_CH_5G_UNII_3; +} + +static inline bool +mt7615_ext_pa_enabled(struct mt7615_dev *dev, enum nl80211_band band) +{ + u8 *eep = dev->mt76.eeprom.data; + + if (band == NL80211_BAND_5GHZ) + return !(eep[MT_EE_NIC_CONF_1 + 1] & MT_EE_NIC_CONF_TSSI_5G); + else + return !(eep[MT_EE_NIC_CONF_1 + 1] & MT_EE_NIC_CONF_TSSI_2G); +} + #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c index 3ab3ff553ef2..859de2454ec6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c @@ -9,6 +9,7 @@ #include <linux/etherdevice.h> #include "mt7615.h" #include "mac.h" +#include "eeprom.h" static void mt7615_phy_init(struct mt7615_dev *dev) { @@ -62,16 +63,11 @@ static void mt7615_mac_init(struct mt7615_dev *dev) MT_AGG_ARCR_RATE_DOWN_RATIO_EN | FIELD_PREP(MT_AGG_ARCR_RATE_DOWN_RATIO, 1) | FIELD_PREP(MT_AGG_ARCR_RATE_UP_EXTRA_TH, 4))); - - dev->mt76.global_wcid.idx = MT7615_WTBL_RESERVED; - dev->mt76.global_wcid.hw_key_idx = -1; - rcu_assign_pointer(dev->mt76.wcid[MT7615_WTBL_RESERVED], - &dev->mt76.global_wcid); } static int mt7615_init_hardware(struct mt7615_dev *dev) { - int ret; + int ret, idx; mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); @@ -98,6 +94,15 @@ static int mt7615_init_hardware(struct mt7615_dev *dev) mt7615_mcu_ctrl_pm_state(dev, 0); mt7615_mcu_del_wtbl_all(dev); + /* Beacon and mgmt frames should occupy wcid 0 */ + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7615_WTBL_STA - 1); + if (idx) + return -ENOSPC; + + dev->mt76.global_wcid.idx = idx; + dev->mt76.global_wcid.hw_key_idx = -1; + rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid); + return 0; } @@ -133,6 +138,9 @@ static const struct ieee80211_iface_limit if_limits[] = { { .max = MT7615_MAX_INTERFACES, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_STATION) } }; @@ -158,6 +166,48 @@ static int mt7615_init_debugfs(struct mt7615_dev *dev) return 0; } +static void +mt7615_init_txpower(struct mt7615_dev *dev, + struct ieee80211_supported_band *sband) +{ + int i, n_chains = hweight8(dev->mt76.antenna_mask), target_chains; + u8 *eep = (u8 *)dev->mt76.eeprom.data; + enum nl80211_band band = sband->band; + + target_chains = mt7615_ext_pa_enabled(dev, band) ? 1 : n_chains; + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + u8 target_power = 0; + int j; + + for (j = 0; j < target_chains; j++) { + int index; + + index = mt7615_eeprom_get_power_index(dev, chan, j); + target_power = max(target_power, eep[index]); + } + + target_power = DIV_ROUND_UP(target_power, 2); + switch (n_chains) { + case 4: + target_power += 6; + break; + case 3: + target_power += 4; + break; + case 2: + target_power += 3; + break; + default: + break; + } + + chan->max_power = min_t(int, chan->max_reg_power, + target_power); + chan->orig_mpwr = target_power; + } +} + int mt7615_register_device(struct mt7615_dev *dev) { struct ieee80211_hw *hw = mt76_hw(dev); @@ -195,6 +245,9 @@ int mt7615_register_device(struct mt7615_dev *dev) dev->mt76.antenna_mask = 0xf; wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif BIT(NL80211_IFTYPE_AP); ret = mt76_register_device(&dev->mt76, true, mt7615_rates, @@ -202,6 +255,9 @@ int mt7615_register_device(struct mt7615_dev *dev) if (ret) return ret; + mt7615_init_txpower(dev, &dev->mt76.sband_2g.sband); + mt7615_init_txpower(dev, &dev->mt76.sband_5g.sband); + hw->max_tx_fragments = MT_TXP_MAX_BUF_NUM; return mt7615_init_debugfs(dev); @@ -212,6 +268,10 @@ void mt7615_unregister_device(struct mt7615_dev *dev) struct mt76_txwi_cache *txwi; int id; + mt76_unregister_device(&dev->mt76); + mt7615_mcu_exit(dev); + mt7615_dma_cleanup(dev); + spin_lock_bh(&dev->token_lock); idr_for_each_entry(&dev->token, txwi, id) { mt7615_txp_skb_unmap(&dev->mt76, txwi); @@ -221,9 +281,6 @@ void mt7615_unregister_device(struct mt7615_dev *dev) } spin_unlock_bh(&dev->token_lock); idr_destroy(&dev->token); - mt76_unregister_device(&dev->mt76); - mt7615_mcu_exit(dev); - mt7615_dma_cleanup(dev); - ieee80211_free_hw(mt76_hw(dev)); + mt76_free_device(&dev->mt76); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index b8f48d10f27a..1eb0e9c9970c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -13,6 +13,11 @@ #include "../dma.h" #include "mac.h" +static inline s8 to_rssi(u32 field, u32 rxv) +{ + return (FIELD_GET(field, rxv) - 220) / 2; +} + static struct mt76_wcid *mt7615_rx_get_wcid(struct mt7615_dev *dev, u8 idx, bool unicast) { @@ -36,54 +41,6 @@ static struct mt76_wcid *mt7615_rx_get_wcid(struct mt7615_dev *dev, return &sta->vif->sta.wcid; } -static int mt7615_get_rate(struct mt7615_dev *dev, - struct ieee80211_supported_band *sband, - int idx, bool cck) -{ - int offset = 0; - int len = sband->n_bitrates; - int i; - - if (cck) { - if (sband == &dev->mt76.sband_5g.sband) - return 0; - - idx &= ~BIT(2); /* short preamble */ - } else if (sband == &dev->mt76.sband_2g.sband) { - offset = 4; - } - - for (i = offset; i < len; i++) { - if ((sband->bitrates[i].hw_value & GENMASK(7, 0)) == idx) - return i; - } - - return 0; -} - -static void mt7615_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id) -{ - struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; - int hdr_len = ieee80211_get_hdrlen_from_skb(skb); - u8 *pn = status->iv; - u8 *hdr; - - __skb_push(skb, 8); - memmove(skb->data, skb->data + 8, hdr_len); - hdr = skb->data + hdr_len; - - hdr[0] = pn[5]; - hdr[1] = pn[4]; - hdr[2] = 0; - hdr[3] = 0x20 | (key_id << 6); - hdr[4] = pn[3]; - hdr[5] = pn[2]; - hdr[6] = pn[1]; - hdr[7] = pn[0]; - - status->flag &= ~RX_FLAG_IV_STRIPPED; -} - int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb) { struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; @@ -96,6 +53,9 @@ int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb) bool unicast, remove_pad, insert_ccmp_hdr = false; int i, idx; + if (!test_bit(MT76_STATE_RUNNING, &dev->mt76.state)) + return -EINVAL; + memset(status, 0, sizeof(*status)); unicast = (rxd1 & MT_RXD1_NORMAL_ADDR_TYPE) == MT_RXD1_NORMAL_U2M; @@ -165,6 +125,7 @@ int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb) if (rxd0 & MT_RXD0_NORMAL_GROUP_3) { u32 rxdg0 = le32_to_cpu(rxd[0]); u32 rxdg1 = le32_to_cpu(rxd[1]); + u32 rxdg3 = le32_to_cpu(rxd[3]); u8 stbc = FIELD_GET(MT_RXV1_HT_STBC, rxdg0); bool cck = false; @@ -174,7 +135,7 @@ int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb) cck = true; /* fall through */ case MT_PHY_TYPE_OFDM: - i = mt7615_get_rate(dev, sband, i, cck); + i = mt76_get_rate(&dev->mt76, sband, i, cck); break; case MT_PHY_TYPE_HT_GF: case MT_PHY_TYPE_HT: @@ -214,7 +175,21 @@ int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb) status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; - /* TODO: RSSI */ + status->chains = dev->mt76.antenna_mask; + status->chain_signal[0] = to_rssi(MT_RXV4_RCPI0, rxdg3); + status->chain_signal[1] = to_rssi(MT_RXV4_RCPI1, rxdg3); + status->chain_signal[2] = to_rssi(MT_RXV4_RCPI2, rxdg3); + status->chain_signal[3] = to_rssi(MT_RXV4_RCPI3, rxdg3); + status->signal = status->chain_signal[0]; + + for (i = 1; i < hweight8(dev->mt76.antenna_mask); i++) { + if (!(status->chains & BIT(i))) + continue; + + status->signal = max(status->signal, + status->chain_signal[i]); + } + rxd += 6; if ((u8 *)rxd - skb->data >= skb->len) return -EINVAL; @@ -225,7 +200,7 @@ int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb) if (insert_ccmp_hdr) { u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); - mt7615_insert_ccmp_hdr(skb, key_id); + mt76_insert_ccmp_hdr(skb, key_id); } hdr = (struct ieee80211_hdr *)skb->data; @@ -549,23 +524,20 @@ static bool mt7615_fill_txs(struct mt7615_dev *dev, struct mt7615_sta *sta, { struct ieee80211_supported_band *sband; int i, idx, count, final_idx = 0; - bool fixed_rate, final_mpdu, ack_timeout; + bool fixed_rate, ack_timeout; bool probe, ampdu, cck = false; u32 final_rate, final_rate_flags, final_nss, txs; - u8 pid; fixed_rate = info->status.rates[0].count; probe = !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); txs = le32_to_cpu(txs_data[1]); - final_mpdu = txs & MT_TXS1_ACKED_MPDU; ampdu = !fixed_rate && (txs & MT_TXS1_AMPDU); txs = le32_to_cpu(txs_data[3]); count = FIELD_GET(MT_TXS3_TX_COUNT, txs); txs = le32_to_cpu(txs_data[0]); - pid = FIELD_GET(MT_TXS0_PID, txs); final_rate = FIELD_GET(MT_TXS0_TX_RATE, txs); ack_timeout = txs & MT_TXS0_ACK_TIMEOUT; @@ -628,7 +600,8 @@ out: else sband = &dev->mt76.sband_2g.sband; final_rate &= MT_TX_RATE_IDX; - final_rate = mt7615_get_rate(dev, sband, final_rate, cck); + final_rate = mt76_get_rate(&dev->mt76, sband, final_rate, + cck); final_rate_flags = 0; break; case MT_PHY_TYPE_HT_GF: diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.h b/drivers/net/wireless/mediatek/mt76/mt7615/mac.h index 18ad4b8a3807..b00ce8db58e9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.h @@ -98,6 +98,11 @@ enum rx_pkt_type { #define MT_RXV2_GROUP_ID GENMASK(26, 21) #define MT_RXV2_LENGTH GENMASK(20, 0) +#define MT_RXV4_RCPI3 GENMASK(31, 24) +#define MT_RXV4_RCPI2 GENMASK(23, 16) +#define MT_RXV4_RCPI1 GENMASK(15, 8) +#define MT_RXV4_RCPI0 GENMASK(7, 0) + enum tx_header_format { MT_HDR_FORMAT_802_3, MT_HDR_FORMAT_CMD, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 80e6b211f60b..b4d6af812c54 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -37,6 +37,7 @@ static int get_omac_idx(enum nl80211_iftype type, u32 mask) switch (type) { case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: /* ap use hw bssid 0 and ext bssid */ if (~mask & BIT(HW_BSSID_0)) return HW_BSSID_0; @@ -77,11 +78,12 @@ static int mt7615_add_interface(struct ieee80211_hw *hw, goto out; } - mvif->omac_idx = get_omac_idx(vif->type, dev->omac_mask); - if (mvif->omac_idx < 0) { + idx = get_omac_idx(vif->type, dev->omac_mask); + if (idx < 0) { ret = -ENOSPC; goto out; } + mvif->omac_idx = idx; /* TODO: DBDC support. Use band 0 and wmm 0 for now */ mvif->band_idx = 0; @@ -93,7 +95,7 @@ static int mt7615_add_interface(struct ieee80211_hw *hw, dev->vif_mask |= BIT(mvif->idx); dev->omac_mask |= BIT(mvif->omac_idx); - idx = MT7615_WTBL_RESERVED - 1 - mvif->idx; + idx = MT7615_WTBL_RESERVED - mvif->idx; mvif->sta.wcid.idx = idx; mvif->sta.wcid.hw_key_idx = -1; @@ -128,8 +130,7 @@ static void mt7615_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); } -static int mt7615_set_channel(struct mt7615_dev *dev, - struct cfg80211_chan_def *def) +static int mt7615_set_channel(struct mt7615_dev *dev) { int ret; @@ -190,28 +191,28 @@ static int mt7615_config(struct ieee80211_hw *hw, u32 changed) struct mt7615_dev *dev = hw->priv; int ret = 0; - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - mutex_lock(&dev->mt76.mutex); + mutex_lock(&dev->mt76.mutex); + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { ieee80211_stop_queues(hw); - ret = mt7615_set_channel(dev, &hw->conf.chandef); + ret = mt7615_set_channel(dev); ieee80211_wake_queues(hw); - - mutex_unlock(&dev->mt76.mutex); } - if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - mutex_lock(&dev->mt76.mutex); + if (changed & IEEE80211_CONF_CHANGE_POWER) + ret = mt7615_mcu_set_tx_power(dev); + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) dev->mt76.rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; else dev->mt76.rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; mt76_wr(dev, MT_WF_RFCR, dev->mt76.rxfilter); - - mutex_unlock(&dev->mt76.mutex); } + + mutex_unlock(&dev->mt76.mutex); + return ret; } @@ -281,26 +282,18 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw, mutex_lock(&dev->mt76.mutex); - /* TODO: sta mode connect/disconnect - * BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID - */ + if (changed & BSS_CHANGED_ASSOC) + mt7615_mcu_set_bss_info(dev, vif, info->assoc); /* TODO: update beacon content * BSS_CHANGED_BEACON */ if (changed & BSS_CHANGED_BEACON_ENABLED) { - if (info->enable_beacon) { - mt7615_mcu_set_bss_info(dev, vif, 1); - mt7615_mcu_add_wtbl_bmc(dev, vif); - mt7615_mcu_set_sta_rec_bmc(dev, vif, 1); - mt7615_mcu_set_bcn(dev, vif, 1); - } else { - mt7615_mcu_set_sta_rec_bmc(dev, vif, 0); - mt7615_mcu_del_wtbl_bmc(dev, vif); - mt7615_mcu_set_bss_info(dev, vif, 0); - mt7615_mcu_set_bcn(dev, vif, 0); - } + mt7615_mcu_set_bss_info(dev, vif, info->enable_beacon); + mt7615_mcu_wtbl_bmc(dev, vif, info->enable_beacon); + mt7615_mcu_set_sta_rec_bmc(dev, vif, info->enable_beacon); + mt7615_mcu_set_bcn(dev, vif, info->enable_beacon); } mutex_unlock(&dev->mt76.mutex); @@ -343,7 +336,7 @@ void mt7615_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); mt7615_mcu_set_sta_rec(dev, vif, sta, 0); - mt7615_mcu_del_wtbl(dev, vif, sta); + mt7615_mcu_del_wtbl(dev, sta); } static void mt7615_sta_rate_tbl_update(struct ieee80211_hw *hw, @@ -496,4 +489,5 @@ const struct ieee80211_ops mt7615_ops = { .sw_scan_start = mt7615_sw_scan, .sw_scan_complete = mt7615_sw_scan_complete, .release_buffered_frames = mt76_release_buffered_frames, + .get_txpower = mt76_get_txpower, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c index ea67c6022fe6..cdad2c8dc297 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -49,7 +49,7 @@ struct mt7615_fw_trailer { #define FW_START_WORKING_PDA_CR4 BIT(2) static int __mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, - int cmd, int query, int dest, int *wait_seq) + int cmd, int *wait_seq) { struct mt7615_mcu_txd *mcu_txd; u8 seq, q_idx, pkt_fmt; @@ -57,9 +57,6 @@ static int __mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, u32 val; __le32 *txd; - if (!skb) - return -EINVAL; - seq = ++dev->mt76.mmio.mcu.msg_seq & 0xf; if (!seq) seq = ++dev->mt76.mmio.mcu.msg_seq & 0xf; @@ -94,16 +91,15 @@ static int __mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, mcu_txd->seq = seq; if (cmd < 0) { + mcu_txd->set_query = MCU_Q_NA; mcu_txd->cid = -cmd; } else { mcu_txd->cid = MCU_CMD_EXT_CID; + mcu_txd->set_query = MCU_Q_SET; mcu_txd->ext_cid = cmd; - if (query != MCU_Q_NA) - mcu_txd->ext_cid_ack = 1; + mcu_txd->ext_cid_ack = 1; } - - mcu_txd->set_query = query; - mcu_txd->s2d_index = dest; + mcu_txd->s2d_index = MCU_S2D_H2N; if (wait_seq) *wait_seq = seq; @@ -116,24 +112,30 @@ static int __mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, return mt76_tx_queue_skb_raw(dev, qid, skb, 0); } -static int mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, - int cmd, int query, int dest, - struct sk_buff **skb_ret) +static int +mt7615_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, + int len, bool wait_resp) { + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); unsigned long expires = jiffies + 10 * HZ; struct mt7615_mcu_rxd *rxd; + struct sk_buff *skb; int ret, seq; - mutex_lock(&dev->mt76.mmio.mcu.mutex); + skb = mt7615_mcu_msg_alloc(data, len); + if (!skb) + return -ENOMEM; - ret = __mt7615_mcu_msg_send(dev, skb, cmd, query, dest, &seq); + mutex_lock(&mdev->mmio.mcu.mutex); + + ret = __mt7615_mcu_msg_send(dev, skb, cmd, &seq); if (ret) goto out; - while (1) { - skb = mt76_mcu_get_response(&dev->mt76, expires); + while (wait_resp) { + skb = mt76_mcu_get_response(mdev, expires); if (!skb) { - dev_err(dev->mt76.dev, "Message %d (seq %d) timeout\n", + dev_err(mdev->dev, "Message %d (seq %d) timeout\n", cmd, seq); ret = -ETIMEDOUT; break; @@ -143,23 +145,16 @@ static int mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, if (seq != rxd->seq) continue; - if (skb_ret) { - int hdr_len = sizeof(*rxd); - - if (!test_bit(MT76_STATE_MCU_RUNNING, - &dev->mt76.state)) - hdr_len -= 4; - skb_pull(skb, hdr_len); - *skb_ret = skb; - } else { - dev_kfree_skb(skb); + if (cmd == -MCU_CMD_PATCH_SEM_CONTROL) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; } - + dev_kfree_skb(skb); break; } out: - mutex_unlock(&dev->mt76.mmio.mcu.mutex); + mutex_unlock(&mdev->mmio.mcu.mutex); return ret; } @@ -176,28 +171,22 @@ static int mt7615_mcu_init_download(struct mt7615_dev *dev, u32 addr, .len = cpu_to_le32(len), .mode = cpu_to_le32(mode), }; - struct sk_buff *skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - return mt7615_mcu_msg_send(dev, skb, -MCU_CMD_TARGET_ADDRESS_LEN_REQ, - MCU_Q_NA, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, -MCU_CMD_TARGET_ADDRESS_LEN_REQ, + &req, sizeof(req), true); } static int mt7615_mcu_send_firmware(struct mt7615_dev *dev, const void *data, int len) { - struct sk_buff *skb; - int ret = 0; + int ret = 0, cur_len; while (len > 0) { - int cur_len = min_t(int, 4096 - sizeof(struct mt7615_mcu_txd), - len); - - skb = mt7615_mcu_msg_alloc(data, cur_len); - if (!skb) - return -ENOMEM; + cur_len = min_t(int, 4096 - sizeof(struct mt7615_mcu_txd), + len); - ret = __mt7615_mcu_msg_send(dev, skb, -MCU_CMD_FW_SCATTER, - MCU_Q_NA, MCU_S2D_H2N, NULL); + ret = __mt76_mcu_send_msg(&dev->mt76, -MCU_CMD_FW_SCATTER, + data, cur_len, false); if (ret) break; @@ -218,47 +207,27 @@ static int mt7615_mcu_start_firmware(struct mt7615_dev *dev, u32 addr, .option = cpu_to_le32(option), .addr = cpu_to_le32(addr), }; - struct sk_buff *skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - return mt7615_mcu_msg_send(dev, skb, -MCU_CMD_FW_START_REQ, - MCU_Q_NA, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, -MCU_CMD_FW_START_REQ, + &req, sizeof(req), true); } -static int mt7615_mcu_restart(struct mt7615_dev *dev) +static int mt7615_mcu_restart(struct mt76_dev *dev) { - struct sk_buff *skb = mt7615_mcu_msg_alloc(NULL, 0); - - return mt7615_mcu_msg_send(dev, skb, -MCU_CMD_RESTART_DL_REQ, - MCU_Q_NA, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(dev, -MCU_CMD_RESTART_DL_REQ, NULL, + 0, true); } static int mt7615_mcu_patch_sem_ctrl(struct mt7615_dev *dev, bool get) { struct { - __le32 operation; + __le32 op; } req = { - .operation = cpu_to_le32(get ? PATCH_SEM_GET : - PATCH_SEM_RELEASE), + .op = cpu_to_le32(get ? PATCH_SEM_GET : PATCH_SEM_RELEASE), }; - struct event { - u8 status; - u8 reserved[3]; - } *resp; - struct sk_buff *skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - struct sk_buff *skb_ret; - int ret; - ret = mt7615_mcu_msg_send(dev, skb, -MCU_CMD_PATCH_SEM_CONTROL, - MCU_Q_NA, MCU_S2D_H2N, &skb_ret); - if (ret) - goto out; - - resp = (struct event *)(skb_ret->data); - ret = resp->status; - dev_kfree_skb(skb_ret); - -out: - return ret; + return __mt76_mcu_send_msg(&dev->mt76, -MCU_CMD_PATCH_SEM_CONTROL, + &req, sizeof(req), true); } static int mt7615_mcu_start_patch(struct mt7615_dev *dev) @@ -269,10 +238,9 @@ static int mt7615_mcu_start_patch(struct mt7615_dev *dev) } req = { .check_crc = 0, }; - struct sk_buff *skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - return mt7615_mcu_msg_send(dev, skb, -MCU_CMD_PATCH_FINISH_REQ, - MCU_Q_NA, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, -MCU_CMD_PATCH_FINISH_REQ, + &req, sizeof(req), true); } static int mt7615_driver_own(struct mt7615_dev *dev) @@ -508,8 +476,14 @@ static int mt7615_load_firmware(struct mt7615_dev *dev) int mt7615_mcu_init(struct mt7615_dev *dev) { + static const struct mt76_mcu_ops mt7615_mcu_ops = { + .mcu_send_msg = mt7615_mcu_msg_send, + .mcu_restart = mt7615_mcu_restart, + }; int ret; + dev->mt76.mcu_ops = &mt7615_mcu_ops, + ret = mt7615_driver_own(dev); if (ret) return ret; @@ -525,16 +499,13 @@ int mt7615_mcu_init(struct mt7615_dev *dev) void mt7615_mcu_exit(struct mt7615_dev *dev) { - mt7615_mcu_restart(dev); + __mt76_mcu_restart(&dev->mt76); mt76_wr(dev, MT_CFG_LPCR_HOST, MT_CFG_LPCR_HOST_FW_OWN); skb_queue_purge(&dev->mt76.mmio.mcu.res_q); } int mt7615_mcu_set_eeprom(struct mt7615_dev *dev) { - struct req_data { - u8 val; - } __packed; struct { u8 buffer_mode; u8 pad; @@ -543,23 +514,22 @@ int mt7615_mcu_set_eeprom(struct mt7615_dev *dev) .buffer_mode = 1, .len = __MT_EE_MAX - MT_EE_NIC_CONF_0, }; - struct sk_buff *skb; - struct req_data *data; - const int size = (__MT_EE_MAX - MT_EE_NIC_CONF_0) * - sizeof(struct req_data); - u8 *eep = (u8 *)dev->mt76.eeprom.data; - u16 off; - - skb = mt7615_mcu_msg_alloc(NULL, size + sizeof(req_hdr)); - memcpy(skb_put(skb, sizeof(req_hdr)), &req_hdr, sizeof(req_hdr)); - data = (struct req_data *)skb_put(skb, size); - memset(data, 0, size); - - for (off = MT_EE_NIC_CONF_0; off < __MT_EE_MAX; off++) - data[off - MT_EE_NIC_CONF_0].val = eep[off]; - - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_EFUSE_BUFFER_MODE, - MCU_Q_SET, MCU_S2D_H2N, NULL); + int ret, len = sizeof(req_hdr) + __MT_EE_MAX - MT_EE_NIC_CONF_0; + u8 *req, *eep = (u8 *)dev->mt76.eeprom.data; + + req = kzalloc(len, GFP_KERNEL); + if (!req) + return -ENOMEM; + + memcpy(req, &req_hdr, sizeof(req_hdr)); + memcpy(req + sizeof(req_hdr), eep + MT_EE_NIC_CONF_0, + __MT_EE_MAX - MT_EE_NIC_CONF_0); + + ret = __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_EFUSE_BUFFER_MODE, + req, len, true); + kfree(req); + + return ret; } int mt7615_mcu_init_mac(struct mt7615_dev *dev) @@ -572,10 +542,9 @@ int mt7615_mcu_init_mac(struct mt7615_dev *dev) .enable = 1, .band = 0, }; - struct sk_buff *skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_MAC_INIT_CTRL, - MCU_Q_SET, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_MAC_INIT_CTRL, + &req, sizeof(req), true); } int mt7615_mcu_set_rts_thresh(struct mt7615_dev *dev, u32 val) @@ -592,10 +561,9 @@ int mt7615_mcu_set_rts_thresh(struct mt7615_dev *dev, u32 val) .len_thresh = cpu_to_le32(val), .pkt_thresh = cpu_to_le32(0x2), }; - struct sk_buff *skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_PROTECT_CTRL, - MCU_Q_SET, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_PROTECT_CTRL, + &req, sizeof(req), true); } int mt7615_mcu_set_wmm(struct mt7615_dev *dev, u8 queue, @@ -621,7 +589,6 @@ int mt7615_mcu_set_wmm(struct mt7615_dev *dev, u8 queue, .aifs = params->aifs, .txop = cpu_to_le16(params->txop), }; - struct sk_buff *skb; if (params->cw_min) { req.valid |= WMM_CW_MIN_SET; @@ -632,9 +599,8 @@ int mt7615_mcu_set_wmm(struct mt7615_dev *dev, u8 queue, req.cw_max = cpu_to_le16(params->cw_max); } - skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_EDCA_UPDATE, - MCU_Q_SET, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_EDCA_UPDATE, + &req, sizeof(req), true); } int mt7615_mcu_ctrl_pm_state(struct mt7615_dev *dev, int enter) @@ -662,300 +628,200 @@ int mt7615_mcu_ctrl_pm_state(struct mt7615_dev *dev, int enter) .pm_state = (enter) ? ENTER_PM_STATE : EXIT_PM_STATE, .band_idx = 0, }; - struct sk_buff *skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_PM_STATE_CTRL, - MCU_Q_SET, MCU_S2D_H2N, NULL); -} - -static int __mt7615_mcu_set_dev_info(struct mt7615_dev *dev, - struct dev_info *dev_info) -{ - struct req_hdr { - u8 omac_idx; - u8 band_idx; - __le16 tlv_num; - u8 is_tlv_append; - u8 rsv[3]; - } __packed req_hdr = {0}; - struct req_tlv { - __le16 tag; - __le16 len; - u8 active; - u8 band_idx; - u8 omac_addr[ETH_ALEN]; - } __packed; - struct sk_buff *skb; - u16 tlv_num = 0; - - skb = mt7615_mcu_msg_alloc(NULL, sizeof(req_hdr) + - sizeof(struct req_tlv)); - skb_reserve(skb, sizeof(req_hdr)); - - if (dev_info->feature & BIT(DEV_INFO_ACTIVE)) { - struct req_tlv req_tlv = { - .tag = cpu_to_le16(DEV_INFO_ACTIVE), - .len = cpu_to_le16(sizeof(req_tlv)), - .active = dev_info->enable, - .band_idx = dev_info->band_idx, - }; - memcpy(req_tlv.omac_addr, dev_info->omac_addr, ETH_ALEN); - memcpy(skb_put(skb, sizeof(req_tlv)), &req_tlv, - sizeof(req_tlv)); - tlv_num++; - } - - req_hdr.omac_idx = dev_info->omac_idx; - req_hdr.band_idx = dev_info->band_idx; - req_hdr.tlv_num = cpu_to_le16(tlv_num); - req_hdr.is_tlv_append = tlv_num ? 1 : 0; - memcpy(skb_push(skb, sizeof(req_hdr)), &req_hdr, sizeof(req_hdr)); - - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_DEV_INFO_UPDATE, - MCU_Q_SET, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_PM_STATE_CTRL, + &req, sizeof(req), true); } -int mt7615_mcu_set_dev_info(struct mt7615_dev *dev, struct ieee80211_vif *vif, - int en) +int mt7615_mcu_set_dev_info(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable) { struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - struct dev_info dev_info = {0}; - - dev_info.omac_idx = mvif->omac_idx; - memcpy(dev_info.omac_addr, vif->addr, ETH_ALEN); - dev_info.band_idx = mvif->band_idx; - dev_info.enable = en; - dev_info.feature = BIT(DEV_INFO_ACTIVE); + struct { + struct req_hdr { + u8 omac_idx; + u8 band_idx; + __le16 tlv_num; + u8 is_tlv_append; + u8 rsv[3]; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 band_idx; + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } data = { + .hdr = { + .omac_idx = mvif->omac_idx, + .band_idx = mvif->band_idx, + .tlv_num = cpu_to_le16(1), + .is_tlv_append = 1, + }, + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = enable, + .band_idx = mvif->band_idx, + }, + }; - return __mt7615_mcu_set_dev_info(dev, &dev_info); + memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_DEV_INFO_UPDATE, + &data, sizeof(data), true); } -static void bss_info_omac_handler (struct mt7615_dev *dev, - struct bss_info *bss_info, - struct sk_buff *skb) +static void +mt7615_mcu_bss_info_omac_header(struct mt7615_vif *mvif, u8 *data, + u32 conn_type) { - struct bss_info_omac tlv = {0}; - - tlv.tag = cpu_to_le16(BSS_INFO_OMAC); - tlv.len = cpu_to_le16(sizeof(tlv)); - tlv.hw_bss_idx = (bss_info->omac_idx > EXT_BSSID_START) ? - HW_BSSID_0 : bss_info->omac_idx; - tlv.omac_idx = bss_info->omac_idx; - tlv.band_idx = bss_info->band_idx; - tlv.conn_type = cpu_to_le32(bss_info->conn_type); - - memcpy(skb_put(skb, sizeof(tlv)), &tlv, sizeof(tlv)); + struct bss_info_omac *hdr = (struct bss_info_omac *)data; + u8 idx; + + idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; + hdr->tag = cpu_to_le16(BSS_INFO_OMAC); + hdr->len = cpu_to_le16(sizeof(struct bss_info_omac)); + hdr->hw_bss_idx = idx; + hdr->omac_idx = mvif->omac_idx; + hdr->band_idx = mvif->band_idx; + hdr->conn_type = cpu_to_le32(conn_type); } -static void bss_info_basic_handler (struct mt7615_dev *dev, - struct bss_info *bss_info, - struct sk_buff *skb) +static void +mt7615_mcu_bss_info_basic_header(struct ieee80211_vif *vif, u8 *data, + u32 net_type, u8 tx_wlan_idx, + bool enable) { - struct bss_info_basic tlv = {0}; - - tlv.tag = cpu_to_le16(BSS_INFO_BASIC); - tlv.len = cpu_to_le16(sizeof(tlv)); - tlv.network_type = cpu_to_le32(bss_info->network_type); - tlv.active = bss_info->enable; - tlv.bcn_interval = cpu_to_le16(bss_info->bcn_interval); - memcpy(tlv.bssid, bss_info->bssid, ETH_ALEN); - tlv.wmm_idx = bss_info->wmm_idx; - tlv.dtim_period = bss_info->dtim_period; - tlv.bmc_tx_wlan_idx = bss_info->bmc_tx_wlan_idx; - - memcpy(skb_put(skb, sizeof(tlv)), &tlv, sizeof(tlv)); + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct bss_info_basic *hdr = (struct bss_info_basic *)data; + + hdr->tag = cpu_to_le16(BSS_INFO_BASIC); + hdr->len = cpu_to_le16(sizeof(struct bss_info_basic)); + hdr->network_type = cpu_to_le32(net_type); + hdr->active = enable; + hdr->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); + memcpy(hdr->bssid, vif->bss_conf.bssid, ETH_ALEN); + hdr->wmm_idx = mvif->wmm_idx; + hdr->dtim_period = vif->bss_conf.dtim_period; + hdr->bmc_tx_wlan_idx = tx_wlan_idx; } -static void bss_info_ext_bss_handler (struct mt7615_dev *dev, - struct bss_info *bss_info, - struct sk_buff *skb) +static void +mt7615_mcu_bss_info_ext_header(struct mt7615_vif *mvif, u8 *data) { /* SIFS 20us + 512 byte beacon tranmitted by 1Mbps (3906us) */ #define BCN_TX_ESTIMATE_TIME (4096 + 20) - struct bss_info_ext_bss tlv = {0}; - int ext_bss_idx; - - ext_bss_idx = bss_info->omac_idx - EXT_BSSID_START; + struct bss_info_ext_bss *hdr = (struct bss_info_ext_bss *)data; + int ext_bss_idx, tsf_offset; + ext_bss_idx = mvif->omac_idx - EXT_BSSID_START; if (ext_bss_idx < 0) return; - tlv.tag = cpu_to_le16(BSS_INFO_EXT_BSS); - tlv.len = cpu_to_le16(sizeof(tlv)); - tlv.mbss_tsf_offset = ext_bss_idx * BCN_TX_ESTIMATE_TIME; - - memcpy(skb_put(skb, sizeof(tlv)), &tlv, sizeof(tlv)); + hdr->tag = cpu_to_le16(BSS_INFO_EXT_BSS); + hdr->len = cpu_to_le16(sizeof(struct bss_info_ext_bss)); + tsf_offset = ext_bss_idx * BCN_TX_ESTIMATE_TIME; + hdr->mbss_tsf_offset = cpu_to_le32(tsf_offset); } -static struct bss_info_tag_handler bss_info_tag_handler[] = { - {BSS_INFO_OMAC, sizeof(struct bss_info_omac), bss_info_omac_handler}, - {BSS_INFO_BASIC, sizeof(struct bss_info_basic), bss_info_basic_handler}, - {BSS_INFO_RF_CH, sizeof(struct bss_info_rf_ch), NULL}, - {BSS_INFO_PM, 0, NULL}, - {BSS_INFO_UAPSD, 0, NULL}, - {BSS_INFO_ROAM_DETECTION, 0, NULL}, - {BSS_INFO_LQ_RM, 0, NULL}, - {BSS_INFO_EXT_BSS, sizeof(struct bss_info_ext_bss), bss_info_ext_bss_handler}, - {BSS_INFO_BMC_INFO, 0, NULL}, - {BSS_INFO_SYNC_MODE, 0, NULL}, - {BSS_INFO_RA, 0, NULL}, - {BSS_INFO_MAX_NUM, 0, NULL}, -}; - -static int __mt7615_mcu_set_bss_info(struct mt7615_dev *dev, - struct bss_info *bss_info) +int mt7615_mcu_set_bss_info(struct mt7615_dev *dev, + struct ieee80211_vif *vif, int en) { + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; struct req_hdr { u8 bss_idx; u8 rsv0; __le16 tlv_num; u8 is_tlv_append; u8 rsv1[3]; - } __packed req_hdr = {0}; - struct sk_buff *skb; - u16 tlv_num = 0; - u32 size = 0; - int i; + } __packed; + int len = sizeof(struct req_hdr) + sizeof(struct bss_info_basic); + int ret, i, features = BIT(BSS_INFO_BASIC), ntlv = 1; + u32 conn_type = 0, net_type = NETWORK_INFRA; + u8 *buf, *data, tx_wlan_idx = 0; + struct req_hdr *hdr; - for (i = 0; i < BSS_INFO_MAX_NUM; i++) - if ((BIT(bss_info_tag_handler[i].tag) & bss_info->feature) && - bss_info_tag_handler[i].handler) { - tlv_num++; - size += bss_info_tag_handler[i].len; + if (en) { + len += sizeof(struct bss_info_omac); + features |= BIT(BSS_INFO_OMAC); + if (mvif->omac_idx > EXT_BSSID_START) { + len += sizeof(struct bss_info_ext_bss); + features |= BIT(BSS_INFO_EXT_BSS); + ntlv++; } + ntlv++; + } - skb = mt7615_mcu_msg_alloc(NULL, sizeof(req_hdr) + size); - - req_hdr.bss_idx = bss_info->bss_idx; - req_hdr.tlv_num = cpu_to_le16(tlv_num); - req_hdr.is_tlv_append = tlv_num ? 1 : 0; - - memcpy(skb_put(skb, sizeof(req_hdr)), &req_hdr, sizeof(req_hdr)); - - for (i = 0; i < BSS_INFO_MAX_NUM; i++) - if ((BIT(bss_info_tag_handler[i].tag) & bss_info->feature) && - bss_info_tag_handler[i].handler) - bss_info_tag_handler[i].handler(dev, bss_info, skb); - - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_BSS_INFO_UPDATE, - MCU_Q_SET, MCU_S2D_H2N, NULL); -} - -static void bss_info_convert_vif_type(enum nl80211_iftype type, - u32 *network_type, u32 *conn_type) -{ - switch (type) { + switch (vif->type) { case NL80211_IFTYPE_AP: - if (network_type) - *network_type = NETWORK_INFRA; - if (conn_type) - *conn_type = CONNECTION_INFRA_AP; + case NL80211_IFTYPE_MESH_POINT: + tx_wlan_idx = mvif->sta.wcid.idx; + conn_type = CONNECTION_INFRA_AP; break; - case NL80211_IFTYPE_STATION: - if (network_type) - *network_type = NETWORK_INFRA; - if (conn_type) - *conn_type = CONNECTION_INFRA_STA; + case NL80211_IFTYPE_STATION: { + /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ + if (en) { + struct ieee80211_sta *sta; + struct mt7615_sta *msta; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (!sta) { + rcu_read_unlock(); + return -EINVAL; + } + + msta = (struct mt7615_sta *)sta->drv_priv; + tx_wlan_idx = msta->wcid.idx; + rcu_read_unlock(); + } + conn_type = CONNECTION_INFRA_STA; break; + } default: WARN_ON(1); break; - }; -} - -int mt7615_mcu_set_bss_info(struct mt7615_dev *dev, struct ieee80211_vif *vif, - int en) -{ - struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - struct bss_info bss_info = {0}; - u8 bmc_tx_wlan_idx = 0; - u32 network_type = 0, conn_type = 0; - - if (vif->type == NL80211_IFTYPE_AP) { - bmc_tx_wlan_idx = mvif->sta.wcid.idx; - } else if (vif->type == NL80211_IFTYPE_STATION) { - /* find the unicast entry for sta mode bmc tx */ - struct ieee80211_sta *ap_sta; - struct mt7615_sta *msta; - - rcu_read_lock(); - - ap_sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); - if (!ap_sta) { - rcu_read_unlock(); - return -EINVAL; - } - - msta = (struct mt7615_sta *)ap_sta->drv_priv; - bmc_tx_wlan_idx = msta->wcid.idx; - - rcu_read_unlock(); - } else { - WARN_ON(1); } - bss_info_convert_vif_type(vif->type, &network_type, &conn_type); - - bss_info.bss_idx = mvif->idx; - memcpy(bss_info.bssid, vif->bss_conf.bssid, ETH_ALEN); - bss_info.omac_idx = mvif->omac_idx; - bss_info.band_idx = mvif->band_idx; - bss_info.bmc_tx_wlan_idx = bmc_tx_wlan_idx; - bss_info.wmm_idx = mvif->wmm_idx; - bss_info.network_type = network_type; - bss_info.conn_type = conn_type; - bss_info.bcn_interval = vif->bss_conf.beacon_int; - bss_info.dtim_period = vif->bss_conf.dtim_period; - bss_info.enable = en; - bss_info.feature = BIT(BSS_INFO_BASIC); - if (en) { - bss_info.feature |= BIT(BSS_INFO_OMAC); - if (mvif->omac_idx > EXT_BSSID_START) - bss_info.feature |= BIT(BSS_INFO_EXT_BSS); - } - - return __mt7615_mcu_set_bss_info(dev, &bss_info); -} + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; -static int __mt7615_mcu_set_wtbl(struct mt7615_dev *dev, int wlan_idx, - int operation, void *buf, int buf_len) -{ - struct req_hdr { - u8 wlan_idx; - u8 operation; - __le16 tlv_num; - u8 rsv[4]; - } __packed req_hdr = {0}; - struct tlv { - __le16 tag; - __le16 len; - u8 buf[0]; - } __packed; - struct sk_buff *skb; - u16 tlv_num = 0; - int offset = 0; + hdr = (struct req_hdr *)buf; + hdr->bss_idx = mvif->idx; + hdr->tlv_num = cpu_to_le16(ntlv); + hdr->is_tlv_append = 1; - while (offset < buf_len) { - struct tlv *tlv = (struct tlv *)((u8 *)buf + offset); + data = buf + sizeof(*hdr); + for (i = 0; i < BSS_INFO_MAX_NUM; i++) { + int tag = ffs(features & BIT(i)) - 1; - tlv_num++; - offset += tlv->len; + switch (tag) { + case BSS_INFO_OMAC: + mt7615_mcu_bss_info_omac_header(mvif, data, + conn_type); + data += sizeof(struct bss_info_omac); + break; + case BSS_INFO_BASIC: + mt7615_mcu_bss_info_basic_header(vif, data, net_type, + tx_wlan_idx, en); + data += sizeof(struct bss_info_basic); + break; + case BSS_INFO_EXT_BSS: + mt7615_mcu_bss_info_ext_header(mvif, data); + data += sizeof(struct bss_info_ext_bss); + break; + default: + break; + } } - skb = mt7615_mcu_msg_alloc(NULL, sizeof(req_hdr) + buf_len); - - req_hdr.wlan_idx = wlan_idx; - req_hdr.operation = operation; - req_hdr.tlv_num = cpu_to_le16(tlv_num); - - memcpy(skb_put(skb, sizeof(req_hdr)), &req_hdr, sizeof(req_hdr)); - - if (buf && buf_len) - memcpy(skb_put(skb, buf_len), buf, buf_len); + ret = __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_BSS_INFO_UPDATE, + buf, len, true); + kfree(buf); - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_WTBL_UPDATE, - MCU_Q_SET, MCU_S2D_H2N, NULL); + return ret; } static enum mt7615_cipher_type @@ -995,70 +861,90 @@ int mt7615_mcu_set_wtbl_key(struct mt7615_dev *dev, int wcid, struct ieee80211_key_conf *key, enum set_key_cmd cmd) { - struct wtbl_sec_key wtbl_sec_key = {0}; - int buf_len = sizeof(struct wtbl_sec_key); - u8 cipher; - - wtbl_sec_key.tag = cpu_to_le16(WTBL_SEC_KEY); - wtbl_sec_key.len = cpu_to_le16(buf_len); - wtbl_sec_key.add = cmd; + struct { + struct wtbl_req_hdr hdr; + struct wtbl_sec_key key; + } req = { + .hdr = { + .wlan_idx = wcid, + .operation = WTBL_SET, + .tlv_num = cpu_to_le16(1), + }, + .key = { + .tag = cpu_to_le16(WTBL_SEC_KEY), + .len = cpu_to_le16(sizeof(struct wtbl_sec_key)), + .add = cmd, + }, + }; if (cmd == SET_KEY) { - cipher = mt7615_get_key_info(key, wtbl_sec_key.key_material); - if (cipher == MT_CIPHER_NONE && key) + u8 cipher; + + cipher = mt7615_get_key_info(key, req.key.key_material); + if (cipher == MT_CIPHER_NONE) return -EOPNOTSUPP; - wtbl_sec_key.cipher_id = cipher; - wtbl_sec_key.key_id = key->keyidx; - wtbl_sec_key.key_len = key->keylen; + req.key.rkv = 1; + req.key.cipher_id = cipher; + req.key.key_id = key->keyidx; + req.key.key_len = key->keylen; } else { - wtbl_sec_key.key_len = sizeof(wtbl_sec_key.key_material); + req.key.key_len = sizeof(req.key.key_material); } - return __mt7615_mcu_set_wtbl(dev, wcid, WTBL_SET, &wtbl_sec_key, - buf_len); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + &req, sizeof(req), true); } -int mt7615_mcu_add_wtbl_bmc(struct mt7615_dev *dev, struct ieee80211_vif *vif) +static int +mt7615_mcu_add_wtbl_bmc(struct mt7615_dev *dev, + struct mt7615_vif *mvif) { - struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - struct wtbl_generic *wtbl_generic; - struct wtbl_rx *wtbl_rx; - int buf_len, ret; - u8 *buf; - - buf = kzalloc(MT7615_WTBL_UPDATE_MAX_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - wtbl_generic = (struct wtbl_generic *)buf; - buf_len = sizeof(*wtbl_generic); - wtbl_generic->tag = cpu_to_le16(WTBL_GENERIC); - wtbl_generic->len = cpu_to_le16(buf_len); - eth_broadcast_addr(wtbl_generic->peer_addr); - wtbl_generic->muar_idx = 0xe; - - wtbl_rx = (struct wtbl_rx *)(buf + buf_len); - buf_len += sizeof(*wtbl_rx); - wtbl_rx->tag = cpu_to_le16(WTBL_RX); - wtbl_rx->len = cpu_to_le16(sizeof(*wtbl_rx)); - wtbl_rx->rca1 = 1; - wtbl_rx->rca2 = 1; - wtbl_rx->rv = 1; - - ret = __mt7615_mcu_set_wtbl(dev, mvif->sta.wcid.idx, - WTBL_RESET_AND_SET, buf, buf_len); + struct { + struct wtbl_req_hdr hdr; + struct wtbl_generic g_wtbl; + struct wtbl_rx rx_wtbl; + } req = { + .hdr = { + .wlan_idx = mvif->sta.wcid.idx, + .operation = WTBL_RESET_AND_SET, + .tlv_num = cpu_to_le16(2), + }, + .g_wtbl = { + .tag = cpu_to_le16(WTBL_GENERIC), + .len = cpu_to_le16(sizeof(struct wtbl_generic)), + .muar_idx = 0xe, + }, + .rx_wtbl = { + .tag = cpu_to_le16(WTBL_RX), + .len = cpu_to_le16(sizeof(struct wtbl_rx)), + .rca1 = 1, + .rca2 = 1, + .rv = 1, + }, + }; + eth_broadcast_addr(req.g_wtbl.peer_addr); - kfree(buf); - return ret; + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + &req, sizeof(req), true); } -int mt7615_mcu_del_wtbl_bmc(struct mt7615_dev *dev, struct ieee80211_vif *vif) +int mt7615_mcu_wtbl_bmc(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable) { struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - return __mt7615_mcu_set_wtbl(dev, mvif->sta.wcid.idx, - WTBL_RESET_AND_SET, NULL, 0); + if (!enable) { + struct wtbl_req_hdr req = { + .wlan_idx = mvif->sta.wcid.idx, + .operation = WTBL_RESET_AND_SET, + }; + + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + &req, sizeof(req), true); + } + + return mt7615_mcu_add_wtbl_bmc(dev, mvif); } int mt7615_mcu_add_wtbl(struct mt7615_dev *dev, struct ieee80211_vif *vif, @@ -1066,175 +952,153 @@ int mt7615_mcu_add_wtbl(struct mt7615_dev *dev, struct ieee80211_vif *vif, { struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; - struct wtbl_generic *wtbl_generic; - struct wtbl_rx *wtbl_rx; - int buf_len, ret; - u8 *buf; - - buf = kzalloc(MT7615_WTBL_UPDATE_MAX_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - wtbl_generic = (struct wtbl_generic *)buf; - buf_len = sizeof(*wtbl_generic); - wtbl_generic->tag = cpu_to_le16(WTBL_GENERIC); - wtbl_generic->len = cpu_to_le16(buf_len); - memcpy(wtbl_generic->peer_addr, sta->addr, ETH_ALEN); - wtbl_generic->muar_idx = mvif->omac_idx; - wtbl_generic->qos = sta->wme; - wtbl_generic->partial_aid = cpu_to_le16(sta->aid); - - wtbl_rx = (struct wtbl_rx *)(buf + buf_len); - buf_len += sizeof(*wtbl_rx); - wtbl_rx->tag = cpu_to_le16(WTBL_RX); - wtbl_rx->len = cpu_to_le16(sizeof(*wtbl_rx)); - wtbl_rx->rca1 = (vif->type == NL80211_IFTYPE_AP) ? 0 : 1; - wtbl_rx->rca2 = 1; - wtbl_rx->rv = 1; - - ret = __mt7615_mcu_set_wtbl(dev, msta->wcid.idx, - WTBL_RESET_AND_SET, buf, buf_len); + struct { + struct wtbl_req_hdr hdr; + struct wtbl_generic g_wtbl; + struct wtbl_rx rx_wtbl; + } req = { + .hdr = { + .wlan_idx = msta->wcid.idx, + .operation = WTBL_RESET_AND_SET, + .tlv_num = cpu_to_le16(2), + }, + .g_wtbl = { + .tag = cpu_to_le16(WTBL_GENERIC), + .len = cpu_to_le16(sizeof(struct wtbl_generic)), + .muar_idx = mvif->omac_idx, + .qos = sta->wme, + .partial_aid = cpu_to_le16(sta->aid), + }, + .rx_wtbl = { + .tag = cpu_to_le16(WTBL_RX), + .len = cpu_to_le16(sizeof(struct wtbl_rx)), + .rca1 = vif->type != NL80211_IFTYPE_AP, + .rca2 = 1, + .rv = 1, + }, + }; + memcpy(req.g_wtbl.peer_addr, sta->addr, ETH_ALEN); - kfree(buf); - return ret; + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + &req, sizeof(req), true); } -int mt7615_mcu_del_wtbl(struct mt7615_dev *dev, struct ieee80211_vif *vif, +int mt7615_mcu_del_wtbl(struct mt7615_dev *dev, struct ieee80211_sta *sta) { struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + struct wtbl_req_hdr req = { + .wlan_idx = msta->wcid.idx, + .operation = WTBL_RESET_AND_SET, + }; - return __mt7615_mcu_set_wtbl(dev, msta->wcid.idx, - WTBL_RESET_AND_SET, NULL, 0); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + &req, sizeof(req), true); } int mt7615_mcu_del_wtbl_all(struct mt7615_dev *dev) { - return __mt7615_mcu_set_wtbl(dev, 0, WTBL_RESET_ALL, NULL, 0); -} - -static int __mt7615_mcu_set_sta_rec(struct mt7615_dev *dev, int bss_idx, - int wlan_idx, int muar_idx, void *buf, - int buf_len) -{ - struct req_hdr { - u8 bss_idx; - u8 wlan_idx; - __le16 tlv_num; - u8 is_tlv_append; - u8 muar_idx; - u8 rsv[2]; - } __packed req_hdr = {0}; - struct tlv { - __le16 tag; - __le16 len; - u8 buf[0]; - } __packed; - struct sk_buff *skb; - u16 tlv_num = 0; - int offset = 0; - - while (offset < buf_len) { - struct tlv *tlv = (struct tlv *)((u8 *)buf + offset); - - tlv_num++; - offset += tlv->len; - } - - skb = mt7615_mcu_msg_alloc(NULL, sizeof(req_hdr) + buf_len); - - req_hdr.bss_idx = bss_idx; - req_hdr.wlan_idx = wlan_idx; - req_hdr.tlv_num = cpu_to_le16(tlv_num); - req_hdr.is_tlv_append = tlv_num ? 1 : 0; - req_hdr.muar_idx = muar_idx; - - memcpy(skb_put(skb, sizeof(req_hdr)), &req_hdr, sizeof(req_hdr)); - - if (buf && buf_len) - memcpy(skb_put(skb, buf_len), buf, buf_len); + struct wtbl_req_hdr req = { + .operation = WTBL_RESET_ALL, + }; - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_STA_REC_UPDATE, - MCU_Q_SET, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + &req, sizeof(req), true); } int mt7615_mcu_set_sta_rec_bmc(struct mt7615_dev *dev, struct ieee80211_vif *vif, bool en) { struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - struct sta_rec_basic sta_rec_basic = {0}; - int buf_len = sizeof(struct sta_rec_basic); + struct { + struct sta_req_hdr hdr; + struct sta_rec_basic basic; + } req = { + .hdr = { + .bss_idx = mvif->idx, + .wlan_idx = mvif->sta.wcid.idx, + .tlv_num = cpu_to_le16(1), + .is_tlv_append = 1, + .muar_idx = mvif->omac_idx, + }, + .basic = { + .tag = cpu_to_le16(STA_REC_BASIC), + .len = cpu_to_le16(sizeof(struct sta_rec_basic)), + .conn_type = cpu_to_le32(CONNECTION_INFRA_BC), + }, + }; + eth_broadcast_addr(req.basic.peer_addr); - sta_rec_basic.tag = cpu_to_le16(STA_REC_BASIC); - sta_rec_basic.len = cpu_to_le16(buf_len); - sta_rec_basic.conn_type = cpu_to_le32(CONNECTION_INFRA_BC); - eth_broadcast_addr(sta_rec_basic.peer_addr); if (en) { - sta_rec_basic.conn_state = CONN_STATE_PORT_SECURE; - sta_rec_basic.extra_info = - cpu_to_le16(EXTRA_INFO_VER | EXTRA_INFO_NEW); + req.basic.conn_state = CONN_STATE_PORT_SECURE; + req.basic.extra_info = cpu_to_le16(EXTRA_INFO_VER | + EXTRA_INFO_NEW); } else { - sta_rec_basic.conn_state = CONN_STATE_DISCONNECT; - sta_rec_basic.extra_info = cpu_to_le16(EXTRA_INFO_VER); + req.basic.conn_state = CONN_STATE_DISCONNECT; + req.basic.extra_info = cpu_to_le16(EXTRA_INFO_VER); } - return __mt7615_mcu_set_sta_rec(dev, mvif->idx, mvif->sta.wcid.idx, - mvif->omac_idx, &sta_rec_basic, - buf_len); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_STA_REC_UPDATE, + &req, sizeof(req), true); } -static void sta_rec_convert_vif_type(enum nl80211_iftype type, u32 *conn_type) +int mt7615_mcu_set_sta_rec(struct mt7615_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool en) { - switch (type) { + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + + struct { + struct sta_req_hdr hdr; + struct sta_rec_basic basic; + } req = { + .hdr = { + .bss_idx = mvif->idx, + .wlan_idx = msta->wcid.idx, + .tlv_num = cpu_to_le16(1), + .is_tlv_append = 1, + .muar_idx = mvif->omac_idx, + }, + .basic = { + .tag = cpu_to_le16(STA_REC_BASIC), + .len = cpu_to_le16(sizeof(struct sta_rec_basic)), + .qos = sta->wme, + .aid = cpu_to_le16(sta->aid), + }, + }; + memcpy(req.basic.peer_addr, sta->addr, ETH_ALEN); + + switch (vif->type) { case NL80211_IFTYPE_AP: - if (conn_type) - *conn_type = CONNECTION_INFRA_STA; + case NL80211_IFTYPE_MESH_POINT: + req.basic.conn_type = cpu_to_le32(CONNECTION_INFRA_STA); break; case NL80211_IFTYPE_STATION: - if (conn_type) - *conn_type = CONNECTION_INFRA_AP; + req.basic.conn_type = cpu_to_le32(CONNECTION_INFRA_AP); break; default: WARN_ON(1); break; }; -} - -int mt7615_mcu_set_sta_rec(struct mt7615_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool en) -{ - struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; - struct sta_rec_basic sta_rec_basic = {0}; - int buf_len = sizeof(struct sta_rec_basic); - u32 conn_type = 0; - - sta_rec_convert_vif_type(vif->type, &conn_type); - - sta_rec_basic.tag = cpu_to_le16(STA_REC_BASIC); - sta_rec_basic.len = cpu_to_le16(buf_len); - sta_rec_basic.conn_type = cpu_to_le32(conn_type); - sta_rec_basic.qos = sta->wme; - sta_rec_basic.aid = cpu_to_le16(sta->aid); - memcpy(sta_rec_basic.peer_addr, sta->addr, ETH_ALEN); if (en) { - sta_rec_basic.conn_state = CONN_STATE_PORT_SECURE; - sta_rec_basic.extra_info = - cpu_to_le16(EXTRA_INFO_VER | EXTRA_INFO_NEW); + req.basic.conn_state = CONN_STATE_PORT_SECURE; + req.basic.extra_info = cpu_to_le16(EXTRA_INFO_VER | + EXTRA_INFO_NEW); } else { - sta_rec_basic.conn_state = CONN_STATE_DISCONNECT; - sta_rec_basic.extra_info = cpu_to_le16(EXTRA_INFO_VER); + req.basic.conn_state = CONN_STATE_DISCONNECT; + req.basic.extra_info = cpu_to_le16(EXTRA_INFO_VER); } - return __mt7615_mcu_set_sta_rec(dev, mvif->idx, msta->wcid.idx, - mvif->omac_idx, &sta_rec_basic, - buf_len); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_STA_REC_UPDATE, + &req, sizeof(req), true); } int mt7615_mcu_set_bcn(struct mt7615_dev *dev, struct ieee80211_vif *vif, int en) { + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; struct req { u8 omac_idx; u8 enable; @@ -1250,14 +1114,18 @@ int mt7615_mcu_set_bcn(struct mt7615_dev *dev, struct ieee80211_vif *vif, /* bss color change */ u8 bcc_cnt; __le16 bcc_ie_pos; - } __packed req = {0}; - struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - struct mt76_wcid *wcid = &dev->mt76.global_wcid; + } __packed req = { + .omac_idx = mvif->omac_idx, + .enable = en, + .wlan_idx = wcid->idx, + .band_idx = mvif->band_idx, + /* pky_type: 0 for bcn, 1 for tim */ + .pkt_type = 0, + }; struct sk_buff *skb; - u16 tim_off, tim_len; - - skb = ieee80211_beacon_get_tim(mt76_hw(dev), vif, &tim_off, &tim_len); + u16 tim_off; + skb = ieee80211_beacon_get_tim(mt76_hw(dev), vif, &tim_off, NULL); if (!skb) return -EINVAL; @@ -1270,21 +1138,79 @@ int mt7615_mcu_set_bcn(struct mt7615_dev *dev, struct ieee80211_vif *vif, mt7615_mac_write_txwi(dev, (__le32 *)(req.pkt), skb, wcid, NULL, 0, NULL); memcpy(req.pkt + MT_TXD_SIZE, skb->data, skb->len); - dev_kfree_skb(skb); - - req.omac_idx = mvif->omac_idx; - req.enable = en; - req.wlan_idx = wcid->idx; - req.band_idx = mvif->band_idx; - /* pky_type: 0 for bcn, 1 for tim */ - req.pkt_type = 0; req.pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); req.tim_ie_pos = cpu_to_le16(MT_TXD_SIZE + tim_off); - skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); + dev_kfree_skb(skb); + + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_BCN_OFFLOAD, + &req, sizeof(req), true); +} + +int mt7615_mcu_set_tx_power(struct mt7615_dev *dev) +{ + int i, ret, n_chains = hweight8(dev->mt76.antenna_mask); + struct cfg80211_chan_def *chandef = &dev->mt76.chandef; + int freq = chandef->center_freq1, len, target_chains; + u8 *req, *data, *eep = (u8 *)dev->mt76.eeprom.data; + enum nl80211_band band = chandef->chan->band; + struct ieee80211_hw *hw = mt76_hw(dev); + struct { + u8 center_chan; + u8 dbdc_idx; + u8 band; + u8 rsv; + } __packed req_hdr = { + .center_chan = ieee80211_frequency_to_channel(freq), + .band = band, + }; + s8 tx_power; + + len = sizeof(req_hdr) + __MT_EE_MAX - MT_EE_NIC_CONF_0; + req = kzalloc(len, GFP_KERNEL); + if (!req) + return -ENOMEM; - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_BCN_OFFLOAD, - MCU_Q_SET, MCU_S2D_H2N, NULL); + memcpy(req, &req_hdr, sizeof(req_hdr)); + data = req + sizeof(req_hdr); + memcpy(data, eep + MT_EE_NIC_CONF_0, + __MT_EE_MAX - MT_EE_NIC_CONF_0); + + tx_power = hw->conf.power_level * 2; + switch (n_chains) { + case 4: + tx_power -= 12; + break; + case 3: + tx_power -= 8; + break; + case 2: + tx_power -= 6; + break; + default: + break; + } + tx_power = max_t(s8, tx_power, 0); + dev->mt76.txpower_cur = tx_power; + + target_chains = mt7615_ext_pa_enabled(dev, band) ? 1 : n_chains; + for (i = 0; i < target_chains; i++) { + int index = -MT_EE_NIC_CONF_0; + + ret = mt7615_eeprom_get_power_index(dev, chandef->chan, i); + if (ret < 0) + goto out; + + index += ret; + data[index] = min_t(u8, data[index], tx_power); + } + + ret = __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_SET_TX_POWER_CTRL, + req, len, true); +out: + kfree(req); + + return ret; } int mt7615_mcu_set_channel(struct mt7615_dev *dev) @@ -1309,7 +1235,6 @@ int mt7615_mcu_set_channel(struct mt7615_dev *dev) u8 txpower_sku[53]; u8 rsv2[3]; } req = {0}; - struct sk_buff *skb; int ret; req.control_chan = chdef->chan->hw_value; @@ -1345,18 +1270,15 @@ int mt7615_mcu_set_channel(struct mt7615_dev *dev) default: req.bw = CMD_CBW_20MHZ; } - memset(req.txpower_sku, 0x3f, 49); - skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - ret = mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_CHANNEL_SWITCH, - MCU_Q_SET, MCU_S2D_H2N, NULL); + ret = __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_CHANNEL_SWITCH, + &req, sizeof(req), true); if (ret) return ret; - skb = mt7615_mcu_msg_alloc(&req, sizeof(req)); - return mt7615_mcu_msg_send(dev, skb, MCU_EXT_CMD_SET_RX_PATH, - MCU_Q_SET, MCU_S2D_H2N, NULL); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_SET_RX_PATH, + &req, sizeof(req), true); } int mt7615_mcu_set_ht_cap(struct mt7615_dev *dev, struct ieee80211_vif *vif, @@ -1364,10 +1286,12 @@ int mt7615_mcu_set_ht_cap(struct mt7615_dev *dev, struct ieee80211_vif *vif, { struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; - struct wtbl_ht *wtbl_ht; + struct wtbl_req_hdr *wtbl_hdr; + struct sta_req_hdr *sta_hdr; struct wtbl_raw *wtbl_raw; - struct sta_rec_ht *sta_rec_ht; - int buf_len, ret; + struct sta_rec_ht *sta_ht; + struct wtbl_ht *wtbl_ht; + int buf_len, ret, ntlv = 2; u32 msk, val = 0; u8 *buf; @@ -1375,15 +1299,20 @@ int mt7615_mcu_set_ht_cap(struct mt7615_dev *dev, struct ieee80211_vif *vif, if (!buf) return -ENOMEM; + wtbl_hdr = (struct wtbl_req_hdr *)buf; + wtbl_hdr->wlan_idx = msta->wcid.idx; + wtbl_hdr->operation = WTBL_SET; + buf_len = sizeof(*wtbl_hdr); + /* ht basic */ - buf_len = sizeof(*wtbl_ht); - wtbl_ht = (struct wtbl_ht *)buf; + wtbl_ht = (struct wtbl_ht *)(buf + buf_len); wtbl_ht->tag = cpu_to_le16(WTBL_HT); wtbl_ht->len = cpu_to_le16(sizeof(*wtbl_ht)); wtbl_ht->ht = 1; wtbl_ht->ldpc = sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING; wtbl_ht->af = sta->ht_cap.ampdu_factor; wtbl_ht->mm = sta->ht_cap.ampdu_density; + buf_len += sizeof(*wtbl_ht); if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) val |= MT_WTBL_W5_SHORT_GI_20; @@ -1400,6 +1329,7 @@ int mt7615_mcu_set_ht_cap(struct mt7615_dev *dev, struct ieee80211_vif *vif, wtbl_vht->len = cpu_to_le16(sizeof(*wtbl_vht)); wtbl_vht->ldpc = sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC; wtbl_vht->vht = 1; + ntlv++; if (sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) val |= MT_WTBL_W5_SHORT_GI_80; @@ -1416,6 +1346,7 @@ int mt7615_mcu_set_ht_cap(struct mt7615_dev *dev, struct ieee80211_vif *vif, wtbl_smps->tag = cpu_to_le16(WTBL_SMPS); wtbl_smps->len = cpu_to_le16(sizeof(*wtbl_smps)); wtbl_smps->smps = 1; + ntlv++; } /* sgi */ @@ -1431,38 +1362,46 @@ int mt7615_mcu_set_ht_cap(struct mt7615_dev *dev, struct ieee80211_vif *vif, wtbl_raw->msk = cpu_to_le32(~msk); wtbl_raw->val = cpu_to_le32(val); - ret = __mt7615_mcu_set_wtbl(dev, msta->wcid.idx, WTBL_SET, buf, - buf_len); - if (ret) { - kfree(buf); - return ret; - } + wtbl_hdr->tlv_num = cpu_to_le16(ntlv); + ret = __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + buf, buf_len, true); + if (ret) + goto out; memset(buf, 0, MT7615_WTBL_UPDATE_MAX_SIZE); - buf_len = sizeof(*sta_rec_ht); - sta_rec_ht = (struct sta_rec_ht *)buf; - sta_rec_ht->tag = cpu_to_le16(STA_REC_HT); - sta_rec_ht->len = cpu_to_le16(sizeof(*sta_rec_ht)); - sta_rec_ht->ht_cap = cpu_to_le16(sta->ht_cap.cap); + sta_hdr = (struct sta_req_hdr *)buf; + sta_hdr->bss_idx = mvif->idx; + sta_hdr->wlan_idx = msta->wcid.idx; + sta_hdr->is_tlv_append = 1; + ntlv = sta->vht_cap.vht_supported ? 2 : 1; + sta_hdr->tlv_num = cpu_to_le16(ntlv); + sta_hdr->muar_idx = mvif->omac_idx; + buf_len = sizeof(*sta_hdr); + + sta_ht = (struct sta_rec_ht *)(buf + buf_len); + sta_ht->tag = cpu_to_le16(STA_REC_HT); + sta_ht->len = cpu_to_le16(sizeof(*sta_ht)); + sta_ht->ht_cap = cpu_to_le16(sta->ht_cap.cap); + buf_len += sizeof(*sta_ht); if (sta->vht_cap.vht_supported) { - struct sta_rec_vht *sta_rec_vht; - - sta_rec_vht = (struct sta_rec_vht *)(buf + buf_len); - buf_len += sizeof(*sta_rec_vht); - sta_rec_vht->tag = cpu_to_le16(STA_REC_VHT); - sta_rec_vht->len = cpu_to_le16(sizeof(*sta_rec_vht)); - sta_rec_vht->vht_cap = cpu_to_le32(sta->vht_cap.cap); - sta_rec_vht->vht_rx_mcs_map = - cpu_to_le16(sta->vht_cap.vht_mcs.rx_mcs_map); - sta_rec_vht->vht_tx_mcs_map = - cpu_to_le16(sta->vht_cap.vht_mcs.tx_mcs_map); + struct sta_rec_vht *sta_vht; + + sta_vht = (struct sta_rec_vht *)(buf + buf_len); + buf_len += sizeof(*sta_vht); + sta_vht->tag = cpu_to_le16(STA_REC_VHT); + sta_vht->len = cpu_to_le16(sizeof(*sta_vht)); + sta_vht->vht_cap = cpu_to_le32(sta->vht_cap.cap); + sta_vht->vht_rx_mcs_map = sta->vht_cap.vht_mcs.rx_mcs_map; + sta_vht->vht_tx_mcs_map = sta->vht_cap.vht_mcs.tx_mcs_map; } - ret = __mt7615_mcu_set_sta_rec(dev, mvif->idx, msta->wcid.idx, - mvif->omac_idx, buf, buf_len); + ret = __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_STA_REC_UPDATE, + buf, buf_len, true); +out: kfree(buf); + return ret; } @@ -1470,98 +1409,128 @@ int mt7615_mcu_set_tx_ba(struct mt7615_dev *dev, struct ieee80211_ampdu_params *params, bool add) { - struct ieee80211_sta *sta = params->sta; - struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; struct mt7615_vif *mvif = msta->vif; - u8 ba_range[8] = {4, 8, 12, 24, 36, 48, 54, 64}; - u16 tid = params->tid; - u16 ba_size = params->buf_size; - u16 ssn = params->ssn; - struct wtbl_ba wtbl_ba = {0}; - struct sta_rec_ba sta_rec_ba = {0}; - int ret, buf_len; - - buf_len = sizeof(struct wtbl_ba); - - wtbl_ba.tag = cpu_to_le16(WTBL_BA); - wtbl_ba.len = cpu_to_le16(buf_len); - wtbl_ba.tid = tid; - wtbl_ba.ba_type = MT_BA_TYPE_ORIGINATOR; + struct { + struct wtbl_req_hdr hdr; + struct wtbl_ba ba; + } wtbl_req = { + .hdr = { + .wlan_idx = msta->wcid.idx, + .operation = WTBL_SET, + .tlv_num = cpu_to_le16(1), + }, + .ba = { + .tag = cpu_to_le16(WTBL_BA), + .len = cpu_to_le16(sizeof(struct wtbl_ba)), + .tid = params->tid, + .ba_type = MT_BA_TYPE_ORIGINATOR, + .sn = add ? cpu_to_le16(params->ssn) : 0, + .ba_en = add, + }, + }; + struct { + struct sta_req_hdr hdr; + struct sta_rec_ba ba; + } sta_req = { + .hdr = { + .bss_idx = mvif->idx, + .wlan_idx = msta->wcid.idx, + .tlv_num = cpu_to_le16(1), + .is_tlv_append = 1, + .muar_idx = mvif->omac_idx, + }, + .ba = { + .tag = cpu_to_le16(STA_REC_BA), + .len = cpu_to_le16(sizeof(struct sta_rec_ba)), + .tid = params->tid, + .ba_type = MT_BA_TYPE_ORIGINATOR, + .amsdu = params->amsdu, + .ba_en = add << params->tid, + .ssn = cpu_to_le16(params->ssn), + .winsize = cpu_to_le16(params->buf_size), + }, + }; + int ret; if (add) { - u8 idx; + u8 idx, ba_range[] = { 4, 8, 12, 24, 36, 48, 54, 64 }; for (idx = 7; idx > 0; idx--) { - if (ba_size >= ba_range[idx]) + if (params->buf_size >= ba_range[idx]) break; } - wtbl_ba.sn = cpu_to_le16(ssn); - wtbl_ba.ba_en = 1; - wtbl_ba.ba_winsize_idx = idx; + wtbl_req.ba.ba_winsize_idx = idx; } - ret = __mt7615_mcu_set_wtbl(dev, msta->wcid.idx, WTBL_SET, &wtbl_ba, - buf_len); + ret = __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + &wtbl_req, sizeof(wtbl_req), true); if (ret) return ret; - buf_len = sizeof(struct sta_rec_ba); - - sta_rec_ba.tag = cpu_to_le16(STA_REC_BA); - sta_rec_ba.len = cpu_to_le16(buf_len); - sta_rec_ba.tid = tid; - sta_rec_ba.ba_type = MT_BA_TYPE_ORIGINATOR; - sta_rec_ba.amsdu = params->amsdu; - sta_rec_ba.ba_en = add << tid; - sta_rec_ba.ssn = cpu_to_le16(ssn); - sta_rec_ba.winsize = cpu_to_le16(ba_size); - - return __mt7615_mcu_set_sta_rec(dev, mvif->idx, msta->wcid.idx, - mvif->omac_idx, &sta_rec_ba, buf_len); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_STA_REC_UPDATE, + &sta_req, sizeof(sta_req), true); } int mt7615_mcu_set_rx_ba(struct mt7615_dev *dev, struct ieee80211_ampdu_params *params, bool add) { - struct ieee80211_sta *sta = params->sta; - struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; struct mt7615_vif *mvif = msta->vif; - u16 tid = params->tid; - struct wtbl_ba wtbl_ba = {0}; - struct sta_rec_ba sta_rec_ba = {0}; - int ret, buf_len; - - buf_len = sizeof(struct sta_rec_ba); - - sta_rec_ba.tag = cpu_to_le16(STA_REC_BA); - sta_rec_ba.len = cpu_to_le16(buf_len); - sta_rec_ba.tid = tid; - sta_rec_ba.ba_type = MT_BA_TYPE_RECIPIENT; - sta_rec_ba.amsdu = params->amsdu; - sta_rec_ba.ba_en = add << tid; - sta_rec_ba.ssn = cpu_to_le16(params->ssn); - sta_rec_ba.winsize = cpu_to_le16(params->buf_size); - - ret = __mt7615_mcu_set_sta_rec(dev, mvif->idx, msta->wcid.idx, - mvif->omac_idx, &sta_rec_ba, buf_len); - if (ret || !add) - return ret; + struct { + struct wtbl_req_hdr hdr; + struct wtbl_ba ba; + } wtbl_req = { + .hdr = { + .wlan_idx = msta->wcid.idx, + .operation = WTBL_SET, + .tlv_num = cpu_to_le16(1), + }, + .ba = { + .tag = cpu_to_le16(WTBL_BA), + .len = cpu_to_le16(sizeof(struct wtbl_ba)), + .tid = params->tid, + .ba_type = MT_BA_TYPE_RECIPIENT, + .rst_ba_tid = params->tid, + .rst_ba_sel = RST_BA_MAC_TID_MATCH, + .rst_ba_sb = 1, + }, + }; + struct { + struct sta_req_hdr hdr; + struct sta_rec_ba ba; + } sta_req = { + .hdr = { + .bss_idx = mvif->idx, + .wlan_idx = msta->wcid.idx, + .tlv_num = cpu_to_le16(1), + .is_tlv_append = 1, + .muar_idx = mvif->omac_idx, + }, + .ba = { + .tag = cpu_to_le16(STA_REC_BA), + .len = cpu_to_le16(sizeof(struct sta_rec_ba)), + .tid = params->tid, + .ba_type = MT_BA_TYPE_RECIPIENT, + .amsdu = params->amsdu, + .ba_en = add << params->tid, + .ssn = cpu_to_le16(params->ssn), + .winsize = cpu_to_le16(params->buf_size), + }, + }; + int ret; - buf_len = sizeof(struct wtbl_ba); + memcpy(wtbl_req.ba.peer_addr, params->sta->addr, ETH_ALEN); - wtbl_ba.tag = cpu_to_le16(WTBL_BA); - wtbl_ba.len = cpu_to_le16(buf_len); - wtbl_ba.tid = tid; - wtbl_ba.ba_type = MT_BA_TYPE_RECIPIENT; - memcpy(wtbl_ba.peer_addr, sta->addr, ETH_ALEN); - wtbl_ba.rst_ba_tid = tid; - wtbl_ba.rst_ba_sel = RST_BA_MAC_TID_MATCH; - wtbl_ba.rst_ba_sb = 1; + ret = __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_STA_REC_UPDATE, + &sta_req, sizeof(sta_req), true); + if (ret || !add) + return ret; - return __mt7615_mcu_set_wtbl(dev, msta->wcid.idx, WTBL_SET, - &wtbl_ba, buf_len); + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_WTBL_UPDATE, + &wtbl_req, sizeof(wtbl_req), true); } void mt7615_mcu_set_rates(struct mt7615_dev *dev, struct mt7615_sta *sta, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h index 9455f8fa475d..f8b51ad25220 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h @@ -70,6 +70,7 @@ enum { enum { MCU_EXT_CMD_PM_STATE_CTRL = 0x07, MCU_EXT_CMD_CHANNEL_SWITCH = 0x08, + MCU_EXT_CMD_SET_TX_POWER_CTRL = 0x11, MCU_EXT_CMD_EFUSE_BUFFER_MODE = 0x21, MCU_EXT_CMD_STA_REC_UPDATE = 0x25, MCU_EXT_CMD_BSS_INFO_UPDATE = 0x26, @@ -105,25 +106,19 @@ enum { #define STA_TYPE_STA BIT(0) #define STA_TYPE_AP BIT(1) #define STA_TYPE_ADHOC BIT(2) -#define STA_TYPE_TDLS BIT(3) #define STA_TYPE_WDS BIT(4) #define STA_TYPE_BC BIT(5) #define NETWORK_INFRA BIT(16) #define NETWORK_P2P BIT(17) #define NETWORK_IBSS BIT(18) -#define NETWORK_MESH BIT(19) -#define NETWORK_BOW BIT(20) #define NETWORK_WDS BIT(21) #define CONNECTION_INFRA_STA (STA_TYPE_STA | NETWORK_INFRA) #define CONNECTION_INFRA_AP (STA_TYPE_AP | NETWORK_INFRA) #define CONNECTION_P2P_GC (STA_TYPE_STA | NETWORK_P2P) #define CONNECTION_P2P_GO (STA_TYPE_AP | NETWORK_P2P) -#define CONNECTION_MESH_STA (STA_TYPE_STA | NETWORK_MESH) -#define CONNECTION_MESH_AP (STA_TYPE_AP | NETWORK_MESH) #define CONNECTION_IBSS_ADHOC (STA_TYPE_ADHOC | NETWORK_IBSS) -#define CONNECTION_TDLS (STA_TYPE_STA | NETWORK_INFRA | STA_TYPE_TDLS) #define CONNECTION_WDS (STA_TYPE_WDS | NETWORK_WDS) #define CONNECTION_INFRA_BC (STA_TYPE_BC | NETWORK_INFRA) @@ -131,41 +126,11 @@ enum { #define CONN_STATE_CONNECT 1 #define CONN_STATE_PORT_SECURE 2 -struct dev_info { - u8 omac_idx; - u8 omac_addr[ETH_ALEN]; - u8 band_idx; - u8 enable; - u32 feature; -}; - enum { DEV_INFO_ACTIVE, DEV_INFO_MAX_NUM }; -struct bss_info { - u8 bss_idx; - u8 bssid[ETH_ALEN]; - u8 omac_idx; - u8 band_idx; - u8 bmc_tx_wlan_idx; /* for bmc tx (sta mode use uc entry) */ - u8 wmm_idx; - u32 network_type; - u32 conn_type; - u16 bcn_interval; - u8 dtim_period; - u8 enable; - u32 feature; -}; - -struct bss_info_tag_handler { - u32 tag; - u32 len; - void (*handler)(struct mt7615_dev *dev, - struct bss_info *bss_info, struct sk_buff *skb); -}; - struct bss_info_omac { __le16 tag; __le16 len; @@ -231,6 +196,13 @@ enum { WTBL_RESET_ALL }; +struct wtbl_req_hdr { + u8 wlan_idx; + u8 operation; + __le16 tlv_num; + u8 rsv[4]; +} __packed; + struct wtbl_generic { __le16 tag; __le16 len; @@ -396,7 +368,8 @@ struct wtbl_raw { __le32 val; } __packed; -#define MT7615_WTBL_UPDATE_MAX_SIZE (sizeof(struct wtbl_generic) + \ +#define MT7615_WTBL_UPDATE_MAX_SIZE (sizeof(struct wtbl_req_hdr) + \ + sizeof(struct wtbl_generic) + \ sizeof(struct wtbl_rx) + \ sizeof(struct wtbl_ht) + \ sizeof(struct wtbl_vht) + \ @@ -430,6 +403,15 @@ enum { WTBL_MAX_NUM }; +struct sta_req_hdr { + u8 bss_idx; + u8 wlan_idx; + __le16 tlv_num; + u8 is_tlv_append; + u8 muar_idx; + u8 rsv[2]; +} __packed; + struct sta_rec_basic { __le16 tag; __le16 len; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index 895c2904d7eb..f02ffcffe637 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -105,11 +105,14 @@ u32 mt7615_reg_map(struct mt7615_dev *dev, u32 addr); int mt7615_register_device(struct mt7615_dev *dev); void mt7615_unregister_device(struct mt7615_dev *dev); int mt7615_eeprom_init(struct mt7615_dev *dev); +int mt7615_eeprom_get_power_index(struct mt7615_dev *dev, + struct ieee80211_channel *chan, + u8 chain_idx); int mt7615_dma_init(struct mt7615_dev *dev); void mt7615_dma_cleanup(struct mt7615_dev *dev); int mt7615_mcu_init(struct mt7615_dev *dev); -int mt7615_mcu_set_dev_info(struct mt7615_dev *dev, struct ieee80211_vif *vif, - int en); +int mt7615_mcu_set_dev_info(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable); int mt7615_mcu_set_bss_info(struct mt7615_dev *dev, struct ieee80211_vif *vif, int en); int mt7615_mcu_set_wtbl_key(struct mt7615_dev *dev, int wcid, @@ -118,12 +121,11 @@ int mt7615_mcu_set_wtbl_key(struct mt7615_dev *dev, int wcid, void mt7615_mcu_set_rates(struct mt7615_dev *dev, struct mt7615_sta *sta, struct ieee80211_tx_rate *probe_rate, struct ieee80211_tx_rate *rates); -int mt7615_mcu_add_wtbl_bmc(struct mt7615_dev *dev, struct ieee80211_vif *vif); -int mt7615_mcu_del_wtbl_bmc(struct mt7615_dev *dev, struct ieee80211_vif *vif); +int mt7615_mcu_wtbl_bmc(struct mt7615_dev *dev, struct ieee80211_vif *vif, + bool enable); int mt7615_mcu_add_wtbl(struct mt7615_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); -int mt7615_mcu_del_wtbl(struct mt7615_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); +int mt7615_mcu_del_wtbl(struct mt7615_dev *dev, struct ieee80211_sta *sta); int mt7615_mcu_del_wtbl_all(struct mt7615_dev *dev); int mt7615_mcu_set_sta_rec_bmc(struct mt7615_dev *dev, struct ieee80211_vif *vif, bool en); @@ -168,6 +170,7 @@ int mt7615_mcu_set_eeprom(struct mt7615_dev *dev); int mt7615_mcu_init_mac(struct mt7615_dev *dev); int mt7615_mcu_set_rts_thresh(struct mt7615_dev *dev, u32 val); int mt7615_mcu_ctrl_pm_state(struct mt7615_dev *dev, int enter); +int mt7615_mcu_set_tx_power(struct mt7615_dev *dev); void mt7615_mcu_exit(struct mt7615_dev *dev); int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, @@ -180,7 +183,6 @@ void mt7615_tx_complete_skb(struct mt76_dev *mdev, enum mt76_txq_id qid, void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, struct sk_buff *skb); -void mt7615_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); void mt7615_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); int mt7615_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci.c index 11122bd2d727..9e82cb53fd60 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci.c @@ -27,14 +27,15 @@ u32 mt7615_reg_map(struct mt7615_dev *dev, u32 addr) return MT_PCIE_REMAP_BASE_2 + offset; } -void mt7615_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) +static void +mt7615_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) { struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); mt7615_irq_enable(dev, MT_INT_RX_DONE(q)); } -irqreturn_t mt7615_irq_handler(int irq, void *dev_instance) +static irqreturn_t mt7615_irq_handler(int irq, void *dev_instance) { struct mt7615_dev *dev = dev_instance; u32 intr; @@ -49,7 +50,7 @@ irqreturn_t mt7615_irq_handler(int irq, void *dev_instance) if (intr & MT_INT_TX_DONE_ALL) { mt7615_irq_disable(dev, MT_INT_TX_DONE_ALL); - tasklet_schedule(&dev->mt76.tx_tasklet); + napi_schedule(&dev->mt76.tx_napi); } if (intr & MT_INT_RX_DONE(0)) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c index 71237d5cdf7f..cf7fc307322b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c @@ -271,8 +271,9 @@ mt76x0_init_txpower(struct mt76x02_dev *dev, mt76x0_get_tx_power_per_rate(dev, chan, &t); mt76x0_get_power_info(dev, chan, &tp); - chan->max_power = (mt76x02_get_max_rate_power(&t) + tp) / 2; - chan->orig_mpwr = chan->max_power; + chan->orig_mpwr = (mt76x02_get_max_rate_power(&t) + tp) / 2; + chan->max_power = min_t(int, chan->max_reg_power, + chan->orig_mpwr); } } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/main.c b/drivers/net/wireless/mediatek/mt76/mt76x0/main.c index a7f335d6e8f8..d7bf7bc15e52 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/main.c @@ -25,7 +25,7 @@ mt76x0_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef) mt76_rr(dev, MT_CH_IDLE); mt76_rr(dev, MT_CH_BUSY); - mt76x02_edcca_init(dev, true); + mt76x02_edcca_init(dev); if (mt76_is_mmio(dev)) { mt76x02_dfs_init_params(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c index e11da6900222..1ecfc334ae79 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c @@ -422,15 +422,15 @@ mt76x0_phy_set_chan_bbp_params(struct mt76x02_dev *dev, u16 rf_bw_band) static void mt76x0_phy_ant_select(struct mt76x02_dev *dev) { u16 ee_ant = mt76x02_eeprom_get(dev, MT_EE_ANTENNA); + u16 ee_cfg1 = mt76x02_eeprom_get(dev, MT_EE_CFG1_INIT); u16 nic_conf2 = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2); - u32 wlan, coex3, cmb; + u32 wlan, coex3; bool ant_div; wlan = mt76_rr(dev, MT_WLAN_FUN_CTRL); - cmb = mt76_rr(dev, MT_CMB_CTRL); coex3 = mt76_rr(dev, MT_COEXCFG3); - cmb &= ~(BIT(14) | BIT(12)); + ee_ant &= ~(BIT(14) | BIT(12)); wlan &= ~(BIT(6) | BIT(5)); coex3 &= ~GENMASK(5, 2); @@ -439,7 +439,7 @@ static void mt76x0_phy_ant_select(struct mt76x02_dev *dev) ant_div = !(nic_conf2 & MT_EE_NIC_CONF_2_ANT_OPT) && (nic_conf2 & MT_EE_NIC_CONF_2_ANT_DIV); if (ant_div) - cmb |= BIT(12); + ee_ant |= BIT(12); else coex3 |= BIT(4); coex3 |= BIT(3); @@ -456,10 +456,11 @@ static void mt76x0_phy_ant_select(struct mt76x02_dev *dev) } if (is_mt7630(dev)) - cmb |= BIT(14) | BIT(11); + ee_ant |= BIT(14) | BIT(11); mt76_wr(dev, MT_WLAN_FUN_CTRL, wlan); - mt76_wr(dev, MT_CMB_CTRL, cmb); + mt76_rmw(dev, MT_CMB_CTRL, GENMASK(15, 0), ee_ant); + mt76_rmw(dev, MT_CSR_EE_CFG1, GENMASK(15, 0), ee_cfg1); mt76_clear(dev, MT_COEXCFG0, BIT(2)); mt76_wr(dev, MT_COEXCFG3, coex3); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index 2dc67e68c6a2..627ed1fc7b15 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -183,7 +183,7 @@ static int mt76x0u_register_device(struct mt76x02_dev *dev) /* check hw sg support in order to enable AMSDU */ if (dev->mt76.usb.sg_en) - hw->max_tx_fragments = MT_SG_MAX_SIZE; + hw->max_tx_fragments = MT_TX_SG_MAX_SIZE; else hw->max_tx_fragments = 1; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index 687bd14b2d77..f7fd53a1738a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -90,7 +90,6 @@ struct mt76x02_dev { struct sk_buff *rx_head; - struct napi_struct tx_napi; struct delayed_work cal_work; struct delayed_work wdt_work; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c index e196b9c0a686..d61c686e08de 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c @@ -189,10 +189,8 @@ mt76x02_resync_beacon_timer(struct mt76x02_dev *dev) mt76_rmw_field(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_INTVAL, timer_val); - if (dev->tbtt_count >= 64) { + if (dev->tbtt_count >= 64) dev->tbtt_count = 0; - return; - } } EXPORT_SYMBOL_GPL(mt76x02_resync_beacon_timer); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c index b1d6fd4861e3..1b1e424ccbb2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c @@ -120,12 +120,16 @@ static int mt76_edcca_set(void *data, u64 val) { struct mt76x02_dev *dev = data; - enum nl80211_dfs_regions region = dev->dfs_pd.region; + enum nl80211_dfs_regions region = dev->mt76.region; + + mutex_lock(&dev->mt76.mutex); dev->ed_monitor_enabled = !!val; dev->ed_monitor = dev->ed_monitor_enabled && region == NL80211_DFS_ETSI; - mt76x02_edcca_init(dev, true); + mt76x02_edcca_init(dev); + + mutex_unlock(&dev->mt76.mutex); return 0; } @@ -153,7 +157,7 @@ void mt76x02_init_debugfs(struct mt76x02_dev *dev) debugfs_create_u8("temperature", 0400, dir, &dev->cal.temp); debugfs_create_bool("tpc", 0600, dir, &dev->enable_tpc); - debugfs_create_file("edcca", 0400, dir, dev, &fops_edcca); + debugfs_create_file("edcca", 0600, dir, dev, &fops_edcca); debugfs_create_file("ampdu_stat", 0400, dir, dev, &fops_ampdu_stat); debugfs_create_file("dfs_stats", 0400, dir, dev, &fops_dfs_stat); debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c index 17d12d212d1b..50e9b310e496 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c @@ -283,7 +283,7 @@ static bool mt76x02_dfs_check_hw_pulse(struct mt76x02_dev *dev, if (!pulse->period || !pulse->w1) return false; - switch (dev->dfs_pd.region) { + switch (dev->mt76.region) { case NL80211_DFS_FCC: if (pulse->engine > 3) break; @@ -457,7 +457,7 @@ static int mt76x02_dfs_create_sequence(struct mt76x02_dev *dev, with_sum = event->width + cur_event->width; sw_params = &dfs_pd->sw_dpd_params; - switch (dev->dfs_pd.region) { + switch (dev->mt76.region) { case NL80211_DFS_FCC: case NL80211_DFS_JP: if (with_sum < 600) @@ -685,7 +685,7 @@ static void mt76x02_dfs_init_sw_detector(struct mt76x02_dev *dev) { struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; - switch (dev->dfs_pd.region) { + switch (dev->mt76.region) { case NL80211_DFS_FCC: dfs_pd->sw_dpd_params.max_pri = MT_DFS_FCC_MAX_PRI; dfs_pd->sw_dpd_params.min_pri = MT_DFS_FCC_MIN_PRI; @@ -725,7 +725,7 @@ static void mt76x02_dfs_set_bbp_params(struct mt76x02_dev *dev) break; } - switch (dev->dfs_pd.region) { + switch (dev->mt76.region) { case NL80211_DFS_FCC: radar_specs = &fcc_radar_specs[shift]; break; @@ -836,7 +836,7 @@ void mt76x02_dfs_init_params(struct mt76x02_dev *dev) struct cfg80211_chan_def *chandef = &dev->mt76.chandef; if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) && - dev->dfs_pd.region != NL80211_DFS_UNSET) { + dev->mt76.region != NL80211_DFS_UNSET) { mt76x02_dfs_init_sw_detector(dev); mt76x02_dfs_set_bbp_params(dev); /* enable debug mode */ @@ -869,7 +869,7 @@ void mt76x02_dfs_init_detector(struct mt76x02_dev *dev) INIT_LIST_HEAD(&dfs_pd->sequences); INIT_LIST_HEAD(&dfs_pd->seq_pool); - dfs_pd->region = NL80211_DFS_UNSET; + dev->mt76.region = NL80211_DFS_UNSET; dfs_pd->last_sw_check = jiffies; tasklet_init(&dfs_pd->dfs_tasklet, mt76x02_dfs_tasklet, (unsigned long)dev); @@ -882,14 +882,14 @@ mt76x02_dfs_set_domain(struct mt76x02_dev *dev, struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; mutex_lock(&dev->mt76.mutex); - if (dfs_pd->region != region) { + if (dev->mt76.region != region) { tasklet_disable(&dfs_pd->dfs_tasklet); dev->ed_monitor = dev->ed_monitor_enabled && region == NL80211_DFS_ETSI; - mt76x02_edcca_init(dev, true); + mt76x02_edcca_init(dev); - dfs_pd->region = region; + dev->mt76.region = region; mt76x02_dfs_init_params(dev); tasklet_enable(&dfs_pd->dfs_tasklet); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h index 70b394e17340..0408613b45a4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h @@ -118,8 +118,6 @@ struct mt76x02_dfs_seq_stats { }; struct mt76x02_dfs_pattern_detector { - enum nl80211_dfs_regions region; - u8 chirp_pulse_cnt; u32 chirp_pulse_ts; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h index e3442bc4e0a4..0ba536de3d6e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h @@ -26,6 +26,7 @@ enum mt76x02_eeprom_field { MT_EE_MAC_ADDR = 0x004, MT_EE_PCI_ID = 0x00A, MT_EE_ANTENNA = 0x022, + MT_EE_CFG1_INIT = 0x024, MT_EE_NIC_CONF_0 = 0x034, MT_EE_NIC_CONF_1 = 0x036, MT_EE_COUNTRY_REGION_5GHZ = 0x038, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c index 56510a1a843a..82bafb5ac326 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c @@ -420,30 +420,92 @@ void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi, EXPORT_SYMBOL_GPL(mt76x02_mac_write_txwi); static void -mt76x02_mac_fill_tx_status(struct mt76x02_dev *dev, +mt76x02_tx_rate_fallback(struct ieee80211_tx_rate *rates, int idx, int phy) +{ + u8 mcs, nss; + + if (!idx) + return; + + rates += idx - 1; + rates[1] = rates[0]; + switch (phy) { + case MT_PHY_TYPE_VHT: + mcs = ieee80211_rate_get_vht_mcs(rates); + nss = ieee80211_rate_get_vht_nss(rates); + + if (mcs == 0) + nss = max_t(int, nss - 1, 1); + else + mcs--; + + ieee80211_rate_set_vht(rates + 1, mcs, nss); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + /* MCS 8 falls back to MCS 0 */ + if (rates[0].idx == 8) { + rates[1].idx = 0; + break; + } + /* fall through */ + default: + rates[1].idx = max_t(int, rates[0].idx - 1, 0); + break; + } +} + +static void +mt76x02_mac_fill_tx_status(struct mt76x02_dev *dev, struct mt76x02_sta *msta, struct ieee80211_tx_info *info, struct mt76x02_tx_status *st, int n_frames) { struct ieee80211_tx_rate *rate = info->status.rates; - int cur_idx, last_rate; + struct ieee80211_tx_rate last_rate; + u16 first_rate; + int retry = st->retry; + int phy; int i; if (!n_frames) return; - last_rate = min_t(int, st->retry, IEEE80211_TX_MAX_RATES - 1); - mt76x02_mac_process_tx_rate(&rate[last_rate], st->rate, + phy = FIELD_GET(MT_RXWI_RATE_PHY, st->rate); + + if (st->pktid & MT_PACKET_ID_HAS_RATE) { + first_rate = st->rate & ~MT_RXWI_RATE_INDEX; + first_rate |= st->pktid & MT_RXWI_RATE_INDEX; + + mt76x02_mac_process_tx_rate(&rate[0], first_rate, + dev->mt76.chandef.chan->band); + } else if (rate[0].idx < 0) { + if (!msta) + return; + + mt76x02_mac_process_tx_rate(&rate[0], msta->wcid.tx_info, + dev->mt76.chandef.chan->band); + } + + mt76x02_mac_process_tx_rate(&last_rate, st->rate, dev->mt76.chandef.chan->band); - if (last_rate < IEEE80211_TX_MAX_RATES - 1) - rate[last_rate + 1].idx = -1; - - cur_idx = rate[last_rate].idx + last_rate; - for (i = 0; i <= last_rate; i++) { - rate[i].flags = rate[last_rate].flags; - rate[i].idx = max_t(int, 0, cur_idx - i); - rate[i].count = 1; + + for (i = 0; i < ARRAY_SIZE(info->status.rates); i++) { + retry--; + if (i + 1 == ARRAY_SIZE(info->status.rates)) { + info->status.rates[i] = last_rate; + info->status.rates[i].count = max_t(int, retry, 1); + break; + } + + mt76x02_tx_rate_fallback(info->status.rates, i, phy); + if (info->status.rates[i].idx == last_rate.idx) + break; + } + + if (i + 1 < ARRAY_SIZE(info->status.rates)) { + info->status.rates[i + 1].idx = -1; + info->status.rates[i + 1].count = 0; } - rate[last_rate].count = st->retry + 1 - last_rate; info->status.ampdu_len = n_frames; info->status.ampdu_ack_len = st->success ? n_frames : 0; @@ -489,13 +551,19 @@ void mt76x02_send_tx_status(struct mt76x02_dev *dev, mt76_tx_status_lock(mdev, &list); if (wcid) { - if (stat->pktid >= MT_PACKET_ID_FIRST) + if (mt76_is_skb_pktid(stat->pktid)) status.skb = mt76_tx_status_skb_get(mdev, wcid, stat->pktid, &list); if (status.skb) status.info = IEEE80211_SKB_CB(status.skb); } + if (!status.skb && !(stat->pktid & MT_PACKET_ID_HAS_RATE)) { + mt76_tx_status_unlock(mdev, &list); + rcu_read_unlock(); + return; + } + if (msta && stat->aggr && !status.skb) { u32 stat_val, stat_cache; @@ -512,14 +580,14 @@ void mt76x02_send_tx_status(struct mt76x02_dev *dev, return; } - mt76x02_mac_fill_tx_status(dev, status.info, &msta->status, - msta->n_frames); + mt76x02_mac_fill_tx_status(dev, msta, status.info, + &msta->status, msta->n_frames); msta->status = *stat; msta->n_frames = 1; *update = 0; } else { - mt76x02_mac_fill_tx_status(dev, status.info, stat, 1); + mt76x02_mac_fill_tx_status(dev, msta, status.info, stat, 1); *update = 1; } @@ -945,12 +1013,12 @@ mt76x02_edcca_tx_enable(struct mt76x02_dev *dev, bool enable) dev->ed_tx_blocked = !enable; } -void mt76x02_edcca_init(struct mt76x02_dev *dev, bool enable) +void mt76x02_edcca_init(struct mt76x02_dev *dev) { dev->ed_trigger = 0; dev->ed_silent = 0; - if (dev->ed_monitor && enable) { + if (dev->ed_monitor) { struct ieee80211_channel *chan = dev->mt76.chandef.chan; u8 ed_th = chan->band == NL80211_BAND_5GHZ ? 0x0e : 0x20; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h index e4a9e0d0924b..cb39da79527a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h @@ -209,5 +209,5 @@ int mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx, void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, struct ieee80211_vif *vif, bool val); -void mt76x02_edcca_init(struct mt76x02_dev *dev, bool enable); +void mt76x02_edcca_init(struct mt76x02_dev *dev); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c index 7b7163bc3b62..467b28379870 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c @@ -166,7 +166,8 @@ static void mt76x02_tx_tasklet(unsigned long data) static int mt76x02_poll_tx(struct napi_struct *napi, int budget) { - struct mt76x02_dev *dev = container_of(napi, struct mt76x02_dev, tx_napi); + struct mt76x02_dev *dev = container_of(napi, struct mt76x02_dev, + mt76.tx_napi); int i; mt76x02_mac_poll_tx_status(dev, false); @@ -245,9 +246,9 @@ int mt76x02_dma_init(struct mt76x02_dev *dev) if (ret) return ret; - netif_tx_napi_add(&dev->mt76.napi_dev, &dev->tx_napi, mt76x02_poll_tx, - NAPI_POLL_WEIGHT); - napi_enable(&dev->tx_napi); + netif_tx_napi_add(&dev->mt76.napi_dev, &dev->mt76.tx_napi, + mt76x02_poll_tx, NAPI_POLL_WEIGHT); + napi_enable(&dev->mt76.tx_napi); return 0; } @@ -303,7 +304,7 @@ irqreturn_t mt76x02_irq_handler(int irq, void *dev_instance) if (intr & (MT_INT_TX_STAT | MT_INT_TX_DONE_ALL)) { mt76x02_irq_disable(dev, MT_INT_TX_DONE_ALL); - napi_schedule(&dev->tx_napi); + napi_schedule(&dev->mt76.tx_napi); } if (intr & MT_INT_GPTIMER) { @@ -334,7 +335,6 @@ static void mt76x02_dma_enable(struct mt76x02_dev *dev) void mt76x02_dma_cleanup(struct mt76x02_dev *dev) { tasklet_kill(&dev->mt76.tx_tasklet); - netif_napi_del(&dev->tx_napi); mt76_dma_cleanup(&dev->mt76); } EXPORT_SYMBOL_GPL(mt76x02_dma_cleanup); @@ -454,7 +454,7 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev) tasklet_disable(&dev->mt76.pre_tbtt_tasklet); tasklet_disable(&dev->mt76.tx_tasklet); - napi_disable(&dev->tx_napi); + napi_disable(&dev->mt76.tx_napi); for (i = 0; i < ARRAY_SIZE(dev->mt76.napi); i++) napi_disable(&dev->mt76.napi[i]); @@ -508,8 +508,8 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev) clear_bit(MT76_RESET, &dev->mt76.state); tasklet_enable(&dev->mt76.tx_tasklet); - napi_enable(&dev->tx_napi); - napi_schedule(&dev->tx_napi); + napi_enable(&dev->mt76.tx_napi); + napi_schedule(&dev->mt76.tx_napi); tasklet_enable(&dev->mt76.pre_tbtt_tasklet); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h index 2ce05b543dff..ea7833964ec0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h @@ -66,6 +66,9 @@ #define MT_WLAN_FUN_CTRL_GPIO_OUT GENMASK(23, 16) /* MT76x0 */ #define MT_WLAN_FUN_CTRL_GPIO_OUT_EN GENMASK(31, 24) /* MT76x0 */ +/* MT76x0 */ +#define MT_CSR_EE_CFG1 0x0104 + #define MT_XO_CTRL0 0x0100 #define MT_XO_CTRL1 0x0104 #define MT_XO_CTRL2 0x0108 diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c index cf7abd9b7d2e..04118f08debc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c @@ -154,6 +154,7 @@ int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data; struct mt76x02_txwi *txwi = txwi_ptr; + bool ampdu = IEEE80211_SKB_CB(tx_info->skb)->flags & IEEE80211_TX_CTL_AMPDU; int hdrlen, len, pid, qsel = MT_QSEL_EDCA; if (qid == MT_TXQ_PSD && wcid && wcid->idx < 128) @@ -164,9 +165,15 @@ int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, mt76x02_mac_write_txwi(dev, txwi, tx_info->skb, wcid, sta, len); pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + + /* encode packet rate for no-skb packet id to fix up status reporting */ + if (pid == MT_PACKET_ID_NO_SKB) + pid = MT_PACKET_ID_HAS_RATE | + (le16_to_cpu(txwi->rate) & MT_RXWI_RATE_INDEX); + txwi->pktid = pid; - if (pid >= MT_PACKET_ID_FIRST) + if (mt76_is_skb_pktid(pid) && ampdu) qsel = MT_QSEL_MGMT; tx_info->info = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) | diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c index 6b89f7eab26c..5e4f3a8c5784 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "mt76x02.h" +#include "mt76x02_usb.h" static void mt76x02u_remove_dma_hdr(struct sk_buff *skb) { @@ -79,6 +79,7 @@ int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data, struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); int pid, len = tx_info->skb->len, ep = q2ep(mdev->q_tx[qid].q->hw_idx); struct mt76x02_txwi *txwi; + bool ampdu = IEEE80211_SKB_CB(tx_info->skb)->flags & IEEE80211_TX_CTL_AMPDU; enum mt76_qsel qsel; u32 flags; @@ -89,9 +90,15 @@ int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data, skb_push(tx_info->skb, sizeof(*txwi)); pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + + /* encode packet rate for no-skb packet id to fix up status reporting */ + if (pid == MT_PACKET_ID_NO_SKB) + pid = MT_PACKET_ID_HAS_RATE | + (le16_to_cpu(txwi->rate) & MT_RXWI_RATE_INDEX); + txwi->pktid = pid; - if (pid >= MT_PACKET_ID_FIRST || ep == MT_EP_OUT_HCCA) + if ((mt76_is_skb_pktid(pid) && ampdu) || ep == MT_EP_OUT_HCCA) qsel = MT_QSEL_MGMT; else qsel = MT_QSEL_EDCA; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c index c6078e90ca43..97c3543eed8a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c @@ -173,13 +173,14 @@ void mt76x2_init_txpower(struct mt76x02_dev *dev, mt76x2_get_power_info(dev, &txp, chan); mt76x2_get_rate_power(dev, &t, chan); - chan->max_power = mt76x02_get_max_rate_power(&t) + + chan->orig_mpwr = mt76x02_get_max_rate_power(&t) + txp.target_power; - chan->max_power = DIV_ROUND_UP(chan->max_power, 2); + chan->orig_mpwr = DIV_ROUND_UP(chan->orig_mpwr, 2); /* convert to combined output power on 2x2 devices */ - chan->max_power += 3; - chan->orig_mpwr = chan->max_power; + chan->orig_mpwr += 3; + chan->max_power = min_t(int, chan->max_reg_power, + chan->orig_mpwr); } } EXPORT_SYMBOL_GPL(mt76x2_init_txpower); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c index e416eee6a306..3a1467326f4d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c @@ -54,14 +54,14 @@ mt76x2_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef) int ret; cancel_delayed_work_sync(&dev->cal_work); + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + tasklet_disable(&dev->dfs_pd.dfs_tasklet); + mutex_lock(&dev->mt76.mutex); set_bit(MT76_RESET, &dev->mt76.state); mt76_set_channel(&dev->mt76); - tasklet_disable(&dev->mt76.pre_tbtt_tasklet); - tasklet_disable(&dev->dfs_pd.dfs_tasklet); - mt76x2_mac_stop(dev, true); ret = mt76x2_phy_set_channel(dev, chandef); @@ -72,10 +72,12 @@ mt76x2_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef) mt76x02_dfs_init_params(dev); mt76x2_mac_resume(dev); - tasklet_enable(&dev->dfs_pd.dfs_tasklet); - tasklet_enable(&dev->mt76.pre_tbtt_tasklet); clear_bit(MT76_RESET, &dev->mt76.state); + mutex_unlock(&dev->mt76.mutex); + + tasklet_enable(&dev->dfs_pd.dfs_tasklet); + tasklet_enable(&dev->mt76.pre_tbtt_tasklet); mt76_txq_schedule_all(&dev->mt76); @@ -111,14 +113,14 @@ mt76x2_config(struct ieee80211_hw *hw, u32 changed) } } + mutex_unlock(&dev->mt76.mutex); + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { ieee80211_stop_queues(hw); ret = mt76x2_set_channel(dev, &hw->conf.chandef); ieee80211_wake_queues(hw); } - mutex_unlock(&dev->mt76.mutex); - return ret; } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c index cc1aebcb0696..2edf1bd0c18c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c @@ -74,7 +74,7 @@ mt76x2_phy_channel_calibrate(struct mt76x02_dev *dev, bool mac_stopped) mt76x2_mac_resume(dev); mt76x2_apply_gain_adj(dev); - mt76x02_edcca_init(dev, true); + mt76x02_edcca_init(dev); dev->cal.channel_cal_done = true; } @@ -294,10 +294,16 @@ void mt76x2_phy_calibrate(struct work_struct *work) struct mt76x02_dev *dev; dev = container_of(work, struct mt76x02_dev, cal_work.work); + + mutex_lock(&dev->mt76.mutex); + mt76x2_phy_channel_calibrate(dev, false); mt76x2_phy_tssi_compensate(dev); mt76x2_phy_temp_compensate(dev); mt76x2_phy_update_channel_gain(dev); + + mutex_unlock(&dev->mt76.mutex); + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, MT_CALIBRATE_INTERVAL); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c index f2c57d5b87f9..94f52f98019b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c @@ -225,7 +225,7 @@ int mt76x2u_register_device(struct mt76x02_dev *dev) /* check hw sg support in order to enable AMSDU */ if (dev->mt76.usb.sg_en) - hw->max_tx_fragments = MT_SG_MAX_SIZE; + hw->max_tx_fragments = MT_TX_SG_MAX_SIZE; else hw->max_tx_fragments = 1; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c index 97bcf6494ec1..e4dfc3bea3c5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c @@ -48,22 +48,23 @@ mt76x2u_set_channel(struct mt76x02_dev *dev, int err; cancel_delayed_work_sync(&dev->cal_work); + dev->beacon_ops->pre_tbtt_enable(dev, false); + + mutex_lock(&dev->mt76.mutex); set_bit(MT76_RESET, &dev->mt76.state); mt76_set_channel(&dev->mt76); - dev->beacon_ops->pre_tbtt_enable(dev, false); - mt76x2_mac_stop(dev, false); err = mt76x2u_phy_set_channel(dev, chandef); mt76x2_mac_resume(dev); - mt76x02_edcca_init(dev, true); - - dev->beacon_ops->pre_tbtt_enable(dev, true); clear_bit(MT76_RESET, &dev->mt76.state); + mutex_unlock(&dev->mt76.mutex); + + dev->beacon_ops->pre_tbtt_enable(dev, true); mt76_txq_schedule_all(&dev->mt76); return err; @@ -85,12 +86,6 @@ mt76x2u_config(struct ieee80211_hw *hw, u32 changed) mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - ieee80211_stop_queues(hw); - err = mt76x2u_set_channel(dev, &hw->conf.chandef); - ieee80211_wake_queues(hw); - } - if (changed & IEEE80211_CONF_CHANGE_POWER) { dev->mt76.txpower_conf = hw->conf.power_level * 2; @@ -103,6 +98,12 @@ mt76x2u_config(struct ieee80211_hw *hw, u32 changed) mutex_unlock(&dev->mt76.mutex); + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ieee80211_stop_queues(hw); + err = mt76x2u_set_channel(dev, &hw->conf.chandef); + ieee80211_wake_queues(hw); + } + return err; } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c index 07f67cb6854c..dfd54f9b0e97 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c @@ -45,7 +45,7 @@ mt76x2u_phy_channel_calibrate(struct mt76x02_dev *dev, bool mac_stopped) if (!mac_stopped) mt76x2_mac_resume(dev); mt76x2_apply_gain_adj(dev); - mt76x02_edcca_init(dev, true); + mt76x02_edcca_init(dev); dev->cal.channel_cal_done = true; } @@ -55,10 +55,15 @@ void mt76x2u_phy_calibrate(struct work_struct *work) struct mt76x02_dev *dev; dev = container_of(work, struct mt76x02_dev, cal_work.work); + + mutex_lock(&dev->mt76.mutex); + mt76x2u_phy_channel_calibrate(dev, false); mt76x2_phy_tssi_compensate(dev); mt76x2_phy_update_channel_gain(dev); + mutex_unlock(&dev->mt76.mutex); + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, MT_CALIBRATE_INTERVAL); } diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index bbaa1365bbda..61b27f3ec6e4 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -267,12 +267,10 @@ mt76u_set_endpoints(struct usb_interface *intf, if (usb_endpoint_is_bulk_in(ep_desc) && in_ep < __MT_EP_IN_MAX) { usb->in_ep[in_ep] = usb_endpoint_num(ep_desc); - usb->in_max_packet = usb_endpoint_maxp(ep_desc); in_ep++; } else if (usb_endpoint_is_bulk_out(ep_desc) && out_ep < __MT_EP_OUT_MAX) { usb->out_ep[out_ep] = usb_endpoint_num(ep_desc); - usb->out_max_packet = usb_endpoint_maxp(ep_desc); out_ep++; } } @@ -333,12 +331,13 @@ mt76u_refill_rx(struct mt76_dev *dev, struct urb *urb, int nsgs, gfp_t gfp) } static int -mt76u_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e) +mt76u_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e, + int sg_max_size) { unsigned int size = sizeof(struct urb); if (dev->usb.sg_en) - size += MT_SG_MAX_SIZE * sizeof(struct scatterlist); + size += sg_max_size * sizeof(struct scatterlist); e->urb = kzalloc(size, GFP_KERNEL); if (!e->urb) @@ -357,11 +356,12 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e) { int err; - err = mt76u_urb_alloc(dev, e); + err = mt76u_urb_alloc(dev, e, MT_RX_SG_MAX_SIZE); if (err) return err; - return mt76u_refill_rx(dev, e->urb, MT_SG_MAX_SIZE, GFP_KERNEL); + return mt76u_refill_rx(dev, e->urb, MT_RX_SG_MAX_SIZE, + GFP_KERNEL); } static void mt76u_urb_free(struct urb *urb) @@ -577,8 +577,9 @@ static int mt76u_alloc_rx(struct mt76_dev *dev) if (!q->entry) return -ENOMEM; - q->buf_size = dev->usb.sg_en ? MT_RX_BUF_SIZE : PAGE_SIZE; q->ndesc = MT_NUM_RX_ENTRIES; + q->buf_size = PAGE_SIZE; + for (i = 0; i < q->ndesc; i++) { err = mt76u_rx_urb_alloc(dev, &q->entry[i]); if (err < 0) @@ -735,7 +736,7 @@ mt76u_tx_setup_buffers(struct mt76_dev *dev, struct sk_buff *skb, urb->transfer_buffer = skb->data; return 0; } else { - sg_init_table(urb->sg, MT_SG_MAX_SIZE); + sg_init_table(urb->sg, MT_TX_SG_MAX_SIZE); urb->num_sgs = skb_to_sgvec(skb, urb->sg, 0, skb->len); if (urb->num_sgs == 0) return -ENOMEM; @@ -829,7 +830,8 @@ static int mt76u_alloc_tx(struct mt76_dev *dev) q->ndesc = MT_NUM_TX_ENTRIES; for (j = 0; j < q->ndesc; j++) { - err = mt76u_urb_alloc(dev, &q->entry[j]); + err = mt76u_urb_alloc(dev, &q->entry[j], + MT_TX_SG_MAX_SIZE); if (err < 0) return err; } diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 65713245a703..5a2c06267d07 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -198,15 +198,20 @@ void rtw_get_channel_params(struct cfg80211_chan_def *chandef, { struct ieee80211_channel *channel = chandef->chan; enum nl80211_chan_width width = chandef->width; + u8 *cch_by_bw = chan_params->cch_by_bw; u32 primary_freq, center_freq; u8 center_chan; u8 bandwidth = RTW_CHANNEL_WIDTH_20; u8 primary_chan_idx = 0; + u8 i; center_chan = channel->hw_value; primary_freq = channel->center_freq; center_freq = chandef->center_freq1; + /* assign the center channel used while 20M bw is selected */ + cch_by_bw[RTW_CHANNEL_WIDTH_20] = channel->hw_value; + switch (width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: @@ -233,6 +238,10 @@ void rtw_get_channel_params(struct cfg80211_chan_def *chandef, primary_chan_idx = 3; center_chan -= 6; } + /* assign the center channel used + * while 40M bw is selected + */ + cch_by_bw[RTW_CHANNEL_WIDTH_40] = center_chan + 4; } else { if (center_freq - primary_freq == 10) { primary_chan_idx = 2; @@ -241,6 +250,10 @@ void rtw_get_channel_params(struct cfg80211_chan_def *chandef, primary_chan_idx = 4; center_chan += 6; } + /* assign the center channel used + * while 40M bw is selected + */ + cch_by_bw[RTW_CHANNEL_WIDTH_40] = center_chan - 4; } break; default: @@ -251,6 +264,12 @@ void rtw_get_channel_params(struct cfg80211_chan_def *chandef, chan_params->center_chan = center_chan; chan_params->bandwidth = bandwidth; chan_params->primary_chan_idx = primary_chan_idx; + + /* assign the center channel used while current bw is selected */ + cch_by_bw[bandwidth] = center_chan; + + for (i = bandwidth + 1; i <= RTW_MAX_CHANNEL_WIDTH; i++) + cch_by_bw[i] = 0; } void rtw_set_channel(struct rtw_dev *rtwdev) @@ -260,6 +279,7 @@ void rtw_set_channel(struct rtw_dev *rtwdev) struct rtw_chip_info *chip = rtwdev->chip; struct rtw_channel_params ch_param; u8 center_chan, bandwidth, primary_chan_idx; + u8 i; rtw_get_channel_params(&hw->conf.chandef, &ch_param); if (WARN(ch_param.center_chan == 0, "Invalid channel\n")) @@ -272,6 +292,10 @@ void rtw_set_channel(struct rtw_dev *rtwdev) hal->current_band_width = bandwidth; hal->current_channel = center_chan; hal->current_band_type = center_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; + + for (i = RTW_CHANNEL_WIDTH_20; i <= RTW_MAX_CHANNEL_WIDTH; i++) + hal->cch_by_bw[i] = ch_param.cch_by_bw[i]; + chip->ops->set_channel(rtwdev, center_chan, bandwidth, primary_chan_idx); rtw_phy_set_tx_power_level(rtwdev, center_chan); @@ -1047,7 +1071,7 @@ static int rtw_chip_board_info_setup(struct rtw_dev *rtwdev) rtw_phy_setup_phy_cond(rtwdev, 0); - rtw_hw_init_tx_power(hal); + rtw_phy_init_tx_power(rtwdev); rtw_load_table(rtwdev, rfe_def->phy_pg_tbl); rtw_load_table(rtwdev, rfe_def->txpwr_lmt_tbl); rtw_phy_tx_power_by_rate_config(hal); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 927305075a79..8fa05751836b 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -62,6 +62,9 @@ enum rtw_supported_band { RTW_BAND_MAX, }; +/* now, support upto 80M bw */ +#define RTW_MAX_CHANNEL_WIDTH RTW_CHANNEL_WIDTH_80 + enum rtw_bandwidth { RTW_CHANNEL_WIDTH_20 = 0, RTW_CHANNEL_WIDTH_40 = 1, @@ -286,10 +289,16 @@ enum rtw_trx_desc_rate { }; enum rtw_regulatory_domains { - RTW_REGD_FCC = 0, - RTW_REGD_MKK = 1, - RTW_REGD_ETSI = 2, - RTW_REGD_WW = 3, + RTW_REGD_FCC = 0, + RTW_REGD_MKK = 1, + RTW_REGD_ETSI = 2, + RTW_REGD_IC = 3, + RTW_REGD_KCC = 4, + RTW_REGD_ACMA = 5, + RTW_REGD_CHILE = 6, + RTW_REGD_UKRAINE = 7, + RTW_REGD_MEXICO = 8, + RTW_REGD_WW, RTW_REGD_MAX }; @@ -413,6 +422,10 @@ struct rtw_channel_params { u8 center_chan; u8 bandwidth; u8 primary_chan_idx; + /* center channel by different available bandwidth, + * val of (bw > current bandwidth) is invalid + */ + u8 cch_by_bw[RTW_MAX_CHANNEL_WIDTH + 1]; }; struct rtw_hw_reg { @@ -984,6 +997,12 @@ struct rtw_hal { u8 current_channel; u8 current_band_width; u8 current_band_type; + + /* center channel for different available bandwidth, + * val of (bw > current_band_width) is invalid + */ + u8 cch_by_bw[RTW_MAX_CHANNEL_WIDTH + 1]; + u8 sec_ch_offset; u8 rf_type; u8 rf_path_num; diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index 0fcdc3a19468..4ec8dcf17361 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -65,6 +65,56 @@ static const u32 db_invert_table[12][8] = { 1995262315, 2511886432U, 3162277660U, 3981071706U} }; +u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M }; +u8 rtw_ofdm_rates[] = { + DESC_RATE6M, DESC_RATE9M, DESC_RATE12M, + DESC_RATE18M, DESC_RATE24M, DESC_RATE36M, + DESC_RATE48M, DESC_RATE54M +}; +u8 rtw_ht_1s_rates[] = { + DESC_RATEMCS0, DESC_RATEMCS1, DESC_RATEMCS2, + DESC_RATEMCS3, DESC_RATEMCS4, DESC_RATEMCS5, + DESC_RATEMCS6, DESC_RATEMCS7 +}; +u8 rtw_ht_2s_rates[] = { + DESC_RATEMCS8, DESC_RATEMCS9, DESC_RATEMCS10, + DESC_RATEMCS11, DESC_RATEMCS12, DESC_RATEMCS13, + DESC_RATEMCS14, DESC_RATEMCS15 +}; +u8 rtw_vht_1s_rates[] = { + DESC_RATEVHT1SS_MCS0, DESC_RATEVHT1SS_MCS1, + DESC_RATEVHT1SS_MCS2, DESC_RATEVHT1SS_MCS3, + DESC_RATEVHT1SS_MCS4, DESC_RATEVHT1SS_MCS5, + DESC_RATEVHT1SS_MCS6, DESC_RATEVHT1SS_MCS7, + DESC_RATEVHT1SS_MCS8, DESC_RATEVHT1SS_MCS9 +}; +u8 rtw_vht_2s_rates[] = { + DESC_RATEVHT2SS_MCS0, DESC_RATEVHT2SS_MCS1, + DESC_RATEVHT2SS_MCS2, DESC_RATEVHT2SS_MCS3, + DESC_RATEVHT2SS_MCS4, DESC_RATEVHT2SS_MCS5, + DESC_RATEVHT2SS_MCS6, DESC_RATEVHT2SS_MCS7, + DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9 +}; +u8 *rtw_rate_section[RTW_RATE_SECTION_MAX] = { + rtw_cck_rates, rtw_ofdm_rates, + rtw_ht_1s_rates, rtw_ht_2s_rates, + rtw_vht_1s_rates, rtw_vht_2s_rates +}; +u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = { + ARRAY_SIZE(rtw_cck_rates), + ARRAY_SIZE(rtw_ofdm_rates), + ARRAY_SIZE(rtw_ht_1s_rates), + ARRAY_SIZE(rtw_ht_2s_rates), + ARRAY_SIZE(rtw_vht_1s_rates), + ARRAY_SIZE(rtw_vht_2s_rates) +}; +static const u8 rtw_cck_size = ARRAY_SIZE(rtw_cck_rates); +static const u8 rtw_ofdm_size = ARRAY_SIZE(rtw_ofdm_rates); +static const u8 rtw_ht_1s_size = ARRAY_SIZE(rtw_ht_1s_rates); +static const u8 rtw_ht_2s_size = ARRAY_SIZE(rtw_ht_2s_rates); +static const u8 rtw_vht_1s_size = ARRAY_SIZE(rtw_vht_1s_rates); +static const u8 rtw_vht_2s_size = ARRAY_SIZE(rtw_vht_2s_rates); + enum rtw_phy_band_type { PHY_BAND_2G = 0, PHY_BAND_5G = 1, @@ -719,6 +769,353 @@ void rtw_parse_tbl_phy_cond(struct rtw_dev *rtwdev, const struct rtw_table *tbl) } } +#define bcd_to_dec_pwr_by_rate(val, i) bcd2bin(val >> (i * 8)) + +static u8 tbl_to_dec_pwr_by_rate(struct rtw_dev *rtwdev, u32 hex, u8 i) +{ + if (rtwdev->chip->is_pwr_by_rate_dec) + return bcd_to_dec_pwr_by_rate(hex, i); + + return (hex >> (i * 8)) & 0xFF; +} + +static void +rtw_phy_get_rate_values_of_txpwr_by_rate(struct rtw_dev *rtwdev, + u32 addr, u32 mask, u32 val, u8 *rate, + u8 *pwr_by_rate, u8 *rate_num) +{ + int i; + + switch (addr) { + case 0xE00: + case 0x830: + rate[0] = DESC_RATE6M; + rate[1] = DESC_RATE9M; + rate[2] = DESC_RATE12M; + rate[3] = DESC_RATE18M; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xE04: + case 0x834: + rate[0] = DESC_RATE24M; + rate[1] = DESC_RATE36M; + rate[2] = DESC_RATE48M; + rate[3] = DESC_RATE54M; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xE08: + rate[0] = DESC_RATE1M; + pwr_by_rate[0] = bcd_to_dec_pwr_by_rate(val, 1); + *rate_num = 1; + break; + case 0x86C: + if (mask == 0xffffff00) { + rate[0] = DESC_RATE2M; + rate[1] = DESC_RATE5_5M; + rate[2] = DESC_RATE11M; + for (i = 1; i < 4; ++i) + pwr_by_rate[i - 1] = + tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 3; + } else if (mask == 0x000000ff) { + rate[0] = DESC_RATE11M; + pwr_by_rate[0] = bcd_to_dec_pwr_by_rate(val, 0); + *rate_num = 1; + } + break; + case 0xE10: + case 0x83C: + rate[0] = DESC_RATEMCS0; + rate[1] = DESC_RATEMCS1; + rate[2] = DESC_RATEMCS2; + rate[3] = DESC_RATEMCS3; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xE14: + case 0x848: + rate[0] = DESC_RATEMCS4; + rate[1] = DESC_RATEMCS5; + rate[2] = DESC_RATEMCS6; + rate[3] = DESC_RATEMCS7; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xE18: + case 0x84C: + rate[0] = DESC_RATEMCS8; + rate[1] = DESC_RATEMCS9; + rate[2] = DESC_RATEMCS10; + rate[3] = DESC_RATEMCS11; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xE1C: + case 0x868: + rate[0] = DESC_RATEMCS12; + rate[1] = DESC_RATEMCS13; + rate[2] = DESC_RATEMCS14; + rate[3] = DESC_RATEMCS15; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0x838: + rate[0] = DESC_RATE1M; + rate[1] = DESC_RATE2M; + rate[2] = DESC_RATE5_5M; + for (i = 1; i < 4; ++i) + pwr_by_rate[i - 1] = tbl_to_dec_pwr_by_rate(rtwdev, + val, i); + *rate_num = 3; + break; + case 0xC20: + case 0xE20: + case 0x1820: + case 0x1A20: + rate[0] = DESC_RATE1M; + rate[1] = DESC_RATE2M; + rate[2] = DESC_RATE5_5M; + rate[3] = DESC_RATE11M; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC24: + case 0xE24: + case 0x1824: + case 0x1A24: + rate[0] = DESC_RATE6M; + rate[1] = DESC_RATE9M; + rate[2] = DESC_RATE12M; + rate[3] = DESC_RATE18M; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC28: + case 0xE28: + case 0x1828: + case 0x1A28: + rate[0] = DESC_RATE24M; + rate[1] = DESC_RATE36M; + rate[2] = DESC_RATE48M; + rate[3] = DESC_RATE54M; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC2C: + case 0xE2C: + case 0x182C: + case 0x1A2C: + rate[0] = DESC_RATEMCS0; + rate[1] = DESC_RATEMCS1; + rate[2] = DESC_RATEMCS2; + rate[3] = DESC_RATEMCS3; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC30: + case 0xE30: + case 0x1830: + case 0x1A30: + rate[0] = DESC_RATEMCS4; + rate[1] = DESC_RATEMCS5; + rate[2] = DESC_RATEMCS6; + rate[3] = DESC_RATEMCS7; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC34: + case 0xE34: + case 0x1834: + case 0x1A34: + rate[0] = DESC_RATEMCS8; + rate[1] = DESC_RATEMCS9; + rate[2] = DESC_RATEMCS10; + rate[3] = DESC_RATEMCS11; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC38: + case 0xE38: + case 0x1838: + case 0x1A38: + rate[0] = DESC_RATEMCS12; + rate[1] = DESC_RATEMCS13; + rate[2] = DESC_RATEMCS14; + rate[3] = DESC_RATEMCS15; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC3C: + case 0xE3C: + case 0x183C: + case 0x1A3C: + rate[0] = DESC_RATEVHT1SS_MCS0; + rate[1] = DESC_RATEVHT1SS_MCS1; + rate[2] = DESC_RATEVHT1SS_MCS2; + rate[3] = DESC_RATEVHT1SS_MCS3; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC40: + case 0xE40: + case 0x1840: + case 0x1A40: + rate[0] = DESC_RATEVHT1SS_MCS4; + rate[1] = DESC_RATEVHT1SS_MCS5; + rate[2] = DESC_RATEVHT1SS_MCS6; + rate[3] = DESC_RATEVHT1SS_MCS7; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC44: + case 0xE44: + case 0x1844: + case 0x1A44: + rate[0] = DESC_RATEVHT1SS_MCS8; + rate[1] = DESC_RATEVHT1SS_MCS9; + rate[2] = DESC_RATEVHT2SS_MCS0; + rate[3] = DESC_RATEVHT2SS_MCS1; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC48: + case 0xE48: + case 0x1848: + case 0x1A48: + rate[0] = DESC_RATEVHT2SS_MCS2; + rate[1] = DESC_RATEVHT2SS_MCS3; + rate[2] = DESC_RATEVHT2SS_MCS4; + rate[3] = DESC_RATEVHT2SS_MCS5; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xC4C: + case 0xE4C: + case 0x184C: + case 0x1A4C: + rate[0] = DESC_RATEVHT2SS_MCS6; + rate[1] = DESC_RATEVHT2SS_MCS7; + rate[2] = DESC_RATEVHT2SS_MCS8; + rate[3] = DESC_RATEVHT2SS_MCS9; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xCD8: + case 0xED8: + case 0x18D8: + case 0x1AD8: + rate[0] = DESC_RATEMCS16; + rate[1] = DESC_RATEMCS17; + rate[2] = DESC_RATEMCS18; + rate[3] = DESC_RATEMCS19; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xCDC: + case 0xEDC: + case 0x18DC: + case 0x1ADC: + rate[0] = DESC_RATEMCS20; + rate[1] = DESC_RATEMCS21; + rate[2] = DESC_RATEMCS22; + rate[3] = DESC_RATEMCS23; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xCE0: + case 0xEE0: + case 0x18E0: + case 0x1AE0: + rate[0] = DESC_RATEVHT3SS_MCS0; + rate[1] = DESC_RATEVHT3SS_MCS1; + rate[2] = DESC_RATEVHT3SS_MCS2; + rate[3] = DESC_RATEVHT3SS_MCS3; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xCE4: + case 0xEE4: + case 0x18E4: + case 0x1AE4: + rate[0] = DESC_RATEVHT3SS_MCS4; + rate[1] = DESC_RATEVHT3SS_MCS5; + rate[2] = DESC_RATEVHT3SS_MCS6; + rate[3] = DESC_RATEVHT3SS_MCS7; + for (i = 0; i < 4; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 4; + break; + case 0xCE8: + case 0xEE8: + case 0x18E8: + case 0x1AE8: + rate[0] = DESC_RATEVHT3SS_MCS8; + rate[1] = DESC_RATEVHT3SS_MCS9; + for (i = 0; i < 2; ++i) + pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); + *rate_num = 2; + break; + default: + rtw_warn(rtwdev, "invalid tx power index addr 0x%08x\n", addr); + break; + } +} + +static void rtw_phy_store_tx_power_by_rate(struct rtw_dev *rtwdev, + u32 band, u32 rfpath, u32 txnum, + u32 regaddr, u32 bitmask, u32 data) +{ + struct rtw_hal *hal = &rtwdev->hal; + u8 rate_num = 0; + u8 rate; + u8 rates[RTW_RF_PATH_MAX] = {0}; + s8 offset; + s8 pwr_by_rate[RTW_RF_PATH_MAX] = {0}; + int i; + + rtw_phy_get_rate_values_of_txpwr_by_rate(rtwdev, regaddr, bitmask, data, + rates, pwr_by_rate, &rate_num); + + if (WARN_ON(rfpath >= RTW_RF_PATH_MAX || + (band != PHY_BAND_2G && band != PHY_BAND_5G) || + rate_num > RTW_RF_PATH_MAX)) + return; + + for (i = 0; i < rate_num; i++) { + offset = pwr_by_rate[i]; + rate = rates[i]; + if (band == PHY_BAND_2G) + hal->tx_pwr_by_rate_offset_2g[rfpath][rate] = offset; + else if (band == PHY_BAND_5G) + hal->tx_pwr_by_rate_offset_5g[rfpath][rate] = offset; + else + continue; + } +} + void rtw_parse_tbl_bb_pg(struct rtw_dev *rtwdev, const struct rtw_table *tbl) { const struct phy_pg_cfg_pair *p = tbl->data; @@ -731,12 +1128,142 @@ void rtw_parse_tbl_bb_pg(struct rtw_dev *rtwdev, const struct rtw_table *tbl) msleep(50); continue; } - phy_store_tx_power_by_rate(rtwdev, p->band, p->rf_path, - p->tx_num, p->addr, p->bitmask, - p->data); + rtw_phy_store_tx_power_by_rate(rtwdev, p->band, p->rf_path, + p->tx_num, p->addr, p->bitmask, + p->data); } } +static const u8 rtw_channel_idx_5g[RTW_MAX_CHANNEL_NUM_5G] = { + 36, 38, 40, 42, 44, 46, 48, /* Band 1 */ + 52, 54, 56, 58, 60, 62, 64, /* Band 2 */ + 100, 102, 104, 106, 108, 110, 112, /* Band 3 */ + 116, 118, 120, 122, 124, 126, 128, /* Band 3 */ + 132, 134, 136, 138, 140, 142, 144, /* Band 3 */ + 149, 151, 153, 155, 157, 159, 161, /* Band 4 */ + 165, 167, 169, 171, 173, 175, 177}; /* Band 4 */ + +static int rtw_channel_to_idx(u8 band, u8 channel) +{ + int ch_idx; + u8 n_channel; + + if (band == PHY_BAND_2G) { + ch_idx = channel - 1; + n_channel = RTW_MAX_CHANNEL_NUM_2G; + } else if (band == PHY_BAND_5G) { + n_channel = RTW_MAX_CHANNEL_NUM_5G; + for (ch_idx = 0; ch_idx < n_channel; ch_idx++) + if (rtw_channel_idx_5g[ch_idx] == channel) + break; + } else { + return -1; + } + + if (ch_idx >= n_channel) + return -1; + + return ch_idx; +} + +static void rtw_phy_set_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 band, + u8 bw, u8 rs, u8 ch, s8 pwr_limit) +{ + struct rtw_hal *hal = &rtwdev->hal; + u8 max_power_index = rtwdev->chip->max_power_index; + s8 ww; + int ch_idx; + + pwr_limit = clamp_t(s8, pwr_limit, + -max_power_index, max_power_index); + ch_idx = rtw_channel_to_idx(band, ch); + + if (regd >= RTW_REGD_MAX || bw >= RTW_CHANNEL_WIDTH_MAX || + rs >= RTW_RATE_SECTION_MAX || ch_idx < 0) { + WARN(1, + "wrong txpwr_lmt regd=%u, band=%u bw=%u, rs=%u, ch_idx=%u, pwr_limit=%d\n", + regd, band, bw, rs, ch_idx, pwr_limit); + return; + } + + if (band == PHY_BAND_2G) { + hal->tx_pwr_limit_2g[regd][bw][rs][ch_idx] = pwr_limit; + ww = hal->tx_pwr_limit_2g[RTW_REGD_WW][bw][rs][ch_idx]; + ww = min_t(s8, ww, pwr_limit); + hal->tx_pwr_limit_2g[RTW_REGD_WW][bw][rs][ch_idx] = ww; + } else if (band == PHY_BAND_5G) { + hal->tx_pwr_limit_5g[regd][bw][rs][ch_idx] = pwr_limit; + ww = hal->tx_pwr_limit_5g[RTW_REGD_WW][bw][rs][ch_idx]; + ww = min_t(s8, ww, pwr_limit); + hal->tx_pwr_limit_5g[RTW_REGD_WW][bw][rs][ch_idx] = ww; + } +} + +/* cross-reference 5G power limits if values are not assigned */ +static void +rtw_xref_5g_txpwr_lmt(struct rtw_dev *rtwdev, u8 regd, + u8 bw, u8 ch_idx, u8 rs_ht, u8 rs_vht) +{ + struct rtw_hal *hal = &rtwdev->hal; + u8 max_power_index = rtwdev->chip->max_power_index; + s8 lmt_ht = hal->tx_pwr_limit_5g[regd][bw][rs_ht][ch_idx]; + s8 lmt_vht = hal->tx_pwr_limit_5g[regd][bw][rs_vht][ch_idx]; + + if (lmt_ht == lmt_vht) + return; + + if (lmt_ht == max_power_index) + hal->tx_pwr_limit_5g[regd][bw][rs_ht][ch_idx] = lmt_vht; + + else if (lmt_vht == max_power_index) + hal->tx_pwr_limit_5g[regd][bw][rs_vht][ch_idx] = lmt_ht; +} + +/* cross-reference power limits for ht and vht */ +static void +rtw_xref_txpwr_lmt_by_rs(struct rtw_dev *rtwdev, u8 regd, u8 bw, u8 ch_idx) +{ + u8 rs_idx, rs_ht, rs_vht; + u8 rs_cmp[2][2] = {{RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_VHT_1S}, + {RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_2S} }; + + for (rs_idx = 0; rs_idx < 2; rs_idx++) { + rs_ht = rs_cmp[rs_idx][0]; + rs_vht = rs_cmp[rs_idx][1]; + + rtw_xref_5g_txpwr_lmt(rtwdev, regd, bw, ch_idx, rs_ht, rs_vht); + } +} + +/* cross-reference power limits for 5G channels */ +static void +rtw_xref_5g_txpwr_lmt_by_ch(struct rtw_dev *rtwdev, u8 regd, u8 bw) +{ + u8 ch_idx; + + for (ch_idx = 0; ch_idx < RTW_MAX_CHANNEL_NUM_5G; ch_idx++) + rtw_xref_txpwr_lmt_by_rs(rtwdev, regd, bw, ch_idx); +} + +/* cross-reference power limits for 20/40M bandwidth */ +static void +rtw_xref_txpwr_lmt_by_bw(struct rtw_dev *rtwdev, u8 regd) +{ + u8 bw; + + for (bw = RTW_CHANNEL_WIDTH_20; bw <= RTW_CHANNEL_WIDTH_40; bw++) + rtw_xref_5g_txpwr_lmt_by_ch(rtwdev, regd, bw); +} + +/* cross-reference power limits */ +static void rtw_xref_txpwr_lmt(struct rtw_dev *rtwdev) +{ + u8 regd; + + for (regd = 0; regd < RTW_REGD_MAX; regd++) + rtw_xref_txpwr_lmt_by_bw(rtwdev, regd); +} + void rtw_parse_tbl_txpwr_lmt(struct rtw_dev *rtwdev, const struct rtw_table *tbl) { @@ -746,10 +1273,11 @@ void rtw_parse_tbl_txpwr_lmt(struct rtw_dev *rtwdev, BUILD_BUG_ON(sizeof(struct txpwr_lmt_cfg_pair) != sizeof(u8) * 6); for (; p < end; p++) { - phy_set_tx_power_limit(rtwdev, p->regd, p->band, - p->bw, p->rs, - p->ch, p->txpwr_lmt); + rtw_phy_set_tx_power_limit(rtwdev, p->regd, p->band, + p->bw, p->rs, p->ch, p->txpwr_lmt); } + + rtw_xref_txpwr_lmt(rtwdev); } void rtw_phy_cfg_mac(struct rtw_dev *rtwdev, const struct rtw_table *tbl, @@ -824,93 +1352,6 @@ void rtw_phy_load_tables(struct rtw_dev *rtwdev) } } -#define bcd_to_dec_pwr_by_rate(val, i) bcd2bin(val >> (i * 8)) - -#define RTW_MAX_POWER_INDEX 0x3F - -u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M }; -u8 rtw_ofdm_rates[] = { - DESC_RATE6M, DESC_RATE9M, DESC_RATE12M, - DESC_RATE18M, DESC_RATE24M, DESC_RATE36M, - DESC_RATE48M, DESC_RATE54M -}; -u8 rtw_ht_1s_rates[] = { - DESC_RATEMCS0, DESC_RATEMCS1, DESC_RATEMCS2, - DESC_RATEMCS3, DESC_RATEMCS4, DESC_RATEMCS5, - DESC_RATEMCS6, DESC_RATEMCS7 -}; -u8 rtw_ht_2s_rates[] = { - DESC_RATEMCS8, DESC_RATEMCS9, DESC_RATEMCS10, - DESC_RATEMCS11, DESC_RATEMCS12, DESC_RATEMCS13, - DESC_RATEMCS14, DESC_RATEMCS15 -}; -u8 rtw_vht_1s_rates[] = { - DESC_RATEVHT1SS_MCS0, DESC_RATEVHT1SS_MCS1, - DESC_RATEVHT1SS_MCS2, DESC_RATEVHT1SS_MCS3, - DESC_RATEVHT1SS_MCS4, DESC_RATEVHT1SS_MCS5, - DESC_RATEVHT1SS_MCS6, DESC_RATEVHT1SS_MCS7, - DESC_RATEVHT1SS_MCS8, DESC_RATEVHT1SS_MCS9 -}; -u8 rtw_vht_2s_rates[] = { - DESC_RATEVHT2SS_MCS0, DESC_RATEVHT2SS_MCS1, - DESC_RATEVHT2SS_MCS2, DESC_RATEVHT2SS_MCS3, - DESC_RATEVHT2SS_MCS4, DESC_RATEVHT2SS_MCS5, - DESC_RATEVHT2SS_MCS6, DESC_RATEVHT2SS_MCS7, - DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9 -}; - -static u8 rtw_cck_size = ARRAY_SIZE(rtw_cck_rates); -static u8 rtw_ofdm_size = ARRAY_SIZE(rtw_ofdm_rates); -static u8 rtw_ht_1s_size = ARRAY_SIZE(rtw_ht_1s_rates); -static u8 rtw_ht_2s_size = ARRAY_SIZE(rtw_ht_2s_rates); -static u8 rtw_vht_1s_size = ARRAY_SIZE(rtw_vht_1s_rates); -static u8 rtw_vht_2s_size = ARRAY_SIZE(rtw_vht_2s_rates); -u8 *rtw_rate_section[RTW_RATE_SECTION_MAX] = { - rtw_cck_rates, rtw_ofdm_rates, - rtw_ht_1s_rates, rtw_ht_2s_rates, - rtw_vht_1s_rates, rtw_vht_2s_rates -}; -u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = { - ARRAY_SIZE(rtw_cck_rates), - ARRAY_SIZE(rtw_ofdm_rates), - ARRAY_SIZE(rtw_ht_1s_rates), - ARRAY_SIZE(rtw_ht_2s_rates), - ARRAY_SIZE(rtw_vht_1s_rates), - ARRAY_SIZE(rtw_vht_2s_rates) -}; - -static const u8 rtw_channel_idx_5g[RTW_MAX_CHANNEL_NUM_5G] = { - 36, 38, 40, 42, 44, 46, 48, /* Band 1 */ - 52, 54, 56, 58, 60, 62, 64, /* Band 2 */ - 100, 102, 104, 106, 108, 110, 112, /* Band 3 */ - 116, 118, 120, 122, 124, 126, 128, /* Band 3 */ - 132, 134, 136, 138, 140, 142, 144, /* Band 3 */ - 149, 151, 153, 155, 157, 159, 161, /* Band 4 */ - 165, 167, 169, 171, 173, 175, 177}; /* Band 4 */ - -static int rtw_channel_to_idx(u8 band, u8 channel) -{ - int ch_idx; - u8 n_channel; - - if (band == PHY_BAND_2G) { - ch_idx = channel - 1; - n_channel = RTW_MAX_CHANNEL_NUM_2G; - } else if (band == PHY_BAND_5G) { - n_channel = RTW_MAX_CHANNEL_NUM_5G; - for (ch_idx = 0; ch_idx < n_channel; ch_idx++) - if (rtw_channel_idx_5g[ch_idx] == channel) - break; - } else { - return -1; - } - - if (ch_idx >= n_channel) - return -1; - - return ch_idx; -} - static u8 rtw_get_channel_group(u8 channel) { switch (channel) { @@ -1000,10 +1441,10 @@ static u8 rtw_get_channel_group(u8 channel) } } -static u8 phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, - struct rtw_2g_txpwr_idx *pwr_idx_2g, - enum rtw_bandwidth bandwidth, - u8 rate, u8 group) +static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, + struct rtw_2g_txpwr_idx *pwr_idx_2g, + enum rtw_bandwidth bandwidth, + u8 rate, u8 group) { struct rtw_chip_info *chip = rtwdev->chip; u8 tx_power; @@ -1047,10 +1488,10 @@ static u8 phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, return tx_power; } -static u8 phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, - struct rtw_5g_txpwr_idx *pwr_idx_5g, - enum rtw_bandwidth bandwidth, - u8 rate, u8 group) +static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, + struct rtw_5g_txpwr_idx *pwr_idx_5g, + enum rtw_bandwidth bandwidth, + u8 rate, u8 group) { struct rtw_chip_info *chip = rtwdev->chip; u8 tx_power; @@ -1101,81 +1542,112 @@ static u8 phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, return tx_power; } -/* set tx power level by path for each rates, note that the order of the rates - * are *very* important, bacause 8822B/8821C combines every four bytes of tx - * power index into a four-byte power index register, and calls set_tx_agc to - * write these values into hardware - */ -static -void phy_set_tx_power_level_by_path(struct rtw_dev *rtwdev, u8 ch, u8 path) +static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, + enum rtw_bandwidth bw, u8 rf_path, + u8 rate, u8 channel, u8 regd) { struct rtw_hal *hal = &rtwdev->hal; + u8 *cch_by_bw = hal->cch_by_bw; + s8 power_limit = (s8)rtwdev->chip->max_power_index; u8 rs; + int ch_idx; + u8 cur_bw, cur_ch; + s8 cur_lmt; - /* do not need cck rates if we are not in 2.4G */ - if (hal->current_band_type == RTW_BAND_2G) + if (regd > RTW_REGD_WW) + return power_limit; + + if (rate >= DESC_RATE1M && rate <= DESC_RATE11M) rs = RTW_RATE_SECTION_CCK; - else + else if (rate >= DESC_RATE6M && rate <= DESC_RATE54M) rs = RTW_RATE_SECTION_OFDM; + else if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS7) + rs = RTW_RATE_SECTION_HT_1S; + else if (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) + rs = RTW_RATE_SECTION_HT_2S; + else if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9) + rs = RTW_RATE_SECTION_VHT_1S; + else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9) + rs = RTW_RATE_SECTION_VHT_2S; + else + goto err; - for (; rs < RTW_RATE_SECTION_MAX; rs++) - phy_set_tx_power_index_by_rs(rtwdev, ch, path, rs); -} + /* only 20M BW with cck and ofdm */ + if (rs == RTW_RATE_SECTION_CCK || rs == RTW_RATE_SECTION_OFDM) + bw = RTW_CHANNEL_WIDTH_20; -void rtw_phy_set_tx_power_level(struct rtw_dev *rtwdev, u8 channel) -{ - struct rtw_chip_info *chip = rtwdev->chip; - struct rtw_hal *hal = &rtwdev->hal; - u8 path; + /* only 20/40M BW with ht */ + if (rs == RTW_RATE_SECTION_HT_1S || rs == RTW_RATE_SECTION_HT_2S) + bw = min_t(u8, bw, RTW_CHANNEL_WIDTH_40); - mutex_lock(&hal->tx_power_mutex); + /* select min power limit among [20M BW ~ current BW] */ + for (cur_bw = RTW_CHANNEL_WIDTH_20; cur_bw <= bw; cur_bw++) { + cur_ch = cch_by_bw[cur_bw]; - for (path = 0; path < hal->rf_path_num; path++) - phy_set_tx_power_level_by_path(rtwdev, channel, path); + ch_idx = rtw_channel_to_idx(band, cur_ch); + if (ch_idx < 0) + goto err; - chip->ops->set_tx_power_index(rtwdev); - mutex_unlock(&hal->tx_power_mutex); -} + cur_lmt = cur_ch <= RTW_MAX_CHANNEL_NUM_2G ? + hal->tx_pwr_limit_2g[regd][cur_bw][rs][ch_idx] : + hal->tx_pwr_limit_5g[regd][cur_bw][rs][ch_idx]; + + power_limit = min_t(s8, cur_lmt, power_limit); + } -s8 phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, - enum rtw_bandwidth bandwidth, u8 rf_path, - u8 rate, u8 channel, u8 regd); + return power_limit; -static -u8 phy_get_tx_power_index(void *adapter, u8 rf_path, u8 rate, - enum rtw_bandwidth bandwidth, u8 channel, u8 regd) +err: + WARN(1, "invalid arguments, band=%d, bw=%d, path=%d, rate=%d, ch=%d\n", + band, bw, rf_path, rate, channel); + return (s8)rtwdev->chip->max_power_index; +} + +void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw, + u8 ch, u8 regd, struct rtw_power_params *pwr_param) { - struct rtw_dev *rtwdev = adapter; struct rtw_hal *hal = &rtwdev->hal; struct rtw_txpwr_idx *pwr_idx; - u8 tx_power; - u8 group; - u8 band; - s8 offset, limit; + u8 group, band; + u8 *base = &pwr_param->pwr_base; + s8 *offset = &pwr_param->pwr_offset; + s8 *limit = &pwr_param->pwr_limit; - pwr_idx = &rtwdev->efuse.txpwr_idx_table[rf_path]; - group = rtw_get_channel_group(channel); + pwr_idx = &rtwdev->efuse.txpwr_idx_table[path]; + group = rtw_get_channel_group(ch); /* base power index for 2.4G/5G */ - if (channel <= 14) { + if (ch <= 14) { band = PHY_BAND_2G; - tx_power = phy_get_2g_tx_power_index(rtwdev, - &pwr_idx->pwr_idx_2g, - bandwidth, rate, group); - offset = hal->tx_pwr_by_rate_offset_2g[rf_path][rate]; + *base = rtw_phy_get_2g_tx_power_index(rtwdev, + &pwr_idx->pwr_idx_2g, + bw, rate, group); + *offset = hal->tx_pwr_by_rate_offset_2g[path][rate]; } else { band = PHY_BAND_5G; - tx_power = phy_get_5g_tx_power_index(rtwdev, - &pwr_idx->pwr_idx_5g, - bandwidth, rate, group); - offset = hal->tx_pwr_by_rate_offset_5g[rf_path][rate]; + *base = rtw_phy_get_5g_tx_power_index(rtwdev, + &pwr_idx->pwr_idx_5g, + bw, rate, group); + *offset = hal->tx_pwr_by_rate_offset_5g[path][rate]; } - limit = phy_get_tx_power_limit(rtwdev, band, bandwidth, rf_path, - rate, channel, regd); + *limit = rtw_phy_get_tx_power_limit(rtwdev, band, bw, path, + rate, ch, regd); +} + +u8 +rtw_phy_get_tx_power_index(struct rtw_dev *rtwdev, u8 rf_path, u8 rate, + enum rtw_bandwidth bandwidth, u8 channel, u8 regd) +{ + struct rtw_power_params pwr_param = {0}; + u8 tx_power; + s8 offset; + + rtw_get_tx_power_params(rtwdev, rf_path, rate, bandwidth, + channel, regd, &pwr_param); - if (offset > limit) - offset = limit; + tx_power = pwr_param.pwr_base; + offset = min_t(s8, pwr_param.pwr_offset, pwr_param.pwr_limit); tx_power += offset; @@ -1185,9 +1657,9 @@ u8 phy_get_tx_power_index(void *adapter, u8 rf_path, u8 rate, return tx_power; } -void phy_set_tx_power_index_by_rs(void *adapter, u8 ch, u8 path, u8 rs) +static void rtw_phy_set_tx_power_index_by_rs(struct rtw_dev *rtwdev, + u8 ch, u8 path, u8 rs) { - struct rtw_dev *rtwdev = adapter; struct rtw_hal *hal = &rtwdev->hal; u8 regd = rtwdev->regd.txpwr_regd; u8 *rates; @@ -1205,361 +1677,51 @@ void phy_set_tx_power_index_by_rs(void *adapter, u8 ch, u8 path, u8 rs) bw = hal->current_band_width; for (i = 0; i < size; i++) { rate = rates[i]; - pwr_idx = phy_get_tx_power_index(adapter, path, rate, bw, ch, - regd); + pwr_idx = rtw_phy_get_tx_power_index(rtwdev, path, rate, + bw, ch, regd); hal->tx_pwr_tbl[path][rate] = pwr_idx; } } -static u8 tbl_to_dec_pwr_by_rate(struct rtw_dev *rtwdev, u32 hex, u8 i) -{ - if (rtwdev->chip->is_pwr_by_rate_dec) - return bcd_to_dec_pwr_by_rate(hex, i); - else - return (hex >> (i * 8)) & 0xFF; -} - -static void phy_get_rate_values_of_txpwr_by_rate(struct rtw_dev *rtwdev, - u32 addr, u32 mask, - u32 val, u8 *rate, - u8 *pwr_by_rate, u8 *rate_num) +/* set tx power level by path for each rates, note that the order of the rates + * are *very* important, bacause 8822B/8821C combines every four bytes of tx + * power index into a four-byte power index register, and calls set_tx_agc to + * write these values into hardware + */ +static void rtw_phy_set_tx_power_level_by_path(struct rtw_dev *rtwdev, + u8 ch, u8 path) { - int i; + struct rtw_hal *hal = &rtwdev->hal; + u8 rs; - switch (addr) { - case 0xE00: - case 0x830: - rate[0] = DESC_RATE6M; - rate[1] = DESC_RATE9M; - rate[2] = DESC_RATE12M; - rate[3] = DESC_RATE18M; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xE04: - case 0x834: - rate[0] = DESC_RATE24M; - rate[1] = DESC_RATE36M; - rate[2] = DESC_RATE48M; - rate[3] = DESC_RATE54M; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xE08: - rate[0] = DESC_RATE1M; - pwr_by_rate[0] = bcd_to_dec_pwr_by_rate(val, 1); - *rate_num = 1; - break; - case 0x86C: - if (mask == 0xffffff00) { - rate[0] = DESC_RATE2M; - rate[1] = DESC_RATE5_5M; - rate[2] = DESC_RATE11M; - for (i = 1; i < 4; ++i) - pwr_by_rate[i - 1] = - tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 3; - } else if (mask == 0x000000ff) { - rate[0] = DESC_RATE11M; - pwr_by_rate[0] = bcd_to_dec_pwr_by_rate(val, 0); - *rate_num = 1; - } - break; - case 0xE10: - case 0x83C: - rate[0] = DESC_RATEMCS0; - rate[1] = DESC_RATEMCS1; - rate[2] = DESC_RATEMCS2; - rate[3] = DESC_RATEMCS3; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xE14: - case 0x848: - rate[0] = DESC_RATEMCS4; - rate[1] = DESC_RATEMCS5; - rate[2] = DESC_RATEMCS6; - rate[3] = DESC_RATEMCS7; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xE18: - case 0x84C: - rate[0] = DESC_RATEMCS8; - rate[1] = DESC_RATEMCS9; - rate[2] = DESC_RATEMCS10; - rate[3] = DESC_RATEMCS11; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xE1C: - case 0x868: - rate[0] = DESC_RATEMCS12; - rate[1] = DESC_RATEMCS13; - rate[2] = DESC_RATEMCS14; - rate[3] = DESC_RATEMCS15; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; + /* do not need cck rates if we are not in 2.4G */ + if (hal->current_band_type == RTW_BAND_2G) + rs = RTW_RATE_SECTION_CCK; + else + rs = RTW_RATE_SECTION_OFDM; - break; - case 0x838: - rate[0] = DESC_RATE1M; - rate[1] = DESC_RATE2M; - rate[2] = DESC_RATE5_5M; - for (i = 1; i < 4; ++i) - pwr_by_rate[i - 1] = tbl_to_dec_pwr_by_rate(rtwdev, - val, i); - *rate_num = 3; - break; - case 0xC20: - case 0xE20: - case 0x1820: - case 0x1A20: - rate[0] = DESC_RATE1M; - rate[1] = DESC_RATE2M; - rate[2] = DESC_RATE5_5M; - rate[3] = DESC_RATE11M; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC24: - case 0xE24: - case 0x1824: - case 0x1A24: - rate[0] = DESC_RATE6M; - rate[1] = DESC_RATE9M; - rate[2] = DESC_RATE12M; - rate[3] = DESC_RATE18M; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC28: - case 0xE28: - case 0x1828: - case 0x1A28: - rate[0] = DESC_RATE24M; - rate[1] = DESC_RATE36M; - rate[2] = DESC_RATE48M; - rate[3] = DESC_RATE54M; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC2C: - case 0xE2C: - case 0x182C: - case 0x1A2C: - rate[0] = DESC_RATEMCS0; - rate[1] = DESC_RATEMCS1; - rate[2] = DESC_RATEMCS2; - rate[3] = DESC_RATEMCS3; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC30: - case 0xE30: - case 0x1830: - case 0x1A30: - rate[0] = DESC_RATEMCS4; - rate[1] = DESC_RATEMCS5; - rate[2] = DESC_RATEMCS6; - rate[3] = DESC_RATEMCS7; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC34: - case 0xE34: - case 0x1834: - case 0x1A34: - rate[0] = DESC_RATEMCS8; - rate[1] = DESC_RATEMCS9; - rate[2] = DESC_RATEMCS10; - rate[3] = DESC_RATEMCS11; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC38: - case 0xE38: - case 0x1838: - case 0x1A38: - rate[0] = DESC_RATEMCS12; - rate[1] = DESC_RATEMCS13; - rate[2] = DESC_RATEMCS14; - rate[3] = DESC_RATEMCS15; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC3C: - case 0xE3C: - case 0x183C: - case 0x1A3C: - rate[0] = DESC_RATEVHT1SS_MCS0; - rate[1] = DESC_RATEVHT1SS_MCS1; - rate[2] = DESC_RATEVHT1SS_MCS2; - rate[3] = DESC_RATEVHT1SS_MCS3; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC40: - case 0xE40: - case 0x1840: - case 0x1A40: - rate[0] = DESC_RATEVHT1SS_MCS4; - rate[1] = DESC_RATEVHT1SS_MCS5; - rate[2] = DESC_RATEVHT1SS_MCS6; - rate[3] = DESC_RATEVHT1SS_MCS7; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC44: - case 0xE44: - case 0x1844: - case 0x1A44: - rate[0] = DESC_RATEVHT1SS_MCS8; - rate[1] = DESC_RATEVHT1SS_MCS9; - rate[2] = DESC_RATEVHT2SS_MCS0; - rate[3] = DESC_RATEVHT2SS_MCS1; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC48: - case 0xE48: - case 0x1848: - case 0x1A48: - rate[0] = DESC_RATEVHT2SS_MCS2; - rate[1] = DESC_RATEVHT2SS_MCS3; - rate[2] = DESC_RATEVHT2SS_MCS4; - rate[3] = DESC_RATEVHT2SS_MCS5; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xC4C: - case 0xE4C: - case 0x184C: - case 0x1A4C: - rate[0] = DESC_RATEVHT2SS_MCS6; - rate[1] = DESC_RATEVHT2SS_MCS7; - rate[2] = DESC_RATEVHT2SS_MCS8; - rate[3] = DESC_RATEVHT2SS_MCS9; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xCD8: - case 0xED8: - case 0x18D8: - case 0x1AD8: - rate[0] = DESC_RATEMCS16; - rate[1] = DESC_RATEMCS17; - rate[2] = DESC_RATEMCS18; - rate[3] = DESC_RATEMCS19; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xCDC: - case 0xEDC: - case 0x18DC: - case 0x1ADC: - rate[0] = DESC_RATEMCS20; - rate[1] = DESC_RATEMCS21; - rate[2] = DESC_RATEMCS22; - rate[3] = DESC_RATEMCS23; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xCE0: - case 0xEE0: - case 0x18E0: - case 0x1AE0: - rate[0] = DESC_RATEVHT3SS_MCS0; - rate[1] = DESC_RATEVHT3SS_MCS1; - rate[2] = DESC_RATEVHT3SS_MCS2; - rate[3] = DESC_RATEVHT3SS_MCS3; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xCE4: - case 0xEE4: - case 0x18E4: - case 0x1AE4: - rate[0] = DESC_RATEVHT3SS_MCS4; - rate[1] = DESC_RATEVHT3SS_MCS5; - rate[2] = DESC_RATEVHT3SS_MCS6; - rate[3] = DESC_RATEVHT3SS_MCS7; - for (i = 0; i < 4; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 4; - break; - case 0xCE8: - case 0xEE8: - case 0x18E8: - case 0x1AE8: - rate[0] = DESC_RATEVHT3SS_MCS8; - rate[1] = DESC_RATEVHT3SS_MCS9; - for (i = 0; i < 2; ++i) - pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); - *rate_num = 2; - break; - default: - rtw_warn(rtwdev, "invalid tx power index addr 0x%08x\n", addr); - break; - } + for (; rs < RTW_RATE_SECTION_MAX; rs++) + rtw_phy_set_tx_power_index_by_rs(rtwdev, ch, path, rs); } -void phy_store_tx_power_by_rate(void *adapter, u32 band, u32 rfpath, u32 txnum, - u32 regaddr, u32 bitmask, u32 data) +void rtw_phy_set_tx_power_level(struct rtw_dev *rtwdev, u8 channel) { - struct rtw_dev *rtwdev = adapter; + struct rtw_chip_info *chip = rtwdev->chip; struct rtw_hal *hal = &rtwdev->hal; - u8 rate_num = 0; - u8 rate; - u8 rates[RTW_RF_PATH_MAX] = {0}; - s8 offset; - s8 pwr_by_rate[RTW_RF_PATH_MAX] = {0}; - int i; + u8 path; - phy_get_rate_values_of_txpwr_by_rate(rtwdev, regaddr, bitmask, data, - rates, pwr_by_rate, &rate_num); + mutex_lock(&hal->tx_power_mutex); - if (WARN_ON(rfpath >= RTW_RF_PATH_MAX || - (band != PHY_BAND_2G && band != PHY_BAND_5G) || - rate_num > RTW_RF_PATH_MAX)) - return; + for (path = 0; path < hal->rf_path_num; path++) + rtw_phy_set_tx_power_level_by_path(rtwdev, channel, path); - for (i = 0; i < rate_num; i++) { - offset = pwr_by_rate[i]; - rate = rates[i]; - if (band == PHY_BAND_2G) - hal->tx_pwr_by_rate_offset_2g[rfpath][rate] = offset; - else if (band == PHY_BAND_5G) - hal->tx_pwr_by_rate_offset_5g[rfpath][rate] = offset; - else - continue; - } + chip->ops->set_tx_power_index(rtwdev); + mutex_unlock(&hal->tx_power_mutex); } -static -void phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path, - u8 rs, u8 size, u8 *rates) +static void +rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path, + u8 rs, u8 size, u8 *rates) { u8 rate; u8 base_idx, rate_idx; @@ -1585,36 +1747,35 @@ void rtw_phy_tx_power_by_rate_config(struct rtw_hal *hal) u8 path; for (path = 0; path < RTW_RF_PATH_MAX; path++) { - phy_tx_power_by_rate_config_by_path(hal, path, + rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_CCK, rtw_cck_size, rtw_cck_rates); - phy_tx_power_by_rate_config_by_path(hal, path, + rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_OFDM, rtw_ofdm_size, rtw_ofdm_rates); - phy_tx_power_by_rate_config_by_path(hal, path, + rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_HT_1S, rtw_ht_1s_size, rtw_ht_1s_rates); - phy_tx_power_by_rate_config_by_path(hal, path, + rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_HT_2S, rtw_ht_2s_size, rtw_ht_2s_rates); - phy_tx_power_by_rate_config_by_path(hal, path, + rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_VHT_1S, rtw_vht_1s_size, rtw_vht_1s_rates); - phy_tx_power_by_rate_config_by_path(hal, path, + rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_VHT_2S, rtw_vht_2s_size, rtw_vht_2s_rates); } } static void -phy_tx_power_limit_config(struct rtw_hal *hal, u8 regd, u8 bw, u8 rs) +__rtw_phy_tx_power_limit_config(struct rtw_hal *hal, u8 regd, u8 bw, u8 rs) { - s8 base, orig; + s8 base; u8 ch; for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_2G; ch++) { base = hal->tx_pwr_by_rate_base_2g[0][rs]; - orig = hal->tx_pwr_limit_2g[regd][bw][rs][ch]; hal->tx_pwr_limit_2g[regd][bw][rs][ch] -= base; } @@ -1628,98 +1789,34 @@ void rtw_phy_tx_power_limit_config(struct rtw_hal *hal) { u8 regd, bw, rs; + /* default at channel 1 */ + hal->cch_by_bw[RTW_CHANNEL_WIDTH_20] = 1; + for (regd = 0; regd < RTW_REGD_MAX; regd++) for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) - phy_tx_power_limit_config(hal, regd, bw, rs); -} - -static s8 get_tx_power_limit(struct rtw_hal *hal, u8 bw, u8 rs, u8 ch, u8 regd) -{ - if (regd > RTW_REGD_WW) - return RTW_MAX_POWER_INDEX; - - return hal->tx_pwr_limit_2g[regd][bw][rs][ch]; -} - -s8 phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, - enum rtw_bandwidth bw, u8 rf_path, - u8 rate, u8 channel, u8 regd) -{ - struct rtw_hal *hal = &rtwdev->hal; - s8 power_limit; - u8 rs; - int ch_idx; - - if (rate >= DESC_RATE1M && rate <= DESC_RATE11M) - rs = RTW_RATE_SECTION_CCK; - else if (rate >= DESC_RATE6M && rate <= DESC_RATE54M) - rs = RTW_RATE_SECTION_OFDM; - else if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS7) - rs = RTW_RATE_SECTION_HT_1S; - else if (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) - rs = RTW_RATE_SECTION_HT_2S; - else if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9) - rs = RTW_RATE_SECTION_VHT_1S; - else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9) - rs = RTW_RATE_SECTION_VHT_2S; - else - goto err; - - ch_idx = rtw_channel_to_idx(band, channel); - if (ch_idx < 0) - goto err; - - power_limit = get_tx_power_limit(hal, bw, rs, ch_idx, regd); - - return power_limit; - -err: - WARN(1, "invalid arguments, band=%d, bw=%d, path=%d, rate=%d, ch=%d\n", - band, bw, rf_path, rate, channel); - return RTW_MAX_POWER_INDEX; + __rtw_phy_tx_power_limit_config(hal, regd, bw, rs); } -void phy_set_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 band, - u8 bw, u8 rs, u8 ch, s8 pwr_limit) +static void rtw_phy_init_tx_power_limit(struct rtw_dev *rtwdev, + u8 regd, u8 bw, u8 rs) { struct rtw_hal *hal = &rtwdev->hal; - int ch_idx; - - pwr_limit = clamp_t(s8, pwr_limit, - -RTW_MAX_POWER_INDEX, RTW_MAX_POWER_INDEX); - ch_idx = rtw_channel_to_idx(band, ch); - - if (regd >= RTW_REGD_MAX || bw >= RTW_CHANNEL_WIDTH_MAX || - rs >= RTW_RATE_SECTION_MAX || ch_idx < 0) { - WARN(1, - "wrong txpwr_lmt regd=%u, band=%u bw=%u, rs=%u, ch_idx=%u, pwr_limit=%d\n", - regd, band, bw, rs, ch_idx, pwr_limit); - return; - } - - if (band == PHY_BAND_2G) - hal->tx_pwr_limit_2g[regd][bw][rs][ch_idx] = pwr_limit; - else if (band == PHY_BAND_5G) - hal->tx_pwr_limit_5g[regd][bw][rs][ch_idx] = pwr_limit; -} - -static -void rtw_hw_tx_power_limit_init(struct rtw_hal *hal, u8 regd, u8 bw, u8 rs) -{ + s8 max_power_index = (s8)rtwdev->chip->max_power_index; u8 ch; /* 2.4G channels */ for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_2G; ch++) - hal->tx_pwr_limit_2g[regd][bw][rs][ch] = RTW_MAX_POWER_INDEX; + hal->tx_pwr_limit_2g[regd][bw][rs][ch] = max_power_index; /* 5G channels */ for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_5G; ch++) - hal->tx_pwr_limit_5g[regd][bw][rs][ch] = RTW_MAX_POWER_INDEX; + hal->tx_pwr_limit_5g[regd][bw][rs][ch] = max_power_index; } -void rtw_hw_init_tx_power(struct rtw_hal *hal) +void rtw_phy_init_tx_power(struct rtw_dev *rtwdev) { + struct rtw_hal *hal = &rtwdev->hal; u8 regd, path, rate, rs, bw; /* init tx power by rate offset */ @@ -1734,5 +1831,6 @@ void rtw_hw_init_tx_power(struct rtw_hal *hal) for (regd = 0; regd < RTW_REGD_MAX; regd++) for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) - rtw_hw_tx_power_limit_init(hal, regd, bw, rs); + rtw_phy_init_tx_power_limit(rtwdev, regd, bw, + rs); } diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h index ec03a2051e52..7c8eb732b13c 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.h +++ b/drivers/net/wireless/realtek/rtw88/phy.h @@ -27,11 +27,6 @@ bool rtw_phy_write_rf_reg(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data); bool rtw_phy_write_rf_reg_mix(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data); -void phy_store_tx_power_by_rate(void *adapter, u32 band, u32 rfpath, u32 txnum, - u32 regaddr, u32 bitmask, u32 data); -void phy_set_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 band, - u8 bw, u8 rs, u8 ch, s8 pwr_limit); -void phy_set_tx_power_index_by_rs(void *adapter, u8 ch, u8 path, u8 rs); void rtw_phy_setup_phy_cond(struct rtw_dev *rtwdev, u32 pkg); void rtw_parse_tbl_phy_cond(struct rtw_dev *rtwdev, const struct rtw_table *tbl); void rtw_parse_tbl_bb_pg(struct rtw_dev *rtwdev, const struct rtw_table *tbl); @@ -44,7 +39,7 @@ void rtw_phy_cfg_bb(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data); void rtw_phy_cfg_rf(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data); -void rtw_hw_init_tx_power(struct rtw_hal *hal); +void rtw_phy_init_tx_power(struct rtw_dev *rtwdev); void rtw_phy_load_tables(struct rtw_dev *rtwdev); void rtw_phy_set_tx_power_level(struct rtw_dev *rtwdev, u8 channel); void rtw_phy_tx_power_by_rate_config(struct rtw_hal *hal); @@ -110,6 +105,17 @@ static inline int rtw_check_supported_rfe(struct rtw_dev *rtwdev) void rtw_phy_dig_write(struct rtw_dev *rtwdev, u8 igi); +struct rtw_power_params { + u8 pwr_base; + s8 pwr_offset; + s8 pwr_limit; +}; + +void +rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, + u8 rate, u8 bw, u8 ch, u8 regd, + struct rtw_power_params *pwr_param); + #define MASKBYTE0 0xff #define MASKBYTE1 0xff00 #define MASKBYTE2 0xff0000 diff --git a/drivers/net/wireless/realtek/rtw88/regd.c b/drivers/net/wireless/realtek/rtw88/regd.c index e7750a833a8e..69744dd65968 100644 --- a/drivers/net/wireless/realtek/rtw88/regd.c +++ b/drivers/net/wireless/realtek/rtw88/regd.c @@ -21,19 +21,19 @@ static const struct rtw_regulatory rtw_defined_chplan = static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("AD", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("AE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("AE", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("AF", RTW_CHPLAN_ETSI1_ETSI4, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("AG", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("AG", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("AI", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("AL", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("AM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("AN", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("AN", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("AO", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("AQ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("AR", RTW_CHPLAN_FCC2_FCC7, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("AS", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("AT", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("AU", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("AU", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ACMA), COUNTRY_CHPLAN_ENT("AW", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("AZ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("BA", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), @@ -42,31 +42,34 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("BE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("BF", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("BG", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("BH", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("BH", RTW_CHPLAN_WORLD_ETSI7, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("BI", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("BJ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("BM", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("BN", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("BO", RTW_CHPLAN_WORLD_FCC7, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("BR", RTW_CHPLAN_FCC2_FCC1, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("BS", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), - COUNTRY_CHPLAN_ENT("BW", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("BT", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("BV", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("BW", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("BY", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("BZ", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), - COUNTRY_CHPLAN_ENT("CA", RTW_CHPLAN_IC1_IC2, RTW_REGD_FCC), + COUNTRY_CHPLAN_ENT("CA", RTW_CHPLAN_IC1_IC2, RTW_REGD_IC), COUNTRY_CHPLAN_ENT("CC", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("CD", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("CF", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("CG", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("CH", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("CI", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("CI", RTW_CHPLAN_ETSI1_ETSI4, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("CK", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("CL", RTW_CHPLAN_WORLD_CHILE1, RTW_REGD_FCC), + COUNTRY_CHPLAN_ENT("CL", RTW_CHPLAN_WORLD_CHILE1, RTW_REGD_CHILE), COUNTRY_CHPLAN_ENT("CM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("CN", RTW_CHPLAN_WORLD_ETSI7, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("CO", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("CR", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("CV", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("CX", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("CX", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ACMA), COUNTRY_CHPLAN_ENT("CY", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("CZ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("DE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), @@ -90,7 +93,7 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("FR", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("GA", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("GB", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("GD", RTW_CHPLAN_FCC1_FCC7, RTW_REGD_FCC), + COUNTRY_CHPLAN_ENT("GD", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("GE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("GF", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("GG", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), @@ -107,8 +110,8 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("GU", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("GW", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("GY", RTW_CHPLAN_FCC1_NCC3, RTW_REGD_FCC), - COUNTRY_CHPLAN_ENT("HK", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("HM", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("HK", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("HM", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ACMA), COUNTRY_CHPLAN_ENT("HN", RTW_CHPLAN_WORLD_FCC5, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("HR", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("HT", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), @@ -118,20 +121,22 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("IL", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("IM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("IN", RTW_CHPLAN_WORLD_ETSI7, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("IO", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("IQ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("IR", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("IS", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("IT", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("JE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("JM", RTW_CHPLAN_WORLD_ETSI10, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("JM", RTW_CHPLAN_WORLD_FCC5, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("JO", RTW_CHPLAN_WORLD_ETSI8, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("JP", RTW_CHPLAN_MKK1_MKK1, RTW_REGD_MKK), COUNTRY_CHPLAN_ENT("KE", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("KG", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("KH", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("KI", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("KM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("KN", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), - COUNTRY_CHPLAN_ENT("KR", RTW_CHPLAN_KCC1_KCC2, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("KR", RTW_CHPLAN_KCC1_KCC3, RTW_REGD_KCC), COUNTRY_CHPLAN_ENT("KW", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("KY", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("KZ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), @@ -157,7 +162,7 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("ML", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("MM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("MN", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("MO", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("MO", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("MP", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("MQ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("MR", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), @@ -167,26 +172,26 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("MV", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("MW", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("MX", RTW_CHPLAN_FCC2_FCC7, RTW_REGD_FCC), - COUNTRY_CHPLAN_ENT("MY", RTW_CHPLAN_WORLD_ETSI20, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("MY", RTW_CHPLAN_WORLD_ETSI15, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("MZ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("NA", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("NC", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("NE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("NF", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("NF", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ACMA), COUNTRY_CHPLAN_ENT("NG", RTW_CHPLAN_WORLD_ETSI20, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("NI", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("NL", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("NO", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("NP", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("NP", RTW_CHPLAN_WORLD_ETSI7, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("NR", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("NU", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("NZ", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("NU", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ACMA), + COUNTRY_CHPLAN_ENT("NZ", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ACMA), COUNTRY_CHPLAN_ENT("OM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("PA", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("PE", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("PF", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("PG", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("PH", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("PG", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("PH", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("PK", RTW_CHPLAN_WORLD_ETSI10, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("PL", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("PM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), @@ -194,17 +199,17 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("PT", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("PW", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("PY", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), - COUNTRY_CHPLAN_ENT("QA", RTW_CHPLAN_WORLD_ETSI10, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("QA", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("RE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("RO", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("RS", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("RU", RTW_CHPLAN_WORLD_ETSI14, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("RW", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("SA", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("SA", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("SB", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("SC", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("SE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("SG", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("SG", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("SH", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("SI", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("SJ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), @@ -222,14 +227,15 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("TD", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("TF", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("TG", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("TH", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("TH", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("TJ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("TK", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("TK", RTW_CHPLAN_WORLD_ACMA1, RTW_REGD_ACMA), COUNTRY_CHPLAN_ENT("TM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("TN", RTW_CHPLAN_WORLD_ETSI6, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("TO", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("TR", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("TT", RTW_CHPLAN_ETSI1_ETSI4, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("TT", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), + COUNTRY_CHPLAN_ENT("TV", RTW_CHPLAN_ETSI1_NULL, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("TW", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("TZ", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("UA", RTW_CHPLAN_WORLD_ETSI3, RTW_REGD_ETSI), @@ -240,14 +246,15 @@ static const struct rtw_regulatory all_chplan_map[] = { COUNTRY_CHPLAN_ENT("VA", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("VC", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("VE", RTW_CHPLAN_WORLD_FCC3, RTW_REGD_FCC), + COUNTRY_CHPLAN_ENT("VG", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("VI", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), - COUNTRY_CHPLAN_ENT("VN", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("VN", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("VU", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("WF", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("WS", RTW_CHPLAN_FCC2_FCC11, RTW_REGD_FCC), COUNTRY_CHPLAN_ENT("YE", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("YT", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), - COUNTRY_CHPLAN_ENT("ZA", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), + COUNTRY_CHPLAN_ENT("ZA", RTW_CHPLAN_WORLD_ETSI2, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("ZM", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), COUNTRY_CHPLAN_ENT("ZW", RTW_CHPLAN_WORLD_ETSI1, RTW_REGD_ETSI), }; diff --git a/drivers/net/wireless/realtek/rtw88/regd.h b/drivers/net/wireless/realtek/rtw88/regd.h index 7784bb6d3ba7..5d4578331788 100644 --- a/drivers/net/wireless/realtek/rtw88/regd.h +++ b/drivers/net/wireless/realtek/rtw88/regd.h @@ -8,6 +8,7 @@ #define IEEE80211_CHAN_NO_IBSS IEEE80211_CHAN_NO_IR #define IEEE80211_CHAN_PASSIVE_SCAN IEEE80211_CHAN_NO_IR enum rtw_chplan_id { + RTW_CHPLAN_ETSI1_NULL = 0x21, RTW_CHPLAN_WORLD_ETSI1 = 0x26, RTW_CHPLAN_MKK1_MKK1 = 0x27, RTW_CHPLAN_IC1_IC2 = 0x2B, @@ -15,6 +16,7 @@ enum rtw_chplan_id { RTW_CHPLAN_WORLD_FCC3 = 0x30, RTW_CHPLAN_WORLD_FCC5 = 0x32, RTW_CHPLAN_FCC1_FCC7 = 0x34, + RTW_CHPLAN_WORLD_ETSI2 = 0x35, RTW_CHPLAN_WORLD_ETSI3 = 0x36, RTW_CHPLAN_ETSI1_ETSI12 = 0x3D, RTW_CHPLAN_KCC1_KCC2 = 0x3E, @@ -24,10 +26,12 @@ enum rtw_chplan_id { RTW_CHPLAN_WORLD_ETSI6 = 0x47, RTW_CHPLAN_WORLD_ETSI7 = 0x48, RTW_CHPLAN_WORLD_ETSI8 = 0x49, + RTW_CHPLAN_KCC1_KCC3 = 0x4B, RTW_CHPLAN_WORLD_ETSI10 = 0x51, RTW_CHPLAN_WORLD_ETSI14 = 0x59, RTW_CHPLAN_FCC2_FCC7 = 0x61, RTW_CHPLAN_FCC2_FCC1 = 0x62, + RTW_CHPLAN_WORLD_ETSI15 = 0x63, RTW_CHPLAN_WORLD_FCC7 = 0x73, RTW_CHPLAN_FCC2_FCC17 = 0x74, RTW_CHPLAN_WORLD_ETSI20 = 0x75, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c_table.c b/drivers/net/wireless/realtek/rtw88/rtw8822c_table.c index 49044f510c6c..18e609a69829 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c_table.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c_table.c @@ -9489,55 +9489,55 @@ static const u8 rtw8822c_txpwr_lmt_type0[] = { 0, 0, 1, 3, 13, 127, 2, 0, 1, 3, 13, 127, 0, 0, 1, 3, 14, 127, 2, 0, 1, 3, 14, 127, 0, 1, 0, 1, 36, 74, 2, 1, 0, 1, 36, 62, - 0, 1, 0, 1, 40, 80, 2, 1, 0, 1, 40, 62, - 0, 1, 0, 1, 44, 80, 2, 1, 0, 1, 44, 62, - 0, 1, 0, 1, 48, 80, 2, 1, 0, 1, 48, 62, - 0, 1, 0, 1, 52, 80, 2, 1, 0, 1, 52, 62, - 0, 1, 0, 1, 56, 80, 2, 1, 0, 1, 56, 62, - 0, 1, 0, 1, 60, 80, 2, 1, 0, 1, 60, 62, + 0, 1, 0, 1, 40, 76, 2, 1, 0, 1, 40, 62, + 0, 1, 0, 1, 44, 76, 2, 1, 0, 1, 44, 62, + 0, 1, 0, 1, 48, 76, 2, 1, 0, 1, 48, 62, + 0, 1, 0, 1, 52, 76, 2, 1, 0, 1, 52, 62, + 0, 1, 0, 1, 56, 76, 2, 1, 0, 1, 56, 62, + 0, 1, 0, 1, 60, 76, 2, 1, 0, 1, 60, 62, 0, 1, 0, 1, 64, 74, 2, 1, 0, 1, 64, 62, 0, 1, 0, 1, 100, 72, 2, 1, 0, 1, 100, 62, - 0, 1, 0, 1, 104, 80, 2, 1, 0, 1, 104, 62, - 0, 1, 0, 1, 108, 80, 2, 1, 0, 1, 108, 62, - 0, 1, 0, 1, 112, 80, 2, 1, 0, 1, 112, 62, - 0, 1, 0, 1, 116, 80, 2, 1, 0, 1, 116, 62, - 0, 1, 0, 1, 120, 80, 2, 1, 0, 1, 120, 62, - 0, 1, 0, 1, 124, 80, 2, 1, 0, 1, 124, 62, - 0, 1, 0, 1, 128, 80, 2, 1, 0, 1, 128, 62, - 0, 1, 0, 1, 132, 80, 2, 1, 0, 1, 132, 62, - 0, 1, 0, 1, 136, 80, 2, 1, 0, 1, 136, 62, + 0, 1, 0, 1, 104, 76, 2, 1, 0, 1, 104, 62, + 0, 1, 0, 1, 108, 76, 2, 1, 0, 1, 108, 62, + 0, 1, 0, 1, 112, 76, 2, 1, 0, 1, 112, 62, + 0, 1, 0, 1, 116, 76, 2, 1, 0, 1, 116, 62, + 0, 1, 0, 1, 120, 76, 2, 1, 0, 1, 120, 62, + 0, 1, 0, 1, 124, 76, 2, 1, 0, 1, 124, 62, + 0, 1, 0, 1, 128, 76, 2, 1, 0, 1, 128, 62, + 0, 1, 0, 1, 132, 76, 2, 1, 0, 1, 132, 62, + 0, 1, 0, 1, 136, 76, 2, 1, 0, 1, 136, 62, 0, 1, 0, 1, 140, 72, 2, 1, 0, 1, 140, 62, - 0, 1, 0, 1, 144, 80, 2, 1, 0, 1, 144, 127, - 0, 1, 0, 1, 149, 80, 2, 1, 0, 1, 149, 127, - 0, 1, 0, 1, 153, 80, 2, 1, 0, 1, 153, 127, - 0, 1, 0, 1, 157, 80, 2, 1, 0, 1, 157, 127, - 0, 1, 0, 1, 161, 80, 2, 1, 0, 1, 161, 127, - 0, 1, 0, 1, 165, 80, 2, 1, 0, 1, 165, 127, + 0, 1, 0, 1, 144, 76, 2, 1, 0, 1, 144, 127, + 0, 1, 0, 1, 149, 76, 2, 1, 0, 1, 149, -128, + 0, 1, 0, 1, 153, 76, 2, 1, 0, 1, 153, -128, + 0, 1, 0, 1, 157, 76, 2, 1, 0, 1, 157, -128, + 0, 1, 0, 1, 161, 76, 2, 1, 0, 1, 161, -128, + 0, 1, 0, 1, 165, 76, 2, 1, 0, 1, 165, -128, 0, 1, 0, 2, 36, 72, 2, 1, 0, 2, 36, 62, - 0, 1, 0, 2, 40, 80, 2, 1, 0, 2, 40, 62, - 0, 1, 0, 2, 44, 80, 2, 1, 0, 2, 44, 62, - 0, 1, 0, 2, 48, 80, 2, 1, 0, 2, 48, 62, - 0, 1, 0, 2, 52, 80, 2, 1, 0, 2, 52, 62, - 0, 1, 0, 2, 56, 80, 2, 1, 0, 2, 56, 62, - 0, 1, 0, 2, 60, 80, 2, 1, 0, 2, 60, 62, + 0, 1, 0, 2, 40, 76, 2, 1, 0, 2, 40, 62, + 0, 1, 0, 2, 44, 76, 2, 1, 0, 2, 44, 62, + 0, 1, 0, 2, 48, 76, 2, 1, 0, 2, 48, 62, + 0, 1, 0, 2, 52, 76, 2, 1, 0, 2, 52, 62, + 0, 1, 0, 2, 56, 76, 2, 1, 0, 2, 56, 62, + 0, 1, 0, 2, 60, 76, 2, 1, 0, 2, 60, 62, 0, 1, 0, 2, 64, 74, 2, 1, 0, 2, 64, 62, 0, 1, 0, 2, 100, 70, 2, 1, 0, 2, 100, 62, - 0, 1, 0, 2, 104, 80, 2, 1, 0, 2, 104, 62, - 0, 1, 0, 2, 108, 80, 2, 1, 0, 2, 108, 62, - 0, 1, 0, 2, 112, 80, 2, 1, 0, 2, 112, 62, - 0, 1, 0, 2, 116, 80, 2, 1, 0, 2, 116, 62, - 0, 1, 0, 2, 120, 80, 2, 1, 0, 2, 120, 62, - 0, 1, 0, 2, 124, 80, 2, 1, 0, 2, 124, 62, - 0, 1, 0, 2, 128, 80, 2, 1, 0, 2, 128, 62, - 0, 1, 0, 2, 132, 80, 2, 1, 0, 2, 132, 62, - 0, 1, 0, 2, 136, 80, 2, 1, 0, 2, 136, 62, + 0, 1, 0, 2, 104, 76, 2, 1, 0, 2, 104, 62, + 0, 1, 0, 2, 108, 76, 2, 1, 0, 2, 108, 62, + 0, 1, 0, 2, 112, 76, 2, 1, 0, 2, 112, 62, + 0, 1, 0, 2, 116, 76, 2, 1, 0, 2, 116, 62, + 0, 1, 0, 2, 120, 76, 2, 1, 0, 2, 120, 62, + 0, 1, 0, 2, 124, 76, 2, 1, 0, 2, 124, 62, + 0, 1, 0, 2, 128, 76, 2, 1, 0, 2, 128, 62, + 0, 1, 0, 2, 132, 76, 2, 1, 0, 2, 132, 62, + 0, 1, 0, 2, 136, 76, 2, 1, 0, 2, 136, 62, 0, 1, 0, 2, 140, 70, 2, 1, 0, 2, 140, 62, - 0, 1, 0, 2, 144, 80, 2, 1, 0, 2, 144, 127, - 0, 1, 0, 2, 149, 80, 2, 1, 0, 2, 149, 127, - 0, 1, 0, 2, 153, 80, 2, 1, 0, 2, 153, 127, - 0, 1, 0, 2, 157, 80, 2, 1, 0, 2, 157, 127, - 0, 1, 0, 2, 161, 80, 2, 1, 0, 2, 161, 127, - 0, 1, 0, 2, 165, 80, 2, 1, 0, 2, 165, 127, + 0, 1, 0, 2, 144, 76, 2, 1, 0, 2, 144, 127, + 0, 1, 0, 2, 149, 76, 2, 1, 0, 2, 149, -128, + 0, 1, 0, 2, 153, 76, 2, 1, 0, 2, 153, -128, + 0, 1, 0, 2, 157, 76, 2, 1, 0, 2, 157, -128, + 0, 1, 0, 2, 161, 76, 2, 1, 0, 2, 161, -128, + 0, 1, 0, 2, 165, 76, 2, 1, 0, 2, 165, -128, 0, 1, 0, 3, 36, 68, 2, 1, 0, 3, 36, 38, 0, 1, 0, 3, 40, 68, 2, 1, 0, 3, 40, 38, 0, 1, 0, 3, 44, 68, 2, 1, 0, 3, 44, 38, @@ -9558,23 +9558,23 @@ static const u8 rtw8822c_txpwr_lmt_type0[] = { 0, 1, 0, 3, 136, 68, 2, 1, 0, 3, 136, 38, 0, 1, 0, 3, 140, 60, 2, 1, 0, 3, 140, 38, 0, 1, 0, 3, 144, 68, 2, 1, 0, 3, 144, 127, - 0, 1, 0, 3, 149, 80, 2, 1, 0, 3, 149, 127, - 0, 1, 0, 3, 153, 80, 2, 1, 0, 3, 153, 127, - 0, 1, 0, 3, 157, 80, 2, 1, 0, 3, 157, 127, - 0, 1, 0, 3, 161, 80, 2, 1, 0, 3, 161, 127, - 0, 1, 0, 3, 165, 80, 2, 1, 0, 3, 165, 127, + 0, 1, 0, 3, 149, 76, 2, 1, 0, 3, 149, -128, + 0, 1, 0, 3, 153, 76, 2, 1, 0, 3, 153, -128, + 0, 1, 0, 3, 157, 76, 2, 1, 0, 3, 157, -128, + 0, 1, 0, 3, 161, 76, 2, 1, 0, 3, 161, -128, + 0, 1, 0, 3, 165, 76, 2, 1, 0, 3, 165, -128, 0, 1, 1, 2, 38, 66, 2, 1, 1, 2, 38, 64, 0, 1, 1, 2, 46, 72, 2, 1, 1, 2, 46, 64, 0, 1, 1, 2, 54, 72, 2, 1, 1, 2, 54, 64, 0, 1, 1, 2, 62, 64, 2, 1, 1, 2, 62, 64, 0, 1, 1, 2, 102, 58, 2, 1, 1, 2, 102, 64, - 0, 1, 1, 2, 110, 74, 2, 1, 1, 2, 110, 64, - 0, 1, 1, 2, 118, 74, 2, 1, 1, 2, 118, 64, - 0, 1, 1, 2, 126, 74, 2, 1, 1, 2, 126, 64, - 0, 1, 1, 2, 134, 74, 2, 1, 1, 2, 134, 64, - 0, 1, 1, 2, 142, 74, 2, 1, 1, 2, 142, 127, - 0, 1, 1, 2, 151, 74, 2, 1, 1, 2, 151, 127, - 0, 1, 1, 2, 159, 74, 2, 1, 1, 2, 159, 127, + 0, 1, 1, 2, 110, 72, 2, 1, 1, 2, 110, 64, + 0, 1, 1, 2, 118, 72, 2, 1, 1, 2, 118, 64, + 0, 1, 1, 2, 126, 72, 2, 1, 1, 2, 126, 64, + 0, 1, 1, 2, 134, 72, 2, 1, 1, 2, 134, 64, + 0, 1, 1, 2, 142, 72, 2, 1, 1, 2, 142, 127, + 0, 1, 1, 2, 151, 72, 2, 1, 1, 2, 151, -128, + 0, 1, 1, 2, 159, 72, 2, 1, 1, 2, 159, -128, 0, 1, 1, 3, 38, 60, 2, 1, 1, 3, 38, 40, 0, 1, 1, 3, 46, 68, 2, 1, 1, 3, 46, 40, 0, 1, 1, 3, 54, 68, 2, 1, 1, 3, 54, 40, @@ -9585,20 +9585,703 @@ static const u8 rtw8822c_txpwr_lmt_type0[] = { 0, 1, 1, 3, 126, 68, 2, 1, 1, 3, 126, 40, 0, 1, 1, 3, 134, 68, 2, 1, 1, 3, 134, 40, 0, 1, 1, 3, 142, 68, 2, 1, 1, 3, 142, 127, - 0, 1, 1, 3, 151, 74, 2, 1, 1, 3, 151, 127, - 0, 1, 1, 3, 159, 74, 2, 1, 1, 3, 159, 127, + 0, 1, 1, 3, 151, 72, 2, 1, 1, 3, 151, -128, + 0, 1, 1, 3, 159, 72, 2, 1, 1, 3, 159, -128, 0, 1, 2, 4, 42, 64, 2, 1, 2, 4, 42, 64, 0, 1, 2, 4, 58, 62, 2, 1, 2, 4, 58, 64, 0, 1, 2, 4, 106, 58, 2, 1, 2, 4, 106, 64, 0, 1, 2, 4, 122, 72, 2, 1, 2, 4, 122, 64, 0, 1, 2, 4, 138, 72, 2, 1, 2, 4, 138, 127, - 0, 1, 2, 4, 155, 72, 2, 1, 2, 4, 155, 127, + 0, 1, 2, 4, 155, 72, 2, 1, 2, 4, 155, -128, 0, 1, 2, 5, 42, 54, 2, 1, 2, 5, 42, 40, 0, 1, 2, 5, 58, 52, 2, 1, 2, 5, 58, 40, 0, 1, 2, 5, 106, 50, 2, 1, 2, 5, 106, 40, 0, 1, 2, 5, 122, 66, 2, 1, 2, 5, 122, 40, 0, 1, 2, 5, 138, 66, 2, 1, 2, 5, 138, 127, - 0, 1, 2, 5, 155, 62, 2, 1, 2, 5, 155, 127 + 0, 1, 2, 5, 155, 62, 2, 1, 2, 5, 155, -128, + 1, 0, 0, 0, 1, 68, 3, 0, 0, 0, 1, 72, + 4, 0, 0, 0, 1, 76, 5, 0, 0, 0, 1, 60, + 6, 0, 0, 0, 1, 72, 7, 0, 0, 0, 1, 60, + 8, 0, 0, 0, 1, 72, 1, 0, 0, 0, 2, 68, + 3, 0, 0, 0, 2, 72, 4, 0, 0, 0, 2, 76, + 5, 0, 0, 0, 2, 60, 6, 0, 0, 0, 2, 72, + 7, 0, 0, 0, 2, 60, 8, 0, 0, 0, 2, 72, + 1, 0, 0, 0, 3, 68, 3, 0, 0, 0, 3, 76, + 4, 0, 0, 0, 3, 76, 5, 0, 0, 0, 3, 60, + 6, 0, 0, 0, 3, 76, 7, 0, 0, 0, 3, 60, + 8, 0, 0, 0, 3, 76, 1, 0, 0, 0, 4, 68, + 3, 0, 0, 0, 4, 76, 4, 0, 0, 0, 4, 76, + 5, 0, 0, 0, 4, 60, 6, 0, 0, 0, 4, 76, + 7, 0, 0, 0, 4, 60, 8, 0, 0, 0, 4, 76, + 1, 0, 0, 0, 5, 68, 3, 0, 0, 0, 5, 76, + 4, 0, 0, 0, 5, 76, 5, 0, 0, 0, 5, 60, + 6, 0, 0, 0, 5, 76, 7, 0, 0, 0, 5, 60, + 8, 0, 0, 0, 5, 76, 1, 0, 0, 0, 6, 68, + 3, 0, 0, 0, 6, 76, 4, 0, 0, 0, 6, 76, + 5, 0, 0, 0, 6, 60, 6, 0, 0, 0, 6, 76, + 7, 0, 0, 0, 6, 60, 8, 0, 0, 0, 6, 76, + 1, 0, 0, 0, 7, 68, 3, 0, 0, 0, 7, 76, + 4, 0, 0, 0, 7, 76, 5, 0, 0, 0, 7, 60, + 6, 0, 0, 0, 7, 76, 7, 0, 0, 0, 7, 60, + 8, 0, 0, 0, 7, 76, 1, 0, 0, 0, 8, 68, + 3, 0, 0, 0, 8, 76, 4, 0, 0, 0, 8, 76, + 5, 0, 0, 0, 8, 60, 6, 0, 0, 0, 8, 76, + 7, 0, 0, 0, 8, 60, 8, 0, 0, 0, 8, 76, + 1, 0, 0, 0, 9, 68, 3, 0, 0, 0, 9, 76, + 4, 0, 0, 0, 9, 76, 5, 0, 0, 0, 9, 60, + 6, 0, 0, 0, 9, 76, 7, 0, 0, 0, 9, 60, + 8, 0, 0, 0, 9, 76, 1, 0, 0, 0, 10, 68, + 3, 0, 0, 0, 10, 72, 4, 0, 0, 0, 10, 76, + 5, 0, 0, 0, 10, 60, 6, 0, 0, 0, 10, 72, + 7, 0, 0, 0, 10, 60, 8, 0, 0, 0, 10, 72, + 1, 0, 0, 0, 11, 68, 3, 0, 0, 0, 11, 72, + 4, 0, 0, 0, 11, 76, 5, 0, 0, 0, 11, 60, + 6, 0, 0, 0, 11, 72, 7, 0, 0, 0, 11, 60, + 8, 0, 0, 0, 11, 72, 1, 0, 0, 0, 12, 68, + 3, 0, 0, 0, 12, 52, 4, 0, 0, 0, 12, 76, + 5, 0, 0, 0, 12, 60, 6, 0, 0, 0, 12, 52, + 7, 0, 0, 0, 12, 60, 8, 0, 0, 0, 12, 52, + 1, 0, 0, 0, 13, 68, 3, 0, 0, 0, 13, 48, + 4, 0, 0, 0, 13, 76, 5, 0, 0, 0, 13, 60, + 6, 0, 0, 0, 13, 48, 7, 0, 0, 0, 13, 60, + 8, 0, 0, 0, 13, 48, 1, 0, 0, 0, 14, 68, + 3, 0, 0, 0, 14, 127, 4, 0, 0, 0, 14, 127, + 5, 0, 0, 0, 14, 127, 6, 0, 0, 0, 14, 127, + 7, 0, 0, 0, 14, 127, 8, 0, 0, 0, 14, 127, + 1, 0, 0, 1, 1, 76, 3, 0, 0, 1, 1, 52, + 4, 0, 0, 1, 1, 76, 5, 0, 0, 1, 1, 60, + 6, 0, 0, 1, 1, 52, 7, 0, 0, 1, 1, 60, + 8, 0, 0, 1, 1, 52, 1, 0, 0, 1, 2, 76, + 3, 0, 0, 1, 2, 60, 4, 0, 0, 1, 2, 76, + 5, 0, 0, 1, 2, 60, 6, 0, 0, 1, 2, 60, + 7, 0, 0, 1, 2, 60, 8, 0, 0, 1, 2, 60, + 1, 0, 0, 1, 3, 76, 3, 0, 0, 1, 3, 64, + 4, 0, 0, 1, 3, 76, 5, 0, 0, 1, 3, 60, + 6, 0, 0, 1, 3, 64, 7, 0, 0, 1, 3, 60, + 8, 0, 0, 1, 3, 64, 1, 0, 0, 1, 4, 76, + 3, 0, 0, 1, 4, 68, 4, 0, 0, 1, 4, 76, + 5, 0, 0, 1, 4, 60, 6, 0, 0, 1, 4, 68, + 7, 0, 0, 1, 4, 60, 8, 0, 0, 1, 4, 68, + 1, 0, 0, 1, 5, 76, 3, 0, 0, 1, 5, 76, + 4, 0, 0, 1, 5, 76, 5, 0, 0, 1, 5, 60, + 6, 0, 0, 1, 5, 76, 7, 0, 0, 1, 5, 60, + 8, 0, 0, 1, 5, 76, 1, 0, 0, 1, 6, 76, + 3, 0, 0, 1, 6, 76, 4, 0, 0, 1, 6, 76, + 5, 0, 0, 1, 6, 60, 6, 0, 0, 1, 6, 76, + 7, 0, 0, 1, 6, 60, 8, 0, 0, 1, 6, 76, + 1, 0, 0, 1, 7, 76, 3, 0, 0, 1, 7, 76, + 4, 0, 0, 1, 7, 76, 5, 0, 0, 1, 7, 60, + 6, 0, 0, 1, 7, 76, 7, 0, 0, 1, 7, 60, + 8, 0, 0, 1, 7, 76, 1, 0, 0, 1, 8, 76, + 3, 0, 0, 1, 8, 68, 4, 0, 0, 1, 8, 76, + 5, 0, 0, 1, 8, 60, 6, 0, 0, 1, 8, 68, + 7, 0, 0, 1, 8, 60, 8, 0, 0, 1, 8, 68, + 1, 0, 0, 1, 9, 76, 3, 0, 0, 1, 9, 64, + 4, 0, 0, 1, 9, 76, 5, 0, 0, 1, 9, 60, + 6, 0, 0, 1, 9, 64, 7, 0, 0, 1, 9, 60, + 8, 0, 0, 1, 9, 64, 1, 0, 0, 1, 10, 76, + 3, 0, 0, 1, 10, 60, 4, 0, 0, 1, 10, 76, + 5, 0, 0, 1, 10, 60, 6, 0, 0, 1, 10, 60, + 7, 0, 0, 1, 10, 60, 8, 0, 0, 1, 10, 60, + 1, 0, 0, 1, 11, 76, 3, 0, 0, 1, 11, 52, + 4, 0, 0, 1, 11, 76, 5, 0, 0, 1, 11, 60, + 6, 0, 0, 1, 11, 52, 7, 0, 0, 1, 11, 60, + 8, 0, 0, 1, 11, 52, 1, 0, 0, 1, 12, 76, + 3, 0, 0, 1, 12, 40, 4, 0, 0, 1, 12, 76, + 5, 0, 0, 1, 12, 60, 6, 0, 0, 1, 12, 40, + 7, 0, 0, 1, 12, 60, 8, 0, 0, 1, 12, 40, + 1, 0, 0, 1, 13, 76, 3, 0, 0, 1, 13, 28, + 4, 0, 0, 1, 13, 70, 5, 0, 0, 1, 13, 60, + 6, 0, 0, 1, 13, 28, 7, 0, 0, 1, 13, 60, + 8, 0, 0, 1, 13, 28, 1, 0, 0, 1, 14, 127, + 3, 0, 0, 1, 14, 127, 4, 0, 0, 1, 14, 127, + 5, 0, 0, 1, 14, 127, 6, 0, 0, 1, 14, 127, + 7, 0, 0, 1, 14, 127, 8, 0, 0, 1, 14, 127, + 1, 0, 0, 2, 1, 76, 3, 0, 0, 2, 1, 52, + 4, 0, 0, 2, 1, 76, 5, 0, 0, 2, 1, 60, + 6, 0, 0, 2, 1, 52, 7, 0, 0, 2, 1, 60, + 8, 0, 0, 2, 1, 52, 1, 0, 0, 2, 2, 76, + 3, 0, 0, 2, 2, 60, 4, 0, 0, 2, 2, 76, + 5, 0, 0, 2, 2, 60, 6, 0, 0, 2, 2, 60, + 7, 0, 0, 2, 2, 60, 8, 0, 0, 2, 2, 60, + 1, 0, 0, 2, 3, 76, 3, 0, 0, 2, 3, 64, + 4, 0, 0, 2, 3, 76, 5, 0, 0, 2, 3, 60, + 6, 0, 0, 2, 3, 64, 7, 0, 0, 2, 3, 60, + 8, 0, 0, 2, 3, 64, 1, 0, 0, 2, 4, 76, + 3, 0, 0, 2, 4, 68, 4, 0, 0, 2, 4, 76, + 5, 0, 0, 2, 4, 60, 6, 0, 0, 2, 4, 68, + 7, 0, 0, 2, 4, 60, 8, 0, 0, 2, 4, 68, + 1, 0, 0, 2, 5, 76, 3, 0, 0, 2, 5, 76, + 4, 0, 0, 2, 5, 76, 5, 0, 0, 2, 5, 60, + 6, 0, 0, 2, 5, 76, 7, 0, 0, 2, 5, 60, + 8, 0, 0, 2, 5, 76, 1, 0, 0, 2, 6, 76, + 3, 0, 0, 2, 6, 76, 4, 0, 0, 2, 6, 76, + 5, 0, 0, 2, 6, 60, 6, 0, 0, 2, 6, 76, + 7, 0, 0, 2, 6, 60, 8, 0, 0, 2, 6, 76, + 1, 0, 0, 2, 7, 76, 3, 0, 0, 2, 7, 76, + 4, 0, 0, 2, 7, 76, 5, 0, 0, 2, 7, 60, + 6, 0, 0, 2, 7, 76, 7, 0, 0, 2, 7, 60, + 8, 0, 0, 2, 7, 76, 1, 0, 0, 2, 8, 76, + 3, 0, 0, 2, 8, 68, 4, 0, 0, 2, 8, 76, + 5, 0, 0, 2, 8, 60, 6, 0, 0, 2, 8, 68, + 7, 0, 0, 2, 8, 60, 8, 0, 0, 2, 8, 68, + 1, 0, 0, 2, 9, 76, 3, 0, 0, 2, 9, 64, + 4, 0, 0, 2, 9, 76, 5, 0, 0, 2, 9, 60, + 6, 0, 0, 2, 9, 64, 7, 0, 0, 2, 9, 60, + 8, 0, 0, 2, 9, 64, 1, 0, 0, 2, 10, 76, + 3, 0, 0, 2, 10, 60, 4, 0, 0, 2, 10, 76, + 5, 0, 0, 2, 10, 60, 6, 0, 0, 2, 10, 60, + 7, 0, 0, 2, 10, 60, 8, 0, 0, 2, 10, 60, + 1, 0, 0, 2, 11, 76, 3, 0, 0, 2, 11, 52, + 4, 0, 0, 2, 11, 76, 5, 0, 0, 2, 11, 60, + 6, 0, 0, 2, 11, 52, 7, 0, 0, 2, 11, 60, + 8, 0, 0, 2, 11, 52, 1, 0, 0, 2, 12, 76, + 3, 0, 0, 2, 12, 40, 4, 0, 0, 2, 12, 76, + 5, 0, 0, 2, 12, 60, 6, 0, 0, 2, 12, 40, + 7, 0, 0, 2, 12, 60, 8, 0, 0, 2, 12, 40, + 1, 0, 0, 2, 13, 76, 3, 0, 0, 2, 13, 28, + 4, 0, 0, 2, 13, 72, 5, 0, 0, 2, 13, 60, + 6, 0, 0, 2, 13, 28, 7, 0, 0, 2, 13, 60, + 8, 0, 0, 2, 13, 28, 1, 0, 0, 2, 14, 127, + 3, 0, 0, 2, 14, 127, 4, 0, 0, 2, 14, 127, + 5, 0, 0, 2, 14, 127, 6, 0, 0, 2, 14, 127, + 7, 0, 0, 2, 14, 127, 8, 0, 0, 2, 14, 127, + 1, 0, 0, 3, 1, 66, 3, 0, 0, 3, 1, 52, + 4, 0, 0, 3, 1, 68, 5, 0, 0, 3, 1, 36, + 6, 0, 0, 3, 1, 52, 7, 0, 0, 3, 1, 36, + 8, 0, 0, 3, 1, 52, 1, 0, 0, 3, 2, 66, + 3, 0, 0, 3, 2, 60, 4, 0, 0, 3, 2, 70, + 5, 0, 0, 3, 2, 36, 6, 0, 0, 3, 2, 60, + 7, 0, 0, 3, 2, 36, 8, 0, 0, 3, 2, 60, + 1, 0, 0, 3, 3, 66, 3, 0, 0, 3, 3, 64, + 4, 0, 0, 3, 3, 70, 5, 0, 0, 3, 3, 36, + 6, 0, 0, 3, 3, 64, 7, 0, 0, 3, 3, 36, + 8, 0, 0, 3, 3, 64, 1, 0, 0, 3, 4, 66, + 3, 0, 0, 3, 4, 68, 4, 0, 0, 3, 4, 70, + 5, 0, 0, 3, 4, 36, 6, 0, 0, 3, 4, 68, + 7, 0, 0, 3, 4, 36, 8, 0, 0, 3, 4, 68, + 1, 0, 0, 3, 5, 66, 3, 0, 0, 3, 5, 76, + 4, 0, 0, 3, 5, 70, 5, 0, 0, 3, 5, 36, + 6, 0, 0, 3, 5, 76, 7, 0, 0, 3, 5, 36, + 8, 0, 0, 3, 5, 76, 1, 0, 0, 3, 6, 66, + 3, 0, 0, 3, 6, 76, 4, 0, 0, 3, 6, 70, + 5, 0, 0, 3, 6, 36, 6, 0, 0, 3, 6, 76, + 7, 0, 0, 3, 6, 36, 8, 0, 0, 3, 6, 76, + 1, 0, 0, 3, 7, 66, 3, 0, 0, 3, 7, 76, + 4, 0, 0, 3, 7, 70, 5, 0, 0, 3, 7, 36, + 6, 0, 0, 3, 7, 76, 7, 0, 0, 3, 7, 36, + 8, 0, 0, 3, 7, 76, 1, 0, 0, 3, 8, 66, + 3, 0, 0, 3, 8, 68, 4, 0, 0, 3, 8, 70, + 5, 0, 0, 3, 8, 36, 6, 0, 0, 3, 8, 68, + 7, 0, 0, 3, 8, 36, 8, 0, 0, 3, 8, 68, + 1, 0, 0, 3, 9, 66, 3, 0, 0, 3, 9, 64, + 4, 0, 0, 3, 9, 70, 5, 0, 0, 3, 9, 36, + 6, 0, 0, 3, 9, 64, 7, 0, 0, 3, 9, 36, + 8, 0, 0, 3, 9, 64, 1, 0, 0, 3, 10, 66, + 3, 0, 0, 3, 10, 60, 4, 0, 0, 3, 10, 70, + 5, 0, 0, 3, 10, 36, 6, 0, 0, 3, 10, 60, + 7, 0, 0, 3, 10, 36, 8, 0, 0, 3, 10, 60, + 1, 0, 0, 3, 11, 66, 3, 0, 0, 3, 11, 52, + 4, 0, 0, 3, 11, 70, 5, 0, 0, 3, 11, 36, + 6, 0, 0, 3, 11, 52, 7, 0, 0, 3, 11, 36, + 8, 0, 0, 3, 11, 52, 1, 0, 0, 3, 12, 66, + 3, 0, 0, 3, 12, 40, 4, 0, 0, 3, 12, 70, + 5, 0, 0, 3, 12, 36, 6, 0, 0, 3, 12, 40, + 7, 0, 0, 3, 12, 36, 8, 0, 0, 3, 12, 40, + 1, 0, 0, 3, 13, 66, 3, 0, 0, 3, 13, 28, + 4, 0, 0, 3, 13, 62, 5, 0, 0, 3, 13, 36, + 6, 0, 0, 3, 13, 28, 7, 0, 0, 3, 13, 36, + 8, 0, 0, 3, 13, 28, 1, 0, 0, 3, 14, 127, + 3, 0, 0, 3, 14, 127, 4, 0, 0, 3, 14, 127, + 5, 0, 0, 3, 14, 127, 6, 0, 0, 3, 14, 127, + 7, 0, 0, 3, 14, 127, 8, 0, 0, 3, 14, 127, + 1, 0, 1, 2, 1, 127, 3, 0, 1, 2, 1, 127, + 4, 0, 1, 2, 1, 127, 5, 0, 1, 2, 1, 127, + 6, 0, 1, 2, 1, 127, 7, 0, 1, 2, 1, 127, + 8, 0, 1, 2, 1, 127, 1, 0, 1, 2, 2, 127, + 3, 0, 1, 2, 2, 127, 4, 0, 1, 2, 2, 127, + 5, 0, 1, 2, 2, 127, 6, 0, 1, 2, 2, 127, + 7, 0, 1, 2, 2, 127, 8, 0, 1, 2, 2, 127, + 1, 0, 1, 2, 3, 72, 3, 0, 1, 2, 3, 52, + 4, 0, 1, 2, 3, 72, 5, 0, 1, 2, 3, 60, + 6, 0, 1, 2, 3, 52, 7, 0, 1, 2, 3, 60, + 8, 0, 1, 2, 3, 52, 1, 0, 1, 2, 4, 72, + 3, 0, 1, 2, 4, 52, 4, 0, 1, 2, 4, 72, + 5, 0, 1, 2, 4, 60, 6, 0, 1, 2, 4, 52, + 7, 0, 1, 2, 4, 60, 8, 0, 1, 2, 4, 52, + 1, 0, 1, 2, 5, 72, 3, 0, 1, 2, 5, 60, + 4, 0, 1, 2, 5, 72, 5, 0, 1, 2, 5, 60, + 6, 0, 1, 2, 5, 60, 7, 0, 1, 2, 5, 60, + 8, 0, 1, 2, 5, 60, 1, 0, 1, 2, 6, 72, + 3, 0, 1, 2, 6, 64, 4, 0, 1, 2, 6, 72, + 5, 0, 1, 2, 6, 60, 6, 0, 1, 2, 6, 64, + 7, 0, 1, 2, 6, 60, 8, 0, 1, 2, 6, 64, + 1, 0, 1, 2, 7, 72, 3, 0, 1, 2, 7, 60, + 4, 0, 1, 2, 7, 72, 5, 0, 1, 2, 7, 60, + 6, 0, 1, 2, 7, 60, 7, 0, 1, 2, 7, 60, + 8, 0, 1, 2, 7, 60, 1, 0, 1, 2, 8, 72, + 3, 0, 1, 2, 8, 52, 4, 0, 1, 2, 8, 72, + 5, 0, 1, 2, 8, 60, 6, 0, 1, 2, 8, 52, + 7, 0, 1, 2, 8, 60, 8, 0, 1, 2, 8, 52, + 1, 0, 1, 2, 9, 72, 3, 0, 1, 2, 9, 52, + 4, 0, 1, 2, 9, 72, 5, 0, 1, 2, 9, 60, + 6, 0, 1, 2, 9, 52, 7, 0, 1, 2, 9, 60, + 8, 0, 1, 2, 9, 52, 1, 0, 1, 2, 10, 72, + 3, 0, 1, 2, 10, 40, 4, 0, 1, 2, 10, 72, + 5, 0, 1, 2, 10, 60, 6, 0, 1, 2, 10, 40, + 7, 0, 1, 2, 10, 60, 8, 0, 1, 2, 10, 40, + 1, 0, 1, 2, 11, 72, 3, 0, 1, 2, 11, 28, + 4, 0, 1, 2, 11, 70, 5, 0, 1, 2, 11, 60, + 6, 0, 1, 2, 11, 28, 7, 0, 1, 2, 11, 60, + 8, 0, 1, 2, 11, 28, 1, 0, 1, 2, 12, 127, + 3, 0, 1, 2, 12, 127, 4, 0, 1, 2, 12, 127, + 5, 0, 1, 2, 12, 127, 6, 0, 1, 2, 12, 127, + 7, 0, 1, 2, 12, 127, 8, 0, 1, 2, 12, 127, + 1, 0, 1, 2, 13, 127, 3, 0, 1, 2, 13, 127, + 4, 0, 1, 2, 13, 127, 5, 0, 1, 2, 13, 127, + 6, 0, 1, 2, 13, 127, 7, 0, 1, 2, 13, 127, + 8, 0, 1, 2, 13, 127, 1, 0, 1, 2, 14, 127, + 3, 0, 1, 2, 14, 127, 4, 0, 1, 2, 14, 127, + 5, 0, 1, 2, 14, 127, 6, 0, 1, 2, 14, 127, + 7, 0, 1, 2, 14, 127, 8, 0, 1, 2, 14, 127, + 1, 0, 1, 3, 1, 127, 3, 0, 1, 3, 1, 127, + 4, 0, 1, 3, 1, 127, 5, 0, 1, 3, 1, 127, + 6, 0, 1, 3, 1, 127, 7, 0, 1, 3, 1, 127, + 8, 0, 1, 3, 1, 127, 1, 0, 1, 3, 2, 127, + 3, 0, 1, 3, 2, 127, 4, 0, 1, 3, 2, 127, + 5, 0, 1, 3, 2, 127, 6, 0, 1, 3, 2, 127, + 7, 0, 1, 3, 2, 127, 8, 0, 1, 3, 2, 127, + 1, 0, 1, 3, 3, 66, 3, 0, 1, 3, 3, 48, + 4, 0, 1, 3, 3, 66, 5, 0, 1, 3, 3, 36, + 6, 0, 1, 3, 3, 48, 7, 0, 1, 3, 3, 36, + 8, 0, 1, 3, 3, 48, 1, 0, 1, 3, 4, 66, + 3, 0, 1, 3, 4, 48, 4, 0, 1, 3, 4, 70, + 5, 0, 1, 3, 4, 36, 6, 0, 1, 3, 4, 48, + 7, 0, 1, 3, 4, 36, 8, 0, 1, 3, 4, 48, + 1, 0, 1, 3, 5, 66, 3, 0, 1, 3, 5, 60, + 4, 0, 1, 3, 5, 70, 5, 0, 1, 3, 5, 36, + 6, 0, 1, 3, 5, 60, 7, 0, 1, 3, 5, 36, + 8, 0, 1, 3, 5, 60, 1, 0, 1, 3, 6, 66, + 3, 0, 1, 3, 6, 64, 4, 0, 1, 3, 6, 70, + 5, 0, 1, 3, 6, 36, 6, 0, 1, 3, 6, 64, + 7, 0, 1, 3, 6, 36, 8, 0, 1, 3, 6, 64, + 1, 0, 1, 3, 7, 66, 3, 0, 1, 3, 7, 60, + 4, 0, 1, 3, 7, 70, 5, 0, 1, 3, 7, 36, + 6, 0, 1, 3, 7, 60, 7, 0, 1, 3, 7, 36, + 8, 0, 1, 3, 7, 60, 1, 0, 1, 3, 8, 66, + 3, 0, 1, 3, 8, 52, 4, 0, 1, 3, 8, 70, + 5, 0, 1, 3, 8, 36, 6, 0, 1, 3, 8, 52, + 7, 0, 1, 3, 8, 36, 8, 0, 1, 3, 8, 52, + 1, 0, 1, 3, 9, 66, 3, 0, 1, 3, 9, 52, + 4, 0, 1, 3, 9, 70, 5, 0, 1, 3, 9, 36, + 6, 0, 1, 3, 9, 52, 7, 0, 1, 3, 9, 36, + 8, 0, 1, 3, 9, 52, 1, 0, 1, 3, 10, 66, + 3, 0, 1, 3, 10, 40, 4, 0, 1, 3, 10, 70, + 5, 0, 1, 3, 10, 36, 6, 0, 1, 3, 10, 40, + 7, 0, 1, 3, 10, 36, 8, 0, 1, 3, 10, 40, + 1, 0, 1, 3, 11, 66, 3, 0, 1, 3, 11, 26, + 4, 0, 1, 3, 11, 66, 5, 0, 1, 3, 11, 36, + 6, 0, 1, 3, 11, 26, 7, 0, 1, 3, 11, 36, + 8, 0, 1, 3, 11, 26, 1, 0, 1, 3, 12, 127, + 3, 0, 1, 3, 12, 127, 4, 0, 1, 3, 12, 127, + 5, 0, 1, 3, 12, 127, 6, 0, 1, 3, 12, 127, + 7, 0, 1, 3, 12, 127, 8, 0, 1, 3, 12, 127, + 1, 0, 1, 3, 13, 127, 3, 0, 1, 3, 13, 127, + 4, 0, 1, 3, 13, 127, 5, 0, 1, 3, 13, 127, + 6, 0, 1, 3, 13, 127, 7, 0, 1, 3, 13, 127, + 8, 0, 1, 3, 13, 127, 1, 0, 1, 3, 14, 127, + 3, 0, 1, 3, 14, 127, 4, 0, 1, 3, 14, 127, + 5, 0, 1, 3, 14, 127, 6, 0, 1, 3, 14, 127, + 7, 0, 1, 3, 14, 127, 8, 0, 1, 3, 14, 127, + 1, 1, 0, 1, 36, 60, 3, 1, 0, 1, 36, 62, + 4, 1, 0, 1, 36, 76, 5, 1, 0, 1, 36, 62, + 6, 1, 0, 1, 36, 64, 7, 1, 0, 1, 36, 54, + 8, 1, 0, 1, 36, 62, 1, 1, 0, 1, 40, 62, + 3, 1, 0, 1, 40, 62, 4, 1, 0, 1, 40, 76, + 5, 1, 0, 1, 40, 62, 6, 1, 0, 1, 40, 64, + 7, 1, 0, 1, 40, 54, 8, 1, 0, 1, 40, 62, + 1, 1, 0, 1, 44, 62, 3, 1, 0, 1, 44, 62, + 4, 1, 0, 1, 44, 76, 5, 1, 0, 1, 44, 62, + 6, 1, 0, 1, 44, 64, 7, 1, 0, 1, 44, 54, + 8, 1, 0, 1, 44, 62, 1, 1, 0, 1, 48, 62, + 3, 1, 0, 1, 48, 62, 4, 1, 0, 1, 48, 76, + 5, 1, 0, 1, 48, 62, 6, 1, 0, 1, 48, 64, + 7, 1, 0, 1, 48, 54, 8, 1, 0, 1, 48, 62, + 1, 1, 0, 1, 52, 62, 3, 1, 0, 1, 52, 64, + 4, 1, 0, 1, 52, 76, 5, 1, 0, 1, 52, 62, + 6, 1, 0, 1, 52, 76, 7, 1, 0, 1, 52, 54, + 8, 1, 0, 1, 52, 76, 1, 1, 0, 1, 56, 62, + 3, 1, 0, 1, 56, 64, 4, 1, 0, 1, 56, 76, + 5, 1, 0, 1, 56, 62, 6, 1, 0, 1, 56, 76, + 7, 1, 0, 1, 56, 54, 8, 1, 0, 1, 56, 76, + 1, 1, 0, 1, 60, 62, 3, 1, 0, 1, 60, 64, + 4, 1, 0, 1, 60, 76, 5, 1, 0, 1, 60, 62, + 6, 1, 0, 1, 60, 76, 7, 1, 0, 1, 60, 54, + 8, 1, 0, 1, 60, 76, 1, 1, 0, 1, 64, 60, + 3, 1, 0, 1, 64, 64, 4, 1, 0, 1, 64, 76, + 5, 1, 0, 1, 64, 62, 6, 1, 0, 1, 64, 74, + 7, 1, 0, 1, 64, 54, 8, 1, 0, 1, 64, 74, + 1, 1, 0, 1, 100, 76, 3, 1, 0, 1, 100, 72, + 4, 1, 0, 1, 100, 76, 5, 1, 0, 1, 100, 62, + 6, 1, 0, 1, 100, 72, 7, 1, 0, 1, 100, 54, + 8, 1, 0, 1, 100, 72, 1, 1, 0, 1, 104, 76, + 3, 1, 0, 1, 104, 76, 4, 1, 0, 1, 104, 76, + 5, 1, 0, 1, 104, 62, 6, 1, 0, 1, 104, 76, + 7, 1, 0, 1, 104, 54, 8, 1, 0, 1, 104, 76, + 1, 1, 0, 1, 108, 76, 3, 1, 0, 1, 108, 76, + 4, 1, 0, 1, 108, 76, 5, 1, 0, 1, 108, 62, + 6, 1, 0, 1, 108, 76, 7, 1, 0, 1, 108, 54, + 8, 1, 0, 1, 108, 76, 1, 1, 0, 1, 112, 76, + 3, 1, 0, 1, 112, 76, 4, 1, 0, 1, 112, 76, + 5, 1, 0, 1, 112, 62, 6, 1, 0, 1, 112, 76, + 7, 1, 0, 1, 112, 54, 8, 1, 0, 1, 112, 76, + 1, 1, 0, 1, 116, 76, 3, 1, 0, 1, 116, 76, + 4, 1, 0, 1, 116, 76, 5, 1, 0, 1, 116, 62, + 6, 1, 0, 1, 116, 76, 7, 1, 0, 1, 116, 54, + 8, 1, 0, 1, 116, 76, 1, 1, 0, 1, 120, 76, + 3, 1, 0, 1, 120, 127, 4, 1, 0, 1, 120, 76, + 5, 1, 0, 1, 120, 127, 6, 1, 0, 1, 120, 76, + 7, 1, 0, 1, 120, 54, 8, 1, 0, 1, 120, 76, + 1, 1, 0, 1, 124, 76, 3, 1, 0, 1, 124, 127, + 4, 1, 0, 1, 124, 76, 5, 1, 0, 1, 124, 127, + 6, 1, 0, 1, 124, 76, 7, 1, 0, 1, 124, 54, + 8, 1, 0, 1, 124, 76, 1, 1, 0, 1, 128, 76, + 3, 1, 0, 1, 128, 127, 4, 1, 0, 1, 128, 76, + 5, 1, 0, 1, 128, 127, 6, 1, 0, 1, 128, 76, + 7, 1, 0, 1, 128, 54, 8, 1, 0, 1, 128, 76, + 1, 1, 0, 1, 132, 76, 3, 1, 0, 1, 132, 76, + 4, 1, 0, 1, 132, 76, 5, 1, 0, 1, 132, 62, + 6, 1, 0, 1, 132, 76, 7, 1, 0, 1, 132, 54, + 8, 1, 0, 1, 132, 76, 1, 1, 0, 1, 136, 76, + 3, 1, 0, 1, 136, 76, 4, 1, 0, 1, 136, 76, + 5, 1, 0, 1, 136, 62, 6, 1, 0, 1, 136, 76, + 7, 1, 0, 1, 136, 127, 8, 1, 0, 1, 136, 76, + 1, 1, 0, 1, 140, 76, 3, 1, 0, 1, 140, 72, + 4, 1, 0, 1, 140, 76, 5, 1, 0, 1, 140, 62, + 6, 1, 0, 1, 140, 72, 7, 1, 0, 1, 140, 127, + 8, 1, 0, 1, 140, 72, 1, 1, 0, 1, 144, 127, + 3, 1, 0, 1, 144, 76, 4, 1, 0, 1, 144, 76, + 5, 1, 0, 1, 144, 127, 6, 1, 0, 1, 144, 76, + 7, 1, 0, 1, 144, 127, 8, 1, 0, 1, 144, 76, + 1, 1, 0, 1, 149, 127, 3, 1, 0, 1, 149, 76, + 4, 1, 0, 1, 149, 74, 5, 1, 0, 1, 149, 76, + 6, 1, 0, 1, 149, 76, 7, 1, 0, 1, 149, 54, + 8, 1, 0, 1, 149, 76, 1, 1, 0, 1, 153, 127, + 3, 1, 0, 1, 153, 76, 4, 1, 0, 1, 153, 74, + 5, 1, 0, 1, 153, 76, 6, 1, 0, 1, 153, 76, + 7, 1, 0, 1, 153, 54, 8, 1, 0, 1, 153, 76, + 1, 1, 0, 1, 157, 127, 3, 1, 0, 1, 157, 76, + 4, 1, 0, 1, 157, 74, 5, 1, 0, 1, 157, 76, + 6, 1, 0, 1, 157, 76, 7, 1, 0, 1, 157, 54, + 8, 1, 0, 1, 157, 76, 1, 1, 0, 1, 161, 127, + 3, 1, 0, 1, 161, 76, 4, 1, 0, 1, 161, 74, + 5, 1, 0, 1, 161, 76, 6, 1, 0, 1, 161, 76, + 7, 1, 0, 1, 161, 54, 8, 1, 0, 1, 161, 76, + 1, 1, 0, 1, 165, 127, 3, 1, 0, 1, 165, 76, + 4, 1, 0, 1, 165, 74, 5, 1, 0, 1, 165, 76, + 6, 1, 0, 1, 165, 76, 7, 1, 0, 1, 165, 54, + 8, 1, 0, 1, 165, 76, 1, 1, 0, 2, 36, 62, + 3, 1, 0, 2, 36, 62, 4, 1, 0, 2, 36, 76, + 5, 1, 0, 2, 36, 62, 6, 1, 0, 2, 36, 64, + 7, 1, 0, 2, 36, 54, 8, 1, 0, 2, 36, 62, + 1, 1, 0, 2, 40, 62, 3, 1, 0, 2, 40, 62, + 4, 1, 0, 2, 40, 76, 5, 1, 0, 2, 40, 62, + 6, 1, 0, 2, 40, 64, 7, 1, 0, 2, 40, 54, + 8, 1, 0, 2, 40, 62, 1, 1, 0, 2, 44, 62, + 3, 1, 0, 2, 44, 62, 4, 1, 0, 2, 44, 76, + 5, 1, 0, 2, 44, 62, 6, 1, 0, 2, 44, 64, + 7, 1, 0, 2, 44, 54, 8, 1, 0, 2, 44, 62, + 1, 1, 0, 2, 48, 62, 3, 1, 0, 2, 48, 62, + 4, 1, 0, 2, 48, 76, 5, 1, 0, 2, 48, 62, + 6, 1, 0, 2, 48, 64, 7, 1, 0, 2, 48, 54, + 8, 1, 0, 2, 48, 62, 1, 1, 0, 2, 52, 62, + 3, 1, 0, 2, 52, 64, 4, 1, 0, 2, 52, 76, + 5, 1, 0, 2, 52, 62, 6, 1, 0, 2, 52, 76, + 7, 1, 0, 2, 52, 54, 8, 1, 0, 2, 52, 76, + 1, 1, 0, 2, 56, 62, 3, 1, 0, 2, 56, 64, + 4, 1, 0, 2, 56, 76, 5, 1, 0, 2, 56, 62, + 6, 1, 0, 2, 56, 76, 7, 1, 0, 2, 56, 54, + 8, 1, 0, 2, 56, 76, 1, 1, 0, 2, 60, 62, + 3, 1, 0, 2, 60, 64, 4, 1, 0, 2, 60, 76, + 5, 1, 0, 2, 60, 62, 6, 1, 0, 2, 60, 76, + 7, 1, 0, 2, 60, 54, 8, 1, 0, 2, 60, 76, + 1, 1, 0, 2, 64, 60, 3, 1, 0, 2, 64, 64, + 4, 1, 0, 2, 64, 74, 5, 1, 0, 2, 64, 62, + 6, 1, 0, 2, 64, 74, 7, 1, 0, 2, 64, 54, + 8, 1, 0, 2, 64, 74, 1, 1, 0, 2, 100, 76, + 3, 1, 0, 2, 100, 70, 4, 1, 0, 2, 100, 76, + 5, 1, 0, 2, 100, 62, 6, 1, 0, 2, 100, 70, + 7, 1, 0, 2, 100, 54, 8, 1, 0, 2, 100, 70, + 1, 1, 0, 2, 104, 76, 3, 1, 0, 2, 104, 76, + 4, 1, 0, 2, 104, 76, 5, 1, 0, 2, 104, 62, + 6, 1, 0, 2, 104, 76, 7, 1, 0, 2, 104, 54, + 8, 1, 0, 2, 104, 76, 1, 1, 0, 2, 108, 76, + 3, 1, 0, 2, 108, 76, 4, 1, 0, 2, 108, 76, + 5, 1, 0, 2, 108, 62, 6, 1, 0, 2, 108, 76, + 7, 1, 0, 2, 108, 54, 8, 1, 0, 2, 108, 76, + 1, 1, 0, 2, 112, 76, 3, 1, 0, 2, 112, 76, + 4, 1, 0, 2, 112, 76, 5, 1, 0, 2, 112, 62, + 6, 1, 0, 2, 112, 76, 7, 1, 0, 2, 112, 54, + 8, 1, 0, 2, 112, 76, 1, 1, 0, 2, 116, 76, + 3, 1, 0, 2, 116, 76, 4, 1, 0, 2, 116, 76, + 5, 1, 0, 2, 116, 62, 6, 1, 0, 2, 116, 76, + 7, 1, 0, 2, 116, 54, 8, 1, 0, 2, 116, 76, + 1, 1, 0, 2, 120, 76, 3, 1, 0, 2, 120, 127, + 4, 1, 0, 2, 120, 76, 5, 1, 0, 2, 120, 127, + 6, 1, 0, 2, 120, 76, 7, 1, 0, 2, 120, 54, + 8, 1, 0, 2, 120, 76, 1, 1, 0, 2, 124, 76, + 3, 1, 0, 2, 124, 127, 4, 1, 0, 2, 124, 76, + 5, 1, 0, 2, 124, 127, 6, 1, 0, 2, 124, 76, + 7, 1, 0, 2, 124, 54, 8, 1, 0, 2, 124, 76, + 1, 1, 0, 2, 128, 76, 3, 1, 0, 2, 128, 127, + 4, 1, 0, 2, 128, 76, 5, 1, 0, 2, 128, 127, + 6, 1, 0, 2, 128, 76, 7, 1, 0, 2, 128, 54, + 8, 1, 0, 2, 128, 76, 1, 1, 0, 2, 132, 76, + 3, 1, 0, 2, 132, 76, 4, 1, 0, 2, 132, 76, + 5, 1, 0, 2, 132, 62, 6, 1, 0, 2, 132, 76, + 7, 1, 0, 2, 132, 54, 8, 1, 0, 2, 132, 76, + 1, 1, 0, 2, 136, 76, 3, 1, 0, 2, 136, 76, + 4, 1, 0, 2, 136, 76, 5, 1, 0, 2, 136, 62, + 6, 1, 0, 2, 136, 76, 7, 1, 0, 2, 136, 127, + 8, 1, 0, 2, 136, 76, 1, 1, 0, 2, 140, 76, + 3, 1, 0, 2, 140, 70, 4, 1, 0, 2, 140, 76, + 5, 1, 0, 2, 140, 62, 6, 1, 0, 2, 140, 70, + 7, 1, 0, 2, 140, 127, 8, 1, 0, 2, 140, 70, + 1, 1, 0, 2, 144, 127, 3, 1, 0, 2, 144, 76, + 4, 1, 0, 2, 144, 76, 5, 1, 0, 2, 144, 127, + 6, 1, 0, 2, 144, 76, 7, 1, 0, 2, 144, 127, + 8, 1, 0, 2, 144, 76, 1, 1, 0, 2, 149, 127, + 3, 1, 0, 2, 149, 76, 4, 1, 0, 2, 149, 74, + 5, 1, 0, 2, 149, 76, 6, 1, 0, 2, 149, 76, + 7, 1, 0, 2, 149, 54, 8, 1, 0, 2, 149, 76, + 1, 1, 0, 2, 153, 127, 3, 1, 0, 2, 153, 76, + 4, 1, 0, 2, 153, 74, 5, 1, 0, 2, 153, 76, + 6, 1, 0, 2, 153, 76, 7, 1, 0, 2, 153, 54, + 8, 1, 0, 2, 153, 76, 1, 1, 0, 2, 157, 127, + 3, 1, 0, 2, 157, 76, 4, 1, 0, 2, 157, 74, + 5, 1, 0, 2, 157, 76, 6, 1, 0, 2, 157, 76, + 7, 1, 0, 2, 157, 54, 8, 1, 0, 2, 157, 76, + 1, 1, 0, 2, 161, 127, 3, 1, 0, 2, 161, 76, + 4, 1, 0, 2, 161, 74, 5, 1, 0, 2, 161, 76, + 6, 1, 0, 2, 161, 76, 7, 1, 0, 2, 161, 54, + 8, 1, 0, 2, 161, 76, 1, 1, 0, 2, 165, 127, + 3, 1, 0, 2, 165, 76, 4, 1, 0, 2, 165, 74, + 5, 1, 0, 2, 165, 76, 6, 1, 0, 2, 165, 76, + 7, 1, 0, 2, 165, 54, 8, 1, 0, 2, 165, 76, + 1, 1, 0, 3, 36, 50, 3, 1, 0, 3, 36, 38, + 4, 1, 0, 3, 36, 66, 5, 1, 0, 3, 36, 38, + 6, 1, 0, 3, 36, 52, 7, 1, 0, 3, 36, 30, + 8, 1, 0, 3, 36, 50, 1, 1, 0, 3, 40, 50, + 3, 1, 0, 3, 40, 38, 4, 1, 0, 3, 40, 66, + 5, 1, 0, 3, 40, 38, 6, 1, 0, 3, 40, 52, + 7, 1, 0, 3, 40, 30, 8, 1, 0, 3, 40, 50, + 1, 1, 0, 3, 44, 50, 3, 1, 0, 3, 44, 38, + 4, 1, 0, 3, 44, 66, 5, 1, 0, 3, 44, 38, + 6, 1, 0, 3, 44, 52, 7, 1, 0, 3, 44, 30, + 8, 1, 0, 3, 44, 50, 1, 1, 0, 3, 48, 50, + 3, 1, 0, 3, 48, 38, 4, 1, 0, 3, 48, 66, + 5, 1, 0, 3, 48, 38, 6, 1, 0, 3, 48, 52, + 7, 1, 0, 3, 48, 30, 8, 1, 0, 3, 48, 50, + 1, 1, 0, 3, 52, 50, 3, 1, 0, 3, 52, 40, + 4, 1, 0, 3, 52, 66, 5, 1, 0, 3, 52, 38, + 6, 1, 0, 3, 52, 68, 7, 1, 0, 3, 52, 30, + 8, 1, 0, 3, 52, 68, 1, 1, 0, 3, 56, 50, + 3, 1, 0, 3, 56, 40, 4, 1, 0, 3, 56, 66, + 5, 1, 0, 3, 56, 38, 6, 1, 0, 3, 56, 68, + 7, 1, 0, 3, 56, 30, 8, 1, 0, 3, 56, 68, + 1, 1, 0, 3, 60, 50, 3, 1, 0, 3, 60, 40, + 4, 1, 0, 3, 60, 66, 5, 1, 0, 3, 60, 38, + 6, 1, 0, 3, 60, 66, 7, 1, 0, 3, 60, 30, + 8, 1, 0, 3, 60, 66, 1, 1, 0, 3, 64, 50, + 3, 1, 0, 3, 64, 40, 4, 1, 0, 3, 64, 66, + 5, 1, 0, 3, 64, 38, 6, 1, 0, 3, 64, 68, + 7, 1, 0, 3, 64, 30, 8, 1, 0, 3, 64, 68, + 1, 1, 0, 3, 100, 70, 3, 1, 0, 3, 100, 60, + 4, 1, 0, 3, 100, 64, 5, 1, 0, 3, 100, 38, + 6, 1, 0, 3, 100, 60, 7, 1, 0, 3, 100, 30, + 8, 1, 0, 3, 100, 60, 1, 1, 0, 3, 104, 70, + 3, 1, 0, 3, 104, 68, 4, 1, 0, 3, 104, 64, + 5, 1, 0, 3, 104, 38, 6, 1, 0, 3, 104, 68, + 7, 1, 0, 3, 104, 30, 8, 1, 0, 3, 104, 68, + 1, 1, 0, 3, 108, 70, 3, 1, 0, 3, 108, 68, + 4, 1, 0, 3, 108, 64, 5, 1, 0, 3, 108, 38, + 6, 1, 0, 3, 108, 68, 7, 1, 0, 3, 108, 30, + 8, 1, 0, 3, 108, 68, 1, 1, 0, 3, 112, 70, + 3, 1, 0, 3, 112, 68, 4, 1, 0, 3, 112, 64, + 5, 1, 0, 3, 112, 38, 6, 1, 0, 3, 112, 68, + 7, 1, 0, 3, 112, 30, 8, 1, 0, 3, 112, 68, + 1, 1, 0, 3, 116, 70, 3, 1, 0, 3, 116, 68, + 4, 1, 0, 3, 116, 64, 5, 1, 0, 3, 116, 38, + 6, 1, 0, 3, 116, 68, 7, 1, 0, 3, 116, 30, + 8, 1, 0, 3, 116, 68, 1, 1, 0, 3, 120, 70, + 3, 1, 0, 3, 120, 127, 4, 1, 0, 3, 120, 64, + 5, 1, 0, 3, 120, 127, 6, 1, 0, 3, 120, 68, + 7, 1, 0, 3, 120, 30, 8, 1, 0, 3, 120, 68, + 1, 1, 0, 3, 124, 70, 3, 1, 0, 3, 124, 127, + 4, 1, 0, 3, 124, 64, 5, 1, 0, 3, 124, 127, + 6, 1, 0, 3, 124, 68, 7, 1, 0, 3, 124, 30, + 8, 1, 0, 3, 124, 68, 1, 1, 0, 3, 128, 70, + 3, 1, 0, 3, 128, 127, 4, 1, 0, 3, 128, 64, + 5, 1, 0, 3, 128, 127, 6, 1, 0, 3, 128, 68, + 7, 1, 0, 3, 128, 30, 8, 1, 0, 3, 128, 68, + 1, 1, 0, 3, 132, 70, 3, 1, 0, 3, 132, 68, + 4, 1, 0, 3, 132, 64, 5, 1, 0, 3, 132, 38, + 6, 1, 0, 3, 132, 68, 7, 1, 0, 3, 132, 30, + 8, 1, 0, 3, 132, 68, 1, 1, 0, 3, 136, 70, + 3, 1, 0, 3, 136, 68, 4, 1, 0, 3, 136, 64, + 5, 1, 0, 3, 136, 38, 6, 1, 0, 3, 136, 68, + 7, 1, 0, 3, 136, 127, 8, 1, 0, 3, 136, 68, + 1, 1, 0, 3, 140, 70, 3, 1, 0, 3, 140, 60, + 4, 1, 0, 3, 140, 64, 5, 1, 0, 3, 140, 38, + 6, 1, 0, 3, 140, 60, 7, 1, 0, 3, 140, 127, + 8, 1, 0, 3, 140, 60, 1, 1, 0, 3, 144, 127, + 3, 1, 0, 3, 144, 68, 4, 1, 0, 3, 144, 64, + 5, 1, 0, 3, 144, 127, 6, 1, 0, 3, 144, 68, + 7, 1, 0, 3, 144, 127, 8, 1, 0, 3, 144, 68, + 1, 1, 0, 3, 149, 127, 3, 1, 0, 3, 149, 76, + 4, 1, 0, 3, 149, 60, 5, 1, 0, 3, 149, 76, + 6, 1, 0, 3, 149, 76, 7, 1, 0, 3, 149, 30, + 8, 1, 0, 3, 149, 72, 1, 1, 0, 3, 153, 127, + 3, 1, 0, 3, 153, 76, 4, 1, 0, 3, 153, 60, + 5, 1, 0, 3, 153, 76, 6, 1, 0, 3, 153, 76, + 7, 1, 0, 3, 153, 30, 8, 1, 0, 3, 153, 76, + 1, 1, 0, 3, 157, 127, 3, 1, 0, 3, 157, 76, + 4, 1, 0, 3, 157, 60, 5, 1, 0, 3, 157, 76, + 6, 1, 0, 3, 157, 76, 7, 1, 0, 3, 157, 30, + 8, 1, 0, 3, 157, 76, 1, 1, 0, 3, 161, 127, + 3, 1, 0, 3, 161, 76, 4, 1, 0, 3, 161, 60, + 5, 1, 0, 3, 161, 76, 6, 1, 0, 3, 161, 76, + 7, 1, 0, 3, 161, 30, 8, 1, 0, 3, 161, 76, + 1, 1, 0, 3, 165, 127, 3, 1, 0, 3, 165, 76, + 4, 1, 0, 3, 165, 60, 5, 1, 0, 3, 165, 76, + 6, 1, 0, 3, 165, 76, 7, 1, 0, 3, 165, 30, + 8, 1, 0, 3, 165, 76, 1, 1, 1, 2, 38, 62, + 3, 1, 1, 2, 38, 64, 4, 1, 1, 2, 38, 72, + 5, 1, 1, 2, 38, 64, 6, 1, 1, 2, 38, 64, + 7, 1, 1, 2, 38, 54, 8, 1, 1, 2, 38, 62, + 1, 1, 1, 2, 46, 62, 3, 1, 1, 2, 46, 64, + 4, 1, 1, 2, 46, 72, 5, 1, 1, 2, 46, 64, + 6, 1, 1, 2, 46, 64, 7, 1, 1, 2, 46, 54, + 8, 1, 1, 2, 46, 62, 1, 1, 1, 2, 54, 62, + 3, 1, 1, 2, 54, 64, 4, 1, 1, 2, 54, 72, + 5, 1, 1, 2, 54, 64, 6, 1, 1, 2, 54, 72, + 7, 1, 1, 2, 54, 54, 8, 1, 1, 2, 54, 72, + 1, 1, 1, 2, 62, 62, 3, 1, 1, 2, 62, 64, + 4, 1, 1, 2, 62, 70, 5, 1, 1, 2, 62, 64, + 6, 1, 1, 2, 62, 64, 7, 1, 1, 2, 62, 54, + 8, 1, 1, 2, 62, 64, 1, 1, 1, 2, 102, 72, + 3, 1, 1, 2, 102, 58, 4, 1, 1, 2, 102, 72, + 5, 1, 1, 2, 102, 64, 6, 1, 1, 2, 102, 58, + 7, 1, 1, 2, 102, 54, 8, 1, 1, 2, 102, 58, + 1, 1, 1, 2, 110, 72, 3, 1, 1, 2, 110, 72, + 4, 1, 1, 2, 110, 72, 5, 1, 1, 2, 110, 64, + 6, 1, 1, 2, 110, 72, 7, 1, 1, 2, 110, 54, + 8, 1, 1, 2, 110, 72, 1, 1, 1, 2, 118, 72, + 3, 1, 1, 2, 118, 127, 4, 1, 1, 2, 118, 72, + 5, 1, 1, 2, 118, 127, 6, 1, 1, 2, 118, 72, + 7, 1, 1, 2, 118, 54, 8, 1, 1, 2, 118, 72, + 1, 1, 1, 2, 126, 72, 3, 1, 1, 2, 126, 127, + 4, 1, 1, 2, 126, 72, 5, 1, 1, 2, 126, 127, + 6, 1, 1, 2, 126, 72, 7, 1, 1, 2, 126, 54, + 8, 1, 1, 2, 126, 72, 1, 1, 1, 2, 134, 72, + 3, 1, 1, 2, 134, 72, 4, 1, 1, 2, 134, 72, + 5, 1, 1, 2, 134, 64, 6, 1, 1, 2, 134, 72, + 7, 1, 1, 2, 134, 127, 8, 1, 1, 2, 134, 72, + 1, 1, 1, 2, 142, 127, 3, 1, 1, 2, 142, 72, + 4, 1, 1, 2, 142, 72, 5, 1, 1, 2, 142, 127, + 6, 1, 1, 2, 142, 72, 7, 1, 1, 2, 142, 127, + 8, 1, 1, 2, 142, 72, 1, 1, 1, 2, 151, 127, + 3, 1, 1, 2, 151, 72, 4, 1, 1, 2, 151, 72, + 5, 1, 1, 2, 151, 72, 6, 1, 1, 2, 151, 72, + 7, 1, 1, 2, 151, 54, 8, 1, 1, 2, 151, 72, + 1, 1, 1, 2, 159, 127, 3, 1, 1, 2, 159, 72, + 4, 1, 1, 2, 159, 72, 5, 1, 1, 2, 159, 72, + 6, 1, 1, 2, 159, 72, 7, 1, 1, 2, 159, 54, + 8, 1, 1, 2, 159, 72, 1, 1, 1, 3, 38, 50, + 3, 1, 1, 3, 38, 40, 4, 1, 1, 3, 38, 62, + 5, 1, 1, 3, 38, 40, 6, 1, 1, 3, 38, 52, + 7, 1, 1, 3, 38, 30, 8, 1, 1, 3, 38, 50, + 1, 1, 1, 3, 46, 50, 3, 1, 1, 3, 46, 40, + 4, 1, 1, 3, 46, 62, 5, 1, 1, 3, 46, 40, + 6, 1, 1, 3, 46, 52, 7, 1, 1, 3, 46, 30, + 8, 1, 1, 3, 46, 50, 1, 1, 1, 3, 54, 50, + 3, 1, 1, 3, 54, 40, 4, 1, 1, 3, 54, 62, + 5, 1, 1, 3, 54, 40, 6, 1, 1, 3, 54, 68, + 7, 1, 1, 3, 54, 30, 8, 1, 1, 3, 54, 68, + 1, 1, 1, 3, 62, 48, 3, 1, 1, 3, 62, 40, + 4, 1, 1, 3, 62, 58, 5, 1, 1, 3, 62, 40, + 6, 1, 1, 3, 62, 58, 7, 1, 1, 3, 62, 30, + 8, 1, 1, 3, 62, 58, 1, 1, 1, 3, 102, 70, + 3, 1, 1, 3, 102, 54, 4, 1, 1, 3, 102, 64, + 5, 1, 1, 3, 102, 40, 6, 1, 1, 3, 102, 54, + 7, 1, 1, 3, 102, 30, 8, 1, 1, 3, 102, 54, + 1, 1, 1, 3, 110, 70, 3, 1, 1, 3, 110, 68, + 4, 1, 1, 3, 110, 64, 5, 1, 1, 3, 110, 40, + 6, 1, 1, 3, 110, 68, 7, 1, 1, 3, 110, 30, + 8, 1, 1, 3, 110, 68, 1, 1, 1, 3, 118, 70, + 3, 1, 1, 3, 118, 127, 4, 1, 1, 3, 118, 64, + 5, 1, 1, 3, 118, 127, 6, 1, 1, 3, 118, 68, + 7, 1, 1, 3, 118, 30, 8, 1, 1, 3, 118, 68, + 1, 1, 1, 3, 126, 70, 3, 1, 1, 3, 126, 127, + 4, 1, 1, 3, 126, 64, 5, 1, 1, 3, 126, 127, + 6, 1, 1, 3, 126, 68, 7, 1, 1, 3, 126, 30, + 8, 1, 1, 3, 126, 68, 1, 1, 1, 3, 134, 70, + 3, 1, 1, 3, 134, 68, 4, 1, 1, 3, 134, 64, + 5, 1, 1, 3, 134, 40, 6, 1, 1, 3, 134, 68, + 7, 1, 1, 3, 134, 127, 8, 1, 1, 3, 134, 68, + 1, 1, 1, 3, 142, 127, 3, 1, 1, 3, 142, 68, + 4, 1, 1, 3, 142, 64, 5, 1, 1, 3, 142, 127, + 6, 1, 1, 3, 142, 68, 7, 1, 1, 3, 142, 127, + 8, 1, 1, 3, 142, 68, 1, 1, 1, 3, 151, 127, + 3, 1, 1, 3, 151, 72, 4, 1, 1, 3, 151, 66, + 5, 1, 1, 3, 151, 72, 6, 1, 1, 3, 151, 72, + 7, 1, 1, 3, 151, 30, 8, 1, 1, 3, 151, 68, + 1, 1, 1, 3, 159, 127, 3, 1, 1, 3, 159, 72, + 4, 1, 1, 3, 159, 66, 5, 1, 1, 3, 159, 72, + 6, 1, 1, 3, 159, 72, 7, 1, 1, 3, 159, 30, + 8, 1, 1, 3, 159, 72, 1, 1, 2, 4, 42, 64, + 3, 1, 2, 4, 42, 64, 4, 1, 2, 4, 42, 68, + 5, 1, 2, 4, 42, 64, 6, 1, 2, 4, 42, 64, + 7, 1, 2, 4, 42, 54, 8, 1, 2, 4, 42, 62, + 1, 1, 2, 4, 58, 64, 3, 1, 2, 4, 58, 62, + 4, 1, 2, 4, 58, 64, 5, 1, 2, 4, 58, 64, + 6, 1, 2, 4, 58, 62, 7, 1, 2, 4, 58, 54, + 8, 1, 2, 4, 58, 62, 1, 1, 2, 4, 106, 72, + 3, 1, 2, 4, 106, 58, 4, 1, 2, 4, 106, 66, + 5, 1, 2, 4, 106, 64, 6, 1, 2, 4, 106, 58, + 7, 1, 2, 4, 106, 54, 8, 1, 2, 4, 106, 58, + 1, 1, 2, 4, 122, 72, 3, 1, 2, 4, 122, 127, + 4, 1, 2, 4, 122, 68, 5, 1, 2, 4, 122, 127, + 6, 1, 2, 4, 122, 72, 7, 1, 2, 4, 122, 54, + 8, 1, 2, 4, 122, 72, 1, 1, 2, 4, 138, 127, + 3, 1, 2, 4, 138, 72, 4, 1, 2, 4, 138, 68, + 5, 1, 2, 4, 138, 127, 6, 1, 2, 4, 138, 72, + 7, 1, 2, 4, 138, 127, 8, 1, 2, 4, 138, 72, + 1, 1, 2, 4, 155, 127, 3, 1, 2, 4, 155, 72, + 4, 1, 2, 4, 155, 68, 5, 1, 2, 4, 155, 72, + 6, 1, 2, 4, 155, 72, 7, 1, 2, 4, 155, 54, + 8, 1, 2, 4, 155, 68, 1, 1, 2, 5, 42, 50, + 3, 1, 2, 5, 42, 40, 4, 1, 2, 5, 42, 58, + 5, 1, 2, 5, 42, 40, 6, 1, 2, 5, 42, 52, + 7, 1, 2, 5, 42, 30, 8, 1, 2, 5, 42, 50, + 1, 1, 2, 5, 58, 50, 3, 1, 2, 5, 58, 40, + 4, 1, 2, 5, 58, 56, 5, 1, 2, 5, 58, 40, + 6, 1, 2, 5, 58, 52, 7, 1, 2, 5, 58, 30, + 8, 1, 2, 5, 58, 52, 1, 1, 2, 5, 106, 72, + 3, 1, 2, 5, 106, 50, 4, 1, 2, 5, 106, 56, + 5, 1, 2, 5, 106, 40, 6, 1, 2, 5, 106, 50, + 7, 1, 2, 5, 106, 30, 8, 1, 2, 5, 106, 50, + 1, 1, 2, 5, 122, 72, 3, 1, 2, 5, 122, 127, + 4, 1, 2, 5, 122, 56, 5, 1, 2, 5, 122, 127, + 6, 1, 2, 5, 122, 66, 7, 1, 2, 5, 122, 30, + 8, 1, 2, 5, 122, 66, 1, 1, 2, 5, 138, 127, + 3, 1, 2, 5, 138, 66, 4, 1, 2, 5, 138, 58, + 5, 1, 2, 5, 138, 127, 6, 1, 2, 5, 138, 66, + 7, 1, 2, 5, 138, 127, 8, 1, 2, 5, 138, 66, + 1, 1, 2, 5, 155, 127, 3, 1, 2, 5, 155, 62, + 4, 1, 2, 5, 155, 58, 5, 1, 2, 5, 155, 72, + 6, 1, 2, 5, 155, 62, 7, 1, 2, 5, 155, 30, + 8, 1, 2, 5, 155, 62 }; RTW_DECL_TABLE_TXPWR_LMT(rtw8822c_txpwr_lmt_type0); diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index f3fab5d0ea97..9e57c4411734 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -88,6 +88,8 @@ static inline bool br_multicast_router(const struct net_device *dev) #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING) bool br_vlan_enabled(const struct net_device *dev); int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid); +int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid); +int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto); int br_vlan_get_info(const struct net_device *dev, u16 vid, struct bridge_vlan_info *p_vinfo); #else @@ -101,6 +103,16 @@ static inline int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid) return -EINVAL; } +static inline int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto) +{ + return -EINVAL; +} + +static inline int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid) +{ + return -EINVAL; +} + static inline int br_vlan_get_info(const struct net_device *dev, u16 vid, struct bridge_vlan_info *p_vinfo) { diff --git a/include/linux/mlx5/accel.h b/include/linux/mlx5/accel.h index 70e7e5673ce9..5613e677a5f9 100644 --- a/include/linux/mlx5/accel.h +++ b/include/linux/mlx5/accel.h @@ -114,7 +114,7 @@ enum mlx5_accel_ipsec_cap { MLX5_ACCEL_IPSEC_CAP_TX_IV_IS_ESN = 1 << 7, }; -#ifdef CONFIG_MLX5_ACCEL +#ifdef CONFIG_MLX5_FPGA_IPSEC u32 mlx5_accel_ipsec_device_caps(struct mlx5_core_dev *mdev); diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h index 769326ea1d9b..40748fc1b11b 100644 --- a/include/linux/mlx5/cq.h +++ b/include/linux/mlx5/cq.h @@ -47,7 +47,7 @@ struct mlx5_core_cq { struct completion free; unsigned vector; unsigned int irqn; - void (*comp) (struct mlx5_core_cq *); + void (*comp)(struct mlx5_core_cq *cq, struct mlx5_eqe *eqe); void (*event) (struct mlx5_core_cq *, enum mlx5_event); u32 cons_index; unsigned arm_sn; @@ -55,7 +55,7 @@ struct mlx5_core_cq { int pid; struct { struct list_head list; - void (*comp)(struct mlx5_core_cq *); + void (*comp)(struct mlx5_core_cq *cq, struct mlx5_eqe *eqe); void *priv; } tasklet_ctx; int reset_notify_added; @@ -185,7 +185,7 @@ static inline void mlx5_cq_put(struct mlx5_core_cq *cq) } int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, - u32 *in, int inlen); + u32 *in, int inlen, u32 *out, int outlen); int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq); int mlx5_core_query_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, u32 *out, int outlen); diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 35ed38c2ae6c..ce9839c8bc1a 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -351,7 +351,7 @@ enum mlx5_event { MLX5_EVENT_TYPE_DEVICE_TRACER = 0x26, - MLX5_EVENT_TYPE_MAX = MLX5_EVENT_TYPE_DEVICE_TRACER + 1, + MLX5_EVENT_TYPE_MAX = 0x100, }; enum { @@ -437,6 +437,7 @@ enum { MLX5_OPCODE_SET_PSV = 0x20, MLX5_OPCODE_GET_PSV = 0x21, MLX5_OPCODE_CHECK_PSV = 0x22, + MLX5_OPCODE_DUMP = 0x23, MLX5_OPCODE_RGET_PSV = 0x26, MLX5_OPCODE_RCHECK_PSV = 0x27, @@ -445,6 +446,14 @@ enum { }; enum { + MLX5_OPC_MOD_TLS_TIS_STATIC_PARAMS = 0x20, +}; + +enum { + MLX5_OPC_MOD_TLS_TIS_PROGRESS_PARAMS = 0x20, +}; + +enum { MLX5_SET_PORT_RESET_QKEY = 0, MLX5_SET_PORT_GUID0 = 16, MLX5_SET_PORT_NODE_GUID = 17, @@ -1085,6 +1094,9 @@ enum mlx5_cap_type { MLX5_CAP_DEBUG, MLX5_CAP_RESERVED_14, MLX5_CAP_DEV_MEM, + MLX5_CAP_RESERVED_16, + MLX5_CAP_TLS, + MLX5_CAP_DEV_EVENT = 0x14, /* NUM OF CAP Types */ MLX5_CAP_NUM }; @@ -1263,6 +1275,12 @@ enum mlx5_qcam_feature_groups { #define MLX5_CAP64_DEV_MEM(mdev, cap)\ MLX5_GET64(device_mem_cap, mdev->caps.hca_cur[MLX5_CAP_DEV_MEM], cap) +#define MLX5_CAP_TLS(mdev, cap) \ + MLX5_GET(tls_cap, (mdev)->caps.hca_cur[MLX5_CAP_TLS], cap) + +#define MLX5_CAP_DEV_EVENT(mdev, cap)\ + MLX5_ADDR_OF(device_event_cap, (mdev)->caps.hca_cur[MLX5_CAP_DEV_EVENT], cap) + enum { MLX5_CMD_STAT_OK = 0x0, MLX5_CMD_STAT_INT_ERR = 0x1, diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 3ef663900f4d..0e6da1840c7d 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -139,6 +139,7 @@ enum { MLX5_REG_MTPPS = 0x9053, MLX5_REG_MTPPSE = 0x9054, MLX5_REG_MPEGC = 0x9056, + MLX5_REG_MCQS = 0x9060, MLX5_REG_MCQI = 0x9061, MLX5_REG_MCC = 0x9062, MLX5_REG_MCDA = 0x9063, @@ -182,6 +183,11 @@ enum port_state_policy { MLX5_POLICY_INVALID = 0xffffffff }; +enum mlx5_coredev_type { + MLX5_COREDEV_PF, + MLX5_COREDEV_VF +}; + struct mlx5_field_desc { struct dentry *dent; int i; @@ -468,7 +474,6 @@ struct mlx5_vf_context { struct mlx5_core_sriov { struct mlx5_vf_context *vfs_ctx; int num_vfs; - int enabled_vfs; u16 max_vfs; }; @@ -572,7 +577,6 @@ struct mlx5_priv { struct mlx5_core_sriov sriov; struct mlx5_lag *lag; struct mlx5_devcom *devcom; - unsigned long pci_dev_data; struct mlx5_core_roce roce; struct mlx5_fc_stats fc_stats; struct mlx5_rl_table rl_table; @@ -653,6 +657,7 @@ struct mlx5_geneve; struct mlx5_core_dev { struct device *device; + enum mlx5_coredev_type coredev_type; struct pci_dev *pdev; /* sync pci state */ struct mutex pci_status_mutex; @@ -1047,6 +1052,8 @@ int mlx5_register_interface(struct mlx5_interface *intf); void mlx5_unregister_interface(struct mlx5_interface *intf); int mlx5_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb); int mlx5_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb); +int mlx5_eq_notifier_register(struct mlx5_core_dev *dev, struct mlx5_nb *nb); +int mlx5_eq_notifier_unregister(struct mlx5_core_dev *dev, struct mlx5_nb *nb); int mlx5_core_query_vendor_id(struct mlx5_core_dev *mdev, u32 *vendor_id); @@ -1087,9 +1094,9 @@ enum { MLX5_PCI_DEV_IS_VF = 1 << 0, }; -static inline int mlx5_core_is_pf(struct mlx5_core_dev *dev) +static inline bool mlx5_core_is_pf(const struct mlx5_core_dev *dev) { - return !(dev->priv.pci_dev_data & MLX5_PCI_DEV_IS_VF); + return dev->coredev_type == MLX5_COREDEV_PF; } static inline bool mlx5_core_is_ecpf(struct mlx5_core_dev *dev) @@ -1097,17 +1104,18 @@ static inline bool mlx5_core_is_ecpf(struct mlx5_core_dev *dev) return dev->caps.embedded_cpu; } -static inline bool mlx5_core_is_ecpf_esw_manager(struct mlx5_core_dev *dev) +static inline bool +mlx5_core_is_ecpf_esw_manager(const struct mlx5_core_dev *dev) { return dev->caps.embedded_cpu && MLX5_CAP_GEN(dev, eswitch_manager); } -static inline bool mlx5_ecpf_vport_exists(struct mlx5_core_dev *dev) +static inline bool mlx5_ecpf_vport_exists(const struct mlx5_core_dev *dev) { return mlx5_core_is_pf(dev) && MLX5_CAP_ESW(dev, ecpf_vport_exists); } -static inline u16 mlx5_core_max_vfs(struct mlx5_core_dev *dev) +static inline u16 mlx5_core_max_vfs(const struct mlx5_core_dev *dev) { return dev->priv.sriov.max_vfs; } diff --git a/include/linux/mlx5/eq.h b/include/linux/mlx5/eq.h index 70e16dcfb4c4..e49d8c0d4f26 100644 --- a/include/linux/mlx5/eq.h +++ b/include/linux/mlx5/eq.h @@ -15,7 +15,7 @@ struct mlx5_core_dev; struct mlx5_eq_param { u8 irq_index; int nent; - u64 mask; + u64 mask[4]; }; struct mlx5_eq * diff --git a/include/linux/mlx5/eswitch.h b/include/linux/mlx5/eswitch.h index 091e25881f52..46b5ba029802 100644 --- a/include/linux/mlx5/eswitch.h +++ b/include/linux/mlx5/eswitch.h @@ -12,9 +12,9 @@ #define MLX5_ESWITCH_MANAGER(mdev) MLX5_CAP_GEN(mdev, eswitch_manager) enum { - SRIOV_NONE, - SRIOV_LEGACY, - SRIOV_OFFLOADS + MLX5_ESWITCH_NONE, + MLX5_ESWITCH_LEGACY, + MLX5_ESWITCH_OFFLOADS }; enum { @@ -46,6 +46,8 @@ struct mlx5_eswitch_rep { u16 vport; u8 hw_id[ETH_ALEN]; u16 vlan; + /* Only IB rep is using vport_index */ + u16 vport_index; u32 vlan_refcount; }; @@ -64,6 +66,8 @@ struct mlx5_flow_handle * mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *esw, u16 vport_num, u32 sqn); +u16 mlx5_eswitch_get_total_vports(const struct mlx5_core_dev *dev); + #ifdef CONFIG_MLX5_ESWITCH enum devlink_eswitch_encap_mode mlx5_eswitch_get_encap_mode(const struct mlx5_core_dev *dev); @@ -91,4 +95,5 @@ mlx5_eswitch_get_vport_metadata_for_match(const struct mlx5_eswitch *esw, return 0; }; #endif /* CONFIG_MLX5_ESWITCH */ + #endif diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index d4409654f760..06881b79167e 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -91,6 +91,20 @@ enum { enum { MLX5_OBJ_TYPE_GENEVE_TLV_OPT = 0x000b, + MLX5_OBJ_TYPE_MKEY = 0xff01, + MLX5_OBJ_TYPE_QP = 0xff02, + MLX5_OBJ_TYPE_PSV = 0xff03, + MLX5_OBJ_TYPE_RMP = 0xff04, + MLX5_OBJ_TYPE_XRC_SRQ = 0xff05, + MLX5_OBJ_TYPE_RQ = 0xff06, + MLX5_OBJ_TYPE_SQ = 0xff07, + MLX5_OBJ_TYPE_TIR = 0xff08, + MLX5_OBJ_TYPE_TIS = 0xff09, + MLX5_OBJ_TYPE_DCT = 0xff0a, + MLX5_OBJ_TYPE_XRQ = 0xff0b, + MLX5_OBJ_TYPE_RQT = 0xff0e, + MLX5_OBJ_TYPE_FLOW_COUNTER = 0xff0f, + MLX5_OBJ_TYPE_CQ = 0xff10, }; enum { @@ -106,6 +120,9 @@ enum { MLX5_CMD_OP_QUERY_ISSI = 0x10a, MLX5_CMD_OP_SET_ISSI = 0x10b, MLX5_CMD_OP_SET_DRIVER_VERSION = 0x10d, + MLX5_CMD_OP_QUERY_SF_PARTITION = 0x111, + MLX5_CMD_OP_ALLOC_SF = 0x113, + MLX5_CMD_OP_DEALLOC_SF = 0x114, MLX5_CMD_OP_CREATE_MKEY = 0x200, MLX5_CMD_OP_QUERY_MKEY = 0x201, MLX5_CMD_OP_DESTROY_MKEY = 0x202, @@ -713,7 +730,11 @@ struct mlx5_ifc_e_switch_cap_bits { u8 reserved_2b[0x6]; u8 max_encap_header_size[0xa]; - u8 reserved_40[0x7c0]; + u8 reserved_at_40[0xb]; + u8 log_max_esw_sf[0x5]; + u8 esw_sf_base_id[0x10]; + + u8 reserved_at_60[0x7a0]; }; @@ -853,6 +874,12 @@ struct mlx5_ifc_device_mem_cap_bits { u8 reserved_at_180[0x680]; }; +struct mlx5_ifc_device_event_cap_bits { + u8 user_affiliated_events[4][0x40]; + + u8 user_unaffiliated_events[4][0x40]; +}; + enum { MLX5_ATOMIC_CAPS_ATOMIC_SIZE_QP_1_BYTE = 0x0, MLX5_ATOMIC_CAPS_ATOMIC_SIZE_QP_2_BYTES = 0x2, @@ -946,6 +973,16 @@ struct mlx5_ifc_vector_calc_cap_bits { u8 reserved_at_c0[0x720]; }; +struct mlx5_ifc_tls_cap_bits { + u8 tls_1_2_aes_gcm_128[0x1]; + u8 tls_1_3_aes_gcm_128[0x1]; + u8 tls_1_2_aes_gcm_256[0x1]; + u8 tls_1_3_aes_gcm_256[0x1]; + u8 reserved_at_4[0x1c]; + + u8 reserved_at_20[0x7e0]; +}; + enum { MLX5_WQ_TYPE_LINKED_LIST = 0x0, MLX5_WQ_TYPE_CYCLIC = 0x1, @@ -1010,7 +1047,8 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 log_max_srq_sz[0x8]; u8 log_max_qp_sz[0x8]; - u8 reserved_at_90[0x8]; + u8 event_cap[0x1]; + u8 reserved_at_91[0x7]; u8 prio_tag_required[0x1]; u8 reserved_at_99[0x2]; u8 log_max_qp[0x5]; @@ -1058,7 +1096,9 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 cc_modify_allowed[0x1]; u8 start_pad[0x1]; u8 cache_line_128byte[0x1]; - u8 reserved_at_165[0xa]; + u8 reserved_at_165[0x4]; + u8 rts2rts_qp_counters_set_id[0x1]; + u8 reserved_at_16a[0x5]; u8 qcam_reg[0x1]; u8 gid_table_size[0x10]; @@ -1275,7 +1315,8 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 reserved_at_440[0x20]; - u8 reserved_at_460[0x3]; + u8 tls[0x1]; + u8 reserved_at_461[0x2]; u8 log_max_uctx[0x5]; u8 reserved_at_468[0x3]; u8 log_max_umem[0x5]; @@ -1300,7 +1341,9 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 max_geneve_tlv_option_data_len[0x5]; u8 reserved_at_570[0x10]; - u8 reserved_at_580[0x3c]; + u8 reserved_at_580[0x33]; + u8 log_max_dek[0x5]; + u8 reserved_at_5b8[0x4]; u8 mini_cqe_resp_stride_index[0x1]; u8 cqe_128_always[0x1]; u8 cqe_compression_128[0x1]; @@ -1330,13 +1373,24 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 reserved_at_640[0x10]; u8 num_q_monitor_counters[0x10]; - u8 reserved_at_660[0x40]; + u8 reserved_at_660[0x20]; + + u8 sf[0x1]; + u8 sf_set_partition[0x1]; + u8 reserved_at_682[0x1]; + u8 log_max_sf[0x5]; + u8 reserved_at_688[0x8]; + u8 log_min_sf_size[0x8]; + u8 max_num_sf_partitions[0x8]; u8 uctx_cap[0x20]; u8 reserved_at_6c0[0x4]; u8 flex_parser_id_geneve_tlv_option_0[0x4]; - u8 reserved_at_6c8[0x138]; + u8 reserved_at_6c8[0x28]; + u8 sf_base_id[0x10]; + + u8 reserved_at_700[0x100]; }; enum mlx5_flow_destination_type { @@ -2568,6 +2622,7 @@ union mlx5_ifc_hca_cap_union_bits { struct mlx5_ifc_qos_cap_bits qos_cap; struct mlx5_ifc_debug_cap_bits debug_cap; struct mlx5_ifc_fpga_cap_bits fpga_cap; + struct mlx5_ifc_tls_cap_bits tls_cap; u8 reserved_at_0[0x8000]; }; @@ -2707,7 +2762,8 @@ struct mlx5_ifc_traffic_counter_bits { struct mlx5_ifc_tisc_bits { u8 strict_lag_tx_port_affinity[0x1]; - u8 reserved_at_1[0x3]; + u8 tls_en[0x1]; + u8 reserved_at_1[0x2]; u8 lag_tx_port_affinity[0x04]; u8 reserved_at_8[0x4]; @@ -2721,7 +2777,11 @@ struct mlx5_ifc_tisc_bits { u8 reserved_at_140[0x8]; u8 underlay_qpn[0x18]; - u8 reserved_at_160[0x3a0]; + + u8 reserved_at_160[0x8]; + u8 pd[0x18]; + + u8 reserved_at_180[0x380]; }; enum { @@ -7404,9 +7464,9 @@ struct mlx5_ifc_create_eq_in_bits { u8 reserved_at_280[0x40]; - u8 event_bitmask[0x40]; + u8 event_bitmask[4][0x40]; - u8 reserved_at_300[0x580]; + u8 reserved_at_3c0[0x4c0]; u8 pas[0][0x40]; }; @@ -8524,7 +8584,7 @@ struct mlx5_ifc_mcam_access_reg_bits { u8 mcda[0x1]; u8 mcc[0x1]; u8 mcqi[0x1]; - u8 reserved_at_1f[0x1]; + u8 mcqs[0x1]; u8 regs_95_to_87[0x9]; u8 mpegc[0x1]; @@ -9016,6 +9076,24 @@ struct mlx5_ifc_mtppse_reg_bits { u8 reserved_at_40[0x40]; }; +struct mlx5_ifc_mcqs_reg_bits { + u8 last_index_flag[0x1]; + u8 reserved_at_1[0x7]; + u8 fw_device[0x8]; + u8 component_index[0x10]; + + u8 reserved_at_20[0x10]; + u8 identifier[0x10]; + + u8 reserved_at_40[0x17]; + u8 component_status[0x5]; + u8 component_update_state[0x4]; + + u8 last_update_state_changer_type[0x4]; + u8 last_update_state_changer_host_id[0x4]; + u8 reserved_at_68[0x18]; +}; + struct mlx5_ifc_mcqi_cap_bits { u8 supported_info_bitmask[0x20]; @@ -9036,6 +9114,43 @@ struct mlx5_ifc_mcqi_cap_bits { u8 reserved_at_86[0x1a]; }; +struct mlx5_ifc_mcqi_version_bits { + u8 reserved_at_0[0x2]; + u8 build_time_valid[0x1]; + u8 user_defined_time_valid[0x1]; + u8 reserved_at_4[0x14]; + u8 version_string_length[0x8]; + + u8 version[0x20]; + + u8 build_time[0x40]; + + u8 user_defined_time[0x40]; + + u8 build_tool_version[0x20]; + + u8 reserved_at_e0[0x20]; + + u8 version_string[92][0x8]; +}; + +struct mlx5_ifc_mcqi_activation_method_bits { + u8 pending_server_ac_power_cycle[0x1]; + u8 pending_server_dc_power_cycle[0x1]; + u8 pending_server_reboot[0x1]; + u8 pending_fw_reset[0x1]; + u8 auto_activate[0x1]; + u8 all_hosts_sync[0x1]; + u8 device_hw_reset[0x1]; + u8 reserved_at_7[0x19]; +}; + +union mlx5_ifc_mcqi_reg_data_bits { + struct mlx5_ifc_mcqi_cap_bits mcqi_caps; + struct mlx5_ifc_mcqi_version_bits mcqi_version; + struct mlx5_ifc_mcqi_activation_method_bits mcqi_activation_mathod; +}; + struct mlx5_ifc_mcqi_reg_bits { u8 read_pending_component[0x1]; u8 reserved_at_1[0xf]; @@ -9053,7 +9168,7 @@ struct mlx5_ifc_mcqi_reg_bits { u8 reserved_at_a0[0x10]; u8 data_size[0x10]; - u8 data[0][0x20]; + union mlx5_ifc_mcqi_reg_data_bits data[0]; }; struct mlx5_ifc_mcc_reg_bits { @@ -9750,7 +9865,8 @@ struct mlx5_ifc_mtrc_ctrl_bits { struct mlx5_ifc_host_params_context_bits { u8 host_number[0x8]; - u8 reserved_at_8[0x8]; + u8 reserved_at_8[0x7]; + u8 host_pf_disabled[0x1]; u8 host_num_of_vfs[0x10]; u8 host_total_vfs[0x10]; @@ -9786,6 +9902,165 @@ struct mlx5_ifc_query_esw_functions_out_bits { struct mlx5_ifc_host_params_context_bits host_params_context; u8 reserved_at_280[0x180]; + u8 host_sf_enable[0][0x40]; +}; + +struct mlx5_ifc_sf_partition_bits { + u8 reserved_at_0[0x10]; + u8 log_num_sf[0x8]; + u8 log_sf_bar_size[0x8]; +}; + +struct mlx5_ifc_query_sf_partitions_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x18]; + u8 num_sf_partitions[0x8]; + + u8 reserved_at_60[0x20]; + + struct mlx5_ifc_sf_partition_bits sf_partition[0]; +}; + +struct mlx5_ifc_query_sf_partitions_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 reserved_at_40[0x40]; +}; + +struct mlx5_ifc_dealloc_sf_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x40]; +}; + +struct mlx5_ifc_dealloc_sf_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 reserved_at_40[0x10]; + u8 function_id[0x10]; + + u8 reserved_at_60[0x20]; +}; + +struct mlx5_ifc_alloc_sf_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x40]; +}; + +struct mlx5_ifc_alloc_sf_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 reserved_at_40[0x10]; + u8 function_id[0x10]; + + u8 reserved_at_60[0x20]; +}; + +struct mlx5_ifc_affiliated_event_header_bits { + u8 reserved_at_0[0x10]; + u8 obj_type[0x10]; + + u8 obj_id[0x20]; +}; + +enum { + MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_ENCRYPTION_KEY = BIT(0xc), +}; + +enum { + MLX5_GENERAL_OBJECT_TYPES_ENCRYPTION_KEY = 0xc, +}; + +struct mlx5_ifc_encryption_key_obj_bits { + u8 modify_field_select[0x40]; + + u8 reserved_at_40[0x14]; + u8 key_size[0x4]; + u8 reserved_at_58[0x4]; + u8 key_type[0x4]; + + u8 reserved_at_60[0x8]; + u8 pd[0x18]; + + u8 reserved_at_80[0x180]; + u8 key[8][0x20]; + + u8 reserved_at_300[0x500]; +}; + +struct mlx5_ifc_create_encryption_key_in_bits { + struct mlx5_ifc_general_obj_in_cmd_hdr_bits general_obj_in_cmd_hdr; + struct mlx5_ifc_encryption_key_obj_bits encryption_key_object; +}; + +enum { + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_KEY_SIZE_128 = 0x0, + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_KEY_SIZE_256 = 0x1, +}; + +enum { + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_DEK = 0x1, +}; + +struct mlx5_ifc_tls_static_params_bits { + u8 const_2[0x2]; + u8 tls_version[0x4]; + u8 const_1[0x2]; + u8 reserved_at_8[0x14]; + u8 encryption_standard[0x4]; + + u8 reserved_at_20[0x20]; + + u8 initial_record_number[0x40]; + + u8 resync_tcp_sn[0x20]; + + u8 gcm_iv[0x20]; + + u8 implicit_iv[0x40]; + + u8 reserved_at_100[0x8]; + u8 dek_index[0x18]; + + u8 reserved_at_120[0xe0]; +}; + +struct mlx5_ifc_tls_progress_params_bits { + u8 valid[0x1]; + u8 reserved_at_1[0x7]; + u8 pd[0x18]; + + u8 next_record_tcp_sn[0x20]; + + u8 hw_resync_tcp_sn[0x20]; + + u8 record_tracker_state[0x2]; + u8 auth_state[0x2]; + u8 reserved_at_64[0x4]; + u8 hw_offset_record_number[0x18]; }; #endif /* MLX5_IFC_H */ diff --git a/include/linux/mlx5/qp.h b/include/linux/mlx5/qp.h index d1f353c64797..127d224443e3 100644 --- a/include/linux/mlx5/qp.h +++ b/include/linux/mlx5/qp.h @@ -202,7 +202,12 @@ struct mlx5_wqe_ctrl_seg { u8 signature; u8 rsvd[2]; u8 fm_ce_se; - __be32 imm; + union { + __be32 general_id; + __be32 imm; + __be32 umr_mkey; + __be32 tisn; + }; }; #define MLX5_WQE_CTRL_DS_MASK 0x3f diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index 3d1c6cdbbba7..16060fb9b5e5 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -44,9 +44,6 @@ MLX5_VPORT_UPLINK_PLACEHOLDER + \ MLX5_VPORT_ECPF_PLACEHOLDER(mdev)) -#define MLX5_TOTAL_VPORTS(mdev) (MLX5_SPECIAL_VPORTS(mdev) + \ - mlx5_core_max_vfs(mdev)) - #define MLX5_VPORT_MANAGER(mdev) \ (MLX5_CAP_GEN(mdev, vport_group_manager) && \ (MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) && \ @@ -58,6 +55,7 @@ enum { MLX5_CAP_INLINE_MODE_NOT_REQUIRED, }; +/* Vport number for each function must keep unchanged */ enum { MLX5_VPORT_PF = 0x0, MLX5_VPORT_FIRST_VF = 0x1, @@ -69,7 +67,8 @@ u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport); int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport, u8 other_vport, u8 state); int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, - u16 vport, u8 *addr); + u16 vport, bool other, u8 *addr); +int mlx5_query_mac_address(struct mlx5_core_dev *mdev, u8 *addr); int mlx5_query_nic_vport_min_inline(struct mlx5_core_dev *mdev, u16 vport, u8 *min_inline); void mlx5_query_min_inline(struct mlx5_core_dev *mdev, u8 *min_inline); diff --git a/include/linux/phy.h b/include/linux/phy.h index d0af7d37fdf9..1739c6dc470e 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -195,6 +195,8 @@ static inline const char *phy_modes(phy_interface_t interface) /* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */ #define MII_ADDR_C45 (1<<30) +#define MII_DEVADDR_C45_SHIFT 16 +#define MII_REGADDR_C45_MASK GENMASK(15, 0) struct device; struct phylink; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index b5d427b149c9..7ece49d5f8ef 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3919,18 +3919,16 @@ static inline bool __skb_checksum_convert_check(struct sk_buff *skb) return (skb->ip_summed == CHECKSUM_NONE && skb->csum_valid); } -static inline void __skb_checksum_convert(struct sk_buff *skb, - __sum16 check, __wsum pseudo) +static inline void __skb_checksum_convert(struct sk_buff *skb, __wsum pseudo) { skb->csum = ~pseudo; skb->ip_summed = CHECKSUM_COMPLETE; } -#define skb_checksum_try_convert(skb, proto, check, compute_pseudo) \ +#define skb_checksum_try_convert(skb, proto, compute_pseudo) \ do { \ if (__skb_checksum_convert_check(skb)) \ - __skb_checksum_convert(skb, check, \ - compute_pseudo(skb, proto)); \ + __skb_checksum_convert(skb, compute_pseudo(skb, proto)); \ } while (0) static inline void skb_remcsum_adjust_partial(struct sk_buff *skb, void *ptr, diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 9a5330eed794..5bc1e30dedde 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1143,6 +1143,26 @@ struct hci_cp_write_sc_support { __u8 support; } __packed; +#define HCI_OP_READ_AUTH_PAYLOAD_TO 0x0c7b +struct hci_cp_read_auth_payload_to { + __le16 handle; +} __packed; +struct hci_rp_read_auth_payload_to { + __u8 status; + __le16 handle; + __le16 timeout; +} __packed; + +#define HCI_OP_WRITE_AUTH_PAYLOAD_TO 0x0c7c +struct hci_cp_write_auth_payload_to { + __le16 handle; + __le16 timeout; +} __packed; +struct hci_rp_write_auth_payload_to { + __u8 status; + __le16 handle; +} __packed; + #define HCI_OP_READ_LOCAL_OOB_EXT_DATA 0x0c7d struct hci_rp_read_local_oob_ext_data { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 05b1b96f4d9e..ded574b32c20 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -199,6 +199,8 @@ struct adv_info { /* Default min/max age of connection information (1s/3s) */ #define DEFAULT_CONN_INFO_MIN_AGE 1000 #define DEFAULT_CONN_INFO_MAX_AGE 3000 +/* Default authenticated payload timeout 30s */ +#define DEFAULT_AUTH_PAYLOAD_TIMEOUT 0x0bb8 struct amp_assoc { __u16 len; @@ -275,6 +277,7 @@ struct hci_dev { __u16 discov_interleaved_timeout; __u16 conn_info_min_age; __u16 conn_info_max_age; + __u16 auth_payload_timeout; __u8 ssp_debug_mode; __u8 hw_error_code; __u32 clock; @@ -481,6 +484,7 @@ struct hci_conn { __u16 disc_timeout; __u16 conn_timeout; __u16 setting; + __u16 auth_payload_timeout; __u16 le_conn_min_interval; __u16 le_conn_max_interval; __u16 le_conn_interval; diff --git a/include/net/netfilter/nf_conntrack_synproxy.h b/include/net/netfilter/nf_conntrack_synproxy.h index c5659dcf5b1a..8f00125b06f4 100644 --- a/include/net/netfilter/nf_conntrack_synproxy.h +++ b/include/net/netfilter/nf_conntrack_synproxy.h @@ -2,6 +2,7 @@ #ifndef _NF_CONNTRACK_SYNPROXY_H #define _NF_CONNTRACK_SYNPROXY_H +#include <net/netfilter/nf_conntrack_seqadj.h> #include <net/netns/generic.h> struct nf_conn_synproxy { diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 7239105d9d2e..3cb6dcf53a4e 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -120,6 +120,5 @@ nfqueue_hash(const struct sk_buff *skb, u16 queue, u16 queues_total, u8 family, } int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, - const struct nf_hook_entries *entries, unsigned int index, - unsigned int verdict); + unsigned int index, unsigned int verdict); #endif /* _NF_QUEUE_H */ diff --git a/include/net/netfilter/nf_synproxy.h b/include/net/netfilter/nf_synproxy.h index 3e8b3f03b687..87d73fb5279d 100644 --- a/include/net/netfilter/nf_synproxy.h +++ b/include/net/netfilter/nf_synproxy.h @@ -39,6 +39,11 @@ unsigned int ipv6_synproxy_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *nhs); int nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net); void nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net); +#else +static inline int +nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net) { return 0; } +static inline void +nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net) {}; #endif /* CONFIG_IPV6 */ #endif /* _NF_SYNPROXY_SHARED_H */ diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h new file mode 100644 index 000000000000..5c69e9b09388 --- /dev/null +++ b/include/net/netfilter/nft_meta.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _NFT_META_H_ +#define _NFT_META_H_ + +struct nft_meta { + enum nft_meta_keys key:8; + union { + enum nft_registers dreg:8; + enum nft_registers sreg:8; + }; +}; + +extern const struct nla_policy nft_meta_policy[]; + +int nft_meta_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]); + +int nft_meta_set_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]); + +int nft_meta_get_dump(struct sk_buff *skb, + const struct nft_expr *expr); + +int nft_meta_set_dump(struct sk_buff *skb, + const struct nft_expr *expr); + +void nft_meta_get_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt); + +void nft_meta_set_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt); + +void nft_meta_set_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr); + +int nft_meta_set_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data); + +#endif diff --git a/include/net/page_pool.h b/include/net/page_pool.h index ee9c871d2043..2cbcdbdec254 100644 --- a/include/net/page_pool.h +++ b/include/net/page_pool.h @@ -101,6 +101,12 @@ struct page_pool { struct ptr_ring ring; atomic_t pages_state_release_cnt; + + /* A page_pool is strictly tied to a single RX-queue being + * protected by NAPI, due to above pp_alloc_cache. This + * refcnt serves purpose is to simplify drivers error handling. + */ + refcount_t user_cnt; }; struct page *page_pool_alloc_pages(struct page_pool *pool, gfp_t gfp); @@ -134,6 +140,15 @@ static inline void page_pool_free(struct page_pool *pool) #endif } +/* Drivers use this instead of page_pool_free */ +static inline void page_pool_destroy(struct page_pool *pool) +{ + if (!pool) + return; + + page_pool_free(pool); +} + /* Never call this directly, use helpers below */ void __page_pool_put_page(struct page_pool *pool, struct page *page, bool allow_direct); @@ -201,4 +216,14 @@ static inline bool is_page_pool_compiled_in(void) #endif } +static inline void page_pool_get(struct page_pool *pool) +{ + refcount_inc(&pool->user_cnt); +} + +static inline bool page_pool_put(struct page_pool *pool) +{ + return refcount_dec_and_test(&pool->user_cnt); +} + #endif /* _NET_PAGE_POOL_H */ diff --git a/include/net/xfrm.h b/include/net/xfrm.h index a2907873ed56..b22db30c3d88 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -346,22 +346,19 @@ void km_state_expired(struct xfrm_state *x, int hard, u32 portid); int __xfrm_state_delete(struct xfrm_state *x); struct xfrm_state_afinfo { - unsigned int family; - unsigned int proto; - __be16 eth_proto; - struct module *owner; - const struct xfrm_type *type_map[IPPROTO_MAX]; - const struct xfrm_type_offload *type_offload_map[IPPROTO_MAX]; - - int (*init_flags)(struct xfrm_state *x); - void (*init_tempsel)(struct xfrm_selector *sel, - const struct flowi *fl); - void (*init_temprop)(struct xfrm_state *x, - const struct xfrm_tmpl *tmpl, - const xfrm_address_t *daddr, - const xfrm_address_t *saddr); - int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n); - int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n); + u8 family; + u8 proto; + + const struct xfrm_type_offload *type_offload_esp; + + const struct xfrm_type *type_esp; + const struct xfrm_type *type_ipip; + const struct xfrm_type *type_ipip6; + const struct xfrm_type *type_comp; + const struct xfrm_type *type_ah; + const struct xfrm_type *type_routing; + const struct xfrm_type *type_dstopts; + int (*output)(struct net *net, struct sock *sk, struct sk_buff *skb); int (*output_finish)(struct sock *sk, struct sk_buff *skb); int (*extract_input)(struct xfrm_state *x, @@ -407,12 +404,10 @@ struct xfrm_type { int (*reject)(struct xfrm_state *, struct sk_buff *, const struct flowi *); int (*hdr_offset)(struct xfrm_state *, struct sk_buff *, u8 **); - /* Estimate maximal size of result of transformation of a dgram */ - u32 (*get_mtu)(struct xfrm_state *, int size); }; int xfrm_register_type(const struct xfrm_type *type, unsigned short family); -int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family); +void xfrm_unregister_type(const struct xfrm_type *type, unsigned short family); struct xfrm_type_offload { char *description; @@ -424,7 +419,7 @@ struct xfrm_type_offload { }; int xfrm_register_type_offload(const struct xfrm_type_offload *type, unsigned short family); -int xfrm_unregister_type_offload(const struct xfrm_type_offload *type, unsigned short family); +void xfrm_unregister_type_offload(const struct xfrm_type_offload *type, unsigned short family); static inline int xfrm_af2proto(unsigned int family) { @@ -1508,21 +1503,19 @@ struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark, u8 proto, unsigned short family); #ifdef CONFIG_XFRM_SUB_POLICY -int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, - unsigned short family, struct net *net); -int xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, +void xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, unsigned short family); +void xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, + unsigned short family); #else -static inline int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, - int n, unsigned short family, struct net *net) +static inline void xfrm_tmpl_sort(struct xfrm_tmpl **d, struct xfrm_tmpl **s, + int n, unsigned short family) { - return -ENOSYS; } -static inline int xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, - int n, unsigned short family) +static inline void xfrm_state_sort(struct xfrm_state **d, struct xfrm_state **s, + int n, unsigned short family) { - return -ENOSYS; } #endif @@ -1551,7 +1544,7 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); int xfrm_init_replay(struct xfrm_state *x); -int xfrm_state_mtu(struct xfrm_state *x, int mtu); +u32 xfrm_state_mtu(struct xfrm_state *x, int mtu); int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload); int xfrm_init_state(struct xfrm_state *x); int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); diff --git a/include/uapi/linux/ip_vs.h b/include/uapi/linux/ip_vs.h index e4f18061a4fd..4102ddcb4e14 100644 --- a/include/uapi/linux/ip_vs.h +++ b/include/uapi/linux/ip_vs.h @@ -128,6 +128,7 @@ enum { IP_VS_CONN_F_TUNNEL_TYPE_IPIP = 0, /* IPIP */ IP_VS_CONN_F_TUNNEL_TYPE_GUE, /* GUE */ + IP_VS_CONN_F_TUNNEL_TYPE_GRE, /* GRE */ IP_VS_CONN_F_TUNNEL_TYPE_MAX, }; diff --git a/include/uapi/linux/netfilter/nf_SYNPROXY.h b/include/uapi/linux/netfilter/nf_synproxy.h index 068d1b3a6f06..6f3791c8946f 100644 --- a/include/uapi/linux/netfilter/nf_SYNPROXY.h +++ b/include/uapi/linux/netfilter/nf_synproxy.h @@ -9,6 +9,10 @@ #define NF_SYNPROXY_OPT_SACK_PERM 0x04 #define NF_SYNPROXY_OPT_TIMESTAMP 0x08 #define NF_SYNPROXY_OPT_ECN 0x10 +#define NF_SYNPROXY_OPT_MASK (NF_SYNPROXY_OPT_MSS | \ + NF_SYNPROXY_OPT_WSCALE | \ + NF_SYNPROXY_OPT_SACK_PERM | \ + NF_SYNPROXY_OPT_TIMESTAMP) struct nf_synproxy_info { __u8 options; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index c6c8ec5c7c00..0e3462dfb182 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -795,6 +795,8 @@ enum nft_exthdr_attributes { * @NFT_META_SECPATH: boolean, secpath_exists (!!skb->sp) * @NFT_META_IIFKIND: packet input interface kind name (dev->rtnl_link_ops->kind) * @NFT_META_OIFKIND: packet output interface kind name (dev->rtnl_link_ops->kind) + * @NFT_META_BRI_IIFPVID: packet input bridge port pvid + * @NFT_META_BRI_IIFVPROTO: packet input bridge vlan proto */ enum nft_meta_keys { NFT_META_LEN, @@ -825,6 +827,8 @@ enum nft_meta_keys { NFT_META_SECPATH, NFT_META_IIFKIND, NFT_META_OIFKIND, + NFT_META_BRI_IIFPVID, + NFT_META_BRI_IIFVPROTO, }; /** @@ -1552,6 +1556,22 @@ enum nft_osf_flags { }; /** + * enum nft_synproxy_attributes - nf_tables synproxy expression netlink attributes + * + * @NFTA_SYNPROXY_MSS: mss value sent to the backend (NLA_U16) + * @NFTA_SYNPROXY_WSCALE: wscale value sent to the backend (NLA_U8) + * @NFTA_SYNPROXY_FLAGS: flags (NLA_U32) + */ +enum nft_synproxy_attributes { + NFTA_SYNPROXY_UNSPEC, + NFTA_SYNPROXY_MSS, + NFTA_SYNPROXY_WSCALE, + NFTA_SYNPROXY_FLAGS, + __NFTA_SYNPROXY_MAX, +}; +#define NFTA_SYNPROXY_MAX (__NFTA_SYNPROXY_MAX - 1) + +/** * enum nft_device_attributes - nf_tables device netlink attributes * * @NFTA_DEVICE_NAME: name of this device (NLA_STRING) diff --git a/include/uapi/linux/netfilter/xt_SYNPROXY.h b/include/uapi/linux/netfilter/xt_SYNPROXY.h index 4d5611d647df..19c04ed86172 100644 --- a/include/uapi/linux/netfilter/xt_SYNPROXY.h +++ b/include/uapi/linux/netfilter/xt_SYNPROXY.h @@ -2,7 +2,7 @@ #ifndef _XT_SYNPROXY_H #define _XT_SYNPROXY_H -#include <linux/netfilter/nf_SYNPROXY.h> +#include <linux/netfilter/nf_synproxy.h> #define XT_SYNPROXY_OPT_MSS NF_SYNPROXY_OPT_MSS #define XT_SYNPROXY_OPT_WSCALE NF_SYNPROXY_OPT_WSCALE diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h index 53cf446ce2e3..01853cec0209 100644 --- a/net/6lowpan/6lowpan_i.h +++ b/net/6lowpan/6lowpan_i.h @@ -18,24 +18,16 @@ extern const struct ndisc_ops lowpan_ndisc_ops; int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev); #ifdef CONFIG_6LOWPAN_DEBUGFS -int lowpan_dev_debugfs_init(struct net_device *dev); +void lowpan_dev_debugfs_init(struct net_device *dev); void lowpan_dev_debugfs_exit(struct net_device *dev); -int __init lowpan_debugfs_init(void); +void __init lowpan_debugfs_init(void); void lowpan_debugfs_exit(void); #else -static inline int lowpan_dev_debugfs_init(struct net_device *dev) -{ - return 0; -} - +static inline void lowpan_dev_debugfs_init(struct net_device *dev) { } static inline void lowpan_dev_debugfs_exit(struct net_device *dev) { } -static inline int __init lowpan_debugfs_init(void) -{ - return 0; -} - +static inline void __init lowpan_debugfs_init(void) { } static inline void lowpan_debugfs_exit(void) { } #endif /* CONFIG_6LOWPAN_DEBUGFS */ diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c index 2d68351f1ac4..a068757eabaf 100644 --- a/net/6lowpan/core.c +++ b/net/6lowpan/core.c @@ -42,9 +42,7 @@ int lowpan_register_netdevice(struct net_device *dev, if (ret < 0) return ret; - ret = lowpan_dev_debugfs_init(dev); - if (ret < 0) - unregister_netdevice(dev); + lowpan_dev_debugfs_init(dev); return ret; } @@ -152,9 +150,7 @@ static int __init lowpan_module_init(void) { int ret; - ret = lowpan_debugfs_init(); - if (ret < 0) - return ret; + lowpan_debugfs_init(); ret = register_netdevice_notifier(&lowpan_notifier); if (ret < 0) { diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c index f5a8eec9d7a3..1c140af06d52 100644 --- a/net/6lowpan/debugfs.c +++ b/net/6lowpan/debugfs.c @@ -163,11 +163,11 @@ static const struct file_operations lowpan_ctx_pfx_fops = { .release = single_release, }; -static int lowpan_dev_debugfs_ctx_init(struct net_device *dev, - struct dentry *ctx, u8 id) +static void lowpan_dev_debugfs_ctx_init(struct net_device *dev, + struct dentry *ctx, u8 id) { struct lowpan_dev *ldev = lowpan_dev(dev); - struct dentry *dentry, *root; + struct dentry *root; char buf[32]; WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE); @@ -175,34 +175,18 @@ static int lowpan_dev_debugfs_ctx_init(struct net_device *dev, sprintf(buf, "%d", id); root = debugfs_create_dir(buf, ctx); - if (!root) - return -EINVAL; - dentry = debugfs_create_file_unsafe("active", 0644, root, - &ldev->ctx.table[id], - &lowpan_ctx_flag_active_fops); - if (!dentry) - return -EINVAL; + debugfs_create_file("active", 0644, root, &ldev->ctx.table[id], + &lowpan_ctx_flag_active_fops); - dentry = debugfs_create_file_unsafe("compression", 0644, root, - &ldev->ctx.table[id], - &lowpan_ctx_flag_c_fops); - if (!dentry) - return -EINVAL; - - dentry = debugfs_create_file("prefix", 0644, root, - &ldev->ctx.table[id], - &lowpan_ctx_pfx_fops); - if (!dentry) - return -EINVAL; + debugfs_create_file("compression", 0644, root, &ldev->ctx.table[id], + &lowpan_ctx_flag_c_fops); - dentry = debugfs_create_file_unsafe("prefix_len", 0644, root, - &ldev->ctx.table[id], - &lowpan_ctx_plen_fops); - if (!dentry) - return -EINVAL; + debugfs_create_file("prefix", 0644, root, &ldev->ctx.table[id], + &lowpan_ctx_pfx_fops); - return 0; + debugfs_create_file("prefix_len", 0644, root, &ldev->ctx.table[id], + &lowpan_ctx_plen_fops); } static int lowpan_context_show(struct seq_file *file, void *offset) @@ -242,64 +226,39 @@ static int lowpan_short_addr_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(lowpan_short_addr_fops, lowpan_short_addr_get, NULL, "0x%04llx\n"); -static int lowpan_dev_debugfs_802154_init(const struct net_device *dev, +static void lowpan_dev_debugfs_802154_init(const struct net_device *dev, struct lowpan_dev *ldev) { - struct dentry *dentry, *root; + struct dentry *root; if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154)) - return 0; + return; root = debugfs_create_dir("ieee802154", ldev->iface_debugfs); - if (!root) - return -EINVAL; - - dentry = debugfs_create_file_unsafe("short_addr", 0444, root, - lowpan_802154_dev(dev)->wdev->ieee802154_ptr, - &lowpan_short_addr_fops); - if (!dentry) - return -EINVAL; - return 0; + debugfs_create_file("short_addr", 0444, root, + lowpan_802154_dev(dev)->wdev->ieee802154_ptr, + &lowpan_short_addr_fops); } -int lowpan_dev_debugfs_init(struct net_device *dev) +void lowpan_dev_debugfs_init(struct net_device *dev) { struct lowpan_dev *ldev = lowpan_dev(dev); - struct dentry *contexts, *dentry; - int ret, i; + struct dentry *contexts; + int i; /* creating the root */ ldev->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs); - if (!ldev->iface_debugfs) - goto fail; contexts = debugfs_create_dir("contexts", ldev->iface_debugfs); - if (!contexts) - goto remove_root; - - dentry = debugfs_create_file("show", 0644, contexts, - &lowpan_dev(dev)->ctx, - &lowpan_context_fops); - if (!dentry) - goto remove_root; - - for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) { - ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i); - if (ret < 0) - goto remove_root; - } - ret = lowpan_dev_debugfs_802154_init(dev, ldev); - if (ret < 0) - goto remove_root; + debugfs_create_file("show", 0644, contexts, &lowpan_dev(dev)->ctx, + &lowpan_context_fops); - return 0; + for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) + lowpan_dev_debugfs_ctx_init(dev, contexts, i); -remove_root: - lowpan_dev_debugfs_exit(dev); -fail: - return -EINVAL; + lowpan_dev_debugfs_802154_init(dev, ldev); } void lowpan_dev_debugfs_exit(struct net_device *dev) @@ -307,13 +266,9 @@ void lowpan_dev_debugfs_exit(struct net_device *dev) debugfs_remove_recursive(lowpan_dev(dev)->iface_debugfs); } -int __init lowpan_debugfs_init(void) +void __init lowpan_debugfs_init(void) { lowpan_debugfs = debugfs_create_dir("6lowpan", NULL); - if (!lowpan_debugfs) - return -EINVAL; - - return 0; } void lowpan_debugfs_exit(void) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 1555b0c6f7ec..9d41de1ec90f 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -164,26 +164,21 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev, int count = atomic_read(&dev->peer_count); const struct in6_addr *nexthop; struct lowpan_peer *peer; + struct neighbour *neigh; BT_DBG("peers %d addr %pI6c rt %p", count, daddr, rt); - /* If we have multiple 6lowpan peers, then check where we should - * send the packet. If only one peer exists, then we can send the - * packet right away. - */ - if (count == 1) { - rcu_read_lock(); - peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer, - list); - rcu_read_unlock(); - return peer; - } - if (!rt) { - nexthop = &lowpan_cb(skb)->gw; - - if (ipv6_addr_any(nexthop)) - return NULL; + if (ipv6_addr_any(&lowpan_cb(skb)->gw)) { + /* There is neither route nor gateway, + * probably the destination is a direct peer. + */ + nexthop = daddr; + } else { + /* There is a known gateway + */ + nexthop = &lowpan_cb(skb)->gw; + } } else { nexthop = rt6_nexthop(rt, daddr); @@ -209,6 +204,20 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev, } } + /* use the neighbour cache for matching addresses assigned by SLAAC + */ + neigh = __ipv6_neigh_lookup(dev->netdev, nexthop); + if (neigh) { + list_for_each_entry_rcu(peer, &dev->peers, list) { + if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN)) { + neigh_release(neigh); + rcu_read_unlock(); + return peer; + } + } + neigh_release(neigh); + } + rcu_read_unlock(); return NULL; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 15d1cb5aee18..ad5b0ac1f9ce 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -520,6 +520,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, set_bit(HCI_CONN_POWER_SAVE, &conn->flags); conn->disc_timeout = HCI_DISCONN_TIMEOUT; + /* Set Default Authenticated payload timeout to 30s */ + conn->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT; + if (conn->role == HCI_ROLE_MASTER) conn->out = true; @@ -912,7 +915,7 @@ static void hci_req_directed_advertising(struct hci_request *req, sizeof(cp), &cp); } - __hci_req_enable_ext_advertising(req); + __hci_req_enable_ext_advertising(req, 0x00); } else { struct hci_cp_le_set_adv_param cp; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b81bf53c5ac4..b9585e7d9d2e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2827,7 +2827,7 @@ int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags, memset(adv_instance->scan_rsp_data, 0, sizeof(adv_instance->scan_rsp_data)); } else { - if (hdev->adv_instance_cnt >= HCI_MAX_ADV_INSTANCES || + if (hdev->adv_instance_cnt >= hdev->le_num_of_adv_sets || instance < 1 || instance > HCI_MAX_ADV_INSTANCES) return -EOVERFLOW; @@ -3195,11 +3195,13 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_min_key_size = SMP_MIN_ENC_KEY_SIZE; hdev->le_tx_def_phys = HCI_LE_SET_PHY_1M; hdev->le_rx_def_phys = HCI_LE_SET_PHY_1M; + hdev->le_num_of_adv_sets = HCI_MAX_ADV_INSTANCES; hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT; hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT; hdev->conn_info_min_age = DEFAULT_CONN_INFO_MIN_AGE; hdev->conn_info_max_age = DEFAULT_CONN_INFO_MAX_AGE; + hdev->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT; mutex_init(&hdev->lock); mutex_init(&hdev->req_lock); diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 51f5b1efc3a5..bb67f4a5479a 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -941,6 +941,35 @@ static int adv_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get, adv_max_interval_set, "%llu\n"); +static int auth_payload_timeout_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x0001 || val > 0xffff) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->auth_payload_timeout = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int auth_payload_timeout_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->auth_payload_timeout; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(auth_payload_timeout_fops, + auth_payload_timeout_get, + auth_payload_timeout_set, "%llu\n"); + DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter, HCI_QUIRK_STRICT_DUPLICATE_FILTER); DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery, @@ -994,6 +1023,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev) &adv_max_interval_fops); debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs, &hdev->discov_interleaved_timeout); + debugfs_create_file("auth_payload_timeout", 0644, hdev->debugfs, hdev, + &auth_payload_timeout_fops); debugfs_create_file("quirk_strict_duplicate_filter", 0644, hdev->debugfs, hdev, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9e4fcf406d9c..cdb00c2ef242 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -579,6 +579,51 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); } +static void hci_cc_read_auth_payload_timeout(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_auth_payload_to *rp = (void *)skb->data; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn) + conn->auth_payload_timeout = __le16_to_cpu(rp->timeout); + + hci_dev_unlock(hdev); +} + +static void hci_cc_write_auth_payload_timeout(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_write_auth_payload_to *rp = (void *)skb->data; + struct hci_conn *conn; + void *sent; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_AUTH_PAYLOAD_TO); + if (!sent) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn) + conn->auth_payload_timeout = get_unaligned_le16(sent + 2); + + hci_dev_unlock(hdev); +} + static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2975,6 +3020,25 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) goto unlock; } + /* Set the default Authenticated Payload Timeout after + * an LE Link is established. As per Core Spec v5.0, Vol 2, Part B + * Section 3.3, the HCI command WRITE_AUTH_PAYLOAD_TIMEOUT should be + * sent when the link is active and Encryption is enabled, the conn + * type can be either LE or ACL and controller must support LMP Ping. + * Ensure for AES-CCM encryption as well. + */ + if (test_bit(HCI_CONN_ENCRYPT, &conn->flags) && + test_bit(HCI_CONN_AES_CCM, &conn->flags) && + ((conn->type == ACL_LINK && lmp_ping_capable(hdev)) || + (conn->type == LE_LINK && (hdev->le_features[0] & HCI_LE_PING)))) { + struct hci_cp_write_auth_payload_to cp; + + cp.handle = cpu_to_le16(conn->handle); + cp.timeout = cpu_to_le16(hdev->auth_payload_timeout); + hci_send_cmd(conn->hdev, HCI_OP_WRITE_AUTH_PAYLOAD_TO, + sizeof(cp), &cp); + } + notify: if (conn->state == BT_CONFIG) { if (!ev->status) @@ -3170,6 +3234,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb, hci_cc_write_sc_support(hdev, skb); break; + case HCI_OP_READ_AUTH_PAYLOAD_TO: + hci_cc_read_auth_payload_timeout(hdev, skb); + break; + + case HCI_OP_WRITE_AUTH_PAYLOAD_TO: + hci_cc_write_auth_payload_timeout(hdev, skb); + break; + case HCI_OP_READ_LOCAL_VERSION: hci_cc_read_local_version(hdev, skb); break; @@ -5588,6 +5660,11 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, return send_conn_param_neg_reply(hdev, handle, HCI_ERROR_UNKNOWN_CONN_ID); + if (min < hcon->le_conn_min_interval || + max > hcon->le_conn_max_interval) + return send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_INVALID_LL_PARAMS); + if (hci_check_conn_params(min, max, latency, timeout)) return send_conn_param_neg_reply(hdev, handle, HCI_ERROR_INVALID_LL_PARAMS); diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index e9a95ed65491..621f1a97d803 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -1601,7 +1601,7 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance) cp.own_addr_type = own_addr_type; cp.channel_map = hdev->le_adv_channel_map; cp.tx_power = 127; - cp.handle = 0; + cp.handle = instance; if (flags & MGMT_ADV_FLAG_SEC_2M) { cp.primary_phy = HCI_ADV_PHY_1M; @@ -1643,11 +1643,21 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance) return 0; } -void __hci_req_enable_ext_advertising(struct hci_request *req) +int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance) { + struct hci_dev *hdev = req->hdev; struct hci_cp_le_set_ext_adv_enable *cp; struct hci_cp_ext_adv_set *adv_set; u8 data[sizeof(*cp) + sizeof(*adv_set) * 1]; + struct adv_info *adv_instance; + + if (instance > 0) { + adv_instance = hci_find_adv_instance(hdev, instance); + if (!adv_instance) + return -EINVAL; + } else { + adv_instance = NULL; + } cp = (void *) data; adv_set = (void *) cp->data; @@ -1659,11 +1669,23 @@ void __hci_req_enable_ext_advertising(struct hci_request *req) memset(adv_set, 0, sizeof(*adv_set)); - adv_set->handle = 0; + adv_set->handle = instance; + + /* Set duration per instance since controller is responsible for + * scheduling it. + */ + if (adv_instance && adv_instance->duration) { + u16 duration = adv_instance->duration * MSEC_PER_SEC; + + /* Time = N * 10 ms */ + adv_set->duration = cpu_to_le16(duration / 10); + } hci_req_add(req, HCI_OP_LE_SET_EXT_ADV_ENABLE, sizeof(*cp) + sizeof(*adv_set) * cp->num_of_sets, data); + + return 0; } int __hci_req_start_ext_adv(struct hci_request *req, u8 instance) @@ -1679,7 +1701,7 @@ int __hci_req_start_ext_adv(struct hci_request *req, u8 instance) return err; __hci_req_update_scan_rsp_data(req, instance); - __hci_req_enable_ext_advertising(req); + __hci_req_enable_ext_advertising(req, instance); return 0; } @@ -1723,10 +1745,13 @@ int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance, adv_instance->remaining_time = adv_instance->remaining_time - timeout; - hdev->adv_instance_timeout = timeout; - queue_delayed_work(hdev->req_workqueue, + /* Only use work for scheduling instances with legacy advertising */ + if (!ext_adv_capable(hdev)) { + hdev->adv_instance_timeout = timeout; + queue_delayed_work(hdev->req_workqueue, &hdev->adv_instance_expire, msecs_to_jiffies(timeout * 1000)); + } /* If we're just re-scheduling the same instance again then do not * execute any HCI commands. This happens when a single instance is @@ -2744,7 +2769,8 @@ static int powered_update_hci(struct hci_request *req, unsigned long opt) if (!ext_adv_capable(hdev)) __hci_req_enable_advertising(req); else if (!err) - __hci_req_enable_ext_advertising(req); + __hci_req_enable_ext_advertising(req, + 0x00); } } else if (!list_empty(&hdev->adv_instances)) { struct adv_info *adv_instance; diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h index 55b2050cc9ff..a7019fbeadd3 100644 --- a/net/bluetooth/hci_request.h +++ b/net/bluetooth/hci_request.h @@ -83,7 +83,7 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk, int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance); int __hci_req_start_ext_adv(struct hci_request *req, u8 instance); -void __hci_req_enable_ext_advertising(struct hci_request *req); +int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance); void __hci_req_clear_ext_adv_sets(struct hci_request *req); int hci_get_random_address(struct hci_dev *hdev, bool require_privacy, bool use_rpa, struct adv_info *adv_instance, diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index a442e21f3894..5abd423b55fa 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -775,7 +775,7 @@ static int hidp_setup_hid(struct hidp_session *session, hid->version = req->version; hid->country = req->country; - strncpy(hid->name, req->name, sizeof(hid->name)); + strscpy(hid->name, req->name, sizeof(hid->name)); snprintf(hid->phys, sizeof(hid->phys), "%pMR", &l2cap_pi(session->ctrl_sock->sk)->chan->src); diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c index 2151913892ce..03be6a4baef3 100644 --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -192,6 +192,7 @@ static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne ca.version = ca32.version; ca.flags = ca32.flags; ca.idle_to = ca32.idle_to; + ca32.name[sizeof(ca32.name) - 1] = '\0'; memcpy(ca.name, ca32.name, 128); csock = sockfd_lookup(ca.ctrl_sock, &err); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9f77432dbe38..007317b072b4 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -168,11 +168,18 @@ static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, return c; } -static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) +static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src, + u8 src_type) { struct l2cap_chan *c; list_for_each_entry(c, &chan_list, global_l) { + if (src_type == BDADDR_BREDR && c->src_type != BDADDR_BREDR) + continue; + + if (src_type != BDADDR_BREDR && c->src_type == BDADDR_BREDR) + continue; + if (c->sport == psm && !bacmp(&c->src, src)) return c; } @@ -185,7 +192,7 @@ int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) write_lock(&chan_list_lock); - if (psm && __l2cap_global_chan_by_addr(psm, src)) { + if (psm && __l2cap_global_chan_by_addr(psm, src, chan->src_type)) { err = -EADDRINUSE; goto done; } @@ -209,7 +216,8 @@ int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) err = -EINVAL; for (p = start; p <= end; p += incr) - if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) { + if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src, + chan->src_type)) { chan->psm = cpu_to_le16(p); chan->sport = cpu_to_le16(p); err = 0; @@ -4394,6 +4402,12 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_chan_lock(chan); + if (chan->state != BT_DISCONN) { + l2cap_chan_unlock(chan); + mutex_unlock(&conn->chan_lock); + return 0; + } + l2cap_chan_hold(chan); l2cap_chan_del(chan, 0); @@ -5291,7 +5305,14 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, memset(&rsp, 0, sizeof(rsp)); - err = hci_check_conn_params(min, max, latency, to_multiplier); + if (min < hcon->le_conn_min_interval || + max > hcon->le_conn_max_interval) { + BT_DBG("requested connection interval exceeds current bounds."); + err = -EINVAL; + } else { + err = hci_check_conn_params(min, max, latency, to_multiplier); + } + if (err) rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); else diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index e68c715f8d37..6c2b4e6e87ba 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2579,6 +2579,19 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, goto distribute; } + /* Drop IRK if peer is using identity address during pairing but is + * providing different address as identity information. + * + * Microsoft Surface Precision Mouse is known to have this bug. + */ + if (hci_is_identity_address(&hcon->dst, hcon->dst_type) && + (bacmp(&info->bdaddr, &hcon->dst) || + info->addr_type != hcon->dst_type)) { + bt_dev_err(hcon->hdev, + "ignoring IRK with invalid identity address"); + goto distribute; + } + bacpy(&smp->id_addr, &info->bdaddr); smp->id_addr_type = info->addr_type; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 21b74e7a7b2f..512383d5e53f 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -234,7 +234,7 @@ static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb) kfree_skb(skb); return RX_HANDLER_CONSUMED; case NF_QUEUE: - ret = nf_queue(skb, &state, e, i, verdict); + ret = nf_queue(skb, &state, i, verdict); if (ret == 1) continue; return RX_HANDLER_CONSUMED; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index f47f526b4f19..021cc9f66804 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -797,6 +797,16 @@ bool br_vlan_enabled(const struct net_device *dev) } EXPORT_SYMBOL_GPL(br_vlan_enabled); +int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto) +{ + struct net_bridge *br = netdev_priv(dev); + + *p_proto = ntohs(br->vlan_proto); + + return 0; +} +EXPORT_SYMBOL_GPL(br_vlan_get_proto); + int __br_vlan_set_proto(struct net_bridge *br, __be16 proto) { int err = 0; @@ -1227,13 +1237,11 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v, } } -int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid) +static int __br_vlan_get_pvid(const struct net_device *dev, + struct net_bridge_port *p, u16 *p_pvid) { struct net_bridge_vlan_group *vg; - struct net_bridge_port *p; - ASSERT_RTNL(); - p = br_port_get_check_rtnl(dev); if (p) vg = nbp_vlan_group(p); else if (netif_is_bridge_master(dev)) @@ -1244,8 +1252,21 @@ int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid) *p_pvid = br_get_pvid(vg); return 0; } + +int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid) +{ + ASSERT_RTNL(); + + return __br_vlan_get_pvid(dev, br_port_get_check_rtnl(dev), p_pvid); +} EXPORT_SYMBOL_GPL(br_vlan_get_pvid); +int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid) +{ + return __br_vlan_get_pvid(dev, br_port_get_check_rcu(dev), p_pvid); +} +EXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu); + int br_vlan_get_info(const struct net_device *dev, u16 vid, struct bridge_vlan_info *p_vinfo) { diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index f4fb0b9b927d..fbc708508360 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -9,6 +9,12 @@ menuconfig NF_TABLES_BRIDGE bool "Ethernet Bridge nf_tables support" if NF_TABLES_BRIDGE + +config NFT_BRIDGE_META + tristate "Netfilter nf_table bridge meta support" + help + Add support for bridge dedicated meta key. + config NFT_BRIDGE_REJECT tristate "Netfilter nf_tables bridge reject support" depends on NFT_REJECT && NFT_REJECT_IPV4 && NFT_REJECT_IPV6 diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index 9d7767322a64..8e2c5759d964 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -3,6 +3,7 @@ # Makefile for the netfilter modules for Link Layer filtering on a bridge. # +obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o obj-$(CONFIG_NFT_BRIDGE_REJECT) += nft_reject_bridge.o # connection tracking diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c new file mode 100644 index 000000000000..bed66f536b34 --- /dev/null +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netlink.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <net/netfilter/nf_tables.h> +#include <net/netfilter/nft_meta.h> +#include <linux/if_bridge.h> + +static const struct net_device * +nft_meta_get_bridge(const struct net_device *dev) +{ + if (dev && netif_is_bridge_port(dev)) + return netdev_master_upper_dev_get_rcu((struct net_device *)dev); + + return NULL; +} + +static void nft_meta_bridge_get_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_meta *priv = nft_expr_priv(expr); + const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); + u32 *dest = ®s->data[priv->dreg]; + const struct net_device *br_dev; + + switch (priv->key) { + case NFT_META_BRI_IIFNAME: + br_dev = nft_meta_get_bridge(in); + if (!br_dev) + goto err; + break; + case NFT_META_BRI_OIFNAME: + br_dev = nft_meta_get_bridge(out); + if (!br_dev) + goto err; + break; + case NFT_META_BRI_IIFPVID: { + u16 p_pvid; + + br_dev = nft_meta_get_bridge(in); + if (!br_dev || !br_vlan_enabled(br_dev)) + goto err; + + br_vlan_get_pvid_rcu(in, &p_pvid); + nft_reg_store16(dest, p_pvid); + return; + } + case NFT_META_BRI_IIFVPROTO: { + u16 p_proto; + + br_dev = nft_meta_get_bridge(in); + if (!br_dev || !br_vlan_enabled(br_dev)) + goto err; + + br_vlan_get_proto(br_dev, &p_proto); + nft_reg_store16(dest, p_proto); + return; + } + default: + goto out; + } + + strncpy((char *)dest, br_dev->name, IFNAMSIZ); + return; +out: + return nft_meta_get_eval(expr, regs, pkt); +err: + regs->verdict.code = NFT_BREAK; +} + +static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_meta *priv = nft_expr_priv(expr); + unsigned int len; + + priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); + switch (priv->key) { + case NFT_META_BRI_IIFNAME: + case NFT_META_BRI_OIFNAME: + len = IFNAMSIZ; + break; + case NFT_META_BRI_IIFPVID: + case NFT_META_BRI_IIFVPROTO: + len = sizeof(u16); + break; + default: + return nft_meta_get_init(ctx, expr, tb); + } + + priv->dreg = nft_parse_register(tb[NFTA_META_DREG]); + return nft_validate_register_store(ctx, priv->dreg, NULL, + NFT_DATA_VALUE, len); +} + +static struct nft_expr_type nft_meta_bridge_type; +static const struct nft_expr_ops nft_meta_bridge_get_ops = { + .type = &nft_meta_bridge_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .eval = nft_meta_bridge_get_eval, + .init = nft_meta_bridge_get_init, + .dump = nft_meta_get_dump, +}; + +static const struct nft_expr_ops nft_meta_bridge_set_ops = { + .type = &nft_meta_bridge_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .eval = nft_meta_set_eval, + .init = nft_meta_set_init, + .destroy = nft_meta_set_destroy, + .dump = nft_meta_set_dump, + .validate = nft_meta_set_validate, +}; + +static const struct nft_expr_ops * +nft_meta_bridge_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + if (tb[NFTA_META_KEY] == NULL) + return ERR_PTR(-EINVAL); + + if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) + return ERR_PTR(-EINVAL); + + if (tb[NFTA_META_DREG]) + return &nft_meta_bridge_get_ops; + + if (tb[NFTA_META_SREG]) + return &nft_meta_bridge_set_ops; + + return ERR_PTR(-EINVAL); +} + +static struct nft_expr_type nft_meta_bridge_type __read_mostly = { + .family = NFPROTO_BRIDGE, + .name = "meta", + .select_ops = nft_meta_bridge_select_ops, + .policy = nft_meta_policy, + .maxattr = NFTA_META_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_meta_bridge_module_init(void) +{ + return nft_register_expr(&nft_meta_bridge_type); +} + +static void __exit nft_meta_bridge_module_exit(void) +{ + nft_unregister_expr(&nft_meta_bridge_type); +} + +module_init(nft_meta_bridge_module_init); +module_exit(nft_meta_bridge_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("wenxu <[email protected]>"); +MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta"); diff --git a/net/core/page_pool.c b/net/core/page_pool.c index b366f59885c1..3272dc7a8c81 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -49,6 +49,9 @@ static int page_pool_init(struct page_pool *pool, atomic_set(&pool->pages_state_release_cnt, 0); + /* Driver calling page_pool_create() also call page_pool_destroy() */ + refcount_set(&pool->user_cnt, 1); + if (pool->p.flags & PP_FLAG_DMA_MAP) get_device(pool->p.dev); @@ -70,6 +73,7 @@ struct page_pool *page_pool_create(const struct page_pool_params *params) kfree(pool); return ERR_PTR(err); } + return pool; } EXPORT_SYMBOL(page_pool_create); @@ -356,6 +360,10 @@ static void __warn_in_flight(struct page_pool *pool) void __page_pool_free(struct page_pool *pool) { + /* Only last user actually free/release resources */ + if (!page_pool_put(pool)) + return; + WARN(pool->alloc.count, "API usage violation"); WARN(!ptr_ring_empty(&pool->ring), "ptr_ring is not empty"); diff --git a/net/core/xdp.c b/net/core/xdp.c index 829377cc83db..d7bf62ffbb5e 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -370,6 +370,9 @@ int xdp_rxq_info_reg_mem_model(struct xdp_rxq_info *xdp_rxq, goto err; } + if (type == MEM_TYPE_PAGE_POOL) + page_pool_get(xdp_alloc->page_pool); + mutex_unlock(&mem_id_lock); trace_mem_connect(xdp_alloc, xdp_rxq); diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index 9c3afd550612..974179b3b314 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -590,8 +590,7 @@ static void __exit ah4_fini(void) { if (xfrm4_protocol_deregister(&ah4_protocol, IPPROTO_AH) < 0) pr_info("%s: can't remove protocol\n", __func__); - if (xfrm_unregister_type(&ah_type, AF_INET) < 0) - pr_info("%s: can't remove xfrm type\n", __func__); + xfrm_unregister_type(&ah_type, AF_INET); } module_init(ah4_init); diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index b9ae95576084..5c967764041f 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -33,8 +33,6 @@ struct esp_output_extra { #define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0])) -static u32 esp4_get_mtu(struct xfrm_state *x, int mtu); - /* * Allocate an AEAD request structure with extra space for SG and IV. * @@ -506,7 +504,7 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); u32 padto; - padto = min(x->tfcpad, esp4_get_mtu(x, dst->child_mtu_cached)); + padto = min(x->tfcpad, xfrm_state_mtu(x, dst->child_mtu_cached)); if (skb->len < padto) esp.tfclen = padto - skb->len; } @@ -788,28 +786,6 @@ out: return err; } -static u32 esp4_get_mtu(struct xfrm_state *x, int mtu) -{ - struct crypto_aead *aead = x->data; - u32 blksize = ALIGN(crypto_aead_blocksize(aead), 4); - unsigned int net_adj; - - switch (x->props.mode) { - case XFRM_MODE_TRANSPORT: - case XFRM_MODE_BEET: - net_adj = sizeof(struct iphdr); - break; - case XFRM_MODE_TUNNEL: - net_adj = 0; - break; - default: - BUG(); - } - - return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - - net_adj) & ~(blksize - 1)) + net_adj - 2; -} - static int esp4_err(struct sk_buff *skb, u32 info) { struct net *net = dev_net(skb->dev); @@ -1035,7 +1011,6 @@ static const struct xfrm_type esp_type = .flags = XFRM_TYPE_REPLAY_PROT, .init_state = esp_init_state, .destructor = esp_destroy, - .get_mtu = esp4_get_mtu, .input = esp_input, .output = esp_output, }; @@ -1066,8 +1041,7 @@ static void __exit esp4_fini(void) { if (xfrm4_protocol_deregister(&esp4_protocol, IPPROTO_ESP) < 0) pr_info("%s: can't remove protocol\n", __func__); - if (xfrm_unregister_type(&esp_type, AF_INET) < 0) - pr_info("%s: can't remove xfrm type\n", __func__); + xfrm_unregister_type(&esp_type, AF_INET); } module_init(esp4_init); diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index 2e5e377f50a1..0e4a7cf6bc87 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -312,9 +312,7 @@ static int __init esp4_offload_init(void) static void __exit esp4_offload_exit(void) { - if (xfrm_unregister_type_offload(&esp_type_offload, AF_INET) < 0) - pr_info("%s: can't remove xfrm type offload\n", __func__); - + xfrm_unregister_type_offload(&esp_type_offload, AF_INET); inet_del_offload(&esp4_offload, IPPROTO_ESP); } diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c index 293acfb36376..44bfeecac33e 100644 --- a/net/ipv4/gre_demux.c +++ b/net/ipv4/gre_demux.c @@ -83,7 +83,7 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, options = (__be32 *)(greh + 1); if (greh->flags & GRE_CSUM) { if (!skb_checksum_simple_validate(skb)) { - skb_checksum_try_convert(skb, IPPROTO_GRE, 0, + skb_checksum_try_convert(skb, IPPROTO_GRE, null_compute_pseudo); } else if (csum_err) { *csum_err = true; diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index 2f4cdcc13d53..59bfa3825810 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -186,8 +186,7 @@ static void __exit ipcomp4_fini(void) { if (xfrm4_protocol_deregister(&ipcomp4_protocol, IPPROTO_COMP) < 0) pr_info("%s: can't remove protocol\n", __func__); - if (xfrm_unregister_type(&ipcomp_type, AF_INET) < 0) - pr_info("%s: can't remove xfrm type\n", __func__); + xfrm_unregister_type(&ipcomp_type, AF_INET); } module_init(ipcomp4_init); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index dc1f510a7c81..abaa7f9371e5 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1964,17 +1964,30 @@ int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4, break; case 2: memset(&hash_keys, 0, sizeof(hash_keys)); - hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; /* skb is currently provided only when forwarding */ if (skb) { struct flow_keys keys; skb_flow_dissect_flow_keys(skb, &keys, 0); - - hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src; - hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst; + /* Inner can be v4 or v6 */ + if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src; + hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst; + } else if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src; + hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst; + hash_keys.tags.flow_label = keys.tags.flow_label; + hash_keys.basic.ip_proto = keys.basic.ip_proto; + } else { + /* Same as case 0 */ + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + ip_multipath_l3_keys(skb, &hash_keys); + } } else { /* Same as case 0 */ + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; hash_keys.addrs.v4addrs.src = fl4->saddr; hash_keys.addrs.v4addrs.dst = fl4->daddr; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 1b971bd95786..c21862ba9c02 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2224,8 +2224,7 @@ static int udp_unicast_rcv_skb(struct sock *sk, struct sk_buff *skb, int ret; if (inet_get_convert_csum(sk) && uh->check && !IS_UDPLITE(sk)) - skb_checksum_try_convert(skb, IPPROTO_UDP, uh->check, - inet_compute_pseudo); + skb_checksum_try_convert(skb, IPPROTO_UDP, inet_compute_pseudo); ret = udp_queue_rcv_skb(sk, skb); diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index 80c40b4981bb..f8ed3c3bb928 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c @@ -15,46 +15,6 @@ #include <linux/netfilter_ipv4.h> #include <linux/export.h> -static int xfrm4_init_flags(struct xfrm_state *x) -{ - if (xs_net(x)->ipv4.sysctl_ip_no_pmtu_disc) - x->props.flags |= XFRM_STATE_NOPMTUDISC; - return 0; -} - -static void -__xfrm4_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) -{ - const struct flowi4 *fl4 = &fl->u.ip4; - - sel->daddr.a4 = fl4->daddr; - sel->saddr.a4 = fl4->saddr; - sel->dport = xfrm_flowi_dport(fl, &fl4->uli); - sel->dport_mask = htons(0xffff); - sel->sport = xfrm_flowi_sport(fl, &fl4->uli); - sel->sport_mask = htons(0xffff); - sel->family = AF_INET; - sel->prefixlen_d = 32; - sel->prefixlen_s = 32; - sel->proto = fl4->flowi4_proto; - sel->ifindex = fl4->flowi4_oif; -} - -static void -xfrm4_init_temprop(struct xfrm_state *x, const struct xfrm_tmpl *tmpl, - const xfrm_address_t *daddr, const xfrm_address_t *saddr) -{ - x->id = tmpl->id; - if (x->id.daddr.a4 == 0) - x->id.daddr.a4 = daddr->a4; - x->props.saddr = tmpl->saddr; - if (x->props.saddr.a4 == 0) - x->props.saddr.a4 = saddr->a4; - x->props.mode = tmpl->mode; - x->props.reqid = tmpl->reqid; - x->props.family = AF_INET; -} - int xfrm4_extract_header(struct sk_buff *skb) { const struct iphdr *iph = ip_hdr(skb); @@ -74,11 +34,6 @@ int xfrm4_extract_header(struct sk_buff *skb) static struct xfrm_state_afinfo xfrm4_state_afinfo = { .family = AF_INET, .proto = IPPROTO_IPIP, - .eth_proto = htons(ETH_P_IP), - .owner = THIS_MODULE, - .init_flags = xfrm4_init_flags, - .init_tempsel = __xfrm4_init_tempsel, - .init_temprop = xfrm4_init_temprop, .output = xfrm4_output, .output_finish = xfrm4_output_finish, .extract_input = xfrm4_extract_input, diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c index 5d00e54cd319..dc19aff7c2e0 100644 --- a/net/ipv4/xfrm4_tunnel.c +++ b/net/ipv4/xfrm4_tunnel.c @@ -108,8 +108,7 @@ static void __exit ipip_fini(void) if (xfrm4_tunnel_deregister(&xfrm_tunnel_handler, AF_INET)) pr_info("%s: can't remove xfrm handler for AF_INET\n", __func__); - if (xfrm_unregister_type(&ipip_type, AF_INET) < 0) - pr_info("%s: can't remove xfrm type\n", __func__); + xfrm_unregister_type(&ipip_type, AF_INET); } module_init(ipip_init); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 68b9e92e469e..25e1172fd1c3 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -793,9 +793,7 @@ static void __exit ah6_fini(void) if (xfrm6_protocol_deregister(&ah6_protocol, IPPROTO_AH) < 0) pr_info("%s: can't remove protocol\n", __func__); - if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0) - pr_info("%s: can't remove xfrm type\n", __func__); - + xfrm_unregister_type(&ah6_type, AF_INET6); } module_init(ah6_init); diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index ae6a739c5f52..a3b403ba8f8f 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -41,8 +41,6 @@ struct esp_skb_cb { #define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0])) -static u32 esp6_get_mtu(struct xfrm_state *x, int mtu); - /* * Allocate an AEAD request structure with extra space for SG and IV. * @@ -447,7 +445,7 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); u32 padto; - padto = min(x->tfcpad, esp6_get_mtu(x, dst->child_mtu_cached)); + padto = min(x->tfcpad, xfrm_state_mtu(x, dst->child_mtu_cached)); if (skb->len < padto) esp.tfclen = padto - skb->len; } @@ -687,21 +685,6 @@ out: return ret; } -static u32 esp6_get_mtu(struct xfrm_state *x, int mtu) -{ - struct crypto_aead *aead = x->data; - u32 blksize = ALIGN(crypto_aead_blocksize(aead), 4); - unsigned int net_adj; - - if (x->props.mode != XFRM_MODE_TUNNEL) - net_adj = sizeof(struct ipv6hdr); - else - net_adj = 0; - - return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - - net_adj) & ~(blksize - 1)) + net_adj - 2; -} - static int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { @@ -919,7 +902,6 @@ static const struct xfrm_type esp6_type = { .flags = XFRM_TYPE_REPLAY_PROT, .init_state = esp6_init_state, .destructor = esp6_destroy, - .get_mtu = esp6_get_mtu, .input = esp6_input, .output = esp6_output, .hdr_offset = xfrm6_find_1stfragopt, @@ -951,8 +933,7 @@ static void __exit esp6_fini(void) { if (xfrm6_protocol_deregister(&esp6_protocol, IPPROTO_ESP) < 0) pr_info("%s: can't remove protocol\n", __func__); - if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0) - pr_info("%s: can't remove xfrm type\n", __func__); + xfrm_unregister_type(&esp6_type, AF_INET6); } module_init(esp6_init); diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index d0d8528b294a..e31626ffccd1 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -336,9 +336,7 @@ static int __init esp6_offload_init(void) static void __exit esp6_offload_exit(void) { - if (xfrm_unregister_type_offload(&esp6_type_offload, AF_INET6) < 0) - pr_info("%s: can't remove xfrm type offload\n", __func__); - + xfrm_unregister_type_offload(&esp6_type_offload, AF_INET6); inet6_del_offload(&esp6_offload, IPPROTO_ESP); } diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 51fd33294c7c..3752bd3e92ce 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -206,8 +206,7 @@ static void __exit ipcomp6_fini(void) { if (xfrm6_protocol_deregister(&ipcomp6_protocol, IPPROTO_COMP) < 0) pr_info("%s: can't remove protocol\n", __func__); - if (xfrm_unregister_type(&ipcomp6_type, AF_INET6) < 0) - pr_info("%s: can't remove xfrm type\n", __func__); + xfrm_unregister_type(&ipcomp6_type, AF_INET6); } module_init(ipcomp6_init); diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index 91801432878c..878fcec14949 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c @@ -499,10 +499,8 @@ static void __exit mip6_fini(void) { if (rawv6_mh_filter_unregister(mip6_mh_filter) < 0) pr_info("%s: can't remove rawv6 mh filter\n", __func__); - if (xfrm_unregister_type(&mip6_rthdr_type, AF_INET6) < 0) - pr_info("%s: can't remove xfrm type(rthdr)\n", __func__); - if (xfrm_unregister_type(&mip6_destopt_type, AF_INET6) < 0) - pr_info("%s: can't remove xfrm type(destopt)\n", __func__); + xfrm_unregister_type(&mip6_rthdr_type, AF_INET6); + xfrm_unregister_type(&mip6_destopt_type, AF_INET6); } module_init(mip6_init); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 39361f57351a..4d2e6b31a8d6 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2370,6 +2370,42 @@ u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6, hash_keys.basic.ip_proto = fl6->flowi6_proto; } break; + case 2: + memset(&hash_keys, 0, sizeof(hash_keys)); + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + if (skb) { + struct flow_keys keys; + + if (!flkeys) { + skb_flow_dissect_flow_keys(skb, &keys, 0); + flkeys = &keys; + } + + /* Inner can be v4 or v6 */ + if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src; + hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst; + } else if (flkeys->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src; + hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst; + hash_keys.tags.flow_label = flkeys->tags.flow_label; + hash_keys.basic.ip_proto = flkeys->basic.ip_proto; + } else { + /* Same as case 0 */ + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + ip6_multipath_l3_keys(skb, &hash_keys, flkeys); + } + } else { + /* Same as case 0 */ + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + hash_keys.addrs.v6addrs.src = fl6->saddr; + hash_keys.addrs.v6addrs.dst = fl6->daddr; + hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6); + hash_keys.basic.ip_proto = fl6->flowi6_proto; + } + break; } mhash = flow_hash_from_keys(&hash_keys); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 66ca5a4b17c4..4406e059da68 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -826,8 +826,7 @@ static int udp6_unicast_rcv_skb(struct sock *sk, struct sk_buff *skb, int ret; if (inet_get_convert_csum(sk) && uh->check && !IS_UDPLITE(sk)) - skb_checksum_try_convert(skb, IPPROTO_UDP, uh->check, - ip6_compute_pseudo); + skb_checksum_try_convert(skb, IPPROTO_UDP, ip6_compute_pseudo); ret = udpv6_queue_rcv_skb(sk, skb); diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index 5bdca3d5d6b7..78daadecbdef 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c @@ -21,137 +21,6 @@ #include <net/ipv6.h> #include <net/addrconf.h> -static void -__xfrm6_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) -{ - const struct flowi6 *fl6 = &fl->u.ip6; - - /* Initialize temporary selector matching only - * to current session. */ - *(struct in6_addr *)&sel->daddr = fl6->daddr; - *(struct in6_addr *)&sel->saddr = fl6->saddr; - sel->dport = xfrm_flowi_dport(fl, &fl6->uli); - sel->dport_mask = htons(0xffff); - sel->sport = xfrm_flowi_sport(fl, &fl6->uli); - sel->sport_mask = htons(0xffff); - sel->family = AF_INET6; - sel->prefixlen_d = 128; - sel->prefixlen_s = 128; - sel->proto = fl6->flowi6_proto; - sel->ifindex = fl6->flowi6_oif; -} - -static void -xfrm6_init_temprop(struct xfrm_state *x, const struct xfrm_tmpl *tmpl, - const xfrm_address_t *daddr, const xfrm_address_t *saddr) -{ - x->id = tmpl->id; - if (ipv6_addr_any((struct in6_addr *)&x->id.daddr)) - memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); - memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr)); - if (ipv6_addr_any((struct in6_addr *)&x->props.saddr)) - memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr)); - x->props.mode = tmpl->mode; - x->props.reqid = tmpl->reqid; - x->props.family = AF_INET6; -} - -/* distribution counting sort function for xfrm_state and xfrm_tmpl */ -static int -__xfrm6_sort(void **dst, void **src, int n, int (*cmp)(void *p), int maxclass) -{ - int count[XFRM_MAX_DEPTH] = { }; - int class[XFRM_MAX_DEPTH]; - int i; - - for (i = 0; i < n; i++) { - int c; - class[i] = c = cmp(src[i]); - count[c]++; - } - - for (i = 2; i < maxclass; i++) - count[i] += count[i - 1]; - - for (i = 0; i < n; i++) { - dst[count[class[i] - 1]++] = src[i]; - src[i] = NULL; - } - - return 0; -} - -/* - * Rule for xfrm_state: - * - * rule 1: select IPsec transport except AH - * rule 2: select MIPv6 RO or inbound trigger - * rule 3: select IPsec transport AH - * rule 4: select IPsec tunnel - * rule 5: others - */ -static int __xfrm6_state_sort_cmp(void *p) -{ - struct xfrm_state *v = p; - - switch (v->props.mode) { - case XFRM_MODE_TRANSPORT: - if (v->id.proto != IPPROTO_AH) - return 1; - else - return 3; -#if IS_ENABLED(CONFIG_IPV6_MIP6) - case XFRM_MODE_ROUTEOPTIMIZATION: - case XFRM_MODE_IN_TRIGGER: - return 2; -#endif - case XFRM_MODE_TUNNEL: - case XFRM_MODE_BEET: - return 4; - } - return 5; -} - -static int -__xfrm6_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n) -{ - return __xfrm6_sort((void **)dst, (void **)src, n, - __xfrm6_state_sort_cmp, 6); -} - -/* - * Rule for xfrm_tmpl: - * - * rule 1: select IPsec transport - * rule 2: select MIPv6 RO or inbound trigger - * rule 3: select IPsec tunnel - * rule 4: others - */ -static int __xfrm6_tmpl_sort_cmp(void *p) -{ - struct xfrm_tmpl *v = p; - switch (v->mode) { - case XFRM_MODE_TRANSPORT: - return 1; -#if IS_ENABLED(CONFIG_IPV6_MIP6) - case XFRM_MODE_ROUTEOPTIMIZATION: - case XFRM_MODE_IN_TRIGGER: - return 2; -#endif - case XFRM_MODE_TUNNEL: - case XFRM_MODE_BEET: - return 3; - } - return 4; -} - -static int -__xfrm6_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n) -{ - return __xfrm6_sort((void **)dst, (void **)src, n, - __xfrm6_tmpl_sort_cmp, 5); -} - int xfrm6_extract_header(struct sk_buff *skb) { struct ipv6hdr *iph = ipv6_hdr(skb); @@ -171,12 +40,6 @@ int xfrm6_extract_header(struct sk_buff *skb) static struct xfrm_state_afinfo xfrm6_state_afinfo = { .family = AF_INET6, .proto = IPPROTO_IPV6, - .eth_proto = htons(ETH_P_IPV6), - .owner = THIS_MODULE, - .init_tempsel = __xfrm6_init_tempsel, - .init_temprop = xfrm6_init_temprop, - .tmpl_sort = __xfrm6_tmpl_sort, - .state_sort = __xfrm6_state_sort, .output = xfrm6_output, .output_finish = xfrm6_output_finish, .extract_input = xfrm6_extract_input, diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 21025c2c605b..d59742408d9b 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -651,6 +651,17 @@ config NFT_TPROXY help This makes transparent proxy support available in nftables. +config NFT_SYNPROXY + tristate "Netfilter nf_tables SYNPROXY expression support" + depends on NF_CONNTRACK && NETFILTER_ADVANCED + select NETFILTER_SYNPROXY + select SYN_COOKIES + help + The SYNPROXY expression allows you to intercept TCP connections and + establish them using syncookies before they are passed on to the + server. This allows to avoid conntrack and server resource usage + during SYN-flood attacks. + if NF_TABLES_NETDEV config NF_DUP_NETDEV diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 72cca6b48960..deada20975ff 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_NFT_SOCKET) += nft_socket.o obj-$(CONFIG_NFT_OSF) += nft_osf.o obj-$(CONFIG_NFT_TPROXY) += nft_tproxy.o obj-$(CONFIG_NFT_XFRM) += nft_xfrm.o +obj-$(CONFIG_NFT_SYNPROXY) += nft_synproxy.o obj-$(CONFIG_NFT_NAT) += nft_chain_nat.o diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 817a9e5d16e4..5d5bdf450091 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -520,7 +520,7 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, ret = -EPERM; return ret; case NF_QUEUE: - ret = nf_queue(skb, state, e, s, verdict); + ret = nf_queue(skb, state, s, verdict); if (ret == 1) continue; return ret; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index e8651fd621ef..dd4727a5d6ec 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -35,6 +35,7 @@ #include <net/udp.h> #include <net/icmp.h> /* for icmp_send */ #include <net/gue.h> +#include <net/gre.h> #include <net/route.h> #include <net/ip6_checksum.h> #include <net/netns/generic.h> /* net_generic() */ @@ -1610,6 +1611,38 @@ unk: return 0; } +/* Check the GRE tunnel and return its header length */ +static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb, + unsigned int offset, __u16 af, + const union nf_inet_addr *daddr, __u8 *proto) +{ + struct gre_base_hdr _greh, *greh; + struct ip_vs_dest *dest; + + greh = skb_header_pointer(skb, offset, sizeof(_greh), &_greh); + if (!greh) + goto unk; + dest = ip_vs_find_tunnel(ipvs, af, daddr, 0); + if (!dest) + goto unk; + if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) { + __be16 type; + + /* Only support version 0 and C (csum) */ + if ((greh->flags & ~GRE_CSUM) != 0) + goto unk; + type = greh->protocol; + /* Later we can support also IPPROTO_IPV6 */ + if (type != htons(ETH_P_IP)) + goto unk; + *proto = IPPROTO_IPIP; + return gre_calc_hlen(gre_flags_to_tnl_flags(greh->flags)); + } + +unk: + return 0; +} + /* * Handle ICMP messages in the outside-to-inside direction (incoming). * Find any that might be relevant, check against existing connections, @@ -1689,7 +1722,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (cih == NULL) return NF_ACCEPT; /* The packet looks wrong, ignore */ ipip = true; - } else if (cih->protocol == IPPROTO_UDP && /* Can be UDP encap */ + } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */ + cih->protocol == IPPROTO_GRE) && /* Can be GRE encap */ /* Error for our tunnel must arrive at LOCAL_IN */ (skb_rtable(skb)->rt_flags & RTCF_LOCAL)) { __u8 iproto; @@ -1699,10 +1733,14 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (unlikely(cih->frag_off & htons(IP_OFFSET))) return NF_ACCEPT; offset2 = offset + cih->ihl * 4; - ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, raddr, - &iproto); + if (cih->protocol == IPPROTO_UDP) + ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, + raddr, &iproto); + else + ulen = ipvs_gre_decap(ipvs, skb, offset2, AF_INET, + raddr, &iproto); if (ulen > 0) { - /* Skip IP and UDP tunnel headers */ + /* Skip IP and UDP/GRE tunnel headers */ offset = offset2 + ulen; /* Now we should be at the original IP header */ cih = skb_header_pointer(skb, offset, sizeof(_ciph), diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 84384d896e29..998353bec74f 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -525,6 +525,7 @@ static void ip_vs_rs_hash(struct netns_ipvs *ipvs, struct ip_vs_dest *dest) port = dest->tun_port; break; case IP_VS_CONN_F_TUNNEL_TYPE_IPIP: + case IP_VS_CONN_F_TUNNEL_TYPE_GRE: port = 0; break; default: diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 71fc6d63a67f..9c464d24beec 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -29,6 +29,7 @@ #include <linux/tcp.h> /* for tcphdr */ #include <net/ip.h> #include <net/gue.h> +#include <net/gre.h> #include <net/tcp.h> /* for csum_tcpudp_magic */ #include <net/udp.h> #include <net/icmp.h> /* for icmp_send */ @@ -388,6 +389,12 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) && skb->ip_summed == CHECKSUM_PARTIAL) mtu -= GUE_PLEN_REMCSUM + GUE_LEN_PRIV; + } else if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) { + __be16 tflags = 0; + + if (dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) + tflags |= TUNNEL_CSUM; + mtu -= gre_calc_hlen(tflags); } if (mtu < 68) { IP_VS_DBG_RL("%s(): mtu less than 68\n", __func__); @@ -548,6 +555,12 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb, IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) && skb->ip_summed == CHECKSUM_PARTIAL) mtu -= GUE_PLEN_REMCSUM + GUE_LEN_PRIV; + } else if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) { + __be16 tflags = 0; + + if (dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) + tflags |= TUNNEL_CSUM; + mtu -= gre_calc_hlen(tflags); } if (mtu < IPV6_MIN_MTU) { IP_VS_DBG_RL("%s(): mtu less than %d\n", __func__, @@ -1079,6 +1092,24 @@ ipvs_gue_encap(struct net *net, struct sk_buff *skb, return 0; } +static void +ipvs_gre_encap(struct net *net, struct sk_buff *skb, + struct ip_vs_conn *cp, __u8 *next_protocol) +{ + __be16 proto = *next_protocol == IPPROTO_IPIP ? + htons(ETH_P_IP) : htons(ETH_P_IPV6); + __be16 tflags = 0; + size_t hdrlen; + + if (cp->dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) + tflags |= TUNNEL_CSUM; + + hdrlen = gre_calc_hlen(tflags); + gre_build_header(skb, hdrlen, tflags, proto, 0, 0); + + *next_protocol = IPPROTO_GRE; +} + /* * IP Tunneling transmitter * @@ -1151,6 +1182,15 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, gue_hdrlen = sizeof(struct guehdr) + gue_optlen; max_headroom += sizeof(struct udphdr) + gue_hdrlen; + } else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) { + size_t gre_hdrlen; + __be16 tflags = 0; + + if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) + tflags |= TUNNEL_CSUM; + gre_hdrlen = gre_calc_hlen(tflags); + + max_headroom += gre_hdrlen; } /* We only care about the df field if sysctl_pmtu_disc(ipvs) is set */ @@ -1172,6 +1212,11 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ip_summed == CHECKSUM_PARTIAL) { gso_type |= SKB_GSO_TUNNEL_REMCSUM; } + } else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) { + if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) + gso_type |= SKB_GSO_GRE_CSUM; + else + gso_type |= SKB_GSO_GRE; } if (iptunnel_handle_offloads(skb, gso_type)) @@ -1192,8 +1237,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, check = true; udp_set_csum(!check, skb, saddr, cp->daddr.ip, skb->len); - } - + } else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) + ipvs_gre_encap(net, skb, cp, &next_protocol); skb_push(skb, sizeof(struct iphdr)); skb_reset_network_header(skb); @@ -1287,6 +1332,15 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, gue_hdrlen = sizeof(struct guehdr) + gue_optlen; max_headroom += sizeof(struct udphdr) + gue_hdrlen; + } else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) { + size_t gre_hdrlen; + __be16 tflags = 0; + + if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) + tflags |= TUNNEL_CSUM; + gre_hdrlen = gre_calc_hlen(tflags); + + max_headroom += gre_hdrlen; } skb = ip_vs_prepare_tunneled_skb(skb, cp->af, max_headroom, @@ -1306,6 +1360,11 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ip_summed == CHECKSUM_PARTIAL) { gso_type |= SKB_GSO_TUNNEL_REMCSUM; } + } else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) { + if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM) + gso_type |= SKB_GSO_GRE_CSUM; + else + gso_type |= SKB_GSO_GRE; } if (iptunnel_handle_offloads(skb, gso_type)) @@ -1326,7 +1385,8 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, check = true; udp6_set_csum(!check, skb, &saddr, &cp->daddr.in6, skb->len); - } + } else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) + ipvs_gre_encap(net, skb, cp, &next_protocol); skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 3574a212bdc2..bb25d4c794c7 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -374,7 +374,7 @@ static int seq_show(struct seq_file *s, void *v) continue; logger = nft_log_dereference(loggers[*pos][i]); - seq_printf(s, "%s", logger->name); + seq_puts(s, logger->name); if (i == 0 && loggers[*pos][i + 1] != NULL) seq_puts(s, ","); diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index b5b2be55ca82..c72a5bdd123f 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -156,7 +156,6 @@ static void nf_ip6_saveroute(const struct sk_buff *skb, } static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, - const struct nf_hook_entries *entries, unsigned int index, unsigned int queuenum) { int status = -ENOENT; @@ -225,12 +224,11 @@ err: /* Packets leaving via this function must come back through nf_reinject(). */ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, - const struct nf_hook_entries *entries, unsigned int index, - unsigned int verdict) + unsigned int index, unsigned int verdict) { int ret; - ret = __nf_queue(skb, state, entries, index, verdict >> NF_VERDICT_QBITS); + ret = __nf_queue(skb, state, index, verdict >> NF_VERDICT_QBITS); if (ret < 0) { if (ret == -ESRCH && (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) @@ -336,7 +334,7 @@ next_hook: local_bh_enable(); break; case NF_QUEUE: - err = nf_queue(skb, &entry->state, hooks, i, verdict); + err = nf_queue(skb, &entry->state, i, verdict); if (err == 1) goto next_hook; break; diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 409722d23302..b101f187eda8 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -11,7 +11,7 @@ #include <linux/proc_fs.h> #include <linux/netfilter_ipv6.h> -#include <linux/netfilter/nf_SYNPROXY.h> +#include <linux/netfilter/nf_synproxy.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_ecache.h> diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index cae5c46e2dd4..d22d00ca78c1 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2009,15 +2009,31 @@ EXPORT_SYMBOL_GPL(nft_unregister_expr); static const struct nft_expr_type *__nft_expr_type_get(u8 family, struct nlattr *nla) { - const struct nft_expr_type *type; + const struct nft_expr_type *type, *candidate = NULL; list_for_each_entry(type, &nf_tables_expressions, list) { - if (!nla_strcmp(nla, type->name) && - (!type->family || type->family == family)) - return type; + if (!nla_strcmp(nla, type->name)) { + if (!type->family && !candidate) + candidate = type; + else if (type->family == family) + candidate = type; + } } - return NULL; + return candidate; +} + +#ifdef CONFIG_MODULES +static int nft_expr_type_request_module(struct net *net, u8 family, + struct nlattr *nla) +{ + nft_request_module(net, "nft-expr-%u-%.*s", family, + nla_len(nla), (char *)nla_data(nla)); + if (__nft_expr_type_get(family, nla)) + return -EAGAIN; + + return 0; } +#endif static const struct nft_expr_type *nft_expr_type_get(struct net *net, u8 family, @@ -2035,9 +2051,7 @@ static const struct nft_expr_type *nft_expr_type_get(struct net *net, lockdep_nfnl_nft_mutex_not_held(); #ifdef CONFIG_MODULES if (type == NULL) { - nft_request_module(net, "nft-expr-%u-%.*s", family, - nla_len(nla), (char *)nla_data(nla)); - if (__nft_expr_type_get(family, nla)) + if (nft_expr_type_request_module(net, family, nla) == -EAGAIN) return ERR_PTR(-EAGAIN); nft_request_module(net, "nft-expr-%.*s", @@ -2130,6 +2144,12 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx, (const struct nlattr * const *)info->tb); if (IS_ERR(ops)) { err = PTR_ERR(ops); +#ifdef CONFIG_MODULES + if (err == -EAGAIN) + nft_expr_type_request_module(ctx->net, + ctx->family, + tb[NFTA_EXPR_NAME]); +#endif goto err1; } } else diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index b950cd31348b..96c74c4c7176 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -19,6 +19,7 @@ #include <net/netfilter/nf_tables_core.h> #include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_log.h> +#include <net/netfilter/nft_meta.h> static noinline void __nft_trace_packet(struct nft_traceinfo *info, const struct nft_chain *chain, diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index a54329b8634a..417f8d32e9a3 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -21,23 +21,12 @@ #include <net/tcp_states.h> /* for TCP_TIME_WAIT */ #include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables_core.h> +#include <net/netfilter/nft_meta.h> #include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */ -struct nft_meta { - enum nft_meta_keys key:8; - union { - enum nft_registers dreg:8; - enum nft_registers sreg:8; - }; -}; - static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state); -#ifdef CONFIG_NF_TABLES_BRIDGE -#include "../bridge/br_private.h" -#endif - void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -47,9 +36,6 @@ void nft_meta_get_eval(const struct nft_expr *expr, const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); struct sock *sk; u32 *dest = ®s->data[priv->dreg]; -#ifdef CONFIG_NF_TABLES_BRIDGE - const struct net_bridge_port *p; -#endif switch (priv->key) { case NFT_META_LEN: @@ -229,18 +215,6 @@ void nft_meta_get_eval(const struct nft_expr *expr, nft_reg_store8(dest, secpath_exists(skb)); break; #endif -#ifdef CONFIG_NF_TABLES_BRIDGE - case NFT_META_BRI_IIFNAME: - if (in == NULL || (p = br_port_get_rcu(in)) == NULL) - goto err; - strncpy((char *)dest, p->br->dev->name, IFNAMSIZ); - return; - case NFT_META_BRI_OIFNAME: - if (out == NULL || (p = br_port_get_rcu(out)) == NULL) - goto err; - strncpy((char *)dest, p->br->dev->name, IFNAMSIZ); - return; -#endif case NFT_META_IIFKIND: if (in == NULL || in->rtnl_link_ops == NULL) goto err; @@ -260,10 +234,11 @@ void nft_meta_get_eval(const struct nft_expr *expr, err: regs->verdict.code = NFT_BREAK; } +EXPORT_SYMBOL_GPL(nft_meta_get_eval); -static void nft_meta_set_eval(const struct nft_expr *expr, - struct nft_regs *regs, - const struct nft_pktinfo *pkt) +void nft_meta_set_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) { const struct nft_meta *meta = nft_expr_priv(expr); struct sk_buff *skb = pkt->skb; @@ -300,16 +275,18 @@ static void nft_meta_set_eval(const struct nft_expr *expr, WARN_ON(1); } } +EXPORT_SYMBOL_GPL(nft_meta_set_eval); -static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { +const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { [NFTA_META_DREG] = { .type = NLA_U32 }, [NFTA_META_KEY] = { .type = NLA_U32 }, [NFTA_META_SREG] = { .type = NLA_U32 }, }; +EXPORT_SYMBOL_GPL(nft_meta_policy); -static int nft_meta_get_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) +int nft_meta_get_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { struct nft_meta *priv = nft_expr_priv(expr); unsigned int len; @@ -360,14 +337,6 @@ static int nft_meta_get_init(const struct nft_ctx *ctx, len = sizeof(u8); break; #endif -#ifdef CONFIG_NF_TABLES_BRIDGE - case NFT_META_BRI_IIFNAME: - case NFT_META_BRI_OIFNAME: - if (ctx->family != NFPROTO_BRIDGE) - return -EOPNOTSUPP; - len = IFNAMSIZ; - break; -#endif default: return -EOPNOTSUPP; } @@ -376,6 +345,7 @@ static int nft_meta_get_init(const struct nft_ctx *ctx, return nft_validate_register_store(ctx, priv->dreg, NULL, NFT_DATA_VALUE, len); } +EXPORT_SYMBOL_GPL(nft_meta_get_init); static int nft_meta_get_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, @@ -409,9 +379,9 @@ static int nft_meta_get_validate(const struct nft_ctx *ctx, #endif } -static int nft_meta_set_validate(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nft_data **data) +int nft_meta_set_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) { struct nft_meta *priv = nft_expr_priv(expr); unsigned int hooks; @@ -437,10 +407,11 @@ static int nft_meta_set_validate(const struct nft_ctx *ctx, return nft_chain_validate_hooks(ctx->chain, hooks); } +EXPORT_SYMBOL_GPL(nft_meta_set_validate); -static int nft_meta_set_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) +int nft_meta_set_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { struct nft_meta *priv = nft_expr_priv(expr); unsigned int len; @@ -475,9 +446,10 @@ static int nft_meta_set_init(const struct nft_ctx *ctx, return 0; } +EXPORT_SYMBOL_GPL(nft_meta_set_init); -static int nft_meta_get_dump(struct sk_buff *skb, - const struct nft_expr *expr) +int nft_meta_get_dump(struct sk_buff *skb, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -490,8 +462,9 @@ static int nft_meta_get_dump(struct sk_buff *skb, nla_put_failure: return -1; } +EXPORT_SYMBOL_GPL(nft_meta_get_dump); -static int nft_meta_set_dump(struct sk_buff *skb, const struct nft_expr *expr) +int nft_meta_set_dump(struct sk_buff *skb, const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); @@ -505,15 +478,17 @@ static int nft_meta_set_dump(struct sk_buff *skb, const struct nft_expr *expr) nla_put_failure: return -1; } +EXPORT_SYMBOL_GPL(nft_meta_set_dump); -static void nft_meta_set_destroy(const struct nft_ctx *ctx, - const struct nft_expr *expr) +void nft_meta_set_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) { const struct nft_meta *priv = nft_expr_priv(expr); if (priv->key == NFT_META_NFTRACE) static_branch_dec(&nft_trace_enabled); } +EXPORT_SYMBOL_GPL(nft_meta_set_destroy); static const struct nft_expr_ops nft_meta_get_ops = { .type = &nft_meta_type, @@ -544,6 +519,10 @@ nft_meta_select_ops(const struct nft_ctx *ctx, if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) return ERR_PTR(-EINVAL); +#ifdef CONFIG_NF_TABLES_BRIDGE + if (ctx->family == NFPROTO_BRIDGE) + return ERR_PTR(-EAGAIN); +#endif if (tb[NFTA_META_DREG]) return &nft_meta_get_ops; diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c new file mode 100644 index 000000000000..80060ade8a5b --- /dev/null +++ b/net/netfilter/nft_synproxy.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/types.h> +#include <net/ip.h> +#include <net/tcp.h> +#include <net/netlink.h> +#include <net/netfilter/nf_tables.h> +#include <net/netfilter/nf_conntrack.h> +#include <net/netfilter/nf_conntrack_synproxy.h> +#include <net/netfilter/nf_synproxy.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_synproxy.h> + +struct nft_synproxy { + struct nf_synproxy_info info; +}; + +static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = { + [NFTA_SYNPROXY_MSS] = { .type = NLA_U16 }, + [NFTA_SYNPROXY_WSCALE] = { .type = NLA_U8 }, + [NFTA_SYNPROXY_FLAGS] = { .type = NLA_U32 }, +}; + +static void nft_synproxy_tcp_options(struct synproxy_options *opts, + const struct tcphdr *tcp, + struct synproxy_net *snet, + struct nf_synproxy_info *info, + struct nft_synproxy *priv) +{ + this_cpu_inc(snet->stats->syn_received); + if (tcp->ece && tcp->cwr) + opts->options |= NF_SYNPROXY_OPT_ECN; + + opts->options &= priv->info.options; + if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP) + synproxy_init_timestamp_cookie(info, opts); + else + opts->options &= ~(NF_SYNPROXY_OPT_WSCALE | + NF_SYNPROXY_OPT_SACK_PERM | + NF_SYNPROXY_OPT_ECN); +} + +static void nft_synproxy_eval_v4(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt, + const struct tcphdr *tcp, + struct tcphdr *_tcph, + struct synproxy_options *opts) +{ + struct nft_synproxy *priv = nft_expr_priv(expr); + struct nf_synproxy_info info = priv->info; + struct net *net = nft_net(pkt); + struct synproxy_net *snet = synproxy_pernet(net); + struct sk_buff *skb = pkt->skb; + + if (tcp->syn) { + /* Initial SYN from client */ + nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); + synproxy_send_client_synack(net, skb, tcp, opts); + consume_skb(skb); + regs->verdict.code = NF_STOLEN; + } else if (tcp->ack) { + /* ACK from client */ + if (synproxy_recv_client_ack(net, skb, tcp, opts, + ntohl(tcp->seq))) { + consume_skb(skb); + regs->verdict.code = NF_STOLEN; + } else { + regs->verdict.code = NF_DROP; + } + } +} + +#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) +static void nft_synproxy_eval_v6(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt, + const struct tcphdr *tcp, + struct tcphdr *_tcph, + struct synproxy_options *opts) +{ + struct nft_synproxy *priv = nft_expr_priv(expr); + struct nf_synproxy_info info = priv->info; + struct net *net = nft_net(pkt); + struct synproxy_net *snet = synproxy_pernet(net); + struct sk_buff *skb = pkt->skb; + + if (tcp->syn) { + /* Initial SYN from client */ + nft_synproxy_tcp_options(opts, tcp, snet, &info, priv); + synproxy_send_client_synack_ipv6(net, skb, tcp, opts); + consume_skb(skb); + regs->verdict.code = NF_STOLEN; + } else if (tcp->ack) { + /* ACK from client */ + if (synproxy_recv_client_ack_ipv6(net, skb, tcp, opts, + ntohl(tcp->seq))) { + consume_skb(skb); + regs->verdict.code = NF_STOLEN; + } else { + regs->verdict.code = NF_DROP; + } + } +} +#endif /* CONFIG_NF_TABLES_IPV6*/ + +static void nft_synproxy_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct synproxy_options opts = {}; + struct sk_buff *skb = pkt->skb; + int thoff = pkt->xt.thoff; + const struct tcphdr *tcp; + struct tcphdr _tcph; + + if (pkt->tprot != IPPROTO_TCP) { + regs->verdict.code = NFT_BREAK; + return; + } + + if (nf_ip_checksum(skb, nft_hook(pkt), thoff, IPPROTO_TCP)) { + regs->verdict.code = NF_DROP; + return; + } + + tcp = skb_header_pointer(skb, pkt->xt.thoff, + sizeof(struct tcphdr), + &_tcph); + if (!tcp) { + regs->verdict.code = NF_DROP; + return; + } + + if (!synproxy_parse_options(skb, thoff, tcp, &opts)) { + regs->verdict.code = NF_DROP; + return; + } + + switch (skb->protocol) { + case htons(ETH_P_IP): + nft_synproxy_eval_v4(expr, regs, pkt, tcp, &_tcph, &opts); + return; +#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) + case htons(ETH_P_IPV6): + nft_synproxy_eval_v6(expr, regs, pkt, tcp, &_tcph, &opts); + return; +#endif + } + regs->verdict.code = NFT_BREAK; +} + +static int nft_synproxy_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct synproxy_net *snet = synproxy_pernet(ctx->net); + struct nft_synproxy *priv = nft_expr_priv(expr); + u32 flags; + int err; + + if (tb[NFTA_SYNPROXY_MSS]) + priv->info.mss = ntohs(nla_get_be16(tb[NFTA_SYNPROXY_MSS])); + if (tb[NFTA_SYNPROXY_WSCALE]) + priv->info.wscale = nla_get_u8(tb[NFTA_SYNPROXY_WSCALE]); + if (tb[NFTA_SYNPROXY_FLAGS]) { + flags = ntohl(nla_get_be32(tb[NFTA_SYNPROXY_FLAGS])); + if (flags & ~NF_SYNPROXY_OPT_MASK) + return -EOPNOTSUPP; + priv->info.options = flags; + } + + err = nf_ct_netns_get(ctx->net, ctx->family); + if (err) + return err; + + switch (ctx->family) { + case NFPROTO_IPV4: + err = nf_synproxy_ipv4_init(snet, ctx->net); + if (err) + goto nf_ct_failure; + break; +#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) + case NFPROTO_IPV6: + err = nf_synproxy_ipv6_init(snet, ctx->net); + if (err) + goto nf_ct_failure; + break; +#endif + case NFPROTO_INET: + case NFPROTO_BRIDGE: + err = nf_synproxy_ipv4_init(snet, ctx->net); + if (err) + goto nf_ct_failure; + err = nf_synproxy_ipv6_init(snet, ctx->net); + if (err) + goto nf_ct_failure; + break; + } + + return 0; + +nf_ct_failure: + nf_ct_netns_put(ctx->net, ctx->family); + return err; +} + +static void nft_synproxy_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct synproxy_net *snet = synproxy_pernet(ctx->net); + + switch (ctx->family) { + case NFPROTO_IPV4: + nf_synproxy_ipv4_fini(snet, ctx->net); + break; +#if IS_ENABLED(CONFIG_NF_TABLES_IPV6) + case NFPROTO_IPV6: + nf_synproxy_ipv6_fini(snet, ctx->net); + break; +#endif + case NFPROTO_INET: + case NFPROTO_BRIDGE: + nf_synproxy_ipv4_fini(snet, ctx->net); + nf_synproxy_ipv6_fini(snet, ctx->net); + break; + } + nf_ct_netns_put(ctx->net, ctx->family); +} + +static int nft_synproxy_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_synproxy *priv = nft_expr_priv(expr); + + if (nla_put_be16(skb, NFTA_SYNPROXY_MSS, htons(priv->info.mss)) || + nla_put_u8(skb, NFTA_SYNPROXY_WSCALE, priv->info.wscale) || + nla_put_be32(skb, NFTA_SYNPROXY_FLAGS, htonl(priv->info.options))) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} + +static int nft_synproxy_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +{ + return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_FORWARD)); +} + +static struct nft_expr_type nft_synproxy_type; +static const struct nft_expr_ops nft_synproxy_ops = { + .eval = nft_synproxy_eval, + .size = NFT_EXPR_SIZE(sizeof(struct nft_synproxy)), + .init = nft_synproxy_init, + .destroy = nft_synproxy_destroy, + .dump = nft_synproxy_dump, + .type = &nft_synproxy_type, + .validate = nft_synproxy_validate, +}; + +static struct nft_expr_type nft_synproxy_type __read_mostly = { + .ops = &nft_synproxy_ops, + .name = "synproxy", + .owner = THIS_MODULE, + .policy = nft_synproxy_policy, + .maxattr = NFTA_SYNPROXY_MAX, +}; + +static int __init nft_synproxy_module_init(void) +{ + return nft_register_expr(&nft_synproxy_type); +} + +static void __exit nft_synproxy_module_exit(void) +{ + return nft_unregister_expr(&nft_synproxy_type); +} + +module_init(nft_synproxy_module_init); +module_exit(nft_synproxy_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fernando Fernandez <[email protected]>"); +MODULE_ALIAS_NFT_EXPR("synproxy"); diff --git a/net/openvswitch/dp_notify.c b/net/openvswitch/dp_notify.c index 53cf07d141b4..7af0cde8b293 100644 --- a/net/openvswitch/dp_notify.c +++ b/net/openvswitch/dp_notify.c @@ -48,7 +48,7 @@ void ovs_dp_notify_wq(struct work_struct *work) if (vport->ops->type == OVS_VPORT_TYPE_INTERNAL) continue; - if (!(vport->dev->priv_flags & IFF_OVS_DATAPATH)) + if (!(netif_is_ovs_port(vport->dev))) dp_detach_port_notify(vport); } } diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 52a1ed9633ec..57d6436e6f6a 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -156,7 +156,7 @@ void ovs_netdev_detach_dev(struct vport *vport) static void netdev_destroy(struct vport *vport) { rtnl_lock(); - if (vport->dev->priv_flags & IFF_OVS_DATAPATH) + if (netif_is_ovs_port(vport->dev)) ovs_netdev_detach_dev(vport); rtnl_unlock(); @@ -166,7 +166,7 @@ static void netdev_destroy(struct vport *vport) void ovs_netdev_tunnel_destroy(struct vport *vport) { rtnl_lock(); - if (vport->dev->priv_flags & IFF_OVS_DATAPATH) + if (netif_is_ovs_port(vport->dev)) ovs_netdev_detach_dev(vport); /* We can be invoked by both explicit vport deletion and @@ -186,7 +186,7 @@ EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); /* Returns null if this device is not attached to a datapath. */ struct vport *ovs_netdev_get_vport(struct net_device *dev) { - if (likely(dev->priv_flags & IFF_OVS_DATAPATH)) + if (likely(netif_is_ovs_port(dev))) return (struct vport *) rcu_dereference_rtnl(dev->rx_handler_data); else diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 2bed6589f41e..a809c0ec8d15 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -62,7 +62,7 @@ static struct tipc_bearer *bearer_get(struct net *net, int bearer_id) { struct tipc_net *tn = tipc_net(net); - return rcu_dereference_rtnl(tn->bearer_list[bearer_id]); + return rcu_dereference(tn->bearer_list[bearer_id]); } static void bearer_disable(struct net *net, struct tipc_bearer *b); @@ -210,7 +210,7 @@ void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest) struct tipc_bearer *b; rcu_read_lock(); - b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]); + b = rcu_dereference(tn->bearer_list[bearer_id]); if (b) tipc_disc_add_dest(b->disc); rcu_read_unlock(); @@ -222,7 +222,7 @@ void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest) struct tipc_bearer *b; rcu_read_lock(); - b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]); + b = rcu_dereference(tn->bearer_list[bearer_id]); if (b) tipc_disc_remove_dest(b->disc); rcu_read_unlock(); @@ -444,7 +444,7 @@ int tipc_l2_send_msg(struct net *net, struct sk_buff *skb, struct net_device *dev; int delta; - dev = (struct net_device *)rcu_dereference_rtnl(b->media_ptr); + dev = (struct net_device *)rcu_dereference(b->media_ptr); if (!dev) return 0; @@ -481,7 +481,7 @@ int tipc_bearer_mtu(struct net *net, u32 bearer_id) struct tipc_bearer *b; rcu_read_lock(); - b = rcu_dereference_rtnl(tipc_net(net)->bearer_list[bearer_id]); + b = rcu_dereference(tipc_net(net)->bearer_list[bearer_id]); if (b) mtu = b->mtu; rcu_read_unlock(); @@ -574,8 +574,8 @@ static int tipc_l2_rcv_msg(struct sk_buff *skb, struct net_device *dev, struct tipc_bearer *b; rcu_read_lock(); - b = rcu_dereference_rtnl(dev->tipc_ptr) ?: - rcu_dereference_rtnl(orig_dev->tipc_ptr); + b = rcu_dereference(dev->tipc_ptr) ?: + rcu_dereference(orig_dev->tipc_ptr); if (likely(b && test_bit(0, &b->up) && (skb->pkt_type <= PACKET_MULTICAST))) { skb_mark_not_on_list(skb); diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index c0f694ae57ab..287df68721df 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -231,7 +231,7 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb, } skb_set_inner_protocol(skb, htons(ETH_P_TIPC)); - ub = rcu_dereference_rtnl(b->media_ptr); + ub = rcu_dereference(b->media_ptr); if (!ub) { err = -ENODEV; goto out; @@ -490,7 +490,7 @@ int tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb) } } - ub = rcu_dereference_rtnl(b->media_ptr); + ub = rtnl_dereference(b->media_ptr); if (!ub) { rtnl_unlock(); return -EINVAL; @@ -532,7 +532,7 @@ int tipc_udp_nl_add_bearer_data(struct tipc_nl_msg *msg, struct tipc_bearer *b) struct udp_bearer *ub; struct nlattr *nest; - ub = rcu_dereference_rtnl(b->media_ptr); + ub = rtnl_dereference(b->media_ptr); if (!ub) return -ENODEV; @@ -806,7 +806,7 @@ static void tipc_udp_disable(struct tipc_bearer *b) { struct udp_bearer *ub; - ub = rcu_dereference_rtnl(b->media_ptr); + ub = rtnl_dereference(b->media_ptr); if (!ub) { pr_err("UDP bearer instance not found\n"); return; diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index 9c287e3e393c..0815d1357861 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -38,6 +38,7 @@ struct virtio_vsock { * must be accessed with tx_lock held. */ struct mutex tx_lock; + bool tx_run; struct work_struct send_pkt_work; spinlock_t send_pkt_list_lock; @@ -53,6 +54,7 @@ struct virtio_vsock { * must be accessed with rx_lock held. */ struct mutex rx_lock; + bool rx_run; int rx_buf_nr; int rx_buf_max_nr; @@ -60,24 +62,28 @@ struct virtio_vsock { * vqs[VSOCK_VQ_EVENT] must be accessed with event_lock held. */ struct mutex event_lock; + bool event_run; struct virtio_vsock_event event_list[8]; u32 guest_cid; }; -static struct virtio_vsock *virtio_vsock_get(void) -{ - return the_virtio_vsock; -} - static u32 virtio_transport_get_local_cid(void) { - struct virtio_vsock *vsock = virtio_vsock_get(); + struct virtio_vsock *vsock; + u32 ret; - if (!vsock) - return VMADDR_CID_ANY; + rcu_read_lock(); + vsock = rcu_dereference(the_virtio_vsock); + if (!vsock) { + ret = VMADDR_CID_ANY; + goto out_rcu; + } - return vsock->guest_cid; + ret = vsock->guest_cid; +out_rcu: + rcu_read_unlock(); + return ret; } static void virtio_transport_loopback_work(struct work_struct *work) @@ -91,6 +97,10 @@ static void virtio_transport_loopback_work(struct work_struct *work) spin_unlock_bh(&vsock->loopback_list_lock); mutex_lock(&vsock->rx_lock); + + if (!vsock->rx_run) + goto out; + while (!list_empty(&pkts)) { struct virtio_vsock_pkt *pkt; @@ -99,6 +109,7 @@ static void virtio_transport_loopback_work(struct work_struct *work) virtio_transport_recv_pkt(pkt); } +out: mutex_unlock(&vsock->rx_lock); } @@ -127,6 +138,9 @@ virtio_transport_send_pkt_work(struct work_struct *work) mutex_lock(&vsock->tx_lock); + if (!vsock->tx_run) + goto out; + vq = vsock->vqs[VSOCK_VQ_TX]; for (;;) { @@ -185,6 +199,7 @@ virtio_transport_send_pkt_work(struct work_struct *work) if (added) virtqueue_kick(vq); +out: mutex_unlock(&vsock->tx_lock); if (restart_rx) @@ -197,14 +212,18 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt) struct virtio_vsock *vsock; int len = pkt->len; - vsock = virtio_vsock_get(); + rcu_read_lock(); + vsock = rcu_dereference(the_virtio_vsock); if (!vsock) { virtio_transport_free_pkt(pkt); - return -ENODEV; + len = -ENODEV; + goto out_rcu; } - if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) - return virtio_transport_send_pkt_loopback(vsock, pkt); + if (le64_to_cpu(pkt->hdr.dst_cid) == vsock->guest_cid) { + len = virtio_transport_send_pkt_loopback(vsock, pkt); + goto out_rcu; + } if (pkt->reply) atomic_inc(&vsock->queued_replies); @@ -214,6 +233,9 @@ virtio_transport_send_pkt(struct virtio_vsock_pkt *pkt) spin_unlock_bh(&vsock->send_pkt_list_lock); queue_work(virtio_vsock_workqueue, &vsock->send_pkt_work); + +out_rcu: + rcu_read_unlock(); return len; } @@ -222,12 +244,14 @@ virtio_transport_cancel_pkt(struct vsock_sock *vsk) { struct virtio_vsock *vsock; struct virtio_vsock_pkt *pkt, *n; - int cnt = 0; + int cnt = 0, ret; LIST_HEAD(freeme); - vsock = virtio_vsock_get(); + rcu_read_lock(); + vsock = rcu_dereference(the_virtio_vsock); if (!vsock) { - return -ENODEV; + ret = -ENODEV; + goto out_rcu; } spin_lock_bh(&vsock->send_pkt_list_lock); @@ -255,7 +279,11 @@ virtio_transport_cancel_pkt(struct vsock_sock *vsk) queue_work(virtio_vsock_workqueue, &vsock->rx_work); } - return 0; + ret = 0; + +out_rcu: + rcu_read_unlock(); + return ret; } static void virtio_vsock_rx_fill(struct virtio_vsock *vsock) @@ -307,6 +335,10 @@ static void virtio_transport_tx_work(struct work_struct *work) vq = vsock->vqs[VSOCK_VQ_TX]; mutex_lock(&vsock->tx_lock); + + if (!vsock->tx_run) + goto out; + do { struct virtio_vsock_pkt *pkt; unsigned int len; @@ -317,6 +349,8 @@ static void virtio_transport_tx_work(struct work_struct *work) added = true; } } while (!virtqueue_enable_cb(vq)); + +out: mutex_unlock(&vsock->tx_lock); if (added) @@ -345,6 +379,9 @@ static void virtio_transport_rx_work(struct work_struct *work) mutex_lock(&vsock->rx_lock); + if (!vsock->rx_run) + goto out; + do { virtqueue_disable_cb(vq); for (;;) { @@ -454,6 +491,9 @@ static void virtio_transport_event_work(struct work_struct *work) mutex_lock(&vsock->event_lock); + if (!vsock->event_run) + goto out; + do { struct virtio_vsock_event *event; unsigned int len; @@ -468,7 +508,7 @@ static void virtio_transport_event_work(struct work_struct *work) } while (!virtqueue_enable_cb(vq)); virtqueue_kick(vsock->vqs[VSOCK_VQ_EVENT]); - +out: mutex_unlock(&vsock->event_lock); } @@ -565,7 +605,8 @@ static int virtio_vsock_probe(struct virtio_device *vdev) return ret; /* Only one virtio-vsock device per guest is supported */ - if (the_virtio_vsock) { + if (rcu_dereference_protected(the_virtio_vsock, + lockdep_is_held(&the_virtio_vsock_mutex))) { ret = -EBUSY; goto out; } @@ -590,8 +631,6 @@ static int virtio_vsock_probe(struct virtio_device *vdev) vsock->rx_buf_max_nr = 0; atomic_set(&vsock->queued_replies, 0); - vdev->priv = vsock; - the_virtio_vsock = vsock; mutex_init(&vsock->tx_lock); mutex_init(&vsock->rx_lock); mutex_init(&vsock->event_lock); @@ -605,14 +644,23 @@ static int virtio_vsock_probe(struct virtio_device *vdev) INIT_WORK(&vsock->send_pkt_work, virtio_transport_send_pkt_work); INIT_WORK(&vsock->loopback_work, virtio_transport_loopback_work); + mutex_lock(&vsock->tx_lock); + vsock->tx_run = true; + mutex_unlock(&vsock->tx_lock); + mutex_lock(&vsock->rx_lock); virtio_vsock_rx_fill(vsock); + vsock->rx_run = true; mutex_unlock(&vsock->rx_lock); mutex_lock(&vsock->event_lock); virtio_vsock_event_fill(vsock); + vsock->event_run = true; mutex_unlock(&vsock->event_lock); + vdev->priv = vsock; + rcu_assign_pointer(the_virtio_vsock, vsock); + mutex_unlock(&the_virtio_vsock_mutex); return 0; @@ -627,15 +675,33 @@ static void virtio_vsock_remove(struct virtio_device *vdev) struct virtio_vsock *vsock = vdev->priv; struct virtio_vsock_pkt *pkt; - flush_work(&vsock->loopback_work); - flush_work(&vsock->rx_work); - flush_work(&vsock->tx_work); - flush_work(&vsock->event_work); - flush_work(&vsock->send_pkt_work); + mutex_lock(&the_virtio_vsock_mutex); + + vdev->priv = NULL; + rcu_assign_pointer(the_virtio_vsock, NULL); + synchronize_rcu(); /* Reset all connected sockets when the device disappear */ vsock_for_each_connected_socket(virtio_vsock_reset_sock); + /* Stop all work handlers to make sure no one is accessing the device, + * so we can safely call vdev->config->reset(). + */ + mutex_lock(&vsock->rx_lock); + vsock->rx_run = false; + mutex_unlock(&vsock->rx_lock); + + mutex_lock(&vsock->tx_lock); + vsock->tx_run = false; + mutex_unlock(&vsock->tx_lock); + + mutex_lock(&vsock->event_lock); + vsock->event_run = false; + mutex_unlock(&vsock->event_lock); + + /* Flush all device writes and interrupts, device will not use any + * more buffers. + */ vdev->config->reset(vdev); mutex_lock(&vsock->rx_lock); @@ -666,12 +732,20 @@ static void virtio_vsock_remove(struct virtio_device *vdev) } spin_unlock_bh(&vsock->loopback_list_lock); - mutex_lock(&the_virtio_vsock_mutex); - the_virtio_vsock = NULL; - mutex_unlock(&the_virtio_vsock_mutex); - + /* Delete virtqueues and flush outstanding callbacks if any */ vdev->config->del_vqs(vdev); + /* Other works can be queued before 'config->del_vqs()', so we flush + * all works before to free the vsock object to avoid use after free. + */ + flush_work(&vsock->loopback_work); + flush_work(&vsock->rx_work); + flush_work(&vsock->tx_work); + flush_work(&vsock->event_work); + flush_work(&vsock->send_pkt_work); + + mutex_unlock(&the_virtio_vsock_mutex); + kfree(vsock); } diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index ff654306d836..189ef15acbbc 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -271,9 +271,8 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) return false; if ((!dev || (dev == xfrm_dst_path(dst)->dev)) && - (!xdst->child->xfrm && x->type->get_mtu)) { - mtu = x->type->get_mtu(x, xdst->child_mtu_cached); - + (!xdst->child->xfrm)) { + mtu = xfrm_state_mtu(x, xdst->child_mtu_cached); if (skb->len <= mtu) goto ok; diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 314973aaa414..6088bc2dc11e 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -359,28 +359,29 @@ static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb) afinfo = xfrm_state_afinfo_get_rcu(x->outer_mode.family); if (likely(afinfo)) err = afinfo->extract_input(x, skb); + rcu_read_unlock(); - if (err) { - rcu_read_unlock(); + if (err) return err; - } if (x->sel.family == AF_UNSPEC) { inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol); - if (!inner_mode) { - rcu_read_unlock(); + if (!inner_mode) return -EAFNOSUPPORT; - } } - afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); - if (unlikely(!afinfo)) { - rcu_read_unlock(); - return -EAFNOSUPPORT; + switch (inner_mode->family) { + case AF_INET: + skb->protocol = htons(ETH_P_IP); + break; + case AF_INET6: + skb->protocol = htons(ETH_P_IPV6); + break; + default: + WARN_ON_ONCE(1); + break; } - skb->protocol = afinfo->eth_proto; - rcu_read_unlock(); return xfrm_inner_mode_encap_remove(x, inner_mode, skb); } diff --git a/net/xfrm/xfrm_interface.c b/net/xfrm/xfrm_interface.c index ad3a2555c517..f8eb9e342173 100644 --- a/net/xfrm/xfrm_interface.c +++ b/net/xfrm/xfrm_interface.c @@ -793,11 +793,6 @@ static void __net_exit xfrmi_destroy_interfaces(struct xfrmi_net *xfrmn) unregister_netdevice_many(&list); } -static int __net_init xfrmi_init_net(struct net *net) -{ - return 0; -} - static void __net_exit xfrmi_exit_net(struct net *net) { struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id); @@ -808,7 +803,6 @@ static void __net_exit xfrmi_exit_net(struct net *net) } static struct pernet_operations xfrmi_net_ops = { - .init = xfrmi_init_net, .exit = xfrmi_exit_net, .id = &xfrmi_net_id, .size = sizeof(struct xfrmi_net), diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index b1694d5d15d3..1070dfece76b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3628,7 +3628,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, } xfrm_nr = ti; if (npols > 1) { - xfrm_tmpl_sort(stp, tpp, xfrm_nr, family, net); + xfrm_tmpl_sort(stp, tpp, xfrm_nr, family); tpp = stp; } diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 50621d982970..c6f3c4a1bd99 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -27,6 +27,8 @@ #include <linux/interrupt.h> #include <linux/kernel.h> +#include <crypto/aead.h> + #include "xfrm_hash.h" #define xfrm_state_deref_prot(table, net) \ @@ -177,63 +179,132 @@ int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) static bool km_is_alive(const struct km_event *c); void km_state_expired(struct xfrm_state *x, int hard, u32 portid); -static DEFINE_SPINLOCK(xfrm_type_lock); int xfrm_register_type(const struct xfrm_type *type, unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); - const struct xfrm_type **typemap; int err = 0; - if (unlikely(afinfo == NULL)) + if (!afinfo) return -EAFNOSUPPORT; - typemap = afinfo->type_map; - spin_lock_bh(&xfrm_type_lock); - if (likely(typemap[type->proto] == NULL)) - typemap[type->proto] = type; - else - err = -EEXIST; - spin_unlock_bh(&xfrm_type_lock); +#define X(afi, T, name) do { \ + WARN_ON((afi)->type_ ## name); \ + (afi)->type_ ## name = (T); \ + } while (0) + + switch (type->proto) { + case IPPROTO_COMP: + X(afinfo, type, comp); + break; + case IPPROTO_AH: + X(afinfo, type, ah); + break; + case IPPROTO_ESP: + X(afinfo, type, esp); + break; + case IPPROTO_IPIP: + X(afinfo, type, ipip); + break; + case IPPROTO_DSTOPTS: + X(afinfo, type, dstopts); + break; + case IPPROTO_ROUTING: + X(afinfo, type, routing); + break; + case IPPROTO_IPV6: + X(afinfo, type, ipip6); + break; + default: + WARN_ON(1); + err = -EPROTONOSUPPORT; + break; + } +#undef X rcu_read_unlock(); return err; } EXPORT_SYMBOL(xfrm_register_type); -int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family) +void xfrm_unregister_type(const struct xfrm_type *type, unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); - const struct xfrm_type **typemap; - int err = 0; if (unlikely(afinfo == NULL)) - return -EAFNOSUPPORT; - typemap = afinfo->type_map; - spin_lock_bh(&xfrm_type_lock); + return; - if (unlikely(typemap[type->proto] != type)) - err = -ENOENT; - else - typemap[type->proto] = NULL; - spin_unlock_bh(&xfrm_type_lock); +#define X(afi, T, name) do { \ + WARN_ON((afi)->type_ ## name != (T)); \ + (afi)->type_ ## name = NULL; \ + } while (0) + + switch (type->proto) { + case IPPROTO_COMP: + X(afinfo, type, comp); + break; + case IPPROTO_AH: + X(afinfo, type, ah); + break; + case IPPROTO_ESP: + X(afinfo, type, esp); + break; + case IPPROTO_IPIP: + X(afinfo, type, ipip); + break; + case IPPROTO_DSTOPTS: + X(afinfo, type, dstopts); + break; + case IPPROTO_ROUTING: + X(afinfo, type, routing); + break; + case IPPROTO_IPV6: + X(afinfo, type, ipip6); + break; + default: + WARN_ON(1); + break; + } +#undef X rcu_read_unlock(); - return err; } EXPORT_SYMBOL(xfrm_unregister_type); static const struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family) { + const struct xfrm_type *type = NULL; struct xfrm_state_afinfo *afinfo; - const struct xfrm_type **typemap; - const struct xfrm_type *type; int modload_attempted = 0; retry: afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return NULL; - typemap = afinfo->type_map; - type = READ_ONCE(typemap[proto]); + switch (proto) { + case IPPROTO_COMP: + type = afinfo->type_comp; + break; + case IPPROTO_AH: + type = afinfo->type_ah; + break; + case IPPROTO_ESP: + type = afinfo->type_esp; + break; + case IPPROTO_IPIP: + type = afinfo->type_ipip; + break; + case IPPROTO_DSTOPTS: + type = afinfo->type_dstopts; + break; + case IPPROTO_ROUTING: + type = afinfo->type_routing; + break; + case IPPROTO_IPV6: + type = afinfo->type_ipip6; + break; + default: + break; + } + if (unlikely(type && !try_module_get(type->owner))) type = NULL; @@ -253,65 +324,71 @@ static void xfrm_put_type(const struct xfrm_type *type) module_put(type->owner); } -static DEFINE_SPINLOCK(xfrm_type_offload_lock); int xfrm_register_type_offload(const struct xfrm_type_offload *type, unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); - const struct xfrm_type_offload **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; - typemap = afinfo->type_offload_map; - spin_lock_bh(&xfrm_type_offload_lock); - if (likely(typemap[type->proto] == NULL)) - typemap[type->proto] = type; - else - err = -EEXIST; - spin_unlock_bh(&xfrm_type_offload_lock); + switch (type->proto) { + case IPPROTO_ESP: + WARN_ON(afinfo->type_offload_esp); + afinfo->type_offload_esp = type; + break; + default: + WARN_ON(1); + err = -EPROTONOSUPPORT; + break; + } + rcu_read_unlock(); return err; } EXPORT_SYMBOL(xfrm_register_type_offload); -int xfrm_unregister_type_offload(const struct xfrm_type_offload *type, - unsigned short family) +void xfrm_unregister_type_offload(const struct xfrm_type_offload *type, + unsigned short family) { struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); - const struct xfrm_type_offload **typemap; - int err = 0; if (unlikely(afinfo == NULL)) - return -EAFNOSUPPORT; - typemap = afinfo->type_offload_map; - spin_lock_bh(&xfrm_type_offload_lock); + return; - if (unlikely(typemap[type->proto] != type)) - err = -ENOENT; - else - typemap[type->proto] = NULL; - spin_unlock_bh(&xfrm_type_offload_lock); + switch (type->proto) { + case IPPROTO_ESP: + WARN_ON(afinfo->type_offload_esp != type); + afinfo->type_offload_esp = NULL; + break; + default: + WARN_ON(1); + break; + } rcu_read_unlock(); - return err; } EXPORT_SYMBOL(xfrm_unregister_type_offload); static const struct xfrm_type_offload * xfrm_get_type_offload(u8 proto, unsigned short family, bool try_load) { + const struct xfrm_type_offload *type = NULL; struct xfrm_state_afinfo *afinfo; - const struct xfrm_type_offload **typemap; - const struct xfrm_type_offload *type; retry: afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return NULL; - typemap = afinfo->type_offload_map; - type = typemap[proto]; + switch (proto) { + case IPPROTO_ESP: + type = afinfo->type_offload_esp; + break; + default: + break; + } + if ((type && !try_module_get(type->owner))) type = NULL; @@ -770,24 +847,79 @@ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si) EXPORT_SYMBOL(xfrm_sad_getinfo); static void +__xfrm4_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) +{ + const struct flowi4 *fl4 = &fl->u.ip4; + + sel->daddr.a4 = fl4->daddr; + sel->saddr.a4 = fl4->saddr; + sel->dport = xfrm_flowi_dport(fl, &fl4->uli); + sel->dport_mask = htons(0xffff); + sel->sport = xfrm_flowi_sport(fl, &fl4->uli); + sel->sport_mask = htons(0xffff); + sel->family = AF_INET; + sel->prefixlen_d = 32; + sel->prefixlen_s = 32; + sel->proto = fl4->flowi4_proto; + sel->ifindex = fl4->flowi4_oif; +} + +static void +__xfrm6_init_tempsel(struct xfrm_selector *sel, const struct flowi *fl) +{ + const struct flowi6 *fl6 = &fl->u.ip6; + + /* Initialize temporary selector matching only to current session. */ + *(struct in6_addr *)&sel->daddr = fl6->daddr; + *(struct in6_addr *)&sel->saddr = fl6->saddr; + sel->dport = xfrm_flowi_dport(fl, &fl6->uli); + sel->dport_mask = htons(0xffff); + sel->sport = xfrm_flowi_sport(fl, &fl6->uli); + sel->sport_mask = htons(0xffff); + sel->family = AF_INET6; + sel->prefixlen_d = 128; + sel->prefixlen_s = 128; + sel->proto = fl6->flowi6_proto; + sel->ifindex = fl6->flowi6_oif; +} + +static void xfrm_init_tempstate(struct xfrm_state *x, const struct flowi *fl, const struct xfrm_tmpl *tmpl, const xfrm_address_t *daddr, const xfrm_address_t *saddr, unsigned short family) { - struct xfrm_state_afinfo *afinfo = xfrm_state_afinfo_get_rcu(family); - - if (!afinfo) - return; + switch (family) { + case AF_INET: + __xfrm4_init_tempsel(&x->sel, fl); + break; + case AF_INET6: + __xfrm6_init_tempsel(&x->sel, fl); + break; + } - afinfo->init_tempsel(&x->sel, fl); + x->id = tmpl->id; - if (family != tmpl->encap_family) { - afinfo = xfrm_state_afinfo_get_rcu(tmpl->encap_family); - if (!afinfo) - return; + switch (tmpl->encap_family) { + case AF_INET: + if (x->id.daddr.a4 == 0) + x->id.daddr.a4 = daddr->a4; + x->props.saddr = tmpl->saddr; + if (x->props.saddr.a4 == 0) + x->props.saddr.a4 = saddr->a4; + break; + case AF_INET6: + if (ipv6_addr_any((struct in6_addr *)&x->id.daddr)) + memcpy(&x->id.daddr, daddr, sizeof(x->sel.daddr)); + memcpy(&x->props.saddr, &tmpl->saddr, sizeof(x->props.saddr)); + if (ipv6_addr_any((struct in6_addr *)&x->props.saddr)) + memcpy(&x->props.saddr, saddr, sizeof(x->props.saddr)); + break; } - afinfo->init_temprop(x, tmpl, daddr, saddr); + + x->props.mode = tmpl->mode; + x->props.reqid = tmpl->reqid; + x->props.family = tmpl->encap_family; } static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark, @@ -1633,51 +1765,129 @@ xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, EXPORT_SYMBOL(xfrm_find_acq); #ifdef CONFIG_XFRM_SUB_POLICY -int +#if IS_ENABLED(CONFIG_IPV6) +/* distribution counting sort function for xfrm_state and xfrm_tmpl */ +static void +__xfrm6_sort(void **dst, void **src, int n, + int (*cmp)(const void *p), int maxclass) +{ + int count[XFRM_MAX_DEPTH] = { }; + int class[XFRM_MAX_DEPTH]; + int i; + + for (i = 0; i < n; i++) { + int c = cmp(src[i]); + + class[i] = c; + count[c]++; + } + + for (i = 2; i < maxclass; i++) + count[i] += count[i - 1]; + + for (i = 0; i < n; i++) { + dst[count[class[i] - 1]++] = src[i]; + src[i] = NULL; + } +} + +/* Rule for xfrm_state: + * + * rule 1: select IPsec transport except AH + * rule 2: select MIPv6 RO or inbound trigger + * rule 3: select IPsec transport AH + * rule 4: select IPsec tunnel + * rule 5: others + */ +static int __xfrm6_state_sort_cmp(const void *p) +{ + const struct xfrm_state *v = p; + + switch (v->props.mode) { + case XFRM_MODE_TRANSPORT: + if (v->id.proto != IPPROTO_AH) + return 1; + else + return 3; +#if IS_ENABLED(CONFIG_IPV6_MIP6) + case XFRM_MODE_ROUTEOPTIMIZATION: + case XFRM_MODE_IN_TRIGGER: + return 2; +#endif + case XFRM_MODE_TUNNEL: + case XFRM_MODE_BEET: + return 4; + } + return 5; +} + +/* Rule for xfrm_tmpl: + * + * rule 1: select IPsec transport + * rule 2: select MIPv6 RO or inbound trigger + * rule 3: select IPsec tunnel + * rule 4: others + */ +static int __xfrm6_tmpl_sort_cmp(const void *p) +{ + const struct xfrm_tmpl *v = p; + + switch (v->mode) { + case XFRM_MODE_TRANSPORT: + return 1; +#if IS_ENABLED(CONFIG_IPV6_MIP6) + case XFRM_MODE_ROUTEOPTIMIZATION: + case XFRM_MODE_IN_TRIGGER: + return 2; +#endif + case XFRM_MODE_TUNNEL: + case XFRM_MODE_BEET: + return 3; + } + return 4; +} +#else +static inline int __xfrm6_state_sort_cmp(const void *p) { return 5; } +static inline int __xfrm6_tmpl_sort_cmp(const void *p) { return 4; } + +static inline void +__xfrm6_sort(void **dst, void **src, int n, + int (*cmp)(const void *p), int maxclass) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = src[i]; +} +#endif /* CONFIG_IPV6 */ + +void xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, - unsigned short family, struct net *net) + unsigned short family) { int i; - int err = 0; - struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); - if (!afinfo) - return -EAFNOSUPPORT; - spin_lock_bh(&net->xfrm.xfrm_state_lock); /*FIXME*/ - if (afinfo->tmpl_sort) - err = afinfo->tmpl_sort(dst, src, n); + if (family == AF_INET6) + __xfrm6_sort((void **)dst, (void **)src, n, + __xfrm6_tmpl_sort_cmp, 5); else for (i = 0; i < n; i++) dst[i] = src[i]; - spin_unlock_bh(&net->xfrm.xfrm_state_lock); - rcu_read_unlock(); - return err; } -EXPORT_SYMBOL(xfrm_tmpl_sort); -int +void xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, unsigned short family) { int i; - int err = 0; - struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); - struct net *net = xs_net(*src); - - if (!afinfo) - return -EAFNOSUPPORT; - spin_lock_bh(&net->xfrm.xfrm_state_lock); - if (afinfo->state_sort) - err = afinfo->state_sort(dst, src, n); + if (family == AF_INET6) + __xfrm6_sort((void **)dst, (void **)src, n, + __xfrm6_state_sort_cmp, 6); else for (i = 0; i < n; i++) dst[i] = src[i]; - spin_unlock_bh(&net->xfrm.xfrm_state_lock); - rcu_read_unlock(); - return err; } -EXPORT_SYMBOL(xfrm_state_sort); #endif /* Silly enough, but I'm lazy to build resolution list */ @@ -2195,38 +2405,49 @@ void xfrm_state_delete_tunnel(struct xfrm_state *x) } EXPORT_SYMBOL(xfrm_state_delete_tunnel); -int xfrm_state_mtu(struct xfrm_state *x, int mtu) +u32 xfrm_state_mtu(struct xfrm_state *x, int mtu) { const struct xfrm_type *type = READ_ONCE(x->type); + struct crypto_aead *aead; + u32 blksize, net_adj = 0; + + if (x->km.state != XFRM_STATE_VALID || + !type || type->proto != IPPROTO_ESP) + return mtu - x->props.header_len; + + aead = x->data; + blksize = ALIGN(crypto_aead_blocksize(aead), 4); - if (x->km.state == XFRM_STATE_VALID && - type && type->get_mtu) - return type->get_mtu(x, mtu); + switch (x->props.mode) { + case XFRM_MODE_TRANSPORT: + case XFRM_MODE_BEET: + if (x->props.family == AF_INET) + net_adj = sizeof(struct iphdr); + else if (x->props.family == AF_INET6) + net_adj = sizeof(struct ipv6hdr); + break; + case XFRM_MODE_TUNNEL: + break; + default: + WARN_ON_ONCE(1); + break; + } - return mtu - x->props.header_len; + return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - + net_adj) & ~(blksize - 1)) + net_adj - 2; } +EXPORT_SYMBOL_GPL(xfrm_state_mtu); int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) { - const struct xfrm_state_afinfo *afinfo; const struct xfrm_mode *inner_mode; const struct xfrm_mode *outer_mode; int family = x->props.family; int err; - err = -EAFNOSUPPORT; - afinfo = xfrm_state_get_afinfo(family); - if (!afinfo) - goto error; - - err = 0; - if (afinfo->init_flags) - err = afinfo->init_flags(x); - - rcu_read_unlock(); - - if (err) - goto error; + if (family == AF_INET && + xs_net(x)->ipv4.sysctl_ip_no_pmtu_disc) + x->props.flags |= XFRM_STATE_NOPMTUDISC; err = -EPROTONOSUPPORT; diff --git a/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh b/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh new file mode 100755 index 000000000000..e4009f658003 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/gre_inner_v4_multipath.sh @@ -0,0 +1,305 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test traffic distribution when there are multiple routes between an IPv4 +# GRE tunnel. The tunnel carries IPv4 traffic between multiple hosts. +# Multiple routes are in the underlay network. With the default multipath +# policy, SW2 will only look at the outer IP addresses, hence only a single +# route would be used. +# +# +-------------------------+ +# | H1 | +# | $h1 + | +# | 192.0.3.{2-62}/24 | | +# +-------------------|-----+ +# | +# +-------------------|------------------------+ +# | SW1 | | +# | $ol1 + | +# | 192.0.3.1/24 | +# | | +# | + g1 (gre) | +# | loc=192.0.2.65 | +# | rem=192.0.2.66 --. | +# | tos=inherit | | +# | v | +# | + $ul1 | +# | | 192.0.2.129/28 | +# +---------------------|----------------------+ +# | +# +---------------------|----------------------+ +# | SW2 | | +# | $ul21 + | +# | 192.0.2.130/28 | +# | | | +# ! ________________|_____ | +# | / \ | +# | | | | +# | + $ul22.111 (vlan) + $ul22.222 (vlan) | +# | | 192.0.2.145/28 | 192.0.2.161/28 | +# | | | | +# +--|----------------------|------------------+ +# | | +# +--|----------------------|------------------+ +# | | | | +# | + $ul32.111 (vlan) + $ul32.222 (vlan) | +# | | 192.0.2.146/28 | 192.0.2.162/28 | +# | | | | +# | \______________________/ | +# | | | +# | | | +# | $ul31 + | +# | 192.0.2.177/28 | SW3 | +# +---------------------|----------------------+ +# | +# +---------------------|----------------------+ +# | + $ul4 | +# | ^ 192.0.2.178/28 | +# | | | +# | + g2 (gre) | | +# | loc=192.0.2.66 | | +# | rem=192.0.2.65 --' | +# | tos=inherit | +# | | +# | $ol4 + | +# | 192.0.4.1/24 | SW4 | +# +--------------------|-----------------------+ +# | +# +--------------------|---------+ +# | | | +# | $h2 + | +# | 192.0.4.{2-62}/24 H2 | +# +------------------------------+ + +ALL_TESTS=" + ping_ipv4 + multipath_ipv4 +" + +NUM_NETIFS=10 +source lib.sh + +h1_create() +{ + simple_if_init $h1 192.0.3.2/24 + ip route add vrf v$h1 192.0.4.0/24 via 192.0.3.1 +} + +h1_destroy() +{ + ip route del vrf v$h1 192.0.4.0/24 via 192.0.3.1 + simple_if_fini $h1 192.0.3.2/24 +} + +sw1_create() +{ + simple_if_init $ol1 192.0.3.1/24 + __simple_if_init $ul1 v$ol1 192.0.2.129/28 + + tunnel_create g1 gre 192.0.2.65 192.0.2.66 tos inherit dev v$ol1 + __simple_if_init g1 v$ol1 192.0.2.65/32 + ip route add vrf v$ol1 192.0.2.66/32 via 192.0.2.130 + + ip route add vrf v$ol1 192.0.4.0/24 nexthop dev g1 +} + +sw1_destroy() +{ + ip route del vrf v$ol1 192.0.4.0/24 + + ip route del vrf v$ol1 192.0.2.66/32 + __simple_if_fini g1 192.0.2.65/32 + tunnel_destroy g1 + + __simple_if_fini $ul1 192.0.2.129/28 + simple_if_fini $ol1 192.0.3.1/24 +} + +sw2_create() +{ + simple_if_init $ul21 192.0.2.130/28 + __simple_if_init $ul22 v$ul21 + vlan_create $ul22 111 v$ul21 192.0.2.145/28 + vlan_create $ul22 222 v$ul21 192.0.2.161/28 + + ip route add vrf v$ul21 192.0.2.65/32 via 192.0.2.129 + ip route add vrf v$ul21 192.0.2.66/32 \ + nexthop via 192.0.2.146 \ + nexthop via 192.0.2.162 +} + +sw2_destroy() +{ + ip route del vrf v$ul21 192.0.2.66/32 + ip route del vrf v$ul21 192.0.2.65/32 + + vlan_destroy $ul22 222 + vlan_destroy $ul22 111 + __simple_if_fini $ul22 + simple_if_fini $ul21 192.0.2.130/28 +} + +sw3_create() +{ + simple_if_init $ul31 192.0.2.177/28 + __simple_if_init $ul32 v$ul31 + vlan_create $ul32 111 v$ul31 192.0.2.146/28 + vlan_create $ul32 222 v$ul31 192.0.2.162/28 + + ip route add vrf v$ul31 192.0.2.66/32 via 192.0.2.178 + ip route add vrf v$ul31 192.0.2.65/32 \ + nexthop via 192.0.2.145 \ + nexthop via 192.0.2.161 + + tc qdisc add dev $ul32 clsact + tc filter add dev $ul32 ingress pref 111 prot 802.1Q \ + flower vlan_id 111 action pass + tc filter add dev $ul32 ingress pref 222 prot 802.1Q \ + flower vlan_id 222 action pass +} + +sw3_destroy() +{ + tc qdisc del dev $ul32 clsact + + ip route del vrf v$ul31 192.0.2.65/32 + ip route del vrf v$ul31 192.0.2.66/32 + + vlan_destroy $ul32 222 + vlan_destroy $ul32 111 + __simple_if_fini $ul32 + simple_if_fini $ul31 192.0.2.177/28 +} + +sw4_create() +{ + simple_if_init $ol4 192.0.4.1/24 + __simple_if_init $ul4 v$ol4 192.0.2.178/28 + + tunnel_create g2 gre 192.0.2.66 192.0.2.65 tos inherit dev v$ol4 + __simple_if_init g2 v$ol4 192.0.2.66/32 + ip route add vrf v$ol4 192.0.2.65/32 via 192.0.2.177 + + ip route add vrf v$ol4 192.0.3.0/24 nexthop dev g2 +} + +sw4_destroy() +{ + ip route del vrf v$ol4 192.0.3.0/24 + + ip route del vrf v$ol4 192.0.2.65/32 + __simple_if_fini g2 192.0.2.66/32 + tunnel_destroy g2 + + __simple_if_fini $ul4 192.0.2.178/28 + simple_if_fini $ol4 192.0.4.1/24 +} + +h2_create() +{ + simple_if_init $h2 192.0.4.2/24 + ip route add vrf v$h2 192.0.3.0/24 via 192.0.4.1 +} + +h2_destroy() +{ + ip route del vrf v$h2 192.0.3.0/24 via 192.0.4.1 + simple_if_fini $h2 192.0.4.2/24 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + + ol1=${NETIFS[p2]} + ul1=${NETIFS[p3]} + + ul21=${NETIFS[p4]} + ul22=${NETIFS[p5]} + + ul32=${NETIFS[p6]} + ul31=${NETIFS[p7]} + + ul4=${NETIFS[p8]} + ol4=${NETIFS[p9]} + + h2=${NETIFS[p10]} + + vrf_prepare + h1_create + sw1_create + sw2_create + sw3_create + sw4_create + h2_create + + forwarding_enable +} + +cleanup() +{ + pre_cleanup + + forwarding_restore + + h2_destroy + sw4_destroy + sw3_destroy + sw2_destroy + sw1_destroy + h1_destroy + vrf_cleanup +} + +multipath4_test() +{ + local what=$1; shift + local weight1=$1; shift + local weight2=$1; shift + + sysctl_set net.ipv4.fib_multipath_hash_policy 2 + ip route replace vrf v$ul21 192.0.2.66/32 \ + nexthop via 192.0.2.146 weight $weight1 \ + nexthop via 192.0.2.162 weight $weight2 + + local t0_111=$(tc_rule_stats_get $ul32 111 ingress) + local t0_222=$(tc_rule_stats_get $ul32 222 ingress) + + ip vrf exec v$h1 \ + $MZ $h1 -q -p 64 -A "192.0.3.2-192.0.3.62" -B "192.0.4.2-192.0.4.62" \ + -d 1msec -c 50 -t udp "sp=1024,dp=1024" + sleep 1 + + local t1_111=$(tc_rule_stats_get $ul32 111 ingress) + local t1_222=$(tc_rule_stats_get $ul32 222 ingress) + + local d111=$((t1_111 - t0_111)) + local d222=$((t1_222 - t0_222)) + multipath_eval "$what" $weight1 $weight2 $d111 $d222 + + ip route replace vrf v$ul21 192.0.2.66/32 \ + nexthop via 192.0.2.146 \ + nexthop via 192.0.2.162 + sysctl_restore net.ipv4.fib_multipath_hash_policy +} + +ping_ipv4() +{ + ping_test $h1 192.0.4.2 +} + +multipath_ipv4() +{ + log_info "Running IPv4 over GRE over IPv4 multipath tests" + multipath4_test "ECMP" 1 1 + multipath4_test "Weighted MP 2:1" 2 1 + multipath4_test "Weighted MP 11:45" 11 45 +} + +trap cleanup EXIT + +setup_prepare +setup_wait +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh b/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh new file mode 100755 index 000000000000..e449475c4d3e --- /dev/null +++ b/tools/testing/selftests/net/forwarding/gre_inner_v6_multipath.sh @@ -0,0 +1,306 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test traffic distribution when there are multiple routes between an IPv4 +# GRE tunnel. The tunnel carries IPv6 traffic between multiple hosts. +# Multiple routes are in the underlay network. With the default multipath +# policy, SW2 will only look at the outer IP addresses, hence only a single +# route would be used. +# +# +-------------------------+ +# | H1 | +# | $h1 + | +# | 2001:db8:1::2/64 | | +# +-------------------|-----+ +# | +# +-------------------|------------------------+ +# | SW1 | | +# | $ol1 + | +# | 2001:db8:1::1/64 | +# | | +# | + g1 (gre) | +# | loc=192.0.2.65 | +# | rem=192.0.2.66 --. | +# | tos=inherit | | +# | v | +# | + $ul1 | +# | | 192.0.2.129/28 | +# +---------------------|----------------------+ +# | +# +---------------------|----------------------+ +# | SW2 | | +# | $ul21 + | +# | 192.0.2.130/28 | +# | | | +# ! ________________|_____ | +# | / \ | +# | | | | +# | + $ul22.111 (vlan) + $ul22.222 (vlan) | +# | | 192.0.2.145/28 | 192.0.2.161/28 | +# | | | | +# +--|----------------------|------------------+ +# | | +# +--|----------------------|------------------+ +# | | | | +# | + $ul32.111 (vlan) + $ul32.222 (vlan) | +# | | 192.0.2.146/28 | 192.0.2.162/28 | +# | | | | +# | \______________________/ | +# | | | +# | | | +# | $ul31 + | +# | 192.0.2.177/28 | SW3 | +# +---------------------|----------------------+ +# | +# +---------------------|----------------------+ +# | + $ul4 | +# | ^ 192.0.2.178/28 | +# | | | +# | + g2 (gre) | | +# | loc=192.0.2.66 | | +# | rem=192.0.2.65 --' | +# | tos=inherit | +# | | +# | $ol4 + | +# | 2001:db8:2::1/64 | SW4 | +# +--------------------|-----------------------+ +# | +# +--------------------|---------+ +# | | | +# | $h2 + | +# | 2001:db8:2::2/64 H2 | +# +------------------------------+ + +ALL_TESTS=" + ping_ipv6 + multipath_ipv6 +" + +NUM_NETIFS=10 +source lib.sh + +h1_create() +{ + simple_if_init $h1 2001:db8:1::2/64 + ip -6 route add vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1 +} + +h1_destroy() +{ + ip -6 route del vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1 + simple_if_fini $h1 2001:db8:1::2/64 +} + +sw1_create() +{ + simple_if_init $ol1 2001:db8:1::1/64 + __simple_if_init $ul1 v$ol1 192.0.2.129/28 + + tunnel_create g1 gre 192.0.2.65 192.0.2.66 tos inherit dev v$ol1 + __simple_if_init g1 v$ol1 192.0.2.65/32 + ip route add vrf v$ol1 192.0.2.66/32 via 192.0.2.130 + + ip -6 route add vrf v$ol1 2001:db8:2::/64 dev g1 +} + +sw1_destroy() +{ + ip -6 route del vrf v$ol1 2001:db8:2::/64 + + ip route del vrf v$ol1 192.0.2.66/32 + __simple_if_fini g1 192.0.2.65/32 + tunnel_destroy g1 + + __simple_if_fini $ul1 192.0.2.129/28 + simple_if_fini $ol1 2001:db8:1::1/64 +} + +sw2_create() +{ + simple_if_init $ul21 192.0.2.130/28 + __simple_if_init $ul22 v$ul21 + vlan_create $ul22 111 v$ul21 192.0.2.145/28 + vlan_create $ul22 222 v$ul21 192.0.2.161/28 + + ip route add vrf v$ul21 192.0.2.65/32 via 192.0.2.129 + ip route add vrf v$ul21 192.0.2.66/32 \ + nexthop via 192.0.2.146 \ + nexthop via 192.0.2.162 +} + +sw2_destroy() +{ + ip route del vrf v$ul21 192.0.2.66/32 + ip route del vrf v$ul21 192.0.2.65/32 + + vlan_destroy $ul22 222 + vlan_destroy $ul22 111 + __simple_if_fini $ul22 + simple_if_fini $ul21 192.0.2.130/28 +} + +sw3_create() +{ + simple_if_init $ul31 192.0.2.177/28 + __simple_if_init $ul32 v$ul31 + vlan_create $ul32 111 v$ul31 192.0.2.146/28 + vlan_create $ul32 222 v$ul31 192.0.2.162/28 + + ip route add vrf v$ul31 192.0.2.66/32 via 192.0.2.178 + ip route add vrf v$ul31 192.0.2.65/32 \ + nexthop via 192.0.2.145 \ + nexthop via 192.0.2.161 + + tc qdisc add dev $ul32 clsact + tc filter add dev $ul32 ingress pref 111 prot 802.1Q \ + flower vlan_id 111 action pass + tc filter add dev $ul32 ingress pref 222 prot 802.1Q \ + flower vlan_id 222 action pass +} + +sw3_destroy() +{ + tc qdisc del dev $ul32 clsact + + ip route del vrf v$ul31 192.0.2.65/32 + ip route del vrf v$ul31 192.0.2.66/32 + + vlan_destroy $ul32 222 + vlan_destroy $ul32 111 + __simple_if_fini $ul32 + simple_if_fini $ul31 192.0.2.177/28 +} + +sw4_create() +{ + simple_if_init $ol4 2001:db8:2::1/64 + __simple_if_init $ul4 v$ol4 192.0.2.178/28 + + tunnel_create g2 gre 192.0.2.66 192.0.2.65 tos inherit dev v$ol4 + __simple_if_init g2 v$ol4 192.0.2.66/32 + ip route add vrf v$ol4 192.0.2.65/32 via 192.0.2.177 + + ip -6 route add vrf v$ol4 2001:db8:1::/64 dev g2 +} + +sw4_destroy() +{ + ip -6 route del vrf v$ol4 2001:db8:1::/64 + + ip route del vrf v$ol4 192.0.2.65/32 + __simple_if_fini g2 192.0.2.66/32 + tunnel_destroy g2 + + __simple_if_fini $ul4 192.0.2.178/28 + simple_if_fini $ol4 2001:db8:2::1/64 +} + +h2_create() +{ + simple_if_init $h2 2001:db8:2::2/64 + ip -6 route add vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1 +} + +h2_destroy() +{ + ip -6 route del vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1 + simple_if_fini $h2 2001:db8:2::2/64 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + + ol1=${NETIFS[p2]} + ul1=${NETIFS[p3]} + + ul21=${NETIFS[p4]} + ul22=${NETIFS[p5]} + + ul32=${NETIFS[p6]} + ul31=${NETIFS[p7]} + + ul4=${NETIFS[p8]} + ol4=${NETIFS[p9]} + + h2=${NETIFS[p10]} + + vrf_prepare + h1_create + sw1_create + sw2_create + sw3_create + sw4_create + h2_create + + forwarding_enable +} + +cleanup() +{ + pre_cleanup + + forwarding_restore + + h2_destroy + sw4_destroy + sw3_destroy + sw2_destroy + sw1_destroy + h1_destroy + vrf_cleanup +} + +multipath6_test() +{ + local what=$1; shift + local weight1=$1; shift + local weight2=$1; shift + + sysctl_set net.ipv4.fib_multipath_hash_policy 2 + ip route replace vrf v$ul21 192.0.2.66/32 \ + nexthop via 192.0.2.146 weight $weight1 \ + nexthop via 192.0.2.162 weight $weight2 + + local t0_111=$(tc_rule_stats_get $ul32 111 ingress) + local t0_222=$(tc_rule_stats_get $ul32 222 ingress) + + ip vrf exec v$h1 \ + $MZ $h1 -6 -q -p 64 -A "2001:db8:1::2-2001:db8:1::1e" \ + -B "2001:db8:2::2-2001:db8:2::1e" \ + -d 1msec -c 50 -t udp "sp=1024,dp=1024" + sleep 1 + + local t1_111=$(tc_rule_stats_get $ul32 111 ingress) + local t1_222=$(tc_rule_stats_get $ul32 222 ingress) + + local d111=$((t1_111 - t0_111)) + local d222=$((t1_222 - t0_222)) + multipath_eval "$what" $weight1 $weight2 $d111 $d222 + + ip route replace vrf v$ul21 192.0.2.66/32 \ + nexthop via 192.0.2.146 \ + nexthop via 192.0.2.162 + sysctl_restore net.ipv4.fib_multipath_hash_policy +} + +ping_ipv6() +{ + ping_test $h1 2001:db8:2::2 +} + +multipath_ipv6() +{ + log_info "Running IPv6 over GRE over IPv4 multipath tests" + multipath6_test "ECMP" 1 1 + multipath6_test "Weighted MP 2:1" 2 1 + multipath6_test "Weighted MP 11:45" 11 45 +} + +trap cleanup EXIT + +setup_prepare +setup_wait +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh b/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh new file mode 100755 index 000000000000..a257979d3fc5 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/ip6gre_inner_v4_multipath.sh @@ -0,0 +1,304 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test traffic distribution when there are multiple routes between an IPv6 +# GRE tunnel. The tunnel carries IPv4 traffic between multiple hosts. +# Multiple routes are in the underlay network. With the default multipath +# policy, SW2 will only look at the outer IP addresses, hence only a single +# route would be used. +# +# +-------------------------+ +# | H1 | +# | $h1 + | +# | 192.0.3.{2-62}/24 | | +# +-------------------|-----+ +# | +# +-------------------|-------------------------+ +# | SW1 | | +# | $ol1 + | +# | 192.0.3.1/24 | +# | | +# | + g1 (gre) | +# | loc=2001:db8:40::1 | +# | rem=2001:db8:40::2 --. | +# | tos=inherit | | +# | v | +# | + $ul1 | +# | | 2001:db8:80::1/64 | +# +-------------------------|-------------------+ +# | +# +-------------------------|-------------------+ +# | SW2 | | +# | $ul21 + | +# | 2001:db8:80::2/64 | +# | | | +# ! ________________|_____ | +# | / \ | +# | | | | +# | + $ul22.111 (vlan) + $ul22.222 (vlan) | +# | | 2001:db8:81::1/64 | 2001:db8:82::1/64 | +# | | | | +# +--|----------------------|-------------------+ +# | | +# +--|----------------------|-------------------+ +# | | | | +# | + $ul32.111 (vlan) + $ul32.222 (vlan) | +# | | 2001:db8:81::2/64 | 2001:db8:82::2/64 | +# | | | | +# | \______________________/ | +# | | | +# | | | +# | $ul31 + | +# | 2001:db8:83::2/64 | SW3 | +# +-------------------------|-------------------+ +# | +# +-------------------------|-------------------+ +# | + $ul4 | +# | ^ 2001:db8:83::1/64 | +# | + g2 (gre) | | +# | loc=2001:db8:40::2 | | +# | rem=2001:db8:40::1 --' | +# | tos=inherit | +# | | +# | $ol4 + | +# | 192.0.4.1/24 | SW4 | +# +--------------------|------------------------+ +# | +# +--------------------|---------+ +# | | | +# | $h2 + | +# | 192.0.4.{2-62}/24 H2 | +# +------------------------------+ + +ALL_TESTS=" + ping_ipv4 + multipath_ipv4 +" + +NUM_NETIFS=10 +source lib.sh + +h1_create() +{ + simple_if_init $h1 192.0.3.2/24 + ip route add vrf v$h1 192.0.4.0/24 via 192.0.3.1 +} + +h1_destroy() +{ + ip route del vrf v$h1 192.0.4.0/24 via 192.0.3.1 + simple_if_fini $h1 192.0.3.2/24 +} + +sw1_create() +{ + simple_if_init $ol1 192.0.3.1/24 + __simple_if_init $ul1 v$ol1 2001:db8:80::1/64 + + tunnel_create g1 ip6gre 2001:db8:40::1 2001:db8:40::2 tos inherit dev v$ol1 + __simple_if_init g1 v$ol1 2001:db8:40::1/128 + ip -6 route add vrf v$ol1 2001:db8:40::2/128 via 2001:db8:80::2 + + ip route add vrf v$ol1 192.0.4.0/24 nexthop dev g1 +} + +sw1_destroy() +{ + ip route del vrf v$ol1 192.0.4.0/24 + + ip -6 route del vrf v$ol1 2001:db8:40::2/128 + __simple_if_fini g1 2001:db8:40::1/128 + tunnel_destroy g1 + + __simple_if_fini $ul1 2001:db8:80::1/64 + simple_if_fini $ol1 192.0.3.1/24 +} + +sw2_create() +{ + simple_if_init $ul21 2001:db8:80::2/64 + __simple_if_init $ul22 v$ul21 + vlan_create $ul22 111 v$ul21 2001:db8:81::1/64 + vlan_create $ul22 222 v$ul21 2001:db8:82::1/64 + + ip -6 route add vrf v$ul21 2001:db8:40::1/128 via 2001:db8:80::1 + ip -6 route add vrf v$ul21 2001:db8:40::2/128 \ + nexthop via 2001:db8:81::2 \ + nexthop via 2001:db8:82::2 +} + +sw2_destroy() +{ + ip -6 route del vrf v$ul21 2001:db8:40::2/128 + ip -6 route del vrf v$ul21 2001:db8:40::1/128 + + vlan_destroy $ul22 222 + vlan_destroy $ul22 111 + __simple_if_fini $ul22 + simple_if_fini $ul21 2001:db8:80::2/64 +} + +sw3_create() +{ + simple_if_init $ul31 2001:db8:83::2/64 + __simple_if_init $ul32 v$ul31 + vlan_create $ul32 111 v$ul31 2001:db8:81::2/64 + vlan_create $ul32 222 v$ul31 2001:db8:82::2/64 + + ip -6 route add vrf v$ul31 2001:db8:40::2/128 via 2001:db8:83::1 + ip -6 route add vrf v$ul31 2001:db8:40::1/128 \ + nexthop via 2001:db8:81::1 \ + nexthop via 2001:db8:82::1 + + tc qdisc add dev $ul32 clsact + tc filter add dev $ul32 ingress pref 111 prot 802.1Q \ + flower vlan_id 111 action pass + tc filter add dev $ul32 ingress pref 222 prot 802.1Q \ + flower vlan_id 222 action pass +} + +sw3_destroy() +{ + tc qdisc del dev $ul32 clsact + + ip -6 route del vrf v$ul31 2001:db8:40::1/128 + ip -6 route del vrf v$ul31 2001:db8:40::2/128 + + vlan_destroy $ul32 222 + vlan_destroy $ul32 111 + __simple_if_fini $ul32 + simple_if_fini $ul31 2001:Db8:83::2/64 +} + +sw4_create() +{ + simple_if_init $ol4 192.0.4.1/24 + __simple_if_init $ul4 v$ol4 2001:db8:83::1/64 + + tunnel_create g2 ip6gre 2001:db8:40::2 2001:db8:40::1 tos inherit dev v$ol4 + __simple_if_init g2 v$ol4 2001:db8:40::2/128 + ip -6 route add vrf v$ol4 2001:db8:40::1/128 via 2001:db8:83::2 + + ip route add vrf v$ol4 192.0.3.0/24 nexthop dev g2 +} + +sw4_destroy() +{ + ip route del vrf v$ol4 192.0.3.0/24 + + ip -6 route del vrf v$ol4 2001:db8:40::1/128 + __simple_if_fini g2 2001:db8:40::2/128 + tunnel_destroy g2 + + __simple_if_fini $ul4 2001:db8:83::1/64 + simple_if_fini $ol4 192.0.4.1/24 +} + +h2_create() +{ + simple_if_init $h2 192.0.4.2/24 + ip route add vrf v$h2 192.0.3.0/24 via 192.0.4.1 +} + +h2_destroy() +{ + ip route del vrf v$h2 192.0.3.0/24 via 192.0.4.1 + simple_if_fini $h2 192.0.4.2/24 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + + ol1=${NETIFS[p2]} + ul1=${NETIFS[p3]} + + ul21=${NETIFS[p4]} + ul22=${NETIFS[p5]} + + ul32=${NETIFS[p6]} + ul31=${NETIFS[p7]} + + ul4=${NETIFS[p8]} + ol4=${NETIFS[p9]} + + h2=${NETIFS[p10]} + + vrf_prepare + h1_create + sw1_create + sw2_create + sw3_create + sw4_create + h2_create + + forwarding_enable +} + +cleanup() +{ + pre_cleanup + + forwarding_restore + + h2_destroy + sw4_destroy + sw3_destroy + sw2_destroy + sw1_destroy + h1_destroy + vrf_cleanup +} + +multipath4_test() +{ + local what=$1; shift + local weight1=$1; shift + local weight2=$1; shift + + sysctl_set net.ipv6.fib_multipath_hash_policy 2 + ip route replace vrf v$ul21 2001:db8:40::2/128 \ + nexthop via 2001:db8:81::2 weight $weight1 \ + nexthop via 2001:db8:82::2 weight $weight2 + + local t0_111=$(tc_rule_stats_get $ul32 111 ingress) + local t0_222=$(tc_rule_stats_get $ul32 222 ingress) + + ip vrf exec v$h1 \ + $MZ $h1 -q -p 64 -A "192.0.3.2-192.0.3.62" -B "192.0.4.2-192.0.4.62" \ + -d 1msec -c 50 -t udp "sp=1024,dp=1024" + sleep 1 + + local t1_111=$(tc_rule_stats_get $ul32 111 ingress) + local t1_222=$(tc_rule_stats_get $ul32 222 ingress) + + local d111=$((t1_111 - t0_111)) + local d222=$((t1_222 - t0_222)) + multipath_eval "$what" $weight1 $weight2 $d111 $d222 + + ip route replace vrf v$ul21 2001:db8:40::2/128 \ + nexthop via 2001:db8:81::2 \ + nexthop via 2001:db8:82::2 + sysctl_restore net.ipv6.fib_multipath_hash_policy +} + +ping_ipv4() +{ + ping_test $h1 192.0.4.2 +} + +multipath_ipv4() +{ + log_info "Running IPv4 over GRE over IPv6 multipath tests" + multipath4_test "ECMP" 1 1 + multipath4_test "Weighted MP 2:1" 2 1 + multipath4_test "Weighted MP 11:45" 11 45 +} + +trap cleanup EXIT + +setup_prepare +setup_wait +tests_run + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh b/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh new file mode 100755 index 000000000000..d208f5243ade --- /dev/null +++ b/tools/testing/selftests/net/forwarding/ip6gre_inner_v6_multipath.sh @@ -0,0 +1,305 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test traffic distribution when there are multiple routes between an IPv6 +# GRE tunnel. The tunnel carries IPv6 traffic between multiple hosts. +# Multiple routes are in the underlay network. With the default multipath +# policy, SW2 will only look at the outer IP addresses, hence only a single +# route would be used. +# +# +-------------------------+ +# | H1 | +# | $h1 + | +# | 2001:db8:1::2/64 | | +# +-------------------|-----+ +# | +# +-------------------|-------------------------+ +# | SW1 | | +# | $ol1 + | +# | 2001:db8:1::1/64 | +# | | +# | + g1 (gre) | +# | loc=2001:db8:40::1 | +# | rem=2001:db8:40::2 --. | +# | tos=inherit | | +# | v | +# | + $ul1 | +# | | 2001:db8:80::1/64 | +# +-------------------------|-------------------+ +# | +# +-------------------------|-------------------+ +# | SW2 | | +# | $ul21 + | +# | 2001:db8:80::2/64 | +# | | | +# ! ________________|_____ | +# | / \ | +# | | | | +# | + $ul22.111 (vlan) + $ul22.222 (vlan) | +# | | 2001:db8:81::1/64 | 2001:db8:82::1/64 | +# | | | | +# +--|----------------------|-------------------+ +# | | +# +--|----------------------|-------------------+ +# | | | | +# | + $ul32.111 (vlan) + $ul32.222 (vlan) | +# | | 2001:db8:81::2/64 | 2001:db8:82::2/64 | +# | | | | +# | \______________________/ | +# | | | +# | | | +# | $ul31 + | +# | 2001:db8:83::2/64 | SW3 | +# +-------------------------|-------------------+ +# | +# +-------------------------|-------------------+ +# | + $ul4 | +# | ^ 2001:db8:83::1/64 | +# | + g2 (gre) | | +# | loc=2001:db8:40::2 | | +# | rem=2001:db8:40::1 --' | +# | tos=inherit | +# | | +# | $ol4 + | +# | 2001:db8:2::1/64 | SW4 | +# +--------------------|------------------------+ +# | +# +--------------------|---------+ +# | | | +# | $h2 + | +# | 2001:db8:2::2/64 H2 | +# +------------------------------+ + +ALL_TESTS=" + ping_ipv6 + multipath_ipv6 +" + +NUM_NETIFS=10 +source lib.sh + +h1_create() +{ + simple_if_init $h1 2001:db8:1::2/64 + ip -6 route add vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1 +} + +h1_destroy() +{ + ip -6 route del vrf v$h1 2001:db8:2::/64 via 2001:db8:1::1 + simple_if_fini $h1 2001:db8:1::2/64 +} + +sw1_create() +{ + simple_if_init $ol1 2001:db8:1::1/64 + __simple_if_init $ul1 v$ol1 2001:db8:80::1/64 + + tunnel_create g1 ip6gre 2001:db8:40::1 2001:db8:40::2 tos inherit dev v$ol1 + __simple_if_init g1 v$ol1 2001:db8:40::1/128 + ip -6 route add vrf v$ol1 2001:db8:40::2/128 via 2001:db8:80::2 + + ip -6 route add vrf v$ol1 2001:db8:2::/64 dev g1 +} + +sw1_destroy() +{ + ip -6 route del vrf v$ol1 2001:db8:2::/64 + + ip -6 route del vrf v$ol1 2001:db8:40::2/128 + __simple_if_fini g1 2001:db8:40::1/128 + tunnel_destroy g1 + + __simple_if_fini $ul1 2001:db8:80::1/64 + simple_if_fini $ol1 2001:db8:1::1/64 +} + +sw2_create() +{ + simple_if_init $ul21 2001:db8:80::2/64 + __simple_if_init $ul22 v$ul21 + vlan_create $ul22 111 v$ul21 2001:db8:81::1/64 + vlan_create $ul22 222 v$ul21 2001:db8:82::1/64 + + ip -6 route add vrf v$ul21 2001:db8:40::1/128 via 2001:db8:80::1 + ip -6 route add vrf v$ul21 2001:db8:40::2/128 \ + nexthop via 2001:db8:81::2 \ + nexthop via 2001:db8:82::2 +} + +sw2_destroy() +{ + ip -6 route del vrf v$ul21 2001:db8:40::2/128 + ip -6 route del vrf v$ul21 2001:db8:40::1/128 + + vlan_destroy $ul22 222 + vlan_destroy $ul22 111 + __simple_if_fini $ul22 + simple_if_fini $ul21 2001:db8:80::2/64 +} + +sw3_create() +{ + simple_if_init $ul31 2001:db8:83::2/64 + __simple_if_init $ul32 v$ul31 + vlan_create $ul32 111 v$ul31 2001:db8:81::2/64 + vlan_create $ul32 222 v$ul31 2001:db8:82::2/64 + + ip -6 route add vrf v$ul31 2001:db8:40::2/128 via 2001:db8:83::1 + ip -6 route add vrf v$ul31 2001:db8:40::1/128 \ + nexthop via 2001:db8:81::1 \ + nexthop via 2001:db8:82::1 + + tc qdisc add dev $ul32 clsact + tc filter add dev $ul32 ingress pref 111 prot 802.1Q \ + flower vlan_id 111 action pass + tc filter add dev $ul32 ingress pref 222 prot 802.1Q \ + flower vlan_id 222 action pass +} + +sw3_destroy() +{ + tc qdisc del dev $ul32 clsact + + ip -6 route del vrf v$ul31 2001:db8:40::1/128 + ip -6 route del vrf v$ul31 2001:db8:40::2/128 + + vlan_destroy $ul32 222 + vlan_destroy $ul32 111 + __simple_if_fini $ul32 + simple_if_fini $ul31 2001:Db8:83::2/64 +} + +sw4_create() +{ + simple_if_init $ol4 2001:db8:2::1/64 + __simple_if_init $ul4 v$ol4 2001:db8:83::1/64 + + tunnel_create g2 ip6gre 2001:db8:40::2 2001:db8:40::1 tos inherit dev v$ol4 + __simple_if_init g2 v$ol4 2001:db8:40::2/128 + ip -6 route add vrf v$ol4 2001:db8:40::1/128 via 2001:db8:83::2 + + ip -6 route add vrf v$ol4 2001:db8:1::/64 dev g2 +} + +sw4_destroy() +{ + ip -6 route del vrf v$ol4 2001:db8:1::/64 + + ip -6 route del vrf v$ol4 2001:db8:40::1/128 + __simple_if_fini g2 2001:db8:40::2/128 + tunnel_destroy g2 + + __simple_if_fini $ul4 2001:db8:83::1/64 + simple_if_fini $ol4 2001:db8:2::1/64 +} + +h2_create() +{ + simple_if_init $h2 2001:db8:2::2/64 + ip -6 route add vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1 +} + +h2_destroy() +{ + ip -6 route del vrf v$h2 2001:db8:1::/64 via 2001:db8:2::1 + simple_if_fini $h2 2001:db8:2::2/64 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + + ol1=${NETIFS[p2]} + ul1=${NETIFS[p3]} + + ul21=${NETIFS[p4]} + ul22=${NETIFS[p5]} + + ul32=${NETIFS[p6]} + ul31=${NETIFS[p7]} + + ul4=${NETIFS[p8]} + ol4=${NETIFS[p9]} + + h2=${NETIFS[p10]} + + vrf_prepare + h1_create + sw1_create + sw2_create + sw3_create + sw4_create + h2_create + + forwarding_enable +} + +cleanup() +{ + pre_cleanup + + forwarding_restore + + h2_destroy + sw4_destroy + sw3_destroy + sw2_destroy + sw1_destroy + h1_destroy + vrf_cleanup +} + +multipath6_test() +{ + local what=$1; shift + local weight1=$1; shift + local weight2=$1; shift + + sysctl_set net.ipv6.fib_multipath_hash_policy 2 + ip route replace vrf v$ul21 2001:db8:40::2/128 \ + nexthop via 2001:db8:81::2 weight $weight1 \ + nexthop via 2001:db8:82::2 weight $weight2 + + local t0_111=$(tc_rule_stats_get $ul32 111 ingress) + local t0_222=$(tc_rule_stats_get $ul32 222 ingress) + + ip vrf exec v$h1 \ + $MZ $h1 -6 -q -p 64 -A "2001:db8:1::2-2001:db8:1::1e" \ + -B "2001:db8:2::2-2001:db8:2::1e" \ + -d 1msec -c 50 -t udp "sp=1024,dp=1024" + sleep 1 + + local t1_111=$(tc_rule_stats_get $ul32 111 ingress) + local t1_222=$(tc_rule_stats_get $ul32 222 ingress) + + local d111=$((t1_111 - t0_111)) + local d222=$((t1_222 - t0_222)) + multipath_eval "$what" $weight1 $weight2 $d111 $d222 + + ip route replace vrf v$ul21 2001:db8:40::2/128 \ + nexthop via 2001:db8:81::2 \ + nexthop via 2001:db8:82::2 + sysctl_restore net.ipv6.fib_multipath_hash_policy +} + +ping_ipv6() +{ + ping_test $h1 2001:db8:2::2 +} + +multipath_ipv6() +{ + log_info "Running IPv6 over GRE over IPv6 multipath tests" + multipath6_test "ECMP" 1 1 + multipath6_test "Weighted MP 2:1" 2 1 + multipath6_test "Weighted MP 11:45" 11 45 +} + +trap cleanup EXIT + +setup_prepare +setup_wait +tests_run + +exit $EXIT_STATUS |