summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/net/actions,owl-emac.yaml92
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/isdn/hardware/mISDN/hfcmulti.c2
-rw-r--r--drivers/net/Makefile2
-rw-r--r--drivers/net/dsa/Kconfig17
-rw-r--r--drivers/net/dsa/mv88e6xxx/global2_scratch.c6
-rw-r--r--drivers/net/ethernet/Kconfig1
-rw-r--r--drivers/net/ethernet/Makefile1
-rw-r--r--drivers/net/ethernet/actions/Kconfig26
-rw-r--r--drivers/net/ethernet/actions/Makefile6
-rw-r--r--drivers/net/ethernet/actions/owl-emac.c1625
-rw-r--r--drivers/net/ethernet/actions/owl-emac.h280
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c108
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h9
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c3
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hnae3.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.c10
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h14
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c1312
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h58
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c277
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c10
-rw-r--r--drivers/nfc/fdp/fdp.c2
-rw-r--r--include/linux/netdevice.h21
-rw-r--r--include/linux/qed/qed_chain.h8
-rw-r--r--net/bridge/br_arp_nd_proxy.c4
-rw-r--r--net/bridge/br_vlan_tunnel.c2
-rw-r--r--net/core/dev.c6
-rw-r--r--net/core/net-procfs.c3
-rw-r--r--net/core/net-sysfs.c2
-rw-r--r--net/dsa/Kconfig10
-rw-r--r--net/l2tp/l2tp_core.c2
-rw-r--r--net/openvswitch/vport.h2
-rw-r--r--net/sched/cls_flower.c40
38 files changed, 3338 insertions, 669 deletions
diff --git a/Documentation/devicetree/bindings/net/actions,owl-emac.yaml b/Documentation/devicetree/bindings/net/actions,owl-emac.yaml
new file mode 100644
index 000000000000..1626e0a821b0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/actions,owl-emac.yaml
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/actions,owl-emac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Actions Semi Owl SoCs Ethernet MAC Controller
+
+maintainers:
+ - Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
+
+description: |
+ This Ethernet MAC is used on the Owl family of SoCs from Actions Semi.
+ It provides the RMII and SMII interfaces and is compliant with the
+ IEEE 802.3 CSMA/CD standard, supporting both half-duplex and full-duplex
+ operation modes at 10/100 Mb/s data transfer rates.
+
+allOf:
+ - $ref: "ethernet-controller.yaml#"
+
+properties:
+ compatible:
+ oneOf:
+ - const: actions,owl-emac
+ - items:
+ - enum:
+ - actions,s500-emac
+ - const: actions,owl-emac
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ minItems: 2
+ maxItems: 2
+
+ clock-names:
+ additionalItems: false
+ items:
+ - const: eth
+ - const: rmii
+
+ resets:
+ maxItems: 1
+
+ actions,ethcfg:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the device containing custom config.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - resets
+ - phy-mode
+ - phy-handle
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/actions,s500-cmu.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/actions,s500-reset.h>
+
+ ethernet@b0310000 {
+ compatible = "actions,s500-emac", "actions,owl-emac";
+ reg = <0xb0310000 0x10000>;
+ interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cmu 59 /*CLK_ETHERNET*/>, <&cmu CLK_RMII_REF>;
+ clock-names = "eth", "rmii";
+ resets = <&cmu RESET_ETHERNET>;
+ phy-mode = "rmii";
+ phy-handle = <&eth_phy>;
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth_phy: ethernet-phy@3 {
+ reg = <0x3>;
+ interrupt-parent = <&sirq>;
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index e1fa5ad9bb30..ad214621655f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1530,6 +1530,7 @@ F: Documentation/devicetree/bindings/dma/owl-dma.yaml
F: Documentation/devicetree/bindings/i2c/i2c-owl.yaml
F: Documentation/devicetree/bindings/interrupt-controller/actions,owl-sirq.yaml
F: Documentation/devicetree/bindings/mmc/owl-mmc.yaml
+F: Documentation/devicetree/bindings/net/actions,owl-emac.yaml
F: Documentation/devicetree/bindings/pinctrl/actions,*
F: Documentation/devicetree/bindings/power/actions,owl-sps.txt
F: Documentation/devicetree/bindings/timer/actions,owl-timer.txt
@@ -1542,6 +1543,7 @@ F: drivers/dma/owl-dma.c
F: drivers/i2c/busses/i2c-owl.c
F: drivers/irqchip/irq-owl-sirq.c
F: drivers/mmc/host/owl-mmc.c
+F: drivers/net/ethernet/actions/
F: drivers/pinctrl/actions/*
F: drivers/soc/actions/
F: include/dt-bindings/power/owl-*
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index 7013a3f08429..14092152b786 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -2748,8 +2748,6 @@ hfcmulti_interrupt(int intno, void *dev_id)
if (hc->ctype != HFC_TYPE_E1)
ph_state_irq(hc, r_irq_statech);
}
- if (status & V_EXT_IRQSTA)
- ; /* external IRQ */
if (status & V_LOST_STA) {
/* LOST IRQ */
HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index f4990ff32fa4..040e20b81317 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -45,7 +45,7 @@ obj-$(CONFIG_ARCNET) += arcnet/
obj-$(CONFIG_DEV_APPLETALK) += appletalk/
obj-$(CONFIG_CAIF) += caif/
obj-$(CONFIG_CAN) += can/
-obj-y += dsa/
+obj-$(CONFIG_NET_DSA) += dsa/
obj-$(CONFIG_ETHERNET) += ethernet/
obj-$(CONFIG_FDDI) += fddi/
obj-$(CONFIG_HIPPI) += hippi/
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 3af373e90806..a5f1aa911fe2 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -1,12 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "Distributed Switch Architecture drivers"
- depends on HAVE_NET_DSA
+ depends on NET_DSA
source "drivers/net/dsa/b53/Kconfig"
config NET_DSA_BCM_SF2
tristate "Broadcom Starfighter 2 Ethernet switch support"
- depends on HAS_IOMEM && NET_DSA
+ depends on HAS_IOMEM
select NET_DSA_TAG_BRCM
select FIXED_PHY
select BCM7XXX_PHY
@@ -18,7 +18,6 @@ config NET_DSA_BCM_SF2
config NET_DSA_LOOP
tristate "DSA mock-up Ethernet switch chip support"
- depends on NET_DSA
select FIXED_PHY
help
This enables support for a fake mock-up switch chip which
@@ -28,7 +27,7 @@ source "drivers/net/dsa/hirschmann/Kconfig"
config NET_DSA_LANTIQ_GSWIP
tristate "Lantiq / Intel GSWIP"
- depends on HAS_IOMEM && NET_DSA
+ depends on HAS_IOMEM
select NET_DSA_TAG_GSWIP
help
This enables support for the Lantiq / Intel GSWIP 2.1 found in
@@ -36,7 +35,6 @@ config NET_DSA_LANTIQ_GSWIP
config NET_DSA_MT7530
tristate "MediaTek MT753x and MT7621 Ethernet switch support"
- depends on NET_DSA
select NET_DSA_TAG_MTK
help
This enables support for the MediaTek MT7530, MT7531, and MT7621
@@ -44,7 +42,6 @@ config NET_DSA_MT7530
config NET_DSA_MV88E6060
tristate "Marvell 88E6060 ethernet switch chip support"
- depends on NET_DSA
select NET_DSA_TAG_TRAILER
help
This enables support for the Marvell 88E6060 ethernet switch
@@ -64,7 +61,6 @@ source "drivers/net/dsa/xrs700x/Kconfig"
config NET_DSA_QCA8K
tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
- depends on NET_DSA
select NET_DSA_TAG_QCA
select REGMAP
help
@@ -73,7 +69,6 @@ config NET_DSA_QCA8K
config NET_DSA_REALTEK_SMI
tristate "Realtek SMI Ethernet switch family support"
- depends on NET_DSA
select NET_DSA_TAG_RTL4_A
select FIXED_PHY
select IRQ_DOMAIN
@@ -93,7 +88,7 @@ config NET_DSA_SMSC_LAN9303
config NET_DSA_SMSC_LAN9303_I2C
tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in I2C managed mode"
- depends on NET_DSA && I2C
+ depends on I2C
select NET_DSA_SMSC_LAN9303
select REGMAP_I2C
help
@@ -102,7 +97,6 @@ config NET_DSA_SMSC_LAN9303_I2C
config NET_DSA_SMSC_LAN9303_MDIO
tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in MDIO managed mode"
- depends on NET_DSA
select NET_DSA_SMSC_LAN9303
help
Enable access functions if the SMSC/Microchip LAN9303 is configured
@@ -110,7 +104,6 @@ config NET_DSA_SMSC_LAN9303_MDIO
config NET_DSA_VITESSE_VSC73XX
tristate
- depends on NET_DSA
select FIXED_PHY
select VITESSE_PHY
select GPIOLIB
@@ -120,7 +113,6 @@ config NET_DSA_VITESSE_VSC73XX
config NET_DSA_VITESSE_VSC73XX_SPI
tristate "Vitesse VSC7385/7388/7395/7398 SPI mode support"
- depends on NET_DSA
depends on SPI
select NET_DSA_VITESSE_VSC73XX
help
@@ -129,7 +121,6 @@ config NET_DSA_VITESSE_VSC73XX_SPI
config NET_DSA_VITESSE_VSC73XX_PLATFORM
tristate "Vitesse VSC7385/7388/7395/7398 Platform mode support"
- depends on NET_DSA
depends on HAS_IOMEM
select NET_DSA_VITESSE_VSC73XX
help
diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
index 7c2c67405322..eda710062933 100644
--- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c
+++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
@@ -42,7 +42,7 @@ static int mv88e6xxx_g2_scratch_write(struct mv88e6xxx_chip *chip, int reg,
}
/**
- * mv88e6xxx_g2_scratch_gpio_get_bit - get a bit
+ * mv88e6xxx_g2_scratch_get_bit - get a bit
* @chip: chip private data
* @base_reg: base of scratch bits
* @offset: index of bit within the register
@@ -67,7 +67,7 @@ static int mv88e6xxx_g2_scratch_get_bit(struct mv88e6xxx_chip *chip,
}
/**
- * mv88e6xxx_g2_scratch_gpio_set_bit - set (or clear) a bit
+ * mv88e6xxx_g2_scratch_set_bit - set (or clear) a bit
* @chip: chip private data
* @base_reg: base of scratch bits
* @offset: index of bit within the register
@@ -240,7 +240,7 @@ const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops = {
};
/**
- * mv88e6xxx_g2_gpio_set_smi - set gpio muxing for external smi
+ * mv88e6xxx_g2_scratch_gpio_set_smi - set gpio muxing for external smi
* @chip: chip private data
* @external: set mux for external smi, or free for gpio usage
*
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index ad04660b97b8..4b85f2b74872 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -19,6 +19,7 @@ config SUNGEM_PHY
tristate
source "drivers/net/ethernet/3com/Kconfig"
+source "drivers/net/ethernet/actions/Kconfig"
source "drivers/net/ethernet/adaptec/Kconfig"
source "drivers/net/ethernet/aeroflex/Kconfig"
source "drivers/net/ethernet/agere/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 1e7dc8a7762d..9394493e8187 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_NET_VENDOR_3COM) += 3com/
obj-$(CONFIG_NET_VENDOR_8390) += 8390/
+obj-$(CONFIG_NET_VENDOR_ACTIONS) += actions/
obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/
obj-$(CONFIG_GRETH) += aeroflex/
obj-$(CONFIG_NET_VENDOR_AGERE) += agere/
diff --git a/drivers/net/ethernet/actions/Kconfig b/drivers/net/ethernet/actions/Kconfig
new file mode 100644
index 000000000000..ccad6a3f4d6f
--- /dev/null
+++ b/drivers/net/ethernet/actions/Kconfig
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config NET_VENDOR_ACTIONS
+ bool "Actions Semi devices"
+ default y
+ depends on ARCH_ACTIONS
+ help
+ If you have a network (Ethernet) card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all the
+ questions about Actions Semi devices. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_ACTIONS
+
+config OWL_EMAC
+ tristate "Actions Semi Owl Ethernet MAC support"
+ select PHYLIB
+ help
+ This driver supports the Actions Semi Ethernet Media Access
+ Controller (EMAC) found on the S500 and S900 SoCs. The controller
+ is compliant with the IEEE 802.3 CSMA/CD standard and supports
+ both half-duplex and full-duplex operation modes at 10/100 Mb/s.
+
+endif # NET_VENDOR_ACTIONS
diff --git a/drivers/net/ethernet/actions/Makefile b/drivers/net/ethernet/actions/Makefile
new file mode 100644
index 000000000000..fde8001d538a
--- /dev/null
+++ b/drivers/net/ethernet/actions/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Actions Semi Owl SoCs built-in ethernet macs
+#
+
+obj-$(CONFIG_OWL_EMAC) += owl-emac.o
diff --git a/drivers/net/ethernet/actions/owl-emac.c b/drivers/net/ethernet/actions/owl-emac.c
new file mode 100644
index 000000000000..b8e771c2bc40
--- /dev/null
+++ b/drivers/net/ethernet/actions/owl-emac.c
@@ -0,0 +1,1625 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Actions Semi Owl SoCs Ethernet MAC driver
+ *
+ * Copyright (c) 2012 Actions Semi Inc.
+ * Copyright (c) 2021 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
+ */
+
+#include <linux/circ_buf.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/reset.h>
+
+#include "owl-emac.h"
+
+#define OWL_EMAC_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK)
+
+static u32 owl_emac_reg_read(struct owl_emac_priv *priv, u32 reg)
+{
+ return readl(priv->base + reg);
+}
+
+static void owl_emac_reg_write(struct owl_emac_priv *priv, u32 reg, u32 data)
+{
+ writel(data, priv->base + reg);
+}
+
+static u32 owl_emac_reg_update(struct owl_emac_priv *priv,
+ u32 reg, u32 mask, u32 val)
+{
+ u32 data, old_val;
+
+ data = owl_emac_reg_read(priv, reg);
+ old_val = data & mask;
+
+ data &= ~mask;
+ data |= val & mask;
+
+ owl_emac_reg_write(priv, reg, data);
+
+ return old_val;
+}
+
+static void owl_emac_reg_set(struct owl_emac_priv *priv, u32 reg, u32 bits)
+{
+ owl_emac_reg_update(priv, reg, bits, bits);
+}
+
+static void owl_emac_reg_clear(struct owl_emac_priv *priv, u32 reg, u32 bits)
+{
+ owl_emac_reg_update(priv, reg, bits, 0);
+}
+
+static struct device *owl_emac_get_dev(struct owl_emac_priv *priv)
+{
+ return priv->netdev->dev.parent;
+}
+
+static void owl_emac_irq_enable(struct owl_emac_priv *priv)
+{
+ /* Enable all interrupts except TU.
+ *
+ * Note the NIE and AIE bits shall also be set in order to actually
+ * enable the selected interrupts.
+ */
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR7,
+ OWL_EMAC_BIT_MAC_CSR7_NIE |
+ OWL_EMAC_BIT_MAC_CSR7_AIE |
+ OWL_EMAC_BIT_MAC_CSR7_ALL_NOT_TUE);
+}
+
+static void owl_emac_irq_disable(struct owl_emac_priv *priv)
+{
+ /* Disable all interrupts.
+ *
+ * WARNING: Unset only the NIE and AIE bits in CSR7 to workaround an
+ * unexpected side effect (MAC hardware bug?!) where some bits in the
+ * status register (CSR5) are cleared automatically before being able
+ * to read them via owl_emac_irq_clear().
+ */
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR7,
+ OWL_EMAC_BIT_MAC_CSR7_ALL_NOT_TUE);
+}
+
+static u32 owl_emac_irq_status(struct owl_emac_priv *priv)
+{
+ return owl_emac_reg_read(priv, OWL_EMAC_REG_MAC_CSR5);
+}
+
+static u32 owl_emac_irq_clear(struct owl_emac_priv *priv)
+{
+ u32 val = owl_emac_irq_status(priv);
+
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR5, val);
+
+ return val;
+}
+
+static dma_addr_t owl_emac_dma_map_rx(struct owl_emac_priv *priv,
+ struct sk_buff *skb)
+{
+ struct device *dev = owl_emac_get_dev(priv);
+
+ /* Buffer pointer for the RX DMA descriptor must be word aligned. */
+ return dma_map_single(dev, skb_tail_pointer(skb),
+ skb_tailroom(skb), DMA_FROM_DEVICE);
+}
+
+static void owl_emac_dma_unmap_rx(struct owl_emac_priv *priv,
+ struct sk_buff *skb, dma_addr_t dma_addr)
+{
+ struct device *dev = owl_emac_get_dev(priv);
+
+ dma_unmap_single(dev, dma_addr, skb_tailroom(skb), DMA_FROM_DEVICE);
+}
+
+static dma_addr_t owl_emac_dma_map_tx(struct owl_emac_priv *priv,
+ struct sk_buff *skb)
+{
+ struct device *dev = owl_emac_get_dev(priv);
+
+ return dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
+}
+
+static void owl_emac_dma_unmap_tx(struct owl_emac_priv *priv,
+ struct sk_buff *skb, dma_addr_t dma_addr)
+{
+ struct device *dev = owl_emac_get_dev(priv);
+
+ dma_unmap_single(dev, dma_addr, skb_headlen(skb), DMA_TO_DEVICE);
+}
+
+static unsigned int owl_emac_ring_num_unused(struct owl_emac_ring *ring)
+{
+ return CIRC_SPACE(ring->head, ring->tail, ring->size);
+}
+
+static unsigned int owl_emac_ring_get_next(struct owl_emac_ring *ring,
+ unsigned int cur)
+{
+ return (cur + 1) & (ring->size - 1);
+}
+
+static void owl_emac_ring_push_head(struct owl_emac_ring *ring)
+{
+ ring->head = owl_emac_ring_get_next(ring, ring->head);
+}
+
+static void owl_emac_ring_pop_tail(struct owl_emac_ring *ring)
+{
+ ring->tail = owl_emac_ring_get_next(ring, ring->tail);
+}
+
+static struct sk_buff *owl_emac_alloc_skb(struct net_device *netdev)
+{
+ struct sk_buff *skb;
+ int offset;
+
+ skb = netdev_alloc_skb(netdev, OWL_EMAC_RX_FRAME_MAX_LEN +
+ OWL_EMAC_SKB_RESERVE);
+ if (unlikely(!skb))
+ return NULL;
+
+ /* Ensure 4 bytes DMA alignment. */
+ offset = ((uintptr_t)skb->data) & (OWL_EMAC_SKB_ALIGN - 1);
+ if (unlikely(offset))
+ skb_reserve(skb, OWL_EMAC_SKB_ALIGN - offset);
+
+ return skb;
+}
+
+static int owl_emac_ring_prepare_rx(struct owl_emac_priv *priv)
+{
+ struct owl_emac_ring *ring = &priv->rx_ring;
+ struct device *dev = owl_emac_get_dev(priv);
+ struct net_device *netdev = priv->netdev;
+ struct owl_emac_ring_desc *desc;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ int i;
+
+ for (i = 0; i < ring->size; i++) {
+ skb = owl_emac_alloc_skb(netdev);
+ if (!skb)
+ return -ENOMEM;
+
+ dma_addr = owl_emac_dma_map_rx(priv, skb);
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ desc = &ring->descs[i];
+ desc->status = OWL_EMAC_BIT_RDES0_OWN;
+ desc->control = skb_tailroom(skb) & OWL_EMAC_MSK_RDES1_RBS1;
+ desc->buf_addr = dma_addr;
+ desc->reserved = 0;
+
+ ring->skbs[i] = skb;
+ ring->skbs_dma[i] = dma_addr;
+ }
+
+ desc->control |= OWL_EMAC_BIT_RDES1_RER;
+
+ ring->head = 0;
+ ring->tail = 0;
+
+ return 0;
+}
+
+static void owl_emac_ring_prepare_tx(struct owl_emac_priv *priv)
+{
+ struct owl_emac_ring *ring = &priv->tx_ring;
+ struct owl_emac_ring_desc *desc;
+ int i;
+
+ for (i = 0; i < ring->size; i++) {
+ desc = &ring->descs[i];
+
+ desc->status = 0;
+ desc->control = OWL_EMAC_BIT_TDES1_IC;
+ desc->buf_addr = 0;
+ desc->reserved = 0;
+ }
+
+ desc->control |= OWL_EMAC_BIT_TDES1_TER;
+
+ memset(ring->skbs_dma, 0, sizeof(dma_addr_t) * ring->size);
+
+ ring->head = 0;
+ ring->tail = 0;
+}
+
+static void owl_emac_ring_unprepare_rx(struct owl_emac_priv *priv)
+{
+ struct owl_emac_ring *ring = &priv->rx_ring;
+ int i;
+
+ for (i = 0; i < ring->size; i++) {
+ ring->descs[i].status = 0;
+
+ if (!ring->skbs_dma[i])
+ continue;
+
+ owl_emac_dma_unmap_rx(priv, ring->skbs[i], ring->skbs_dma[i]);
+ ring->skbs_dma[i] = 0;
+
+ dev_kfree_skb(ring->skbs[i]);
+ ring->skbs[i] = NULL;
+ }
+}
+
+static void owl_emac_ring_unprepare_tx(struct owl_emac_priv *priv)
+{
+ struct owl_emac_ring *ring = &priv->tx_ring;
+ int i;
+
+ for (i = 0; i < ring->size; i++) {
+ ring->descs[i].status = 0;
+
+ if (!ring->skbs_dma[i])
+ continue;
+
+ owl_emac_dma_unmap_tx(priv, ring->skbs[i], ring->skbs_dma[i]);
+ ring->skbs_dma[i] = 0;
+
+ dev_kfree_skb(ring->skbs[i]);
+ ring->skbs[i] = NULL;
+ }
+}
+
+static int owl_emac_ring_alloc(struct device *dev, struct owl_emac_ring *ring,
+ unsigned int size)
+{
+ ring->descs = dmam_alloc_coherent(dev,
+ sizeof(struct owl_emac_ring_desc) * size,
+ &ring->descs_dma, GFP_KERNEL);
+ if (!ring->descs)
+ return -ENOMEM;
+
+ ring->skbs = devm_kcalloc(dev, size, sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!ring->skbs)
+ return -ENOMEM;
+
+ ring->skbs_dma = devm_kcalloc(dev, size, sizeof(dma_addr_t),
+ GFP_KERNEL);
+ if (!ring->skbs_dma)
+ return -ENOMEM;
+
+ ring->size = size;
+
+ return 0;
+}
+
+static void owl_emac_dma_cmd_resume_rx(struct owl_emac_priv *priv)
+{
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR2,
+ OWL_EMAC_VAL_MAC_CSR2_RPD);
+}
+
+static void owl_emac_dma_cmd_resume_tx(struct owl_emac_priv *priv)
+{
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR1,
+ OWL_EMAC_VAL_MAC_CSR1_TPD);
+}
+
+static u32 owl_emac_dma_cmd_set_tx(struct owl_emac_priv *priv, u32 status)
+{
+ return owl_emac_reg_update(priv, OWL_EMAC_REG_MAC_CSR6,
+ OWL_EMAC_BIT_MAC_CSR6_ST, status);
+}
+
+static u32 owl_emac_dma_cmd_start_tx(struct owl_emac_priv *priv)
+{
+ return owl_emac_dma_cmd_set_tx(priv, ~0);
+}
+
+static u32 owl_emac_dma_cmd_set(struct owl_emac_priv *priv, u32 status)
+{
+ return owl_emac_reg_update(priv, OWL_EMAC_REG_MAC_CSR6,
+ OWL_EMAC_MSK_MAC_CSR6_STSR, status);
+}
+
+static u32 owl_emac_dma_cmd_start(struct owl_emac_priv *priv)
+{
+ return owl_emac_dma_cmd_set(priv, ~0);
+}
+
+static u32 owl_emac_dma_cmd_stop(struct owl_emac_priv *priv)
+{
+ return owl_emac_dma_cmd_set(priv, 0);
+}
+
+static void owl_emac_set_hw_mac_addr(struct net_device *netdev)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ u8 *mac_addr = netdev->dev_addr;
+ u32 addr_high, addr_low;
+
+ addr_high = mac_addr[0] << 8 | mac_addr[1];
+ addr_low = mac_addr[2] << 24 | mac_addr[3] << 16 |
+ mac_addr[4] << 8 | mac_addr[5];
+
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR17, addr_high);
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR16, addr_low);
+}
+
+static void owl_emac_update_link_state(struct owl_emac_priv *priv)
+{
+ u32 val, status;
+
+ if (priv->pause) {
+ val = OWL_EMAC_BIT_MAC_CSR20_FCE | OWL_EMAC_BIT_MAC_CSR20_TUE;
+ val |= OWL_EMAC_BIT_MAC_CSR20_TPE | OWL_EMAC_BIT_MAC_CSR20_RPE;
+ val |= OWL_EMAC_BIT_MAC_CSR20_BPE;
+ } else {
+ val = 0;
+ }
+
+ /* Update flow control. */
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR20, val);
+
+ val = (priv->speed == SPEED_100) ? OWL_EMAC_VAL_MAC_CSR6_SPEED_100M :
+ OWL_EMAC_VAL_MAC_CSR6_SPEED_10M;
+ val <<= OWL_EMAC_OFF_MAC_CSR6_SPEED;
+
+ if (priv->duplex == DUPLEX_FULL)
+ val |= OWL_EMAC_BIT_MAC_CSR6_FD;
+
+ spin_lock_bh(&priv->lock);
+
+ /* Temporarily stop DMA TX & RX. */
+ status = owl_emac_dma_cmd_stop(priv);
+
+ /* Update operation modes. */
+ owl_emac_reg_update(priv, OWL_EMAC_REG_MAC_CSR6,
+ OWL_EMAC_MSK_MAC_CSR6_SPEED |
+ OWL_EMAC_BIT_MAC_CSR6_FD, val);
+
+ /* Restore DMA TX & RX status. */
+ owl_emac_dma_cmd_set(priv, status);
+
+ spin_unlock_bh(&priv->lock);
+}
+
+static void owl_emac_adjust_link(struct net_device *netdev)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ struct phy_device *phydev = netdev->phydev;
+ bool state_changed = false;
+
+ if (phydev->link) {
+ if (!priv->link) {
+ priv->link = phydev->link;
+ state_changed = true;
+ }
+
+ if (priv->speed != phydev->speed) {
+ priv->speed = phydev->speed;
+ state_changed = true;
+ }
+
+ if (priv->duplex != phydev->duplex) {
+ priv->duplex = phydev->duplex;
+ state_changed = true;
+ }
+
+ if (priv->pause != phydev->pause) {
+ priv->pause = phydev->pause;
+ state_changed = true;
+ }
+ } else {
+ if (priv->link) {
+ priv->link = phydev->link;
+ state_changed = true;
+ }
+ }
+
+ if (state_changed) {
+ if (phydev->link)
+ owl_emac_update_link_state(priv);
+
+ if (netif_msg_link(priv))
+ phy_print_status(phydev);
+ }
+}
+
+static irqreturn_t owl_emac_handle_irq(int irq, void *data)
+{
+ struct net_device *netdev = data;
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+
+ if (netif_running(netdev)) {
+ owl_emac_irq_disable(priv);
+ napi_schedule(&priv->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void owl_emac_ether_addr_push(u8 **dst, const u8 *src)
+{
+ u32 *a = (u32 *)(*dst);
+ const u16 *b = (const u16 *)src;
+
+ a[0] = b[0];
+ a[1] = b[1];
+ a[2] = b[2];
+
+ *dst += 12;
+}
+
+static void
+owl_emac_setup_frame_prepare(struct owl_emac_priv *priv, struct sk_buff *skb)
+{
+ const u8 bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ const u8 *mac_addr = priv->netdev->dev_addr;
+ u8 *frame;
+ int i;
+
+ skb_put(skb, OWL_EMAC_SETUP_FRAME_LEN);
+
+ frame = skb->data;
+ memset(frame, 0, skb->len);
+
+ owl_emac_ether_addr_push(&frame, mac_addr);
+ owl_emac_ether_addr_push(&frame, bcast_addr);
+
+ /* Fill multicast addresses. */
+ WARN_ON(priv->mcaddr_list.count >= OWL_EMAC_MAX_MULTICAST_ADDRS);
+ for (i = 0; i < priv->mcaddr_list.count; i++) {
+ mac_addr = priv->mcaddr_list.addrs[i];
+ owl_emac_ether_addr_push(&frame, mac_addr);
+ }
+}
+
+/* The setup frame is a special descriptor which is used to provide physical
+ * addresses (i.e. mac, broadcast and multicast) to the MAC hardware for
+ * filtering purposes. To be recognized as a setup frame, the TDES1_SET bit
+ * must be set in the TX descriptor control field.
+ */
+static int owl_emac_setup_frame_xmit(struct owl_emac_priv *priv)
+{
+ struct owl_emac_ring *ring = &priv->tx_ring;
+ struct net_device *netdev = priv->netdev;
+ struct owl_emac_ring_desc *desc;
+ struct sk_buff *skb;
+ unsigned int tx_head;
+ u32 status, control;
+ dma_addr_t dma_addr;
+ int ret;
+
+ skb = owl_emac_alloc_skb(netdev);
+ if (!skb)
+ return -ENOMEM;
+
+ owl_emac_setup_frame_prepare(priv, skb);
+
+ dma_addr = owl_emac_dma_map_tx(priv, skb);
+ if (dma_mapping_error(owl_emac_get_dev(priv), dma_addr)) {
+ ret = -ENOMEM;
+ goto err_free_skb;
+ }
+
+ spin_lock_bh(&priv->lock);
+
+ tx_head = ring->head;
+ desc = &ring->descs[tx_head];
+
+ status = READ_ONCE(desc->status);
+ control = READ_ONCE(desc->control);
+ dma_rmb(); /* Ensure data has been read before used. */
+
+ if (unlikely(status & OWL_EMAC_BIT_TDES0_OWN) ||
+ !owl_emac_ring_num_unused(ring)) {
+ spin_unlock_bh(&priv->lock);
+ owl_emac_dma_unmap_tx(priv, skb, dma_addr);
+ ret = -EBUSY;
+ goto err_free_skb;
+ }
+
+ ring->skbs[tx_head] = skb;
+ ring->skbs_dma[tx_head] = dma_addr;
+
+ control &= OWL_EMAC_BIT_TDES1_IC | OWL_EMAC_BIT_TDES1_TER; /* Maintain bits */
+ control |= OWL_EMAC_BIT_TDES1_SET;
+ control |= OWL_EMAC_MSK_TDES1_TBS1 & skb->len;
+
+ WRITE_ONCE(desc->control, control);
+ WRITE_ONCE(desc->buf_addr, dma_addr);
+ dma_wmb(); /* Flush descriptor before changing ownership. */
+ WRITE_ONCE(desc->status, OWL_EMAC_BIT_TDES0_OWN);
+
+ owl_emac_ring_push_head(ring);
+
+ /* Temporarily enable DMA TX. */
+ status = owl_emac_dma_cmd_start_tx(priv);
+
+ /* Trigger setup frame processing. */
+ owl_emac_dma_cmd_resume_tx(priv);
+
+ /* Restore DMA TX status. */
+ owl_emac_dma_cmd_set_tx(priv, status);
+
+ /* Stop regular TX until setup frame is processed. */
+ netif_stop_queue(netdev);
+
+ spin_unlock_bh(&priv->lock);
+
+ return 0;
+
+err_free_skb:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static netdev_tx_t owl_emac_ndo_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ struct device *dev = owl_emac_get_dev(priv);
+ struct owl_emac_ring *ring = &priv->tx_ring;
+ struct owl_emac_ring_desc *desc;
+ unsigned int tx_head;
+ u32 status, control;
+ dma_addr_t dma_addr;
+
+ dma_addr = owl_emac_dma_map_tx(priv, skb);
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_err_ratelimited(&netdev->dev, "TX DMA mapping failed\n");
+ dev_kfree_skb(skb);
+ netdev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ spin_lock_bh(&priv->lock);
+
+ tx_head = ring->head;
+ desc = &ring->descs[tx_head];
+
+ status = READ_ONCE(desc->status);
+ control = READ_ONCE(desc->control);
+ dma_rmb(); /* Ensure data has been read before used. */
+
+ if (!owl_emac_ring_num_unused(ring) ||
+ unlikely(status & OWL_EMAC_BIT_TDES0_OWN)) {
+ netif_stop_queue(netdev);
+ spin_unlock_bh(&priv->lock);
+
+ dev_dbg_ratelimited(&netdev->dev, "TX buffer full, status=0x%08x\n",
+ owl_emac_irq_status(priv));
+ owl_emac_dma_unmap_tx(priv, skb, dma_addr);
+ netdev->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+ }
+
+ ring->skbs[tx_head] = skb;
+ ring->skbs_dma[tx_head] = dma_addr;
+
+ control &= OWL_EMAC_BIT_TDES1_IC | OWL_EMAC_BIT_TDES1_TER; /* Maintain bits */
+ control |= OWL_EMAC_BIT_TDES1_FS | OWL_EMAC_BIT_TDES1_LS;
+ control |= OWL_EMAC_MSK_TDES1_TBS1 & skb->len;
+
+ WRITE_ONCE(desc->control, control);
+ WRITE_ONCE(desc->buf_addr, dma_addr);
+ dma_wmb(); /* Flush descriptor before changing ownership. */
+ WRITE_ONCE(desc->status, OWL_EMAC_BIT_TDES0_OWN);
+
+ owl_emac_dma_cmd_resume_tx(priv);
+ owl_emac_ring_push_head(ring);
+
+ /* FIXME: The transmission is currently restricted to a single frame
+ * at a time as a workaround for a MAC hardware bug that causes random
+ * freeze of the TX queue processor.
+ */
+ netif_stop_queue(netdev);
+
+ spin_unlock_bh(&priv->lock);
+
+ return NETDEV_TX_OK;
+}
+
+static bool owl_emac_tx_complete_tail(struct owl_emac_priv *priv)
+{
+ struct owl_emac_ring *ring = &priv->tx_ring;
+ struct net_device *netdev = priv->netdev;
+ struct owl_emac_ring_desc *desc;
+ struct sk_buff *skb;
+ unsigned int tx_tail;
+ u32 status;
+
+ tx_tail = ring->tail;
+ desc = &ring->descs[tx_tail];
+
+ status = READ_ONCE(desc->status);
+ dma_rmb(); /* Ensure data has been read before used. */
+
+ if (status & OWL_EMAC_BIT_TDES0_OWN)
+ return false;
+
+ /* Check for errors. */
+ if (status & OWL_EMAC_BIT_TDES0_ES) {
+ dev_dbg_ratelimited(&netdev->dev,
+ "TX complete error status: 0x%08x\n",
+ status);
+
+ netdev->stats.tx_errors++;
+
+ if (status & OWL_EMAC_BIT_TDES0_UF)
+ netdev->stats.tx_fifo_errors++;
+
+ if (status & OWL_EMAC_BIT_TDES0_EC)
+ netdev->stats.tx_aborted_errors++;
+
+ if (status & OWL_EMAC_BIT_TDES0_LC)
+ netdev->stats.tx_window_errors++;
+
+ if (status & OWL_EMAC_BIT_TDES0_NC)
+ netdev->stats.tx_heartbeat_errors++;
+
+ if (status & OWL_EMAC_BIT_TDES0_LO)
+ netdev->stats.tx_carrier_errors++;
+ } else {
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += ring->skbs[tx_tail]->len;
+ }
+
+ /* Some collisions occurred, but pkt has been transmitted. */
+ if (status & OWL_EMAC_BIT_TDES0_DE)
+ netdev->stats.collisions++;
+
+ skb = ring->skbs[tx_tail];
+ owl_emac_dma_unmap_tx(priv, skb, ring->skbs_dma[tx_tail]);
+ dev_kfree_skb(skb);
+
+ ring->skbs[tx_tail] = NULL;
+ ring->skbs_dma[tx_tail] = 0;
+
+ owl_emac_ring_pop_tail(ring);
+
+ if (unlikely(netif_queue_stopped(netdev)))
+ netif_wake_queue(netdev);
+
+ return true;
+}
+
+static void owl_emac_tx_complete(struct owl_emac_priv *priv)
+{
+ struct owl_emac_ring *ring = &priv->tx_ring;
+ struct net_device *netdev = priv->netdev;
+ unsigned int tx_next;
+ u32 status;
+
+ spin_lock(&priv->lock);
+
+ while (ring->tail != ring->head) {
+ if (!owl_emac_tx_complete_tail(priv))
+ break;
+ }
+
+ /* FIXME: This is a workaround for a MAC hardware bug not clearing
+ * (sometimes) the OWN bit for a transmitted frame descriptor.
+ *
+ * At this point, when TX queue is full, the tail descriptor has the
+ * OWN bit set, which normally means the frame has not been processed
+ * or transmitted yet. But if there is at least one descriptor in the
+ * queue having the OWN bit cleared, we can safely assume the tail
+ * frame has been also processed by the MAC hardware.
+ *
+ * If that's the case, let's force the frame completion by manually
+ * clearing the OWN bit.
+ */
+ if (unlikely(!owl_emac_ring_num_unused(ring))) {
+ tx_next = ring->tail;
+
+ while ((tx_next = owl_emac_ring_get_next(ring, tx_next)) != ring->head) {
+ status = READ_ONCE(ring->descs[tx_next].status);
+ dma_rmb(); /* Ensure data has been read before used. */
+
+ if (status & OWL_EMAC_BIT_TDES0_OWN)
+ continue;
+
+ netdev_dbg(netdev, "Found uncleared TX desc OWN bit\n");
+
+ status = READ_ONCE(ring->descs[ring->tail].status);
+ dma_rmb(); /* Ensure data has been read before used. */
+ status &= ~OWL_EMAC_BIT_TDES0_OWN;
+ WRITE_ONCE(ring->descs[ring->tail].status, status);
+
+ owl_emac_tx_complete_tail(priv);
+ break;
+ }
+ }
+
+ spin_unlock(&priv->lock);
+}
+
+static int owl_emac_rx_process(struct owl_emac_priv *priv, int budget)
+{
+ struct owl_emac_ring *ring = &priv->rx_ring;
+ struct device *dev = owl_emac_get_dev(priv);
+ struct net_device *netdev = priv->netdev;
+ struct owl_emac_ring_desc *desc;
+ struct sk_buff *curr_skb, *new_skb;
+ dma_addr_t curr_dma, new_dma;
+ unsigned int rx_tail, len;
+ u32 status;
+ int recv = 0;
+
+ while (recv < budget) {
+ spin_lock(&priv->lock);
+
+ rx_tail = ring->tail;
+ desc = &ring->descs[rx_tail];
+
+ status = READ_ONCE(desc->status);
+ dma_rmb(); /* Ensure data has been read before used. */
+
+ if (status & OWL_EMAC_BIT_RDES0_OWN) {
+ spin_unlock(&priv->lock);
+ break;
+ }
+
+ curr_skb = ring->skbs[rx_tail];
+ curr_dma = ring->skbs_dma[rx_tail];
+ owl_emac_ring_pop_tail(ring);
+
+ spin_unlock(&priv->lock);
+
+ if (status & (OWL_EMAC_BIT_RDES0_DE | OWL_EMAC_BIT_RDES0_RF |
+ OWL_EMAC_BIT_RDES0_TL | OWL_EMAC_BIT_RDES0_CS |
+ OWL_EMAC_BIT_RDES0_DB | OWL_EMAC_BIT_RDES0_CE |
+ OWL_EMAC_BIT_RDES0_ZERO)) {
+ dev_dbg_ratelimited(&netdev->dev,
+ "RX desc error status: 0x%08x\n",
+ status);
+
+ if (status & OWL_EMAC_BIT_RDES0_DE)
+ netdev->stats.rx_over_errors++;
+
+ if (status & (OWL_EMAC_BIT_RDES0_RF | OWL_EMAC_BIT_RDES0_DB))
+ netdev->stats.rx_frame_errors++;
+
+ if (status & OWL_EMAC_BIT_RDES0_TL)
+ netdev->stats.rx_length_errors++;
+
+ if (status & OWL_EMAC_BIT_RDES0_CS)
+ netdev->stats.collisions++;
+
+ if (status & OWL_EMAC_BIT_RDES0_CE)
+ netdev->stats.rx_crc_errors++;
+
+ if (status & OWL_EMAC_BIT_RDES0_ZERO)
+ netdev->stats.rx_fifo_errors++;
+
+ goto drop_skb;
+ }
+
+ len = (status & OWL_EMAC_MSK_RDES0_FL) >> OWL_EMAC_OFF_RDES0_FL;
+ if (unlikely(len > OWL_EMAC_RX_FRAME_MAX_LEN)) {
+ netdev->stats.rx_length_errors++;
+ netdev_err(netdev, "invalid RX frame len: %u\n", len);
+ goto drop_skb;
+ }
+
+ /* Prepare new skb before receiving the current one. */
+ new_skb = owl_emac_alloc_skb(netdev);
+ if (unlikely(!new_skb))
+ goto drop_skb;
+
+ new_dma = owl_emac_dma_map_rx(priv, new_skb);
+ if (dma_mapping_error(dev, new_dma)) {
+ dev_kfree_skb(new_skb);
+ netdev_err(netdev, "RX DMA mapping failed\n");
+ goto drop_skb;
+ }
+
+ owl_emac_dma_unmap_rx(priv, curr_skb, curr_dma);
+
+ skb_put(curr_skb, len - ETH_FCS_LEN);
+ curr_skb->ip_summed = CHECKSUM_NONE;
+ curr_skb->protocol = eth_type_trans(curr_skb, netdev);
+ curr_skb->dev = netdev;
+
+ netif_receive_skb(curr_skb);
+
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += len;
+ recv++;
+ goto push_skb;
+
+drop_skb:
+ netdev->stats.rx_dropped++;
+ netdev->stats.rx_errors++;
+ /* Reuse the current skb. */
+ new_skb = curr_skb;
+ new_dma = curr_dma;
+
+push_skb:
+ spin_lock(&priv->lock);
+
+ ring->skbs[ring->head] = new_skb;
+ ring->skbs_dma[ring->head] = new_dma;
+
+ WRITE_ONCE(desc->buf_addr, new_dma);
+ dma_wmb(); /* Flush descriptor before changing ownership. */
+ WRITE_ONCE(desc->status, OWL_EMAC_BIT_RDES0_OWN);
+
+ owl_emac_ring_push_head(ring);
+
+ spin_unlock(&priv->lock);
+ }
+
+ return recv;
+}
+
+static int owl_emac_poll(struct napi_struct *napi, int budget)
+{
+ int work_done = 0, ru_cnt = 0, recv;
+ static int tx_err_cnt, rx_err_cnt;
+ struct owl_emac_priv *priv;
+ u32 status, proc_status;
+
+ priv = container_of(napi, struct owl_emac_priv, napi);
+
+ while ((status = owl_emac_irq_clear(priv)) &
+ (OWL_EMAC_BIT_MAC_CSR5_NIS | OWL_EMAC_BIT_MAC_CSR5_AIS)) {
+ recv = 0;
+
+ /* TX setup frame raises ETI instead of TI. */
+ if (status & (OWL_EMAC_BIT_MAC_CSR5_TI | OWL_EMAC_BIT_MAC_CSR5_ETI)) {
+ owl_emac_tx_complete(priv);
+ tx_err_cnt = 0;
+
+ /* Count MAC internal RX errors. */
+ proc_status = status & OWL_EMAC_MSK_MAC_CSR5_RS;
+ proc_status >>= OWL_EMAC_OFF_MAC_CSR5_RS;
+ if (proc_status == OWL_EMAC_VAL_MAC_CSR5_RS_DATA ||
+ proc_status == OWL_EMAC_VAL_MAC_CSR5_RS_CDES ||
+ proc_status == OWL_EMAC_VAL_MAC_CSR5_RS_FDES)
+ rx_err_cnt++;
+ }
+
+ if (status & OWL_EMAC_BIT_MAC_CSR5_RI) {
+ recv = owl_emac_rx_process(priv, budget - work_done);
+ rx_err_cnt = 0;
+
+ /* Count MAC internal TX errors. */
+ proc_status = status & OWL_EMAC_MSK_MAC_CSR5_TS;
+ proc_status >>= OWL_EMAC_OFF_MAC_CSR5_TS;
+ if (proc_status == OWL_EMAC_VAL_MAC_CSR5_TS_DATA ||
+ proc_status == OWL_EMAC_VAL_MAC_CSR5_TS_CDES)
+ tx_err_cnt++;
+ } else if (status & OWL_EMAC_BIT_MAC_CSR5_RU) {
+ /* MAC AHB is in suspended state, will return to RX
+ * descriptor processing when the host changes ownership
+ * of the descriptor and either an RX poll demand CMD is
+ * issued or a new frame is recognized by the MAC AHB.
+ */
+ if (++ru_cnt == 2)
+ owl_emac_dma_cmd_resume_rx(priv);
+
+ recv = owl_emac_rx_process(priv, budget - work_done);
+
+ /* Guard against too many RU interrupts. */
+ if (ru_cnt > 3)
+ break;
+ }
+
+ work_done += recv;
+ if (work_done >= budget)
+ break;
+ }
+
+ if (work_done < budget) {
+ napi_complete_done(napi, work_done);
+ owl_emac_irq_enable(priv);
+ }
+
+ /* Reset MAC when getting too many internal TX or RX errors. */
+ if (tx_err_cnt > 10 || rx_err_cnt > 10) {
+ netdev_dbg(priv->netdev, "%s error status: 0x%08x\n",
+ tx_err_cnt > 10 ? "TX" : "RX", status);
+ rx_err_cnt = 0;
+ tx_err_cnt = 0;
+ schedule_work(&priv->mac_reset_task);
+ }
+
+ return work_done;
+}
+
+static void owl_emac_mdio_clock_enable(struct owl_emac_priv *priv)
+{
+ u32 val;
+
+ /* Enable MDC clock generation by adjusting CLKDIV according to
+ * the vendor implementation of the original driver.
+ */
+ val = owl_emac_reg_read(priv, OWL_EMAC_REG_MAC_CSR10);
+ val &= OWL_EMAC_MSK_MAC_CSR10_CLKDIV;
+ val |= OWL_EMAC_VAL_MAC_CSR10_CLKDIV_128 << OWL_EMAC_OFF_MAC_CSR10_CLKDIV;
+
+ val |= OWL_EMAC_BIT_MAC_CSR10_SB;
+ val |= OWL_EMAC_VAL_MAC_CSR10_OPCODE_CDS << OWL_EMAC_OFF_MAC_CSR10_OPCODE;
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR10, val);
+}
+
+static void owl_emac_core_hw_reset(struct owl_emac_priv *priv)
+{
+ /* Trigger hardware reset. */
+ reset_control_assert(priv->reset);
+ usleep_range(10, 20);
+ reset_control_deassert(priv->reset);
+ usleep_range(100, 200);
+}
+
+static int owl_emac_core_sw_reset(struct owl_emac_priv *priv)
+{
+ u32 val;
+ int ret;
+
+ /* Trigger software reset. */
+ owl_emac_reg_set(priv, OWL_EMAC_REG_MAC_CSR0, OWL_EMAC_BIT_MAC_CSR0_SWR);
+ ret = readl_poll_timeout(priv->base + OWL_EMAC_REG_MAC_CSR0,
+ val, !(val & OWL_EMAC_BIT_MAC_CSR0_SWR),
+ OWL_EMAC_POLL_DELAY_USEC,
+ OWL_EMAC_RESET_POLL_TIMEOUT_USEC);
+ if (ret)
+ return ret;
+
+ if (priv->phy_mode == PHY_INTERFACE_MODE_RMII) {
+ /* Enable RMII and use the 50MHz rmii clk as output to PHY. */
+ val = 0;
+ } else {
+ /* Enable SMII and use the 125MHz rmii clk as output to PHY.
+ * Additionally set SMII SYNC delay to 4 half cycle.
+ */
+ val = 0x04 << OWL_EMAC_OFF_MAC_CTRL_SSDC;
+ val |= OWL_EMAC_BIT_MAC_CTRL_RSIS;
+ }
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CTRL, val);
+
+ /* MDC is disabled after reset. */
+ owl_emac_mdio_clock_enable(priv);
+
+ /* Set FIFO pause & restart threshold levels. */
+ val = 0x40 << OWL_EMAC_OFF_MAC_CSR19_FPTL;
+ val |= 0x10 << OWL_EMAC_OFF_MAC_CSR19_FRTL;
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR19, val);
+
+ /* Set flow control pause quanta time to ~100 ms. */
+ val = 0x4FFF << OWL_EMAC_OFF_MAC_CSR18_PQT;
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR18, val);
+
+ /* Setup interrupt mitigation. */
+ val = 7 << OWL_EMAC_OFF_MAC_CSR11_NRP;
+ val |= 4 << OWL_EMAC_OFF_MAC_CSR11_RT;
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR11, val);
+
+ /* Set RX/TX rings base addresses. */
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR3,
+ (u32)(priv->rx_ring.descs_dma));
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR4,
+ (u32)(priv->tx_ring.descs_dma));
+
+ /* Setup initial operation mode. */
+ val = OWL_EMAC_VAL_MAC_CSR6_SPEED_100M << OWL_EMAC_OFF_MAC_CSR6_SPEED;
+ val |= OWL_EMAC_BIT_MAC_CSR6_FD;
+ owl_emac_reg_update(priv, OWL_EMAC_REG_MAC_CSR6,
+ OWL_EMAC_MSK_MAC_CSR6_SPEED |
+ OWL_EMAC_BIT_MAC_CSR6_FD, val);
+ owl_emac_reg_clear(priv, OWL_EMAC_REG_MAC_CSR6,
+ OWL_EMAC_BIT_MAC_CSR6_PR | OWL_EMAC_BIT_MAC_CSR6_PM);
+
+ priv->link = 0;
+ priv->speed = SPEED_UNKNOWN;
+ priv->duplex = DUPLEX_UNKNOWN;
+ priv->pause = 0;
+ priv->mcaddr_list.count = 0;
+
+ return 0;
+}
+
+static int owl_emac_enable(struct net_device *netdev, bool start_phy)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ int ret;
+
+ owl_emac_dma_cmd_stop(priv);
+ owl_emac_irq_disable(priv);
+ owl_emac_irq_clear(priv);
+
+ owl_emac_ring_prepare_tx(priv);
+ ret = owl_emac_ring_prepare_rx(priv);
+ if (ret)
+ goto err_unprep;
+
+ ret = owl_emac_core_sw_reset(priv);
+ if (ret) {
+ netdev_err(netdev, "failed to soft reset MAC core: %d\n", ret);
+ goto err_unprep;
+ }
+
+ owl_emac_set_hw_mac_addr(netdev);
+ owl_emac_setup_frame_xmit(priv);
+
+ netdev_reset_queue(netdev);
+ napi_enable(&priv->napi);
+
+ owl_emac_irq_enable(priv);
+ owl_emac_dma_cmd_start(priv);
+
+ if (start_phy)
+ phy_start(netdev->phydev);
+
+ netif_start_queue(netdev);
+
+ return 0;
+
+err_unprep:
+ owl_emac_ring_unprepare_rx(priv);
+ owl_emac_ring_unprepare_tx(priv);
+
+ return ret;
+}
+
+static void owl_emac_disable(struct net_device *netdev, bool stop_phy)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+
+ owl_emac_dma_cmd_stop(priv);
+ owl_emac_irq_disable(priv);
+
+ netif_stop_queue(netdev);
+ napi_disable(&priv->napi);
+
+ if (stop_phy)
+ phy_stop(netdev->phydev);
+
+ owl_emac_ring_unprepare_rx(priv);
+ owl_emac_ring_unprepare_tx(priv);
+}
+
+static int owl_emac_ndo_open(struct net_device *netdev)
+{
+ return owl_emac_enable(netdev, true);
+}
+
+static int owl_emac_ndo_stop(struct net_device *netdev)
+{
+ owl_emac_disable(netdev, true);
+
+ return 0;
+}
+
+static void owl_emac_set_multicast(struct net_device *netdev, int count)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ struct netdev_hw_addr *ha;
+ int index = 0;
+
+ if (count <= 0) {
+ priv->mcaddr_list.count = 0;
+ return;
+ }
+
+ netdev_for_each_mc_addr(ha, netdev) {
+ if (!is_multicast_ether_addr(ha->addr))
+ continue;
+
+ WARN_ON(index >= OWL_EMAC_MAX_MULTICAST_ADDRS);
+ ether_addr_copy(priv->mcaddr_list.addrs[index++], ha->addr);
+ }
+
+ priv->mcaddr_list.count = index;
+
+ owl_emac_setup_frame_xmit(priv);
+}
+
+static void owl_emac_ndo_set_rx_mode(struct net_device *netdev)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ u32 status, val = 0;
+ int mcast_count = 0;
+
+ if (netdev->flags & IFF_PROMISC) {
+ val = OWL_EMAC_BIT_MAC_CSR6_PR;
+ } else if (netdev->flags & IFF_ALLMULTI) {
+ val = OWL_EMAC_BIT_MAC_CSR6_PM;
+ } else if (netdev->flags & IFF_MULTICAST) {
+ mcast_count = netdev_mc_count(netdev);
+
+ if (mcast_count > OWL_EMAC_MAX_MULTICAST_ADDRS) {
+ val = OWL_EMAC_BIT_MAC_CSR6_PM;
+ mcast_count = 0;
+ }
+ }
+
+ spin_lock_bh(&priv->lock);
+
+ /* Temporarily stop DMA TX & RX. */
+ status = owl_emac_dma_cmd_stop(priv);
+
+ /* Update operation modes. */
+ owl_emac_reg_update(priv, OWL_EMAC_REG_MAC_CSR6,
+ OWL_EMAC_BIT_MAC_CSR6_PR | OWL_EMAC_BIT_MAC_CSR6_PM,
+ val);
+
+ /* Restore DMA TX & RX status. */
+ owl_emac_dma_cmd_set(priv, status);
+
+ spin_unlock_bh(&priv->lock);
+
+ /* Set/reset multicast addr list. */
+ owl_emac_set_multicast(netdev, mcast_count);
+}
+
+static int owl_emac_ndo_set_mac_addr(struct net_device *netdev, void *addr)
+{
+ struct sockaddr *skaddr = addr;
+
+ if (!is_valid_ether_addr(skaddr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ memcpy(netdev->dev_addr, skaddr->sa_data, netdev->addr_len);
+ owl_emac_set_hw_mac_addr(netdev);
+
+ return owl_emac_setup_frame_xmit(netdev_priv(netdev));
+}
+
+static int owl_emac_ndo_do_ioctl(struct net_device *netdev,
+ struct ifreq *req, int cmd)
+{
+ if (!netif_running(netdev))
+ return -EINVAL;
+
+ return phy_mii_ioctl(netdev->phydev, req, cmd);
+}
+
+static void owl_emac_ndo_tx_timeout(struct net_device *netdev,
+ unsigned int txqueue)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+
+ schedule_work(&priv->mac_reset_task);
+}
+
+static void owl_emac_reset_task(struct work_struct *work)
+{
+ struct owl_emac_priv *priv;
+
+ priv = container_of(work, struct owl_emac_priv, mac_reset_task);
+
+ netdev_dbg(priv->netdev, "resetting MAC\n");
+ owl_emac_disable(priv->netdev, false);
+ owl_emac_enable(priv->netdev, false);
+}
+
+static struct net_device_stats *
+owl_emac_ndo_get_stats(struct net_device *netdev)
+{
+ /* FIXME: If possible, try to get stats from MAC hardware registers
+ * instead of tracking them manually in the driver.
+ */
+
+ return &netdev->stats;
+}
+
+static const struct net_device_ops owl_emac_netdev_ops = {
+ .ndo_open = owl_emac_ndo_open,
+ .ndo_stop = owl_emac_ndo_stop,
+ .ndo_start_xmit = owl_emac_ndo_start_xmit,
+ .ndo_set_rx_mode = owl_emac_ndo_set_rx_mode,
+ .ndo_set_mac_address = owl_emac_ndo_set_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = owl_emac_ndo_do_ioctl,
+ .ndo_tx_timeout = owl_emac_ndo_tx_timeout,
+ .ndo_get_stats = owl_emac_ndo_get_stats,
+};
+
+static void owl_emac_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strscpy(info->driver, OWL_EMAC_DRVNAME, sizeof(info->driver));
+}
+
+static u32 owl_emac_ethtool_get_msglevel(struct net_device *netdev)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+
+ return priv->msg_enable;
+}
+
+static void owl_emac_ethtool_set_msglevel(struct net_device *ndev, u32 val)
+{
+ struct owl_emac_priv *priv = netdev_priv(ndev);
+
+ priv->msg_enable = val;
+}
+
+static const struct ethtool_ops owl_emac_ethtool_ops = {
+ .get_drvinfo = owl_emac_ethtool_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .get_msglevel = owl_emac_ethtool_get_msglevel,
+ .set_msglevel = owl_emac_ethtool_set_msglevel,
+};
+
+static int owl_emac_mdio_wait(struct owl_emac_priv *priv)
+{
+ u32 val;
+
+ /* Wait while data transfer is in progress. */
+ return readl_poll_timeout(priv->base + OWL_EMAC_REG_MAC_CSR10,
+ val, !(val & OWL_EMAC_BIT_MAC_CSR10_SB),
+ OWL_EMAC_POLL_DELAY_USEC,
+ OWL_EMAC_MDIO_POLL_TIMEOUT_USEC);
+}
+
+static int owl_emac_mdio_read(struct mii_bus *bus, int addr, int regnum)
+{
+ struct owl_emac_priv *priv = bus->priv;
+ u32 data, tmp;
+ int ret;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ data = OWL_EMAC_BIT_MAC_CSR10_SB;
+ data |= OWL_EMAC_VAL_MAC_CSR10_OPCODE_RD << OWL_EMAC_OFF_MAC_CSR10_OPCODE;
+
+ tmp = addr << OWL_EMAC_OFF_MAC_CSR10_PHYADD;
+ data |= tmp & OWL_EMAC_MSK_MAC_CSR10_PHYADD;
+
+ tmp = regnum << OWL_EMAC_OFF_MAC_CSR10_REGADD;
+ data |= tmp & OWL_EMAC_MSK_MAC_CSR10_REGADD;
+
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR10, data);
+
+ ret = owl_emac_mdio_wait(priv);
+ if (ret)
+ return ret;
+
+ data = owl_emac_reg_read(priv, OWL_EMAC_REG_MAC_CSR10);
+ data &= OWL_EMAC_MSK_MAC_CSR10_DATA;
+
+ return data;
+}
+
+static int
+owl_emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
+{
+ struct owl_emac_priv *priv = bus->priv;
+ u32 data, tmp;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ data = OWL_EMAC_BIT_MAC_CSR10_SB;
+ data |= OWL_EMAC_VAL_MAC_CSR10_OPCODE_WR << OWL_EMAC_OFF_MAC_CSR10_OPCODE;
+
+ tmp = addr << OWL_EMAC_OFF_MAC_CSR10_PHYADD;
+ data |= tmp & OWL_EMAC_MSK_MAC_CSR10_PHYADD;
+
+ tmp = regnum << OWL_EMAC_OFF_MAC_CSR10_REGADD;
+ data |= tmp & OWL_EMAC_MSK_MAC_CSR10_REGADD;
+
+ data |= val & OWL_EMAC_MSK_MAC_CSR10_DATA;
+
+ owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR10, data);
+
+ return owl_emac_mdio_wait(priv);
+}
+
+static int owl_emac_mdio_init(struct net_device *netdev)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ struct device *dev = owl_emac_get_dev(priv);
+ struct device_node *mdio_node;
+ int ret;
+
+ mdio_node = of_get_child_by_name(dev->of_node, "mdio");
+ if (!mdio_node)
+ return -ENODEV;
+
+ if (!of_device_is_available(mdio_node)) {
+ ret = -ENODEV;
+ goto err_put_node;
+ }
+
+ priv->mii = devm_mdiobus_alloc(dev);
+ if (!priv->mii) {
+ ret = -ENOMEM;
+ goto err_put_node;
+ }
+
+ snprintf(priv->mii->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+ priv->mii->name = "owl-emac-mdio";
+ priv->mii->parent = dev;
+ priv->mii->read = owl_emac_mdio_read;
+ priv->mii->write = owl_emac_mdio_write;
+ priv->mii->phy_mask = ~0; /* Mask out all PHYs from auto probing. */
+ priv->mii->priv = priv;
+
+ ret = devm_of_mdiobus_register(dev, priv->mii, mdio_node);
+
+err_put_node:
+ of_node_put(mdio_node);
+ return ret;
+}
+
+static int owl_emac_phy_init(struct net_device *netdev)
+{
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ struct device *dev = owl_emac_get_dev(priv);
+ struct phy_device *phy;
+
+ phy = of_phy_get_and_connect(netdev, dev->of_node,
+ owl_emac_adjust_link);
+ if (!phy)
+ return -ENODEV;
+
+ phy_set_sym_pause(phy, true, true, true);
+
+ if (netif_msg_link(priv))
+ phy_attached_info(phy);
+
+ return 0;
+}
+
+static void owl_emac_get_mac_addr(struct net_device *netdev)
+{
+ struct device *dev = netdev->dev.parent;
+ int ret;
+
+ ret = eth_platform_get_mac_address(dev, netdev->dev_addr);
+ if (!ret && is_valid_ether_addr(netdev->dev_addr))
+ return;
+
+ eth_hw_addr_random(netdev);
+ dev_warn(dev, "using random MAC address %pM\n", netdev->dev_addr);
+}
+
+static __maybe_unused int owl_emac_suspend(struct device *dev)
+{
+ struct net_device *netdev = dev_get_drvdata(dev);
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+
+ disable_irq(netdev->irq);
+
+ if (netif_running(netdev)) {
+ owl_emac_disable(netdev, true);
+ netif_device_detach(netdev);
+ }
+
+ clk_bulk_disable_unprepare(OWL_EMAC_NCLKS, priv->clks);
+
+ return 0;
+}
+
+static __maybe_unused int owl_emac_resume(struct device *dev)
+{
+ struct net_device *netdev = dev_get_drvdata(dev);
+ struct owl_emac_priv *priv = netdev_priv(netdev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(OWL_EMAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ if (netif_running(netdev)) {
+ owl_emac_core_hw_reset(priv);
+ owl_emac_core_sw_reset(priv);
+
+ ret = owl_emac_enable(netdev, true);
+ if (ret) {
+ clk_bulk_disable_unprepare(OWL_EMAC_NCLKS, priv->clks);
+ return ret;
+ }
+
+ netif_device_attach(netdev);
+ }
+
+ enable_irq(netdev->irq);
+
+ return 0;
+}
+
+static void owl_emac_clk_disable_unprepare(void *data)
+{
+ struct owl_emac_priv *priv = data;
+
+ clk_bulk_disable_unprepare(OWL_EMAC_NCLKS, priv->clks);
+}
+
+static int owl_emac_clk_set_rate(struct owl_emac_priv *priv)
+{
+ struct device *dev = owl_emac_get_dev(priv);
+ unsigned long rate;
+ int ret;
+
+ switch (priv->phy_mode) {
+ case PHY_INTERFACE_MODE_RMII:
+ rate = 50000000;
+ break;
+
+ case PHY_INTERFACE_MODE_SMII:
+ rate = 125000000;
+ break;
+
+ default:
+ dev_err(dev, "unsupported phy interface mode %d\n",
+ priv->phy_mode);
+ return -EOPNOTSUPP;
+ }
+
+ ret = clk_set_rate(priv->clks[OWL_EMAC_CLK_RMII].clk, rate);
+ if (ret)
+ dev_err(dev, "failed to set RMII clock rate: %d\n", ret);
+
+ return ret;
+}
+
+static int owl_emac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct net_device *netdev;
+ struct owl_emac_priv *priv;
+ int ret, i;
+
+ netdev = devm_alloc_etherdev(dev, sizeof(*priv));
+ if (!netdev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, netdev);
+ SET_NETDEV_DEV(netdev, dev);
+
+ priv = netdev_priv(netdev);
+ priv->netdev = netdev;
+ priv->msg_enable = netif_msg_init(-1, OWL_EMAC_DEFAULT_MSG_ENABLE);
+
+ ret = of_get_phy_mode(dev->of_node, &priv->phy_mode);
+ if (ret) {
+ dev_err(dev, "failed to get phy mode: %d\n", ret);
+ return ret;
+ }
+
+ spin_lock_init(&priv->lock);
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(dev, "unsupported DMA mask\n");
+ return ret;
+ }
+
+ ret = owl_emac_ring_alloc(dev, &priv->rx_ring, OWL_EMAC_RX_RING_SIZE);
+ if (ret)
+ return ret;
+
+ ret = owl_emac_ring_alloc(dev, &priv->tx_ring, OWL_EMAC_TX_RING_SIZE);
+ if (ret)
+ return ret;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ netdev->irq = platform_get_irq(pdev, 0);
+ if (netdev->irq < 0)
+ return netdev->irq;
+
+ ret = devm_request_irq(dev, netdev->irq, owl_emac_handle_irq,
+ IRQF_SHARED, netdev->name, netdev);
+ if (ret) {
+ dev_err(dev, "failed to request irq: %d\n", netdev->irq);
+ return ret;
+ }
+
+ for (i = 0; i < OWL_EMAC_NCLKS; i++)
+ priv->clks[i].id = owl_emac_clk_names[i];
+
+ ret = devm_clk_bulk_get(dev, OWL_EMAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(OWL_EMAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, owl_emac_clk_disable_unprepare, priv);
+ if (ret)
+ return ret;
+
+ ret = owl_emac_clk_set_rate(priv);
+ if (ret)
+ return ret;
+
+ priv->reset = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(dev, PTR_ERR(priv->reset),
+ "failed to get reset control");
+
+ owl_emac_get_mac_addr(netdev);
+
+ owl_emac_core_hw_reset(priv);
+ owl_emac_mdio_clock_enable(priv);
+
+ ret = owl_emac_mdio_init(netdev);
+ if (ret) {
+ dev_err(dev, "failed to initialize MDIO bus\n");
+ return ret;
+ }
+
+ ret = owl_emac_phy_init(netdev);
+ if (ret) {
+ dev_err(dev, "failed to initialize PHY\n");
+ return ret;
+ }
+
+ INIT_WORK(&priv->mac_reset_task, owl_emac_reset_task);
+
+ netdev->min_mtu = OWL_EMAC_MTU_MIN;
+ netdev->max_mtu = OWL_EMAC_MTU_MAX;
+ netdev->watchdog_timeo = OWL_EMAC_TX_TIMEOUT;
+ netdev->netdev_ops = &owl_emac_netdev_ops;
+ netdev->ethtool_ops = &owl_emac_ethtool_ops;
+ netif_napi_add(netdev, &priv->napi, owl_emac_poll, NAPI_POLL_WEIGHT);
+
+ ret = devm_register_netdev(dev, netdev);
+ if (ret) {
+ netif_napi_del(&priv->napi);
+ phy_disconnect(netdev->phydev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int owl_emac_remove(struct platform_device *pdev)
+{
+ struct owl_emac_priv *priv = platform_get_drvdata(pdev);
+
+ netif_napi_del(&priv->napi);
+ phy_disconnect(priv->netdev->phydev);
+ cancel_work_sync(&priv->mac_reset_task);
+
+ return 0;
+}
+
+static const struct of_device_id owl_emac_of_match[] = {
+ { .compatible = "actions,owl-emac", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, owl_emac_of_match);
+
+static SIMPLE_DEV_PM_OPS(owl_emac_pm_ops,
+ owl_emac_suspend, owl_emac_resume);
+
+static struct platform_driver owl_emac_driver = {
+ .driver = {
+ .name = OWL_EMAC_DRVNAME,
+ .of_match_table = owl_emac_of_match,
+ .pm = &owl_emac_pm_ops,
+ },
+ .probe = owl_emac_probe,
+ .remove = owl_emac_remove,
+};
+module_platform_driver(owl_emac_driver);
+
+MODULE_DESCRIPTION("Actions Semi Owl SoCs Ethernet MAC Driver");
+MODULE_AUTHOR("Actions Semi Inc.");
+MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/actions/owl-emac.h b/drivers/net/ethernet/actions/owl-emac.h
new file mode 100644
index 000000000000..9eb0d1a30242
--- /dev/null
+++ b/drivers/net/ethernet/actions/owl-emac.h
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Actions Semi Owl SoCs Ethernet MAC driver
+ *
+ * Copyright (c) 2012 Actions Semi Inc.
+ * Copyright (c) 2021 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
+ */
+
+#ifndef __OWL_EMAC_H__
+#define __OWL_EMAC_H__
+
+#define OWL_EMAC_DRVNAME "owl-emac"
+
+#define OWL_EMAC_POLL_DELAY_USEC 5
+#define OWL_EMAC_MDIO_POLL_TIMEOUT_USEC 1000
+#define OWL_EMAC_RESET_POLL_TIMEOUT_USEC 2000
+#define OWL_EMAC_TX_TIMEOUT (2 * HZ)
+
+#define OWL_EMAC_MTU_MIN ETH_MIN_MTU
+#define OWL_EMAC_MTU_MAX ETH_DATA_LEN
+#define OWL_EMAC_RX_FRAME_MAX_LEN (ETH_FRAME_LEN + ETH_FCS_LEN)
+#define OWL_EMAC_SKB_ALIGN 4
+#define OWL_EMAC_SKB_RESERVE 18
+
+#define OWL_EMAC_MAX_MULTICAST_ADDRS 14
+#define OWL_EMAC_SETUP_FRAME_LEN 192
+
+#define OWL_EMAC_RX_RING_SIZE 64
+#define OWL_EMAC_TX_RING_SIZE 32
+
+/* Bus mode register */
+#define OWL_EMAC_REG_MAC_CSR0 0x0000
+#define OWL_EMAC_BIT_MAC_CSR0_SWR BIT(0) /* Software reset */
+
+/* Transmit/receive poll demand registers */
+#define OWL_EMAC_REG_MAC_CSR1 0x0008
+#define OWL_EMAC_VAL_MAC_CSR1_TPD 0x01
+#define OWL_EMAC_REG_MAC_CSR2 0x0010
+#define OWL_EMAC_VAL_MAC_CSR2_RPD 0x01
+
+/* Receive/transmit descriptor list base address registers */
+#define OWL_EMAC_REG_MAC_CSR3 0x0018
+#define OWL_EMAC_REG_MAC_CSR4 0x0020
+
+/* Status register */
+#define OWL_EMAC_REG_MAC_CSR5 0x0028
+#define OWL_EMAC_MSK_MAC_CSR5_TS GENMASK(22, 20) /* Transmit process state */
+#define OWL_EMAC_OFF_MAC_CSR5_TS 20
+#define OWL_EMAC_VAL_MAC_CSR5_TS_DATA 0x03 /* Transferring data HOST -> FIFO */
+#define OWL_EMAC_VAL_MAC_CSR5_TS_CDES 0x07 /* Closing transmit descriptor */
+#define OWL_EMAC_MSK_MAC_CSR5_RS GENMASK(19, 17) /* Receive process state */
+#define OWL_EMAC_OFF_MAC_CSR5_RS 17
+#define OWL_EMAC_VAL_MAC_CSR5_RS_FDES 0x01 /* Fetching receive descriptor */
+#define OWL_EMAC_VAL_MAC_CSR5_RS_CDES 0x05 /* Closing receive descriptor */
+#define OWL_EMAC_VAL_MAC_CSR5_RS_DATA 0x07 /* Transferring data FIFO -> HOST */
+#define OWL_EMAC_BIT_MAC_CSR5_NIS BIT(16) /* Normal interrupt summary */
+#define OWL_EMAC_BIT_MAC_CSR5_AIS BIT(15) /* Abnormal interrupt summary */
+#define OWL_EMAC_BIT_MAC_CSR5_ERI BIT(14) /* Early receive interrupt */
+#define OWL_EMAC_BIT_MAC_CSR5_GTE BIT(11) /* General-purpose timer expiration */
+#define OWL_EMAC_BIT_MAC_CSR5_ETI BIT(10) /* Early transmit interrupt */
+#define OWL_EMAC_BIT_MAC_CSR5_RPS BIT(8) /* Receive process stopped */
+#define OWL_EMAC_BIT_MAC_CSR5_RU BIT(7) /* Receive buffer unavailable */
+#define OWL_EMAC_BIT_MAC_CSR5_RI BIT(6) /* Receive interrupt */
+#define OWL_EMAC_BIT_MAC_CSR5_UNF BIT(5) /* Transmit underflow */
+#define OWL_EMAC_BIT_MAC_CSR5_LCIS BIT(4) /* Link change status */
+#define OWL_EMAC_BIT_MAC_CSR5_LCIQ BIT(3) /* Link change interrupt */
+#define OWL_EMAC_BIT_MAC_CSR5_TU BIT(2) /* Transmit buffer unavailable */
+#define OWL_EMAC_BIT_MAC_CSR5_TPS BIT(1) /* Transmit process stopped */
+#define OWL_EMAC_BIT_MAC_CSR5_TI BIT(0) /* Transmit interrupt */
+
+/* Operation mode register */
+#define OWL_EMAC_REG_MAC_CSR6 0x0030
+#define OWL_EMAC_BIT_MAC_CSR6_RA BIT(30) /* Receive all */
+#define OWL_EMAC_BIT_MAC_CSR6_TTM BIT(22) /* Transmit threshold mode */
+#define OWL_EMAC_BIT_MAC_CSR6_SF BIT(21) /* Store and forward */
+#define OWL_EMAC_MSK_MAC_CSR6_SPEED GENMASK(17, 16) /* Eth speed selection */
+#define OWL_EMAC_OFF_MAC_CSR6_SPEED 16
+#define OWL_EMAC_VAL_MAC_CSR6_SPEED_100M 0x00
+#define OWL_EMAC_VAL_MAC_CSR6_SPEED_10M 0x02
+#define OWL_EMAC_BIT_MAC_CSR6_ST BIT(13) /* Start/stop transmit command */
+#define OWL_EMAC_BIT_MAC_CSR6_LP BIT(10) /* Loopback mode */
+#define OWL_EMAC_BIT_MAC_CSR6_FD BIT(9) /* Full duplex mode */
+#define OWL_EMAC_BIT_MAC_CSR6_PM BIT(7) /* Pass all multicast */
+#define OWL_EMAC_BIT_MAC_CSR6_PR BIT(6) /* Promiscuous mode */
+#define OWL_EMAC_BIT_MAC_CSR6_IF BIT(4) /* Inverse filtering */
+#define OWL_EMAC_BIT_MAC_CSR6_PB BIT(3) /* Pass bad frames */
+#define OWL_EMAC_BIT_MAC_CSR6_HO BIT(2) /* Hash only filtering mode */
+#define OWL_EMAC_BIT_MAC_CSR6_SR BIT(1) /* Start/stop receive command */
+#define OWL_EMAC_BIT_MAC_CSR6_HP BIT(0) /* Hash/perfect receive filtering mode */
+#define OWL_EMAC_MSK_MAC_CSR6_STSR (OWL_EMAC_BIT_MAC_CSR6_ST | \
+ OWL_EMAC_BIT_MAC_CSR6_SR)
+
+/* Interrupt enable register */
+#define OWL_EMAC_REG_MAC_CSR7 0x0038
+#define OWL_EMAC_BIT_MAC_CSR7_NIE BIT(16) /* Normal interrupt summary enable */
+#define OWL_EMAC_BIT_MAC_CSR7_AIE BIT(15) /* Abnormal interrupt summary enable */
+#define OWL_EMAC_BIT_MAC_CSR7_ERE BIT(14) /* Early receive interrupt enable */
+#define OWL_EMAC_BIT_MAC_CSR7_GTE BIT(11) /* General-purpose timer overflow */
+#define OWL_EMAC_BIT_MAC_CSR7_ETE BIT(10) /* Early transmit interrupt enable */
+#define OWL_EMAC_BIT_MAC_CSR7_RSE BIT(8) /* Receive stopped enable */
+#define OWL_EMAC_BIT_MAC_CSR7_RUE BIT(7) /* Receive buffer unavailable enable */
+#define OWL_EMAC_BIT_MAC_CSR7_RIE BIT(6) /* Receive interrupt enable */
+#define OWL_EMAC_BIT_MAC_CSR7_UNE BIT(5) /* Underflow interrupt enable */
+#define OWL_EMAC_BIT_MAC_CSR7_TUE BIT(2) /* Transmit buffer unavailable enable */
+#define OWL_EMAC_BIT_MAC_CSR7_TSE BIT(1) /* Transmit stopped enable */
+#define OWL_EMAC_BIT_MAC_CSR7_TIE BIT(0) /* Transmit interrupt enable */
+#define OWL_EMAC_BIT_MAC_CSR7_ALL_NOT_TUE (OWL_EMAC_BIT_MAC_CSR7_ERE | \
+ OWL_EMAC_BIT_MAC_CSR7_GTE | \
+ OWL_EMAC_BIT_MAC_CSR7_ETE | \
+ OWL_EMAC_BIT_MAC_CSR7_RSE | \
+ OWL_EMAC_BIT_MAC_CSR7_RUE | \
+ OWL_EMAC_BIT_MAC_CSR7_RIE | \
+ OWL_EMAC_BIT_MAC_CSR7_UNE | \
+ OWL_EMAC_BIT_MAC_CSR7_TSE | \
+ OWL_EMAC_BIT_MAC_CSR7_TIE)
+
+/* Missed frames and overflow counter register */
+#define OWL_EMAC_REG_MAC_CSR8 0x0040
+/* MII management and serial ROM register */
+#define OWL_EMAC_REG_MAC_CSR9 0x0048
+
+/* MII serial management register */
+#define OWL_EMAC_REG_MAC_CSR10 0x0050
+#define OWL_EMAC_BIT_MAC_CSR10_SB BIT(31) /* Start transfer or busy */
+#define OWL_EMAC_MSK_MAC_CSR10_CLKDIV GENMASK(30, 28) /* Clock divider */
+#define OWL_EMAC_OFF_MAC_CSR10_CLKDIV 28
+#define OWL_EMAC_VAL_MAC_CSR10_CLKDIV_128 0x04
+#define OWL_EMAC_VAL_MAC_CSR10_OPCODE_WR 0x01 /* Register write command */
+#define OWL_EMAC_OFF_MAC_CSR10_OPCODE 26 /* Operation mode */
+#define OWL_EMAC_VAL_MAC_CSR10_OPCODE_DCG 0x00 /* Disable clock generation */
+#define OWL_EMAC_VAL_MAC_CSR10_OPCODE_WR 0x01 /* Register write command */
+#define OWL_EMAC_VAL_MAC_CSR10_OPCODE_RD 0x02 /* Register read command */
+#define OWL_EMAC_VAL_MAC_CSR10_OPCODE_CDS 0x03 /* Clock divider set */
+#define OWL_EMAC_MSK_MAC_CSR10_PHYADD GENMASK(25, 21) /* Physical layer address */
+#define OWL_EMAC_OFF_MAC_CSR10_PHYADD 21
+#define OWL_EMAC_MSK_MAC_CSR10_REGADD GENMASK(20, 16) /* Register address */
+#define OWL_EMAC_OFF_MAC_CSR10_REGADD 16
+#define OWL_EMAC_MSK_MAC_CSR10_DATA GENMASK(15, 0) /* Register data */
+
+/* General-purpose timer and interrupt mitigation control register */
+#define OWL_EMAC_REG_MAC_CSR11 0x0058
+#define OWL_EMAC_OFF_MAC_CSR11_TT 27 /* Transmit timer */
+#define OWL_EMAC_OFF_MAC_CSR11_NTP 24 /* No. of transmit packets */
+#define OWL_EMAC_OFF_MAC_CSR11_RT 20 /* Receive timer */
+#define OWL_EMAC_OFF_MAC_CSR11_NRP 17 /* No. of receive packets */
+
+/* MAC address low/high registers */
+#define OWL_EMAC_REG_MAC_CSR16 0x0080
+#define OWL_EMAC_REG_MAC_CSR17 0x0088
+
+/* Pause time & cache thresholds register */
+#define OWL_EMAC_REG_MAC_CSR18 0x0090
+#define OWL_EMAC_OFF_MAC_CSR18_CPTL 24 /* Cache pause threshold level */
+#define OWL_EMAC_OFF_MAC_CSR18_CRTL 16 /* Cache restart threshold level */
+#define OWL_EMAC_OFF_MAC_CSR18_PQT 0 /* Flow control pause quanta time */
+
+/* FIFO pause & restart threshold register */
+#define OWL_EMAC_REG_MAC_CSR19 0x0098
+#define OWL_EMAC_OFF_MAC_CSR19_FPTL 16 /* FIFO pause threshold level */
+#define OWL_EMAC_OFF_MAC_CSR19_FRTL 0 /* FIFO restart threshold level */
+
+/* Flow control setup & status register */
+#define OWL_EMAC_REG_MAC_CSR20 0x00A0
+#define OWL_EMAC_BIT_MAC_CSR20_FCE BIT(31) /* Flow Control Enable */
+#define OWL_EMAC_BIT_MAC_CSR20_TUE BIT(30) /* Transmit Un-pause frames Enable */
+#define OWL_EMAC_BIT_MAC_CSR20_TPE BIT(29) /* Transmit Pause frames Enable */
+#define OWL_EMAC_BIT_MAC_CSR20_RPE BIT(28) /* Receive Pause frames Enable */
+#define OWL_EMAC_BIT_MAC_CSR20_BPE BIT(27) /* Back pressure (half-duplex) Enable */
+
+/* MII control register */
+#define OWL_EMAC_REG_MAC_CTRL 0x00B0
+#define OWL_EMAC_BIT_MAC_CTRL_RRSB BIT(8) /* RMII_REFCLK select bit */
+#define OWL_EMAC_OFF_MAC_CTRL_SSDC 4 /* SMII SYNC delay cycle */
+#define OWL_EMAC_BIT_MAC_CTRL_RCPS BIT(1) /* REF_CLK phase select */
+#define OWL_EMAC_BIT_MAC_CTRL_RSIS BIT(0) /* RMII/SMII interface select */
+
+/* Receive descriptor status field */
+#define OWL_EMAC_BIT_RDES0_OWN BIT(31) /* Ownership bit */
+#define OWL_EMAC_BIT_RDES0_FF BIT(30) /* Filtering fail */
+#define OWL_EMAC_MSK_RDES0_FL GENMASK(29, 16) /* Frame length */
+#define OWL_EMAC_OFF_RDES0_FL 16
+#define OWL_EMAC_BIT_RDES0_ES BIT(15) /* Error summary */
+#define OWL_EMAC_BIT_RDES0_DE BIT(14) /* Descriptor error */
+#define OWL_EMAC_BIT_RDES0_RF BIT(11) /* Runt frame */
+#define OWL_EMAC_BIT_RDES0_MF BIT(10) /* Multicast frame */
+#define OWL_EMAC_BIT_RDES0_FS BIT(9) /* First descriptor */
+#define OWL_EMAC_BIT_RDES0_LS BIT(8) /* Last descriptor */
+#define OWL_EMAC_BIT_RDES0_TL BIT(7) /* Frame too long */
+#define OWL_EMAC_BIT_RDES0_CS BIT(6) /* Collision seen */
+#define OWL_EMAC_BIT_RDES0_FT BIT(5) /* Frame type */
+#define OWL_EMAC_BIT_RDES0_RE BIT(3) /* Report on MII error */
+#define OWL_EMAC_BIT_RDES0_DB BIT(2) /* Dribbling bit */
+#define OWL_EMAC_BIT_RDES0_CE BIT(1) /* CRC error */
+#define OWL_EMAC_BIT_RDES0_ZERO BIT(0) /* Legal frame length indicator */
+
+/* Receive descriptor control and count field */
+#define OWL_EMAC_BIT_RDES1_RER BIT(25) /* Receive end of ring */
+#define OWL_EMAC_MSK_RDES1_RBS1 GENMASK(10, 0) /* Buffer 1 size */
+
+/* Transmit descriptor status field */
+#define OWL_EMAC_BIT_TDES0_OWN BIT(31) /* Ownership bit */
+#define OWL_EMAC_BIT_TDES0_ES BIT(15) /* Error summary */
+#define OWL_EMAC_BIT_TDES0_LO BIT(11) /* Loss of carrier */
+#define OWL_EMAC_BIT_TDES0_NC BIT(10) /* No carrier */
+#define OWL_EMAC_BIT_TDES0_LC BIT(9) /* Late collision */
+#define OWL_EMAC_BIT_TDES0_EC BIT(8) /* Excessive collisions */
+#define OWL_EMAC_MSK_TDES0_CC GENMASK(6, 3) /* Collision count */
+#define OWL_EMAC_BIT_TDES0_UF BIT(1) /* Underflow error */
+#define OWL_EMAC_BIT_TDES0_DE BIT(0) /* Deferred */
+
+/* Transmit descriptor control and count field */
+#define OWL_EMAC_BIT_TDES1_IC BIT(31) /* Interrupt on completion */
+#define OWL_EMAC_BIT_TDES1_LS BIT(30) /* Last descriptor */
+#define OWL_EMAC_BIT_TDES1_FS BIT(29) /* First descriptor */
+#define OWL_EMAC_BIT_TDES1_FT1 BIT(28) /* Filtering type */
+#define OWL_EMAC_BIT_TDES1_SET BIT(27) /* Setup packet */
+#define OWL_EMAC_BIT_TDES1_AC BIT(26) /* Add CRC disable */
+#define OWL_EMAC_BIT_TDES1_TER BIT(25) /* Transmit end of ring */
+#define OWL_EMAC_BIT_TDES1_DPD BIT(23) /* Disabled padding */
+#define OWL_EMAC_BIT_TDES1_FT0 BIT(22) /* Filtering type */
+#define OWL_EMAC_MSK_TDES1_TBS1 GENMASK(10, 0) /* Buffer 1 size */
+
+static const char *const owl_emac_clk_names[] = { "eth", "rmii" };
+#define OWL_EMAC_NCLKS ARRAY_SIZE(owl_emac_clk_names)
+
+enum owl_emac_clk_map {
+ OWL_EMAC_CLK_ETH = 0,
+ OWL_EMAC_CLK_RMII
+};
+
+struct owl_emac_addr_list {
+ u8 addrs[OWL_EMAC_MAX_MULTICAST_ADDRS][ETH_ALEN];
+ int count;
+};
+
+/* TX/RX descriptors */
+struct owl_emac_ring_desc {
+ u32 status;
+ u32 control;
+ u32 buf_addr;
+ u32 reserved; /* 2nd buffer address is not used */
+};
+
+struct owl_emac_ring {
+ struct owl_emac_ring_desc *descs;
+ dma_addr_t descs_dma;
+ struct sk_buff **skbs;
+ dma_addr_t *skbs_dma;
+ unsigned int size;
+ unsigned int head;
+ unsigned int tail;
+};
+
+struct owl_emac_priv {
+ struct net_device *netdev;
+ void __iomem *base;
+
+ struct clk_bulk_data clks[OWL_EMAC_NCLKS];
+ struct reset_control *reset;
+
+ struct owl_emac_ring rx_ring;
+ struct owl_emac_ring tx_ring;
+
+ struct mii_bus *mii;
+ struct napi_struct napi;
+
+ phy_interface_t phy_mode;
+ unsigned int link;
+ int speed;
+ int duplex;
+ int pause;
+ struct owl_emac_addr_list mcaddr_list;
+
+ struct work_struct mac_reset_task;
+
+ u32 msg_enable; /* Debug message level */
+ spinlock_t lock; /* Sync concurrent ring access */
+};
+
+#endif /* __OWL_EMAC_H__ */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index b53a0d87371a..6f13642121c4 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -4470,7 +4470,7 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
writel(1, bp->bar0 + doorbell_offset);
if (!pci_is_enabled(bp->pdev))
- return 0;
+ return -ENODEV;
if (!timeout)
timeout = DFLT_HWRM_CMD_TIMEOUT;
@@ -4500,12 +4500,15 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))
return -EBUSY;
/* on first few passes, just barely sleep */
- if (i < HWRM_SHORT_TIMEOUT_COUNTER)
+ if (i < HWRM_SHORT_TIMEOUT_COUNTER) {
usleep_range(HWRM_SHORT_MIN_TIMEOUT,
HWRM_SHORT_MAX_TIMEOUT);
- else
+ } else {
+ if (HWRM_WAIT_MUST_ABORT(bp, req))
+ break;
usleep_range(HWRM_MIN_TIMEOUT,
HWRM_MAX_TIMEOUT);
+ }
}
if (bp->hwrm_intr_seq_id != (u16)~seq_id) {
@@ -4530,15 +4533,19 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
if (len)
break;
/* on first few passes, just barely sleep */
- if (i < HWRM_SHORT_TIMEOUT_COUNTER)
+ if (i < HWRM_SHORT_TIMEOUT_COUNTER) {
usleep_range(HWRM_SHORT_MIN_TIMEOUT,
HWRM_SHORT_MAX_TIMEOUT);
- else
+ } else {
+ if (HWRM_WAIT_MUST_ABORT(bp, req))
+ goto timeout_abort;
usleep_range(HWRM_MIN_TIMEOUT,
HWRM_MAX_TIMEOUT);
+ }
}
if (i >= tmo_count) {
+timeout_abort:
if (!silent)
netdev_err(bp->dev, "Error (timeout: %d) msg {0x%x 0x%x} len:%d\n",
HWRM_TOTAL_TIMEOUT(i),
@@ -7540,6 +7547,32 @@ static void __bnxt_map_fw_health_reg(struct bnxt *bp, u32 reg)
BNXT_FW_HEALTH_WIN_MAP_OFF);
}
+bool bnxt_is_fw_healthy(struct bnxt *bp)
+{
+ if (bp->fw_health && bp->fw_health->status_reliable) {
+ u32 fw_status;
+
+ fw_status = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
+ if (fw_status && !BNXT_FW_IS_HEALTHY(fw_status))
+ return false;
+ }
+
+ return true;
+}
+
+static void bnxt_inv_fw_health_reg(struct bnxt *bp)
+{
+ struct bnxt_fw_health *fw_health = bp->fw_health;
+ u32 reg_type;
+
+ if (!fw_health || !fw_health->status_reliable)
+ return;
+
+ reg_type = BNXT_FW_HEALTH_REG_TYPE(fw_health->regs[BNXT_FW_HEALTH_REG]);
+ if (reg_type == BNXT_FW_HEALTH_REG_TYPE_GRC)
+ fw_health->status_reliable = false;
+}
+
static void bnxt_try_map_fw_health_reg(struct bnxt *bp)
{
void __iomem *hs;
@@ -7547,6 +7580,9 @@ static void bnxt_try_map_fw_health_reg(struct bnxt *bp)
u32 reg_type;
u32 sig;
+ if (bp->fw_health)
+ bp->fw_health->status_reliable = false;
+
__bnxt_map_fw_health_reg(bp, HCOMM_STATUS_STRUCT_LOC);
hs = bp->bar0 + BNXT_FW_HEALTH_WIN_OFF(HCOMM_STATUS_STRUCT_LOC);
@@ -7558,11 +7594,9 @@ static void bnxt_try_map_fw_health_reg(struct bnxt *bp)
BNXT_FW_HEALTH_WIN_BASE +
BNXT_GRC_REG_CHIP_NUM);
}
- if (!BNXT_CHIP_P5(bp)) {
- if (bp->fw_health)
- bp->fw_health->status_reliable = false;
+ if (!BNXT_CHIP_P5(bp))
return;
- }
+
status_loc = BNXT_GRC_REG_STATUS_P5 |
BNXT_FW_HEALTH_REG_TYPE_BAR0;
} else {
@@ -7592,6 +7626,7 @@ static int bnxt_map_fw_health_regs(struct bnxt *bp)
u32 reg_base = 0xffffffff;
int i;
+ bp->fw_health->status_reliable = false;
/* Only pre-map the monitoring GRC registers using window 3 */
for (i = 0; i < 4; i++) {
u32 reg = fw_health->regs[i];
@@ -7604,6 +7639,7 @@ static int bnxt_map_fw_health_regs(struct bnxt *bp)
return -ERANGE;
fw_health->mapped_regs[i] = BNXT_FW_HEALTH_WIN_OFF(reg);
}
+ bp->fw_health->status_reliable = true;
if (reg_base == 0xffffffff)
return 0;
@@ -9494,9 +9530,10 @@ static int bnxt_try_recover_fw(struct bnxt *bp)
mutex_lock(&bp->hwrm_cmd_lock);
do {
- rc = __bnxt_hwrm_ver_get(bp, true);
sts = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
- if (!sts || !BNXT_FW_IS_BOOTING(sts))
+ rc = __bnxt_hwrm_ver_get(bp, true);
+ if (!sts || (!BNXT_FW_IS_BOOTING(sts) &&
+ !BNXT_FW_IS_RECOVERING(sts)))
break;
retry++;
} while (rc == -EBUSY && retry < BNXT_FW_RETRY);
@@ -9556,13 +9593,17 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
if (rc)
return rc;
- if (!up)
+ if (!up) {
+ bnxt_inv_fw_health_reg(bp);
return 0;
+ }
if (flags & FUNC_DRV_IF_CHANGE_RESP_FLAGS_RESC_CHANGE)
resc_reinit = true;
if (flags & FUNC_DRV_IF_CHANGE_RESP_FLAGS_HOT_FW_RESET_DONE)
fw_reset = true;
+ else if (bp->fw_health && !bp->fw_health->status_reliable)
+ bnxt_try_map_fw_health_reg(bp);
if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state) && !fw_reset) {
netdev_err(bp->dev, "RESET_DONE not set during FW reset.\n");
@@ -9571,6 +9612,7 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
}
if (resc_reinit || fw_reset) {
if (fw_reset) {
+ set_bit(BNXT_STATE_FW_RESET_DET, &bp->state);
if (!test_bit(BNXT_STATE_IN_FW_RESET, &bp->state))
bnxt_ulp_stop(bp);
bnxt_free_ctx_mem(bp);
@@ -9579,21 +9621,25 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
bnxt_dcb_free(bp);
rc = bnxt_fw_init_one(bp);
if (rc) {
+ clear_bit(BNXT_STATE_FW_RESET_DET, &bp->state);
set_bit(BNXT_STATE_ABORT_ERR, &bp->state);
return rc;
}
bnxt_clear_int_mode(bp);
rc = bnxt_init_int_mode(bp);
if (rc) {
+ clear_bit(BNXT_STATE_FW_RESET_DET, &bp->state);
netdev_err(bp->dev, "init int mode failed\n");
return rc;
}
- set_bit(BNXT_STATE_FW_RESET_DET, &bp->state);
}
if (BNXT_NEW_RM(bp)) {
struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
rc = bnxt_hwrm_func_resc_qcaps(bp, true);
+ if (rc)
+ netdev_err(bp->dev, "resc_qcaps failed\n");
+
hw_resc->resv_cp_rings = 0;
hw_resc->resv_stat_ctxs = 0;
hw_resc->resv_irqs = 0;
@@ -9607,7 +9653,7 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
}
}
}
- return 0;
+ return rc;
}
static int bnxt_hwrm_port_led_qcaps(struct bnxt *bp)
@@ -11640,7 +11686,7 @@ static void bnxt_reset_all(struct bnxt *bp)
req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP;
req.flags = FW_RESET_REQ_FLAGS_RESET_GRACEFUL;
rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
- if (rc)
+ if (rc != -ENODEV)
netdev_warn(bp->dev, "Unable to reset FW rc=%d\n", rc);
}
bp->fw_reset_timestamp = jiffies;
@@ -11723,28 +11769,20 @@ static void bnxt_fw_reset_task(struct work_struct *work)
bnxt_queue_fw_reset_work(bp, bp->fw_reset_min_dsecs * HZ / 10);
return;
case BNXT_FW_RESET_STATE_ENABLE_DEV:
- if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state)) {
- u32 val;
-
- if (!bp->fw_reset_min_dsecs) {
- u16 val;
-
- pci_read_config_word(bp->pdev, PCI_SUBSYSTEM_ID,
- &val);
- if (val == 0xffff) {
- if (bnxt_fw_reset_timeout(bp)) {
- netdev_err(bp->dev, "Firmware reset aborted, PCI config space invalid\n");
- goto fw_reset_abort;
- }
- bnxt_queue_fw_reset_work(bp, HZ / 1000);
- return;
+ bnxt_inv_fw_health_reg(bp);
+ if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state) &&
+ !bp->fw_reset_min_dsecs) {
+ u16 val;
+
+ pci_read_config_word(bp->pdev, PCI_SUBSYSTEM_ID, &val);
+ if (val == 0xffff) {
+ if (bnxt_fw_reset_timeout(bp)) {
+ netdev_err(bp->dev, "Firmware reset aborted, PCI config space invalid\n");
+ goto fw_reset_abort;
}
+ bnxt_queue_fw_reset_work(bp, HZ / 1000);
+ return;
}
- val = bnxt_fw_health_readl(bp,
- BNXT_FW_RESET_INPROG_REG);
- if (val)
- netdev_warn(bp->dev, "FW reset inprog %x after min wait time.\n",
- val);
}
clear_bit(BNXT_STATE_FW_FATAL_COND, &bp->state);
if (pci_enable_device(bp->pdev)) {
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 1259e68cba2a..29061c577baa 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -671,6 +671,10 @@ struct nqe_cn {
#define HWRM_MIN_TIMEOUT 25
#define HWRM_MAX_TIMEOUT 40
+#define HWRM_WAIT_MUST_ABORT(bp, req) \
+ (le16_to_cpu((req)->req_type) != HWRM_VER_GET && \
+ !bnxt_is_fw_healthy(bp))
+
#define HWRM_TOTAL_TIMEOUT(n) (((n) <= HWRM_SHORT_TIMEOUT_COUNTER) ? \
((n) * HWRM_SHORT_MIN_TIMEOUT) : \
(HWRM_SHORT_TIMEOUT_COUNTER * HWRM_SHORT_MIN_TIMEOUT + \
@@ -1560,6 +1564,7 @@ struct bnxt_fw_reporter_ctx {
#define BNXT_FW_STATUS_HEALTH_MSK 0xffff
#define BNXT_FW_STATUS_HEALTHY 0x8000
#define BNXT_FW_STATUS_SHUTDOWN 0x100000
+#define BNXT_FW_STATUS_RECOVERING 0x400000
#define BNXT_FW_IS_HEALTHY(sts) (((sts) & BNXT_FW_STATUS_HEALTH_MSK) ==\
BNXT_FW_STATUS_HEALTHY)
@@ -1570,6 +1575,9 @@ struct bnxt_fw_reporter_ctx {
#define BNXT_FW_IS_ERR(sts) (((sts) & BNXT_FW_STATUS_HEALTH_MSK) > \
BNXT_FW_STATUS_HEALTHY)
+#define BNXT_FW_IS_RECOVERING(sts) (BNXT_FW_IS_ERR(sts) && \
+ ((sts) & BNXT_FW_STATUS_RECOVERING))
+
#define BNXT_FW_RETRY 5
#define BNXT_FW_IF_RETRY 10
@@ -2228,6 +2236,7 @@ int bnxt_hwrm_set_link_setting(struct bnxt *, bool, bool);
int bnxt_hwrm_alloc_wol_fltr(struct bnxt *bp);
int bnxt_hwrm_free_wol_fltr(struct bnxt *bp);
int bnxt_hwrm_func_resc_qcaps(struct bnxt *bp, bool all);
+bool bnxt_is_fw_healthy(struct bnxt *bp);
int bnxt_hwrm_fw_set_time(struct bnxt *);
int bnxt_open_nic(struct bnxt *, bool, bool);
int bnxt_half_open_nic(struct bnxt *bp);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
index 6c85a10f465c..d2ba40c19696 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
@@ -3536,8 +3536,7 @@ out:
}
out_free:
- if (data)
- kvfree(data);
+ kvfree(data);
#undef QDESC_GET_FLQ
#undef QDESC_GET_RXQ
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 3a6bf1ab84e7..01d6bfc0917c 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -612,8 +612,6 @@ struct hnae3_ae_ops {
struct ethtool_rxnfc *cmd);
int (*del_fd_entry)(struct hnae3_handle *handle,
struct ethtool_rxnfc *cmd);
- void (*del_all_fd_entries)(struct hnae3_handle *handle,
- bool clear_list);
int (*get_fd_rule_cnt)(struct hnae3_handle *handle,
struct ethtool_rxnfc *cmd);
int (*get_fd_rule_info)(struct hnae3_handle *handle,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index bf4302a5cf95..44b775efd5b9 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -4143,14 +4143,6 @@ static void hns3_uninit_phy(struct net_device *netdev)
h->ae_algo->ops->mac_disconnect_phy(h);
}
-static void hns3_del_all_fd_rules(struct net_device *netdev, bool clear_list)
-{
- struct hnae3_handle *h = hns3_get_handle(netdev);
-
- if (h->ae_algo->ops->del_all_fd_entries)
- h->ae_algo->ops->del_all_fd_entries(h, clear_list);
-}
-
static int hns3_client_start(struct hnae3_handle *handle)
{
if (!handle->ae_algo->ops->client_start)
@@ -4337,8 +4329,6 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
hns3_nic_uninit_irq(priv);
- hns3_del_all_fd_rules(netdev, true);
-
hns3_clear_all_ring(handle, true);
hns3_nic_uninit_vector_data(priv);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 804f4c8360cf..565c5aa54f88 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -243,6 +243,7 @@ enum hclge_opcode_type {
HCLGE_OPC_FD_KEY_CONFIG = 0x1202,
HCLGE_OPC_FD_TCAM_OP = 0x1203,
HCLGE_OPC_FD_AD_OP = 0x1204,
+ HCLGE_OPC_FD_USER_DEF_OP = 0x1207,
/* MDIO command */
HCLGE_OPC_MDIO_CONFIG = 0x1900,
@@ -1082,6 +1083,19 @@ struct hclge_fd_ad_config_cmd {
u8 rsv2[8];
};
+#define HCLGE_FD_USER_DEF_OFT_S 0
+#define HCLGE_FD_USER_DEF_OFT_M GENMASK(14, 0)
+#define HCLGE_FD_USER_DEF_EN_B 15
+struct hclge_fd_user_def_cfg_cmd {
+ __le16 ol2_cfg;
+ __le16 l2_cfg;
+ __le16 ol3_cfg;
+ __le16 l3_cfg;
+ __le16 ol4_cfg;
+ __le16 l4_cfg;
+ u8 rsv[12];
+};
+
struct hclge_get_m7_bd_cmd {
__le32 bd_num;
u8 rsv[20];
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index a664383271c8..058317ce579c 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -62,7 +62,7 @@ static void hclge_sync_vlan_filter(struct hclge_dev *hdev);
static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev);
static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle);
static void hclge_rfs_filter_expire(struct hclge_dev *hdev);
-static void hclge_clear_arfs_rules(struct hnae3_handle *handle);
+static int hclge_clear_arfs_rules(struct hclge_dev *hdev);
static enum hnae3_reset_type hclge_get_reset_level(struct hnae3_ae_dev *ae_dev,
unsigned long *addr);
static int hclge_set_default_loopback(struct hclge_dev *hdev);
@@ -70,6 +70,7 @@ static int hclge_set_default_loopback(struct hclge_dev *hdev);
static void hclge_sync_mac_table(struct hclge_dev *hdev);
static void hclge_restore_hw_table(struct hclge_dev *hdev);
static void hclge_sync_promisc_mode(struct hclge_dev *hdev);
+static void hclge_sync_fd_table(struct hclge_dev *hdev);
static struct hnae3_ae_algo ae_algo;
@@ -384,36 +385,62 @@ static const struct key_info meta_data_key_info[] = {
};
static const struct key_info tuple_key_info[] = {
- { OUTER_DST_MAC, 48},
- { OUTER_SRC_MAC, 48},
- { OUTER_VLAN_TAG_FST, 16},
- { OUTER_VLAN_TAG_SEC, 16},
- { OUTER_ETH_TYPE, 16},
- { OUTER_L2_RSV, 16},
- { OUTER_IP_TOS, 8},
- { OUTER_IP_PROTO, 8},
- { OUTER_SRC_IP, 32},
- { OUTER_DST_IP, 32},
- { OUTER_L3_RSV, 16},
- { OUTER_SRC_PORT, 16},
- { OUTER_DST_PORT, 16},
- { OUTER_L4_RSV, 32},
- { OUTER_TUN_VNI, 24},
- { OUTER_TUN_FLOW_ID, 8},
- { INNER_DST_MAC, 48},
- { INNER_SRC_MAC, 48},
- { INNER_VLAN_TAG_FST, 16},
- { INNER_VLAN_TAG_SEC, 16},
- { INNER_ETH_TYPE, 16},
- { INNER_L2_RSV, 16},
- { INNER_IP_TOS, 8},
- { INNER_IP_PROTO, 8},
- { INNER_SRC_IP, 32},
- { INNER_DST_IP, 32},
- { INNER_L3_RSV, 16},
- { INNER_SRC_PORT, 16},
- { INNER_DST_PORT, 16},
- { INNER_L4_RSV, 32},
+ { OUTER_DST_MAC, 48, KEY_OPT_MAC, -1, -1 },
+ { OUTER_SRC_MAC, 48, KEY_OPT_MAC, -1, -1 },
+ { OUTER_VLAN_TAG_FST, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_ETH_TYPE, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_L2_RSV, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_IP_TOS, 8, KEY_OPT_U8, -1, -1 },
+ { OUTER_IP_PROTO, 8, KEY_OPT_U8, -1, -1 },
+ { OUTER_SRC_IP, 32, KEY_OPT_IP, -1, -1 },
+ { OUTER_DST_IP, 32, KEY_OPT_IP, -1, -1 },
+ { OUTER_L3_RSV, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_SRC_PORT, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_DST_PORT, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_L4_RSV, 32, KEY_OPT_LE32, -1, -1 },
+ { OUTER_TUN_VNI, 24, KEY_OPT_VNI, -1, -1 },
+ { OUTER_TUN_FLOW_ID, 8, KEY_OPT_U8, -1, -1 },
+ { INNER_DST_MAC, 48, KEY_OPT_MAC,
+ offsetof(struct hclge_fd_rule, tuples.dst_mac),
+ offsetof(struct hclge_fd_rule, tuples_mask.dst_mac) },
+ { INNER_SRC_MAC, 48, KEY_OPT_MAC,
+ offsetof(struct hclge_fd_rule, tuples.src_mac),
+ offsetof(struct hclge_fd_rule, tuples_mask.src_mac) },
+ { INNER_VLAN_TAG_FST, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.vlan_tag1),
+ offsetof(struct hclge_fd_rule, tuples_mask.vlan_tag1) },
+ { INNER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 },
+ { INNER_ETH_TYPE, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.ether_proto),
+ offsetof(struct hclge_fd_rule, tuples_mask.ether_proto) },
+ { INNER_L2_RSV, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.l2_user_def),
+ offsetof(struct hclge_fd_rule, tuples_mask.l2_user_def) },
+ { INNER_IP_TOS, 8, KEY_OPT_U8,
+ offsetof(struct hclge_fd_rule, tuples.ip_tos),
+ offsetof(struct hclge_fd_rule, tuples_mask.ip_tos) },
+ { INNER_IP_PROTO, 8, KEY_OPT_U8,
+ offsetof(struct hclge_fd_rule, tuples.ip_proto),
+ offsetof(struct hclge_fd_rule, tuples_mask.ip_proto) },
+ { INNER_SRC_IP, 32, KEY_OPT_IP,
+ offsetof(struct hclge_fd_rule, tuples.src_ip),
+ offsetof(struct hclge_fd_rule, tuples_mask.src_ip) },
+ { INNER_DST_IP, 32, KEY_OPT_IP,
+ offsetof(struct hclge_fd_rule, tuples.dst_ip),
+ offsetof(struct hclge_fd_rule, tuples_mask.dst_ip) },
+ { INNER_L3_RSV, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.l3_user_def),
+ offsetof(struct hclge_fd_rule, tuples_mask.l3_user_def) },
+ { INNER_SRC_PORT, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.src_port),
+ offsetof(struct hclge_fd_rule, tuples_mask.src_port) },
+ { INNER_DST_PORT, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.dst_port),
+ offsetof(struct hclge_fd_rule, tuples_mask.dst_port) },
+ { INNER_L4_RSV, 32, KEY_OPT_LE32,
+ offsetof(struct hclge_fd_rule, tuples.l4_user_def),
+ offsetof(struct hclge_fd_rule, tuples_mask.l4_user_def) },
};
static int hclge_mac_update_stats_defective(struct hclge_dev *hdev)
@@ -4241,6 +4268,7 @@ static void hclge_periodic_service_task(struct hclge_dev *hdev)
hclge_update_link_status(hdev);
hclge_sync_mac_table(hdev);
hclge_sync_promisc_mode(hdev);
+ hclge_sync_fd_table(hdev);
if (time_is_after_jiffies(hdev->last_serv_processed + HZ)) {
delta = jiffies - hdev->last_serv_processed;
@@ -5142,6 +5170,285 @@ static void hclge_request_update_promisc_mode(struct hnae3_handle *handle)
set_bit(HCLGE_STATE_PROMISC_CHANGED, &hdev->state);
}
+static void hclge_sync_fd_state(struct hclge_dev *hdev)
+{
+ if (hlist_empty(&hdev->fd_rule_list))
+ hdev->fd_active_type = HCLGE_FD_RULE_NONE;
+}
+
+static void hclge_fd_inc_rule_cnt(struct hclge_dev *hdev, u16 location)
+{
+ if (!test_bit(location, hdev->fd_bmap)) {
+ set_bit(location, hdev->fd_bmap);
+ hdev->hclge_fd_rule_num++;
+ }
+}
+
+static void hclge_fd_dec_rule_cnt(struct hclge_dev *hdev, u16 location)
+{
+ if (test_bit(location, hdev->fd_bmap)) {
+ clear_bit(location, hdev->fd_bmap);
+ hdev->hclge_fd_rule_num--;
+ }
+}
+
+static void hclge_fd_free_node(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ hlist_del(&rule->rule_node);
+ kfree(rule);
+ hclge_sync_fd_state(hdev);
+}
+
+static void hclge_update_fd_rule_node(struct hclge_dev *hdev,
+ struct hclge_fd_rule *old_rule,
+ struct hclge_fd_rule *new_rule,
+ enum HCLGE_FD_NODE_STATE state)
+{
+ switch (state) {
+ case HCLGE_FD_TO_ADD:
+ case HCLGE_FD_ACTIVE:
+ /* 1) if the new state is TO_ADD, just replace the old rule
+ * with the same location, no matter its state, because the
+ * new rule will be configured to the hardware.
+ * 2) if the new state is ACTIVE, it means the new rule
+ * has been configured to the hardware, so just replace
+ * the old rule node with the same location.
+ * 3) for it doesn't add a new node to the list, so it's
+ * unnecessary to update the rule number and fd_bmap.
+ */
+ new_rule->rule_node.next = old_rule->rule_node.next;
+ new_rule->rule_node.pprev = old_rule->rule_node.pprev;
+ memcpy(old_rule, new_rule, sizeof(*old_rule));
+ kfree(new_rule);
+ break;
+ case HCLGE_FD_DELETED:
+ hclge_fd_dec_rule_cnt(hdev, old_rule->location);
+ hclge_fd_free_node(hdev, old_rule);
+ break;
+ case HCLGE_FD_TO_DEL:
+ /* if new request is TO_DEL, and old rule is existent
+ * 1) the state of old rule is TO_DEL, we need do nothing,
+ * because we delete rule by location, other rule content
+ * is unncessary.
+ * 2) the state of old rule is ACTIVE, we need to change its
+ * state to TO_DEL, so the rule will be deleted when periodic
+ * task being scheduled.
+ * 3) the state of old rule is TO_ADD, it means the rule hasn't
+ * been added to hardware, so we just delete the rule node from
+ * fd_rule_list directly.
+ */
+ if (old_rule->state == HCLGE_FD_TO_ADD) {
+ hclge_fd_dec_rule_cnt(hdev, old_rule->location);
+ hclge_fd_free_node(hdev, old_rule);
+ return;
+ }
+ old_rule->state = HCLGE_FD_TO_DEL;
+ break;
+ }
+}
+
+static struct hclge_fd_rule *hclge_find_fd_rule(struct hlist_head *hlist,
+ u16 location,
+ struct hclge_fd_rule **parent)
+{
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+
+ hlist_for_each_entry_safe(rule, node, hlist, rule_node) {
+ if (rule->location == location)
+ return rule;
+ else if (rule->location > location)
+ return NULL;
+ /* record the parent node, use to keep the nodes in fd_rule_list
+ * in ascend order.
+ */
+ *parent = rule;
+ }
+
+ return NULL;
+}
+
+/* insert fd rule node in ascend order according to rule->location */
+static void hclge_fd_insert_rule_node(struct hlist_head *hlist,
+ struct hclge_fd_rule *rule,
+ struct hclge_fd_rule *parent)
+{
+ INIT_HLIST_NODE(&rule->rule_node);
+
+ if (parent)
+ hlist_add_behind(&rule->rule_node, &parent->rule_node);
+ else
+ hlist_add_head(&rule->rule_node, hlist);
+}
+
+static int hclge_fd_set_user_def_cmd(struct hclge_dev *hdev,
+ struct hclge_fd_user_def_cfg *cfg)
+{
+ struct hclge_fd_user_def_cfg_cmd *req;
+ struct hclge_desc desc;
+ u16 data = 0;
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_USER_DEF_OP, false);
+
+ req = (struct hclge_fd_user_def_cfg_cmd *)desc.data;
+
+ hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[0].ref_cnt > 0);
+ hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
+ HCLGE_FD_USER_DEF_OFT_S, cfg[0].offset);
+ req->ol2_cfg = cpu_to_le16(data);
+
+ data = 0;
+ hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[1].ref_cnt > 0);
+ hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
+ HCLGE_FD_USER_DEF_OFT_S, cfg[1].offset);
+ req->ol3_cfg = cpu_to_le16(data);
+
+ data = 0;
+ hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[2].ref_cnt > 0);
+ hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
+ HCLGE_FD_USER_DEF_OFT_S, cfg[2].offset);
+ req->ol4_cfg = cpu_to_le16(data);
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "failed to set fd user def data, ret= %d\n", ret);
+ return ret;
+}
+
+static void hclge_sync_fd_user_def_cfg(struct hclge_dev *hdev, bool locked)
+{
+ int ret;
+
+ if (!test_and_clear_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state))
+ return;
+
+ if (!locked)
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ ret = hclge_fd_set_user_def_cmd(hdev, hdev->fd_cfg.user_def_cfg);
+ if (ret)
+ set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
+
+ if (!locked)
+ spin_unlock_bh(&hdev->fd_rule_lock);
+}
+
+static int hclge_fd_check_user_def_refcnt(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ struct hlist_head *hlist = &hdev->fd_rule_list;
+ struct hclge_fd_rule *fd_rule, *parent = NULL;
+ struct hclge_fd_user_def_info *info, *old_info;
+ struct hclge_fd_user_def_cfg *cfg;
+
+ if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
+ rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
+ return 0;
+
+ /* for valid layer is start from 1, so need minus 1 to get the cfg */
+ cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
+ info = &rule->ep.user_def;
+
+ if (!cfg->ref_cnt || cfg->offset == info->offset)
+ return 0;
+
+ if (cfg->ref_cnt > 1)
+ goto error;
+
+ fd_rule = hclge_find_fd_rule(hlist, rule->location, &parent);
+ if (fd_rule) {
+ old_info = &fd_rule->ep.user_def;
+ if (info->layer == old_info->layer)
+ return 0;
+ }
+
+error:
+ dev_err(&hdev->pdev->dev,
+ "No available offset for layer%d fd rule, each layer only support one user def offset.\n",
+ info->layer + 1);
+ return -ENOSPC;
+}
+
+static void hclge_fd_inc_user_def_refcnt(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ struct hclge_fd_user_def_cfg *cfg;
+
+ if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
+ rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
+ return;
+
+ cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
+ if (!cfg->ref_cnt) {
+ cfg->offset = rule->ep.user_def.offset;
+ set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
+ }
+ cfg->ref_cnt++;
+}
+
+static void hclge_fd_dec_user_def_refcnt(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ struct hclge_fd_user_def_cfg *cfg;
+
+ if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
+ rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
+ return;
+
+ cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
+ if (!cfg->ref_cnt)
+ return;
+
+ cfg->ref_cnt--;
+ if (!cfg->ref_cnt) {
+ cfg->offset = 0;
+ set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
+ }
+}
+
+static void hclge_update_fd_list(struct hclge_dev *hdev,
+ enum HCLGE_FD_NODE_STATE state, u16 location,
+ struct hclge_fd_rule *new_rule)
+{
+ struct hlist_head *hlist = &hdev->fd_rule_list;
+ struct hclge_fd_rule *fd_rule, *parent = NULL;
+
+ fd_rule = hclge_find_fd_rule(hlist, location, &parent);
+ if (fd_rule) {
+ hclge_fd_dec_user_def_refcnt(hdev, fd_rule);
+ if (state == HCLGE_FD_ACTIVE)
+ hclge_fd_inc_user_def_refcnt(hdev, new_rule);
+ hclge_sync_fd_user_def_cfg(hdev, true);
+
+ hclge_update_fd_rule_node(hdev, fd_rule, new_rule, state);
+ return;
+ }
+
+ /* it's unlikely to fail here, because we have checked the rule
+ * exist before.
+ */
+ if (unlikely(state == HCLGE_FD_TO_DEL || state == HCLGE_FD_DELETED)) {
+ dev_warn(&hdev->pdev->dev,
+ "failed to delete fd rule %u, it's inexistent\n",
+ location);
+ return;
+ }
+
+ hclge_fd_inc_user_def_refcnt(hdev, new_rule);
+ hclge_sync_fd_user_def_cfg(hdev, true);
+
+ hclge_fd_insert_rule_node(hlist, new_rule, parent);
+ hclge_fd_inc_rule_cnt(hdev, new_rule->location);
+
+ if (state == HCLGE_FD_TO_ADD) {
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+ hclge_task_schedule(hdev, 0);
+ }
+}
+
static int hclge_get_fd_mode(struct hclge_dev *hdev, u8 *fd_mode)
{
struct hclge_get_fd_mode_cmd *req;
@@ -5220,6 +5527,17 @@ static int hclge_set_fd_key_config(struct hclge_dev *hdev,
return ret;
}
+static void hclge_fd_disable_user_def(struct hclge_dev *hdev)
+{
+ struct hclge_fd_user_def_cfg *cfg = hdev->fd_cfg.user_def_cfg;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+ memset(cfg, 0, sizeof(hdev->fd_cfg.user_def_cfg));
+ spin_unlock_bh(&hdev->fd_rule_lock);
+
+ hclge_fd_set_user_def_cmd(hdev, cfg);
+}
+
static int hclge_init_fd_config(struct hclge_dev *hdev)
{
#define LOW_2_WORDS 0x03
@@ -5260,9 +5578,12 @@ static int hclge_init_fd_config(struct hclge_dev *hdev)
BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
/* If use max 400bit key, we can support tuples for ether type */
- if (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1)
+ if (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
key_cfg->tuple_active |=
BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC);
+ if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3)
+ key_cfg->tuple_active |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
+ }
/* roce_type is used to filter roce frames
* dst_vport is used to specify the rule
@@ -5371,96 +5692,57 @@ static int hclge_fd_ad_config(struct hclge_dev *hdev, u8 stage, int loc,
static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 *key_x, u8 *key_y,
struct hclge_fd_rule *rule)
{
+ int offset, moffset, ip_offset;
+ enum HCLGE_FD_KEY_OPT key_opt;
u16 tmp_x_s, tmp_y_s;
u32 tmp_x_l, tmp_y_l;
+ u8 *p = (u8 *)rule;
int i;
- if (rule->unused_tuple & tuple_bit)
+ if (rule->unused_tuple & BIT(tuple_bit))
return true;
- switch (tuple_bit) {
- case BIT(INNER_DST_MAC):
- for (i = 0; i < ETH_ALEN; i++) {
- calc_x(key_x[ETH_ALEN - 1 - i], rule->tuples.dst_mac[i],
- rule->tuples_mask.dst_mac[i]);
- calc_y(key_y[ETH_ALEN - 1 - i], rule->tuples.dst_mac[i],
- rule->tuples_mask.dst_mac[i]);
- }
+ key_opt = tuple_key_info[tuple_bit].key_opt;
+ offset = tuple_key_info[tuple_bit].offset;
+ moffset = tuple_key_info[tuple_bit].moffset;
- return true;
- case BIT(INNER_SRC_MAC):
- for (i = 0; i < ETH_ALEN; i++) {
- calc_x(key_x[ETH_ALEN - 1 - i], rule->tuples.src_mac[i],
- rule->tuples_mask.src_mac[i]);
- calc_y(key_y[ETH_ALEN - 1 - i], rule->tuples.src_mac[i],
- rule->tuples_mask.src_mac[i]);
- }
+ switch (key_opt) {
+ case KEY_OPT_U8:
+ calc_x(*key_x, p[offset], p[moffset]);
+ calc_y(*key_y, p[offset], p[moffset]);
return true;
- case BIT(INNER_VLAN_TAG_FST):
- calc_x(tmp_x_s, rule->tuples.vlan_tag1,
- rule->tuples_mask.vlan_tag1);
- calc_y(tmp_y_s, rule->tuples.vlan_tag1,
- rule->tuples_mask.vlan_tag1);
+ case KEY_OPT_LE16:
+ calc_x(tmp_x_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset]));
+ calc_y(tmp_y_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset]));
*(__le16 *)key_x = cpu_to_le16(tmp_x_s);
*(__le16 *)key_y = cpu_to_le16(tmp_y_s);
return true;
- case BIT(INNER_ETH_TYPE):
- calc_x(tmp_x_s, rule->tuples.ether_proto,
- rule->tuples_mask.ether_proto);
- calc_y(tmp_y_s, rule->tuples.ether_proto,
- rule->tuples_mask.ether_proto);
- *(__le16 *)key_x = cpu_to_le16(tmp_x_s);
- *(__le16 *)key_y = cpu_to_le16(tmp_y_s);
-
- return true;
- case BIT(INNER_IP_TOS):
- calc_x(*key_x, rule->tuples.ip_tos, rule->tuples_mask.ip_tos);
- calc_y(*key_y, rule->tuples.ip_tos, rule->tuples_mask.ip_tos);
-
- return true;
- case BIT(INNER_IP_PROTO):
- calc_x(*key_x, rule->tuples.ip_proto,
- rule->tuples_mask.ip_proto);
- calc_y(*key_y, rule->tuples.ip_proto,
- rule->tuples_mask.ip_proto);
-
- return true;
- case BIT(INNER_SRC_IP):
- calc_x(tmp_x_l, rule->tuples.src_ip[IPV4_INDEX],
- rule->tuples_mask.src_ip[IPV4_INDEX]);
- calc_y(tmp_y_l, rule->tuples.src_ip[IPV4_INDEX],
- rule->tuples_mask.src_ip[IPV4_INDEX]);
+ case KEY_OPT_LE32:
+ calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
+ calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
*(__le32 *)key_x = cpu_to_le32(tmp_x_l);
*(__le32 *)key_y = cpu_to_le32(tmp_y_l);
return true;
- case BIT(INNER_DST_IP):
- calc_x(tmp_x_l, rule->tuples.dst_ip[IPV4_INDEX],
- rule->tuples_mask.dst_ip[IPV4_INDEX]);
- calc_y(tmp_y_l, rule->tuples.dst_ip[IPV4_INDEX],
- rule->tuples_mask.dst_ip[IPV4_INDEX]);
- *(__le32 *)key_x = cpu_to_le32(tmp_x_l);
- *(__le32 *)key_y = cpu_to_le32(tmp_y_l);
-
- return true;
- case BIT(INNER_SRC_PORT):
- calc_x(tmp_x_s, rule->tuples.src_port,
- rule->tuples_mask.src_port);
- calc_y(tmp_y_s, rule->tuples.src_port,
- rule->tuples_mask.src_port);
- *(__le16 *)key_x = cpu_to_le16(tmp_x_s);
- *(__le16 *)key_y = cpu_to_le16(tmp_y_s);
+ case KEY_OPT_MAC:
+ for (i = 0; i < ETH_ALEN; i++) {
+ calc_x(key_x[ETH_ALEN - 1 - i], p[offset + i],
+ p[moffset + i]);
+ calc_y(key_y[ETH_ALEN - 1 - i], p[offset + i],
+ p[moffset + i]);
+ }
return true;
- case BIT(INNER_DST_PORT):
- calc_x(tmp_x_s, rule->tuples.dst_port,
- rule->tuples_mask.dst_port);
- calc_y(tmp_y_s, rule->tuples.dst_port,
- rule->tuples_mask.dst_port);
- *(__le16 *)key_x = cpu_to_le16(tmp_x_s);
- *(__le16 *)key_y = cpu_to_le16(tmp_y_s);
+ case KEY_OPT_IP:
+ ip_offset = IPV4_INDEX * sizeof(u32);
+ calc_x(tmp_x_l, *(u32 *)(&p[offset + ip_offset]),
+ *(u32 *)(&p[moffset + ip_offset]));
+ calc_y(tmp_y_l, *(u32 *)(&p[offset + ip_offset]),
+ *(u32 *)(&p[moffset + ip_offset]));
+ *(__le32 *)key_x = cpu_to_le32(tmp_x_l);
+ *(__le32 *)key_y = cpu_to_le32(tmp_y_l);
return true;
default:
@@ -5548,12 +5830,12 @@ static int hclge_config_key(struct hclge_dev *hdev, u8 stage,
for (i = 0 ; i < MAX_TUPLE; i++) {
bool tuple_valid;
- u32 check_tuple;
tuple_size = tuple_key_info[i].key_length / 8;
- check_tuple = key_cfg->tuple_active & BIT(i);
+ if (!(key_cfg->tuple_active & BIT(i)))
+ continue;
- tuple_valid = hclge_fd_convert_tuple(check_tuple, cur_key_x,
+ tuple_valid = hclge_fd_convert_tuple(i, cur_key_x,
cur_key_y, rule);
if (tuple_valid) {
cur_key_x += tuple_size;
@@ -5684,8 +5966,7 @@ static int hclge_fd_check_tcpip6_tuple(struct ethtool_tcpip6_spec *spec,
if (!spec || !unused_tuple)
return -EINVAL;
- *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
- BIT(INNER_IP_TOS);
+ *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
/* check whether src/dst ip address used */
if (ipv6_addr_any((struct in6_addr *)spec->ip6src))
@@ -5700,8 +5981,8 @@ static int hclge_fd_check_tcpip6_tuple(struct ethtool_tcpip6_spec *spec,
if (!spec->pdst)
*unused_tuple |= BIT(INNER_DST_PORT);
- if (spec->tclass)
- return -EOPNOTSUPP;
+ if (!spec->tclass)
+ *unused_tuple |= BIT(INNER_IP_TOS);
return 0;
}
@@ -5713,7 +5994,7 @@ static int hclge_fd_check_ip6_tuple(struct ethtool_usrip6_spec *spec,
return -EINVAL;
*unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
- BIT(INNER_IP_TOS) | BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
+ BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
/* check whether src/dst ip address used */
if (ipv6_addr_any((struct in6_addr *)spec->ip6src))
@@ -5725,8 +6006,8 @@ static int hclge_fd_check_ip6_tuple(struct ethtool_usrip6_spec *spec,
if (!spec->l4_proto)
*unused_tuple |= BIT(INNER_IP_PROTO);
- if (spec->tclass)
- return -EOPNOTSUPP;
+ if (!spec->tclass)
+ *unused_tuple |= BIT(INNER_IP_TOS);
if (spec->l4_4_bytes)
return -EOPNOTSUPP;
@@ -5796,9 +6077,98 @@ static int hclge_fd_check_ext_tuple(struct hclge_dev *hdev,
return 0;
}
+static int hclge_fd_get_user_def_layer(u32 flow_type, u32 *unused_tuple,
+ struct hclge_fd_user_def_info *info)
+{
+ switch (flow_type) {
+ case ETHER_FLOW:
+ info->layer = HCLGE_FD_USER_DEF_L2;
+ *unused_tuple &= ~BIT(INNER_L2_RSV);
+ break;
+ case IP_USER_FLOW:
+ case IPV6_USER_FLOW:
+ info->layer = HCLGE_FD_USER_DEF_L3;
+ *unused_tuple &= ~BIT(INNER_L3_RSV);
+ break;
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ info->layer = HCLGE_FD_USER_DEF_L4;
+ *unused_tuple &= ~BIT(INNER_L4_RSV);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static bool hclge_fd_is_user_def_all_masked(struct ethtool_rx_flow_spec *fs)
+{
+ return be32_to_cpu(fs->m_ext.data[1] | fs->m_ext.data[0]) == 0;
+}
+
+static int hclge_fd_parse_user_def_field(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ u32 *unused_tuple,
+ struct hclge_fd_user_def_info *info)
+{
+ u32 tuple_active = hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1].tuple_active;
+ u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+ u16 data, offset, data_mask, offset_mask;
+ int ret;
+
+ info->layer = HCLGE_FD_USER_DEF_NONE;
+ *unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
+
+ if (!(fs->flow_type & FLOW_EXT) || hclge_fd_is_user_def_all_masked(fs))
+ return 0;
+
+ /* user-def data from ethtool is 64 bit value, the bit0~15 is used
+ * for data, and bit32~47 is used for offset.
+ */
+ data = be32_to_cpu(fs->h_ext.data[1]) & HCLGE_FD_USER_DEF_DATA;
+ data_mask = be32_to_cpu(fs->m_ext.data[1]) & HCLGE_FD_USER_DEF_DATA;
+ offset = be32_to_cpu(fs->h_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET;
+ offset_mask = be32_to_cpu(fs->m_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET;
+
+ if (!(tuple_active & HCLGE_FD_TUPLE_USER_DEF_TUPLES)) {
+ dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (offset > HCLGE_FD_MAX_USER_DEF_OFFSET) {
+ dev_err(&hdev->pdev->dev,
+ "user-def offset[%u] should be no more than %u\n",
+ offset, HCLGE_FD_MAX_USER_DEF_OFFSET);
+ return -EINVAL;
+ }
+
+ if (offset_mask != HCLGE_FD_USER_DEF_OFFSET_UNMASK) {
+ dev_err(&hdev->pdev->dev, "user-def offset can't be masked\n");
+ return -EINVAL;
+ }
+
+ ret = hclge_fd_get_user_def_layer(flow_type, unused_tuple, info);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "unsupported flow type for user-def bytes, ret = %d\n",
+ ret);
+ return ret;
+ }
+
+ info->data = data;
+ info->data_mask = data_mask;
+ info->offset = offset;
+
+ return 0;
+}
+
static int hclge_fd_check_spec(struct hclge_dev *hdev,
struct ethtool_rx_flow_spec *fs,
- u32 *unused_tuple)
+ u32 *unused_tuple,
+ struct hclge_fd_user_def_info *info)
{
u32 flow_type;
int ret;
@@ -5811,11 +6181,9 @@ static int hclge_fd_check_spec(struct hclge_dev *hdev,
return -EINVAL;
}
- if ((fs->flow_type & FLOW_EXT) &&
- (fs->h_ext.data[0] != 0 || fs->h_ext.data[1] != 0)) {
- dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n");
- return -EOPNOTSUPP;
- }
+ ret = hclge_fd_parse_user_def_field(hdev, fs, unused_tuple, info);
+ if (ret)
+ return ret;
flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
switch (flow_type) {
@@ -5867,217 +6235,194 @@ static int hclge_fd_check_spec(struct hclge_dev *hdev,
return hclge_fd_check_ext_tuple(hdev, fs, unused_tuple);
}
-static bool hclge_fd_rule_exist(struct hclge_dev *hdev, u16 location)
+static void hclge_fd_get_tcpip4_tuple(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule, u8 ip_proto)
{
- struct hclge_fd_rule *rule = NULL;
- struct hlist_node *node2;
-
- spin_lock_bh(&hdev->fd_rule_lock);
- hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) {
- if (rule->location >= location)
- break;
- }
+ rule->tuples.src_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src);
+ rule->tuples_mask.src_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src);
- spin_unlock_bh(&hdev->fd_rule_lock);
+ rule->tuples.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst);
+ rule->tuples_mask.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst);
- return rule && rule->location == location;
-}
+ rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc);
+ rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc);
-/* make sure being called after lock up with fd_rule_lock */
-static int hclge_fd_update_rule_list(struct hclge_dev *hdev,
- struct hclge_fd_rule *new_rule,
- u16 location,
- bool is_add)
-{
- struct hclge_fd_rule *rule = NULL, *parent = NULL;
- struct hlist_node *node2;
+ rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst);
+ rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst);
- if (is_add && !new_rule)
- return -EINVAL;
-
- hlist_for_each_entry_safe(rule, node2,
- &hdev->fd_rule_list, rule_node) {
- if (rule->location >= location)
- break;
- parent = rule;
- }
+ rule->tuples.ip_tos = fs->h_u.tcp_ip4_spec.tos;
+ rule->tuples_mask.ip_tos = fs->m_u.tcp_ip4_spec.tos;
- if (rule && rule->location == location) {
- hlist_del(&rule->rule_node);
- kfree(rule);
- hdev->hclge_fd_rule_num--;
+ rule->tuples.ether_proto = ETH_P_IP;
+ rule->tuples_mask.ether_proto = 0xFFFF;
- if (!is_add) {
- if (!hdev->hclge_fd_rule_num)
- hdev->fd_active_type = HCLGE_FD_RULE_NONE;
- clear_bit(location, hdev->fd_bmap);
+ rule->tuples.ip_proto = ip_proto;
+ rule->tuples_mask.ip_proto = 0xFF;
+}
- return 0;
- }
- } else if (!is_add) {
- dev_err(&hdev->pdev->dev,
- "delete fail, rule %u is inexistent\n",
- location);
- return -EINVAL;
- }
+static void hclge_fd_get_ip4_tuple(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ rule->tuples.src_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src);
+ rule->tuples_mask.src_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src);
- INIT_HLIST_NODE(&new_rule->rule_node);
+ rule->tuples.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst);
+ rule->tuples_mask.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst);
- if (parent)
- hlist_add_behind(&new_rule->rule_node, &parent->rule_node);
- else
- hlist_add_head(&new_rule->rule_node, &hdev->fd_rule_list);
+ rule->tuples.ip_tos = fs->h_u.usr_ip4_spec.tos;
+ rule->tuples_mask.ip_tos = fs->m_u.usr_ip4_spec.tos;
- set_bit(location, hdev->fd_bmap);
- hdev->hclge_fd_rule_num++;
- hdev->fd_active_type = new_rule->rule_type;
+ rule->tuples.ip_proto = fs->h_u.usr_ip4_spec.proto;
+ rule->tuples_mask.ip_proto = fs->m_u.usr_ip4_spec.proto;
- return 0;
+ rule->tuples.ether_proto = ETH_P_IP;
+ rule->tuples_mask.ether_proto = 0xFFFF;
}
-static int hclge_fd_get_tuple(struct hclge_dev *hdev,
- struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule)
+static void hclge_fd_get_tcpip6_tuple(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule, u8 ip_proto)
{
- u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+ be32_to_cpu_array(rule->tuples.src_ip, fs->h_u.tcp_ip6_spec.ip6src,
+ IPV6_SIZE);
+ be32_to_cpu_array(rule->tuples_mask.src_ip, fs->m_u.tcp_ip6_spec.ip6src,
+ IPV6_SIZE);
- switch (flow_type) {
- case SCTP_V4_FLOW:
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- rule->tuples.src_ip[IPV4_INDEX] =
- be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src);
- rule->tuples_mask.src_ip[IPV4_INDEX] =
- be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src);
-
- rule->tuples.dst_ip[IPV4_INDEX] =
- be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst);
- rule->tuples_mask.dst_ip[IPV4_INDEX] =
- be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst);
+ be32_to_cpu_array(rule->tuples.dst_ip, fs->h_u.tcp_ip6_spec.ip6dst,
+ IPV6_SIZE);
+ be32_to_cpu_array(rule->tuples_mask.dst_ip, fs->m_u.tcp_ip6_spec.ip6dst,
+ IPV6_SIZE);
- rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc);
- rule->tuples_mask.src_port =
- be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc);
+ rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc);
+ rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc);
- rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst);
- rule->tuples_mask.dst_port =
- be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst);
+ rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst);
+ rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst);
- rule->tuples.ip_tos = fs->h_u.tcp_ip4_spec.tos;
- rule->tuples_mask.ip_tos = fs->m_u.tcp_ip4_spec.tos;
+ rule->tuples.ether_proto = ETH_P_IPV6;
+ rule->tuples_mask.ether_proto = 0xFFFF;
- rule->tuples.ether_proto = ETH_P_IP;
- rule->tuples_mask.ether_proto = 0xFFFF;
+ rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass;
+ rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass;
- break;
- case IP_USER_FLOW:
- rule->tuples.src_ip[IPV4_INDEX] =
- be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src);
- rule->tuples_mask.src_ip[IPV4_INDEX] =
- be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src);
+ rule->tuples.ip_proto = ip_proto;
+ rule->tuples_mask.ip_proto = 0xFF;
+}
- rule->tuples.dst_ip[IPV4_INDEX] =
- be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst);
- rule->tuples_mask.dst_ip[IPV4_INDEX] =
- be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst);
+static void hclge_fd_get_ip6_tuple(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ be32_to_cpu_array(rule->tuples.src_ip, fs->h_u.usr_ip6_spec.ip6src,
+ IPV6_SIZE);
+ be32_to_cpu_array(rule->tuples_mask.src_ip, fs->m_u.usr_ip6_spec.ip6src,
+ IPV6_SIZE);
- rule->tuples.ip_tos = fs->h_u.usr_ip4_spec.tos;
- rule->tuples_mask.ip_tos = fs->m_u.usr_ip4_spec.tos;
+ be32_to_cpu_array(rule->tuples.dst_ip, fs->h_u.usr_ip6_spec.ip6dst,
+ IPV6_SIZE);
+ be32_to_cpu_array(rule->tuples_mask.dst_ip, fs->m_u.usr_ip6_spec.ip6dst,
+ IPV6_SIZE);
- rule->tuples.ip_proto = fs->h_u.usr_ip4_spec.proto;
- rule->tuples_mask.ip_proto = fs->m_u.usr_ip4_spec.proto;
+ rule->tuples.ip_proto = fs->h_u.usr_ip6_spec.l4_proto;
+ rule->tuples_mask.ip_proto = fs->m_u.usr_ip6_spec.l4_proto;
- rule->tuples.ether_proto = ETH_P_IP;
- rule->tuples_mask.ether_proto = 0xFFFF;
+ rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass;
+ rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass;
- break;
- case SCTP_V6_FLOW:
- case TCP_V6_FLOW:
- case UDP_V6_FLOW:
- be32_to_cpu_array(rule->tuples.src_ip,
- fs->h_u.tcp_ip6_spec.ip6src, IPV6_SIZE);
- be32_to_cpu_array(rule->tuples_mask.src_ip,
- fs->m_u.tcp_ip6_spec.ip6src, IPV6_SIZE);
-
- be32_to_cpu_array(rule->tuples.dst_ip,
- fs->h_u.tcp_ip6_spec.ip6dst, IPV6_SIZE);
- be32_to_cpu_array(rule->tuples_mask.dst_ip,
- fs->m_u.tcp_ip6_spec.ip6dst, IPV6_SIZE);
+ rule->tuples.ether_proto = ETH_P_IPV6;
+ rule->tuples_mask.ether_proto = 0xFFFF;
+}
- rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc);
- rule->tuples_mask.src_port =
- be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc);
+static void hclge_fd_get_ether_tuple(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ ether_addr_copy(rule->tuples.src_mac, fs->h_u.ether_spec.h_source);
+ ether_addr_copy(rule->tuples_mask.src_mac, fs->m_u.ether_spec.h_source);
- rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst);
- rule->tuples_mask.dst_port =
- be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst);
+ ether_addr_copy(rule->tuples.dst_mac, fs->h_u.ether_spec.h_dest);
+ ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_u.ether_spec.h_dest);
- rule->tuples.ether_proto = ETH_P_IPV6;
- rule->tuples_mask.ether_proto = 0xFFFF;
+ rule->tuples.ether_proto = be16_to_cpu(fs->h_u.ether_spec.h_proto);
+ rule->tuples_mask.ether_proto = be16_to_cpu(fs->m_u.ether_spec.h_proto);
+}
+static void hclge_fd_get_user_def_tuple(struct hclge_fd_user_def_info *info,
+ struct hclge_fd_rule *rule)
+{
+ switch (info->layer) {
+ case HCLGE_FD_USER_DEF_L2:
+ rule->tuples.l2_user_def = info->data;
+ rule->tuples_mask.l2_user_def = info->data_mask;
break;
- case IPV6_USER_FLOW:
- be32_to_cpu_array(rule->tuples.src_ip,
- fs->h_u.usr_ip6_spec.ip6src, IPV6_SIZE);
- be32_to_cpu_array(rule->tuples_mask.src_ip,
- fs->m_u.usr_ip6_spec.ip6src, IPV6_SIZE);
-
- be32_to_cpu_array(rule->tuples.dst_ip,
- fs->h_u.usr_ip6_spec.ip6dst, IPV6_SIZE);
- be32_to_cpu_array(rule->tuples_mask.dst_ip,
- fs->m_u.usr_ip6_spec.ip6dst, IPV6_SIZE);
-
- rule->tuples.ip_proto = fs->h_u.usr_ip6_spec.l4_proto;
- rule->tuples_mask.ip_proto = fs->m_u.usr_ip6_spec.l4_proto;
-
- rule->tuples.ether_proto = ETH_P_IPV6;
- rule->tuples_mask.ether_proto = 0xFFFF;
-
+ case HCLGE_FD_USER_DEF_L3:
+ rule->tuples.l3_user_def = info->data;
+ rule->tuples_mask.l3_user_def = info->data_mask;
break;
- case ETHER_FLOW:
- ether_addr_copy(rule->tuples.src_mac,
- fs->h_u.ether_spec.h_source);
- ether_addr_copy(rule->tuples_mask.src_mac,
- fs->m_u.ether_spec.h_source);
-
- ether_addr_copy(rule->tuples.dst_mac,
- fs->h_u.ether_spec.h_dest);
- ether_addr_copy(rule->tuples_mask.dst_mac,
- fs->m_u.ether_spec.h_dest);
-
- rule->tuples.ether_proto =
- be16_to_cpu(fs->h_u.ether_spec.h_proto);
- rule->tuples_mask.ether_proto =
- be16_to_cpu(fs->m_u.ether_spec.h_proto);
-
+ case HCLGE_FD_USER_DEF_L4:
+ rule->tuples.l4_user_def = (u32)info->data << 16;
+ rule->tuples_mask.l4_user_def = (u32)info->data_mask << 16;
break;
default:
- return -EOPNOTSUPP;
+ break;
}
+ rule->ep.user_def = *info;
+}
+
+static int hclge_fd_get_tuple(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule,
+ struct hclge_fd_user_def_info *info)
+{
+ u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+
switch (flow_type) {
case SCTP_V4_FLOW:
- case SCTP_V6_FLOW:
- rule->tuples.ip_proto = IPPROTO_SCTP;
- rule->tuples_mask.ip_proto = 0xFF;
+ hclge_fd_get_tcpip4_tuple(hdev, fs, rule, IPPROTO_SCTP);
break;
case TCP_V4_FLOW:
- case TCP_V6_FLOW:
- rule->tuples.ip_proto = IPPROTO_TCP;
- rule->tuples_mask.ip_proto = 0xFF;
+ hclge_fd_get_tcpip4_tuple(hdev, fs, rule, IPPROTO_TCP);
break;
case UDP_V4_FLOW:
+ hclge_fd_get_tcpip4_tuple(hdev, fs, rule, IPPROTO_UDP);
+ break;
+ case IP_USER_FLOW:
+ hclge_fd_get_ip4_tuple(hdev, fs, rule);
+ break;
+ case SCTP_V6_FLOW:
+ hclge_fd_get_tcpip6_tuple(hdev, fs, rule, IPPROTO_SCTP);
+ break;
+ case TCP_V6_FLOW:
+ hclge_fd_get_tcpip6_tuple(hdev, fs, rule, IPPROTO_TCP);
+ break;
case UDP_V6_FLOW:
- rule->tuples.ip_proto = IPPROTO_UDP;
- rule->tuples_mask.ip_proto = 0xFF;
+ hclge_fd_get_tcpip6_tuple(hdev, fs, rule, IPPROTO_UDP);
break;
- default:
+ case IPV6_USER_FLOW:
+ hclge_fd_get_ip6_tuple(hdev, fs, rule);
break;
+ case ETHER_FLOW:
+ hclge_fd_get_ether_tuple(hdev, fs, rule);
+ break;
+ default:
+ return -EOPNOTSUPP;
}
if (fs->flow_type & FLOW_EXT) {
rule->tuples.vlan_tag1 = be16_to_cpu(fs->h_ext.vlan_tci);
rule->tuples_mask.vlan_tag1 = be16_to_cpu(fs->m_ext.vlan_tci);
+ hclge_fd_get_user_def_tuple(info, rule);
}
if (fs->flow_type & FLOW_MAC_EXT) {
@@ -6088,33 +6433,52 @@ static int hclge_fd_get_tuple(struct hclge_dev *hdev,
return 0;
}
-/* make sure being called after lock up with fd_rule_lock */
static int hclge_fd_config_rule(struct hclge_dev *hdev,
struct hclge_fd_rule *rule)
{
int ret;
- if (!rule) {
+ ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
+ if (ret)
+ return ret;
+
+ return hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule);
+}
+
+static int hclge_add_fd_entry_common(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ int ret;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ if (hdev->fd_active_type != rule->rule_type &&
+ (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE ||
+ hdev->fd_active_type == HCLGE_FD_EP_ACTIVE)) {
dev_err(&hdev->pdev->dev,
- "The flow director rule is NULL\n");
+ "mode conflict(new type %d, active type %d), please delete existent rules first\n",
+ rule->rule_type, hdev->fd_active_type);
+ spin_unlock_bh(&hdev->fd_rule_lock);
return -EINVAL;
}
- /* it will never fail here, so needn't to check return value */
- hclge_fd_update_rule_list(hdev, rule, rule->location, true);
+ ret = hclge_fd_check_user_def_refcnt(hdev, rule);
+ if (ret)
+ goto out;
- ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
+ ret = hclge_clear_arfs_rules(hdev);
if (ret)
- goto clear_rule;
+ goto out;
- ret = hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule);
+ ret = hclge_fd_config_rule(hdev, rule);
if (ret)
- goto clear_rule;
+ goto out;
- return 0;
+ hclge_update_fd_list(hdev, HCLGE_FD_ACTIVE, rule->location, rule);
+ hdev->fd_active_type = rule->rule_type;
-clear_rule:
- hclge_fd_update_rule_list(hdev, rule, rule->location, false);
+out:
+ spin_unlock_bh(&hdev->fd_rule_lock);
return ret;
}
@@ -6126,11 +6490,48 @@ static bool hclge_is_cls_flower_active(struct hnae3_handle *handle)
return hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE;
}
+static int hclge_fd_parse_ring_cookie(struct hclge_dev *hdev, u64 ring_cookie,
+ u16 *vport_id, u8 *action, u16 *queue_id)
+{
+ struct hclge_vport *vport = hdev->vport;
+
+ if (ring_cookie == RX_CLS_FLOW_DISC) {
+ *action = HCLGE_FD_ACTION_DROP_PACKET;
+ } else {
+ u32 ring = ethtool_get_flow_spec_ring(ring_cookie);
+ u8 vf = ethtool_get_flow_spec_ring_vf(ring_cookie);
+ u16 tqps;
+
+ if (vf > hdev->num_req_vfs) {
+ dev_err(&hdev->pdev->dev,
+ "Error: vf id (%u) > max vf num (%u)\n",
+ vf, hdev->num_req_vfs);
+ return -EINVAL;
+ }
+
+ *vport_id = vf ? hdev->vport[vf].vport_id : vport->vport_id;
+ tqps = hdev->vport[vf].nic.kinfo.num_tqps;
+
+ if (ring >= tqps) {
+ dev_err(&hdev->pdev->dev,
+ "Error: queue id (%u) > max tqp num (%u)\n",
+ ring, tqps - 1);
+ return -EINVAL;
+ }
+
+ *action = HCLGE_FD_ACTION_SELECT_QUEUE;
+ *queue_id = ring;
+ }
+
+ return 0;
+}
+
static int hclge_add_fd_entry(struct hnae3_handle *handle,
struct ethtool_rxnfc *cmd)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
+ struct hclge_fd_user_def_info info;
u16 dst_vport_id = 0, q_index = 0;
struct ethtool_rx_flow_spec *fs;
struct hclge_fd_rule *rule;
@@ -6150,51 +6551,22 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
return -EOPNOTSUPP;
}
- if (hclge_is_cls_flower_active(handle)) {
- dev_err(&hdev->pdev->dev,
- "please delete all exist cls flower rules first\n");
- return -EINVAL;
- }
-
fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
- ret = hclge_fd_check_spec(hdev, fs, &unused);
+ ret = hclge_fd_check_spec(hdev, fs, &unused, &info);
if (ret)
return ret;
- if (fs->ring_cookie == RX_CLS_FLOW_DISC) {
- action = HCLGE_FD_ACTION_DROP_PACKET;
- } else {
- u32 ring = ethtool_get_flow_spec_ring(fs->ring_cookie);
- u8 vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie);
- u16 tqps;
-
- if (vf > hdev->num_req_vfs) {
- dev_err(&hdev->pdev->dev,
- "Error: vf id (%u) > max vf num (%u)\n",
- vf, hdev->num_req_vfs);
- return -EINVAL;
- }
-
- dst_vport_id = vf ? hdev->vport[vf].vport_id : vport->vport_id;
- tqps = vf ? hdev->vport[vf].alloc_tqps : vport->alloc_tqps;
-
- if (ring >= tqps) {
- dev_err(&hdev->pdev->dev,
- "Error: queue id (%u) > max tqp num (%u)\n",
- ring, tqps - 1);
- return -EINVAL;
- }
-
- action = HCLGE_FD_ACTION_SELECT_QUEUE;
- q_index = ring;
- }
+ ret = hclge_fd_parse_ring_cookie(hdev, fs->ring_cookie, &dst_vport_id,
+ &action, &q_index);
+ if (ret)
+ return ret;
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
- ret = hclge_fd_get_tuple(hdev, fs, rule);
+ ret = hclge_fd_get_tuple(hdev, fs, rule, &info);
if (ret) {
kfree(rule);
return ret;
@@ -6208,15 +6580,9 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
rule->action = action;
rule->rule_type = HCLGE_FD_EP_ACTIVE;
- /* to avoid rule conflict, when user configure rule by ethtool,
- * we need to clear all arfs rules
- */
- spin_lock_bh(&hdev->fd_rule_lock);
- hclge_clear_arfs_rules(handle);
-
- ret = hclge_fd_config_rule(hdev, rule);
-
- spin_unlock_bh(&hdev->fd_rule_lock);
+ ret = hclge_add_fd_entry_common(hdev, rule);
+ if (ret)
+ kfree(rule);
return ret;
}
@@ -6237,32 +6603,30 @@ static int hclge_del_fd_entry(struct hnae3_handle *handle,
if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
return -EINVAL;
- if (hclge_is_cls_flower_active(handle) || !hdev->hclge_fd_rule_num ||
- !hclge_fd_rule_exist(hdev, fs->location)) {
+ spin_lock_bh(&hdev->fd_rule_lock);
+ if (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE ||
+ !test_bit(fs->location, hdev->fd_bmap)) {
dev_err(&hdev->pdev->dev,
"Delete fail, rule %u is inexistent\n", fs->location);
+ spin_unlock_bh(&hdev->fd_rule_lock);
return -ENOENT;
}
ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, fs->location,
NULL, false);
if (ret)
- return ret;
+ goto out;
- spin_lock_bh(&hdev->fd_rule_lock);
- ret = hclge_fd_update_rule_list(hdev, NULL, fs->location, false);
+ hclge_update_fd_list(hdev, HCLGE_FD_DELETED, fs->location, NULL);
+out:
spin_unlock_bh(&hdev->fd_rule_lock);
-
return ret;
}
-/* make sure being called after lock up with fd_rule_lock */
-static void hclge_del_all_fd_entries(struct hnae3_handle *handle,
- bool clear_list)
+static void hclge_clear_fd_rules_in_list(struct hclge_dev *hdev,
+ bool clear_list)
{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
struct hlist_node *node;
u16 location;
@@ -6270,6 +6634,8 @@ static void hclge_del_all_fd_entries(struct hnae3_handle *handle,
if (!hnae3_dev_fd_supported(hdev))
return;
+ spin_lock_bh(&hdev->fd_rule_lock);
+
for_each_set_bit(location, hdev->fd_bmap,
hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, location,
@@ -6286,6 +6652,14 @@ static void hclge_del_all_fd_entries(struct hnae3_handle *handle,
bitmap_zero(hdev->fd_bmap,
hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]);
}
+
+ spin_unlock_bh(&hdev->fd_rule_lock);
+}
+
+static void hclge_del_all_fd_entries(struct hclge_dev *hdev)
+{
+ hclge_clear_fd_rules_in_list(hdev, true);
+ hclge_fd_disable_user_def(hdev);
}
static int hclge_restore_fd_entries(struct hnae3_handle *handle)
@@ -6294,7 +6668,6 @@ static int hclge_restore_fd_entries(struct hnae3_handle *handle)
struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
struct hlist_node *node;
- int ret;
/* Return ok here, because reset error handling will check this
* return value. If error is returned here, the reset process will
@@ -6309,25 +6682,11 @@ static int hclge_restore_fd_entries(struct hnae3_handle *handle)
spin_lock_bh(&hdev->fd_rule_lock);
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
- ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
- if (!ret)
- ret = hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule);
-
- if (ret) {
- dev_warn(&hdev->pdev->dev,
- "Restore rule %u failed, remove it\n",
- rule->location);
- clear_bit(rule->location, hdev->fd_bmap);
- hlist_del(&rule->rule_node);
- kfree(rule);
- hdev->hclge_fd_rule_num--;
- }
+ if (rule->state == HCLGE_FD_ACTIVE)
+ rule->state = HCLGE_FD_TO_ADD;
}
-
- if (hdev->hclge_fd_rule_num)
- hdev->fd_active_type = HCLGE_FD_EP_ACTIVE;
-
spin_unlock_bh(&hdev->fd_rule_lock);
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
return 0;
}
@@ -6415,6 +6774,10 @@ static void hclge_fd_get_tcpip6_info(struct hclge_fd_rule *rule,
cpu_to_be32_array(spec_mask->ip6dst, rule->tuples_mask.dst_ip,
IPV6_SIZE);
+ spec->tclass = rule->tuples.ip_tos;
+ spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ?
+ 0 : rule->tuples_mask.ip_tos;
+
spec->psrc = cpu_to_be16(rule->tuples.src_port);
spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
0 : cpu_to_be16(rule->tuples_mask.src_port);
@@ -6442,6 +6805,10 @@ static void hclge_fd_get_ip6_info(struct hclge_fd_rule *rule,
cpu_to_be32_array(spec_mask->ip6dst,
rule->tuples_mask.dst_ip, IPV6_SIZE);
+ spec->tclass = rule->tuples.ip_tos;
+ spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ?
+ 0 : rule->tuples_mask.ip_tos;
+
spec->l4_proto = rule->tuples.ip_proto;
spec_mask->l4_proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
0 : rule->tuples_mask.ip_proto;
@@ -6469,6 +6836,24 @@ static void hclge_fd_get_ether_info(struct hclge_fd_rule *rule,
0 : cpu_to_be16(rule->tuples_mask.ether_proto);
}
+static void hclge_fd_get_user_def_info(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ if ((rule->unused_tuple & HCLGE_FD_TUPLE_USER_DEF_TUPLES) ==
+ HCLGE_FD_TUPLE_USER_DEF_TUPLES) {
+ fs->h_ext.data[0] = 0;
+ fs->h_ext.data[1] = 0;
+ fs->m_ext.data[0] = 0;
+ fs->m_ext.data[1] = 0;
+ } else {
+ fs->h_ext.data[0] = cpu_to_be32(rule->ep.user_def.offset);
+ fs->h_ext.data[1] = cpu_to_be32(rule->ep.user_def.data);
+ fs->m_ext.data[0] =
+ cpu_to_be32(HCLGE_FD_USER_DEF_OFFSET_UNMASK);
+ fs->m_ext.data[1] = cpu_to_be32(rule->ep.user_def.data_mask);
+ }
+}
+
static void hclge_fd_get_ext_info(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule)
{
@@ -6477,6 +6862,8 @@ static void hclge_fd_get_ext_info(struct ethtool_rx_flow_spec *fs,
fs->m_ext.vlan_tci =
rule->unused_tuple & BIT(INNER_VLAN_TAG_FST) ?
0 : cpu_to_be16(rule->tuples_mask.vlan_tag1);
+
+ hclge_fd_get_user_def_info(fs, rule);
}
if (fs->flow_type & FLOW_MAC_EXT) {
@@ -6588,6 +6975,9 @@ static int hclge_get_all_rules(struct hnae3_handle *handle,
return -EMSGSIZE;
}
+ if (rule->state == HCLGE_FD_TO_DEL)
+ continue;
+
rule_locs[cnt] = rule->location;
cnt++;
}
@@ -6669,9 +7059,7 @@ static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
struct hclge_fd_rule_tuples new_tuples = {};
struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
- u16 tmp_queue_id;
u16 bit_id;
- int ret;
if (!hnae3_dev_fd_supported(hdev))
return -EOPNOTSUPP;
@@ -6707,34 +7095,20 @@ static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
return -ENOMEM;
}
- set_bit(bit_id, hdev->fd_bmap);
rule->location = bit_id;
rule->arfs.flow_id = flow_id;
rule->queue_id = queue_id;
hclge_fd_build_arfs_rule(&new_tuples, rule);
- ret = hclge_fd_config_rule(hdev, rule);
-
- spin_unlock_bh(&hdev->fd_rule_lock);
-
- if (ret)
- return ret;
-
- return rule->location;
+ hclge_update_fd_list(hdev, HCLGE_FD_TO_ADD, rule->location,
+ rule);
+ hdev->fd_active_type = HCLGE_FD_ARFS_ACTIVE;
+ } else if (rule->queue_id != queue_id) {
+ rule->queue_id = queue_id;
+ rule->state = HCLGE_FD_TO_ADD;
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+ hclge_task_schedule(hdev, 0);
}
-
spin_unlock_bh(&hdev->fd_rule_lock);
-
- if (rule->queue_id == queue_id)
- return rule->location;
-
- tmp_queue_id = rule->queue_id;
- rule->queue_id = queue_id;
- ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
- if (ret) {
- rule->queue_id = tmp_queue_id;
- return ret;
- }
-
return rule->location;
}
@@ -6744,7 +7118,6 @@ static void hclge_rfs_filter_expire(struct hclge_dev *hdev)
struct hnae3_handle *handle = &hdev->vport[0].nic;
struct hclge_fd_rule *rule;
struct hlist_node *node;
- HLIST_HEAD(del_list);
spin_lock_bh(&hdev->fd_rule_lock);
if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) {
@@ -6752,33 +7125,50 @@ static void hclge_rfs_filter_expire(struct hclge_dev *hdev)
return;
}
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
+ if (rule->state != HCLGE_FD_ACTIVE)
+ continue;
if (rps_may_expire_flow(handle->netdev, rule->queue_id,
rule->arfs.flow_id, rule->location)) {
- hlist_del_init(&rule->rule_node);
- hlist_add_head(&rule->rule_node, &del_list);
- hdev->hclge_fd_rule_num--;
- clear_bit(rule->location, hdev->fd_bmap);
+ rule->state = HCLGE_FD_TO_DEL;
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
}
}
spin_unlock_bh(&hdev->fd_rule_lock);
-
- hlist_for_each_entry_safe(rule, node, &del_list, rule_node) {
- hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
- rule->location, NULL, false);
- kfree(rule);
- }
#endif
}
/* make sure being called after lock up with fd_rule_lock */
-static void hclge_clear_arfs_rules(struct hnae3_handle *handle)
+static int hclge_clear_arfs_rules(struct hclge_dev *hdev)
{
#ifdef CONFIG_RFS_ACCEL
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+ int ret;
+
+ if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE)
+ return 0;
+
+ hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
+ switch (rule->state) {
+ case HCLGE_FD_TO_DEL:
+ case HCLGE_FD_ACTIVE:
+ ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
+ rule->location, NULL, false);
+ if (ret)
+ return ret;
+ fallthrough;
+ case HCLGE_FD_TO_ADD:
+ hclge_fd_dec_rule_cnt(hdev, rule->location);
+ hlist_del(&rule->rule_node);
+ kfree(rule);
+ break;
+ default:
+ break;
+ }
+ }
+ hclge_sync_fd_state(hdev);
- if (hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE)
- hclge_del_all_fd_entries(handle, true);
+ return 0;
#endif
}
@@ -6961,12 +7351,6 @@ static int hclge_add_cls_flower(struct hnae3_handle *handle,
struct hclge_fd_rule *rule;
int ret;
- if (hdev->fd_active_type == HCLGE_FD_EP_ACTIVE) {
- dev_err(&hdev->pdev->dev,
- "please remove all exist fd rules via ethtool first\n");
- return -EINVAL;
- }
-
ret = hclge_check_cls_flower(hdev, cls_flower, tc);
if (ret) {
dev_err(&hdev->pdev->dev,
@@ -6979,8 +7363,10 @@ static int hclge_add_cls_flower(struct hnae3_handle *handle,
return -ENOMEM;
ret = hclge_parse_cls_flower(hdev, cls_flower, rule);
- if (ret)
- goto err;
+ if (ret) {
+ kfree(rule);
+ return ret;
+ }
rule->action = HCLGE_FD_ACTION_SELECT_TC;
rule->cls_flower.tc = tc;
@@ -6989,22 +7375,10 @@ static int hclge_add_cls_flower(struct hnae3_handle *handle,
rule->cls_flower.cookie = cls_flower->cookie;
rule->rule_type = HCLGE_FD_TC_FLOWER_ACTIVE;
- spin_lock_bh(&hdev->fd_rule_lock);
- hclge_clear_arfs_rules(handle);
-
- ret = hclge_fd_config_rule(hdev, rule);
-
- spin_unlock_bh(&hdev->fd_rule_lock);
-
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "failed to add cls flower rule, ret = %d\n", ret);
- goto err;
- }
+ ret = hclge_add_fd_entry_common(hdev, rule);
+ if (ret)
+ kfree(rule);
- return 0;
-err:
- kfree(rule);
return ret;
}
@@ -7041,25 +7415,66 @@ static int hclge_del_cls_flower(struct hnae3_handle *handle,
ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, rule->location,
NULL, false);
if (ret) {
- dev_err(&hdev->pdev->dev,
- "failed to delete cls flower rule %u, ret = %d\n",
- rule->location, ret);
spin_unlock_bh(&hdev->fd_rule_lock);
return ret;
}
- ret = hclge_fd_update_rule_list(hdev, NULL, rule->location, false);
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "failed to delete cls flower rule %u in list, ret = %d\n",
- rule->location, ret);
- spin_unlock_bh(&hdev->fd_rule_lock);
- return ret;
+ hclge_update_fd_list(hdev, HCLGE_FD_DELETED, rule->location, NULL);
+ spin_unlock_bh(&hdev->fd_rule_lock);
+
+ return 0;
+}
+
+static void hclge_sync_fd_list(struct hclge_dev *hdev, struct hlist_head *hlist)
+{
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+ int ret = 0;
+
+ if (!test_and_clear_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state))
+ return;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ hlist_for_each_entry_safe(rule, node, hlist, rule_node) {
+ switch (rule->state) {
+ case HCLGE_FD_TO_ADD:
+ ret = hclge_fd_config_rule(hdev, rule);
+ if (ret)
+ goto out;
+ rule->state = HCLGE_FD_ACTIVE;
+ break;
+ case HCLGE_FD_TO_DEL:
+ ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
+ rule->location, NULL, false);
+ if (ret)
+ goto out;
+ hclge_fd_dec_rule_cnt(hdev, rule->location);
+ hclge_fd_free_node(hdev, rule);
+ break;
+ default:
+ break;
+ }
}
+out:
+ if (ret)
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+
spin_unlock_bh(&hdev->fd_rule_lock);
+}
- return 0;
+static void hclge_sync_fd_table(struct hclge_dev *hdev)
+{
+ if (test_and_clear_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state)) {
+ bool clear_list = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE;
+
+ hclge_clear_fd_rules_in_list(hdev, clear_list);
+ }
+
+ hclge_sync_fd_user_def_cfg(hdev, false);
+
+ hclge_sync_fd_list(hdev, &hdev->fd_rule_list);
}
static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle)
@@ -7099,18 +7514,15 @@ static void hclge_enable_fd(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
- bool clear;
hdev->fd_en = enable;
- clear = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE;
- if (!enable) {
- spin_lock_bh(&hdev->fd_rule_lock);
- hclge_del_all_fd_entries(handle, clear);
- spin_unlock_bh(&hdev->fd_rule_lock);
- } else {
+ if (!enable)
+ set_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state);
+ else
hclge_restore_fd_entries(handle);
- }
+
+ hclge_task_schedule(hdev, 0);
}
static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable)
@@ -7581,7 +7993,7 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
set_bit(HCLGE_STATE_DOWN, &hdev->state);
spin_lock_bh(&hdev->fd_rule_lock);
- hclge_clear_arfs_rules(handle);
+ hclge_clear_arfs_rules(hdev);
spin_unlock_bh(&hdev->fd_rule_lock);
/* If it is not PF reset, the firmware will disable the MAC,
@@ -9617,7 +10029,7 @@ static void hclge_restore_hw_table(struct hclge_dev *hdev)
hclge_restore_mac_table_common(vport);
hclge_restore_vport_vlan_table(vport);
set_bit(HCLGE_STATE_PROMISC_CHANGED, &hdev->state);
-
+ set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
hclge_restore_fd_entries(handle);
}
@@ -11306,6 +11718,7 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
hclge_misc_affinity_teardown(hdev);
hclge_state_uninit(hdev);
hclge_uninit_mac_table(hdev);
+ hclge_del_all_fd_entries(hdev);
if (mac->phydev)
mdiobus_unregister(mac->mdio_bus);
@@ -12129,7 +12542,6 @@ static const struct hnae3_ae_ops hclge_ops = {
.get_link_mode = hclge_get_link_mode,
.add_fd_entry = hclge_add_fd_entry,
.del_fd_entry = hclge_del_fd_entry,
- .del_all_fd_entries = hclge_del_all_fd_entries,
.get_fd_rule_cnt = hclge_get_fd_rule_cnt,
.get_fd_rule_info = hclge_get_fd_rule_info,
.get_fd_all_rules = hclge_get_all_rules,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index 19d7f28773f3..97e77e2f7539 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -223,6 +223,9 @@ enum HCLGE_DEV_STATE {
HCLGE_STATE_LINK_UPDATING,
HCLGE_STATE_PROMISC_CHANGED,
HCLGE_STATE_RST_FAIL,
+ HCLGE_STATE_FD_TBL_CHANGED,
+ HCLGE_STATE_FD_CLEAR_ALL,
+ HCLGE_STATE_FD_USER_DEF_CHANGED,
HCLGE_STATE_MAX
};
@@ -536,6 +539,9 @@ enum HCLGE_FD_TUPLE {
MAX_TUPLE,
};
+#define HCLGE_FD_TUPLE_USER_DEF_TUPLES \
+ (BIT(INNER_L2_RSV) | BIT(INNER_L3_RSV) | BIT(INNER_L4_RSV))
+
enum HCLGE_FD_META_DATA {
PACKET_TYPE_ID,
IP_FRAGEMENT,
@@ -548,9 +554,21 @@ enum HCLGE_FD_META_DATA {
MAX_META_DATA,
};
+enum HCLGE_FD_KEY_OPT {
+ KEY_OPT_U8,
+ KEY_OPT_LE16,
+ KEY_OPT_LE32,
+ KEY_OPT_MAC,
+ KEY_OPT_IP,
+ KEY_OPT_VNI,
+};
+
struct key_info {
u8 key_type;
u8 key_length; /* use bit as unit */
+ enum HCLGE_FD_KEY_OPT key_opt;
+ int offset;
+ int moffset;
};
#define MAX_KEY_LENGTH 400
@@ -558,6 +576,11 @@ struct key_info {
#define MAX_KEY_BYTES (MAX_KEY_DWORDS * 4)
#define MAX_META_DATA_LENGTH 32
+#define HCLGE_FD_MAX_USER_DEF_OFFSET 9000
+#define HCLGE_FD_USER_DEF_DATA GENMASK(15, 0)
+#define HCLGE_FD_USER_DEF_OFFSET GENMASK(15, 0)
+#define HCLGE_FD_USER_DEF_OFFSET_UNMASK GENMASK(15, 0)
+
/* assigned by firmware, the real filter number for each pf may be less */
#define MAX_FD_FILTER_NUM 4096
#define HCLGE_ARFS_EXPIRE_INTERVAL 5UL
@@ -580,6 +603,33 @@ enum HCLGE_FD_ACTION {
HCLGE_FD_ACTION_SELECT_TC,
};
+enum HCLGE_FD_NODE_STATE {
+ HCLGE_FD_TO_ADD,
+ HCLGE_FD_TO_DEL,
+ HCLGE_FD_ACTIVE,
+ HCLGE_FD_DELETED,
+};
+
+enum HCLGE_FD_USER_DEF_LAYER {
+ HCLGE_FD_USER_DEF_NONE,
+ HCLGE_FD_USER_DEF_L2,
+ HCLGE_FD_USER_DEF_L3,
+ HCLGE_FD_USER_DEF_L4,
+};
+
+#define HCLGE_FD_USER_DEF_LAYER_NUM 3
+struct hclge_fd_user_def_cfg {
+ u16 ref_cnt;
+ u16 offset;
+};
+
+struct hclge_fd_user_def_info {
+ enum HCLGE_FD_USER_DEF_LAYER layer;
+ u16 data;
+ u16 data_mask;
+ u16 offset;
+};
+
struct hclge_fd_key_cfg {
u8 key_sel;
u8 inner_sipv6_word_en;
@@ -596,6 +646,7 @@ struct hclge_fd_cfg {
u32 rule_num[MAX_STAGE_NUM]; /* rule entry number */
u16 cnt_num[MAX_STAGE_NUM]; /* rule hit counter number */
struct hclge_fd_key_cfg key_cfg[MAX_STAGE_NUM];
+ struct hclge_fd_user_def_cfg user_def_cfg[HCLGE_FD_USER_DEF_LAYER_NUM];
};
#define IPV4_INDEX 3
@@ -612,6 +663,9 @@ struct hclge_fd_rule_tuples {
u16 dst_port;
u16 vlan_tag1;
u16 ether_proto;
+ u16 l2_user_def;
+ u16 l3_user_def;
+ u32 l4_user_def;
u8 ip_tos;
u8 ip_proto;
};
@@ -630,11 +684,15 @@ struct hclge_fd_rule {
struct {
u16 flow_id; /* only used for arfs */
} arfs;
+ struct {
+ struct hclge_fd_user_def_info user_def;
+ } ep;
};
u16 queue_id;
u16 vf_id;
u16 location;
enum HCLGE_FD_ACTIVE_RULE_TYPE rule_type;
+ enum HCLGE_FD_NODE_STATE state;
u8 action;
};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index df4ec063adcb..efc7acb4842c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -2934,7 +2934,6 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->acl_tcam_ops = &mlxsw_sp1_acl_tcam_ops;
mlxsw_sp->nve_ops_arr = mlxsw_sp1_nve_ops_arr;
mlxsw_sp->mac_mask = mlxsw_sp1_mac_mask;
- mlxsw_sp->rif_ops_arr = mlxsw_sp1_rif_ops_arr;
mlxsw_sp->sb_vals = &mlxsw_sp1_sb_vals;
mlxsw_sp->sb_ops = &mlxsw_sp1_sb_ops;
mlxsw_sp->port_type_speed_ops = &mlxsw_sp1_port_type_speed_ops;
@@ -2943,6 +2942,7 @@ static int mlxsw_sp1_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->policer_core_ops = &mlxsw_sp1_policer_core_ops;
mlxsw_sp->trap_ops = &mlxsw_sp1_trap_ops;
mlxsw_sp->mall_ops = &mlxsw_sp1_mall_ops;
+ mlxsw_sp->router_ops = &mlxsw_sp1_router_ops;
mlxsw_sp->listeners = mlxsw_sp1_listener;
mlxsw_sp->listeners_count = ARRAY_SIZE(mlxsw_sp1_listener);
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP1;
@@ -2965,7 +2965,6 @@ static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
- mlxsw_sp->rif_ops_arr = mlxsw_sp2_rif_ops_arr;
mlxsw_sp->sb_vals = &mlxsw_sp2_sb_vals;
mlxsw_sp->sb_ops = &mlxsw_sp2_sb_ops;
mlxsw_sp->port_type_speed_ops = &mlxsw_sp2_port_type_speed_ops;
@@ -2974,6 +2973,7 @@ static int mlxsw_sp2_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
mlxsw_sp->mall_ops = &mlxsw_sp2_mall_ops;
+ mlxsw_sp->router_ops = &mlxsw_sp2_router_ops;
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP2;
return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
@@ -2994,7 +2994,6 @@ static int mlxsw_sp3_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->acl_tcam_ops = &mlxsw_sp2_acl_tcam_ops;
mlxsw_sp->nve_ops_arr = mlxsw_sp2_nve_ops_arr;
mlxsw_sp->mac_mask = mlxsw_sp2_mac_mask;
- mlxsw_sp->rif_ops_arr = mlxsw_sp2_rif_ops_arr;
mlxsw_sp->sb_vals = &mlxsw_sp2_sb_vals;
mlxsw_sp->sb_ops = &mlxsw_sp3_sb_ops;
mlxsw_sp->port_type_speed_ops = &mlxsw_sp2_port_type_speed_ops;
@@ -3003,6 +3002,7 @@ static int mlxsw_sp3_init(struct mlxsw_core *mlxsw_core,
mlxsw_sp->policer_core_ops = &mlxsw_sp2_policer_core_ops;
mlxsw_sp->trap_ops = &mlxsw_sp2_trap_ops;
mlxsw_sp->mall_ops = &mlxsw_sp2_mall_ops;
+ mlxsw_sp->router_ops = &mlxsw_sp2_router_ops;
mlxsw_sp->lowest_shaper_bs = MLXSW_REG_QEEC_LOWEST_SHAPER_BS_SP3;
return mlxsw_sp_init(mlxsw_core, mlxsw_bus_info, extack);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 03655e418edb..97d074d7b78d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -87,10 +87,10 @@ enum mlxsw_sp_rif_type {
MLXSW_SP_RIF_TYPE_MAX,
};
-struct mlxsw_sp_rif_ops;
+struct mlxsw_sp_router_ops;
-extern const struct mlxsw_sp_rif_ops *mlxsw_sp1_rif_ops_arr[];
-extern const struct mlxsw_sp_rif_ops *mlxsw_sp2_rif_ops_arr[];
+extern const struct mlxsw_sp_router_ops mlxsw_sp1_router_ops;
+extern const struct mlxsw_sp_router_ops mlxsw_sp2_router_ops;
struct mlxsw_sp_switchdev_ops;
@@ -180,7 +180,6 @@ struct mlxsw_sp {
const struct mlxsw_sp_acl_rulei_ops *acl_rulei_ops;
const struct mlxsw_sp_acl_tcam_ops *acl_tcam_ops;
const struct mlxsw_sp_nve_ops **nve_ops_arr;
- const struct mlxsw_sp_rif_ops **rif_ops_arr;
const struct mlxsw_sp_sb_vals *sb_vals;
const struct mlxsw_sp_sb_ops *sb_ops;
const struct mlxsw_sp_port_type_speed_ops *port_type_speed_ops;
@@ -189,6 +188,7 @@ struct mlxsw_sp {
const struct mlxsw_sp_policer_core_ops *policer_core_ops;
const struct mlxsw_sp_trap_ops *trap_ops;
const struct mlxsw_sp_mall_ops *mall_ops;
+ const struct mlxsw_sp_router_ops *router_ops;
const struct mlxsw_listener *listeners;
size_t listeners_count;
u32 lowest_shaper_bs;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
index ed81d4fa48ac..af2093fc5025 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
@@ -912,9 +912,8 @@ static u64 mlxsw_sp_dpipe_table_adj_size(struct mlxsw_sp *mlxsw_sp)
u64 size = 0;
mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router)
- if (mlxsw_sp_nexthop_offload(nh) &&
- !mlxsw_sp_nexthop_group_has_ipip(nh) &&
- !mlxsw_sp_nexthop_is_discard(nh))
+ if (mlxsw_sp_nexthop_is_forward(nh) &&
+ !mlxsw_sp_nexthop_group_has_ipip(nh))
size++;
return size;
}
@@ -1105,9 +1104,8 @@ start_again:
nh_skip = nh_count;
nh_count = 0;
mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router) {
- if (!mlxsw_sp_nexthop_offload(nh) ||
- mlxsw_sp_nexthop_group_has_ipip(nh) ||
- mlxsw_sp_nexthop_is_discard(nh))
+ if (!mlxsw_sp_nexthop_is_forward(nh) ||
+ mlxsw_sp_nexthop_group_has_ipip(nh))
continue;
if (nh_count < nh_skip)
@@ -1187,9 +1185,8 @@ static int mlxsw_sp_dpipe_table_adj_counters_update(void *priv, bool enable)
u32 adj_size = 0;
mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router) {
- if (!mlxsw_sp_nexthop_offload(nh) ||
- mlxsw_sp_nexthop_group_has_ipip(nh) ||
- mlxsw_sp_nexthop_is_discard(nh))
+ if (!mlxsw_sp_nexthop_is_forward(nh) ||
+ mlxsw_sp_nexthop_group_has_ipip(nh))
continue;
mlxsw_sp_nexthop_indexes(nh, &adj_index, &adj_size,
@@ -1198,8 +1195,8 @@ static int mlxsw_sp_dpipe_table_adj_counters_update(void *priv, bool enable)
mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
else
mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
- mlxsw_sp_nexthop_update(mlxsw_sp,
- adj_index + adj_hash_index, nh);
+ mlxsw_sp_nexthop_eth_update(mlxsw_sp,
+ adj_index + adj_hash_index, nh);
}
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index eda99d82766a..75c9fc47cd69 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -113,6 +113,10 @@ struct mlxsw_sp_rif_ops {
void (*fdb_del)(struct mlxsw_sp_rif *rif, const char *mac);
};
+struct mlxsw_sp_router_ops {
+ int (*init)(struct mlxsw_sp *mlxsw_sp);
+};
+
static struct mlxsw_sp_rif *
mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
const struct net_device *dev);
@@ -2662,6 +2666,10 @@ static void mlxsw_sp_router_neigh_event_work(struct work_struct *work)
goto out;
}
+ if (neigh_entry->connected && entry_connected &&
+ !memcmp(neigh_entry->ha, ha, ETH_ALEN))
+ goto out;
+
memcpy(neigh_entry->ha, ha, ETH_ALEN);
mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, entry_connected);
mlxsw_sp_nexthop_neigh_update(mlxsw_sp, neigh_entry, !entry_connected,
@@ -2842,6 +2850,15 @@ enum mlxsw_sp_nexthop_type {
MLXSW_SP_NEXTHOP_TYPE_IPIP,
};
+enum mlxsw_sp_nexthop_action {
+ /* Nexthop forwards packets to an egress RIF */
+ MLXSW_SP_NEXTHOP_ACTION_FORWARD,
+ /* Nexthop discards packets */
+ MLXSW_SP_NEXTHOP_ACTION_DISCARD,
+ /* Nexthop traps packets */
+ MLXSW_SP_NEXTHOP_ACTION_TRAP,
+};
+
struct mlxsw_sp_nexthop_key {
struct fib_nh *fib_nh;
};
@@ -2862,16 +2879,16 @@ struct mlxsw_sp_nexthop {
int norm_nh_weight;
int num_adj_entries;
struct mlxsw_sp_rif *rif;
- u8 should_offload:1, /* set indicates this neigh is connected and
- * should be put to KVD linear area of this group.
+ u8 should_offload:1, /* set indicates this nexthop should be written
+ * to the adjacency table.
*/
- offloaded:1, /* set in case the neigh is actually put into
- * KVD linear area of this group.
+ offloaded:1, /* set indicates this nexthop was written to the
+ * adjacency table.
*/
- update:1, /* set indicates that MAC of this neigh should be
- * updated in HW
+ update:1; /* set indicates this nexthop should be updated in the
+ * adjacency table (f.e., its MAC changed).
*/
- discard:1; /* nexthop is programmed to discard packets */
+ enum mlxsw_sp_nexthop_action action;
enum mlxsw_sp_nexthop_type type;
union {
struct mlxsw_sp_neigh_entry *neigh_entry;
@@ -2979,14 +2996,15 @@ struct mlxsw_sp_nexthop *mlxsw_sp_nexthop_next(struct mlxsw_sp_router *router,
return list_next_entry(nh, router_list_node);
}
-bool mlxsw_sp_nexthop_offload(struct mlxsw_sp_nexthop *nh)
+bool mlxsw_sp_nexthop_is_forward(const struct mlxsw_sp_nexthop *nh)
{
- return nh->offloaded;
+ return nh->offloaded && nh->action == MLXSW_SP_NEXTHOP_ACTION_FORWARD;
}
unsigned char *mlxsw_sp_nexthop_ha(struct mlxsw_sp_nexthop *nh)
{
- if (!nh->offloaded)
+ if (nh->type != MLXSW_SP_NEXTHOP_TYPE_ETH ||
+ !mlxsw_sp_nexthop_is_forward(nh))
return NULL;
return nh->neigh_entry->ha;
}
@@ -3036,11 +3054,6 @@ bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh)
return false;
}
-bool mlxsw_sp_nexthop_is_discard(const struct mlxsw_sp_nexthop *nh)
-{
- return nh->discard;
-}
-
static const struct rhashtable_params mlxsw_sp_nexthop_group_vr_ht_params = {
.key_offset = offsetof(struct mlxsw_sp_nexthop_group_vr_entry, key),
.head_offset = offsetof(struct mlxsw_sp_nexthop_group_vr_entry, ht_node),
@@ -3403,20 +3416,36 @@ err_mass_update_vr:
return err;
}
-static int __mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
- struct mlxsw_sp_nexthop *nh)
+static int __mlxsw_sp_nexthop_eth_update(struct mlxsw_sp *mlxsw_sp,
+ u32 adj_index,
+ struct mlxsw_sp_nexthop *nh)
{
struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
char ratr_pl[MLXSW_REG_RATR_LEN];
+ u16 rif_index;
+ rif_index = nh->rif ? nh->rif->rif_index :
+ mlxsw_sp->router->lb_rif_index;
mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
true, MLXSW_REG_RATR_TYPE_ETHERNET,
- adj_index, nh->rif->rif_index);
- if (nh->discard)
+ adj_index, rif_index);
+ switch (nh->action) {
+ case MLXSW_SP_NEXTHOP_ACTION_FORWARD:
+ mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
+ break;
+ case MLXSW_SP_NEXTHOP_ACTION_DISCARD:
mlxsw_reg_ratr_trap_action_set(ratr_pl,
MLXSW_REG_RATR_TRAP_ACTION_DISCARD_ERRORS);
- else
- mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha);
+ break;
+ case MLXSW_SP_NEXTHOP_ACTION_TRAP:
+ mlxsw_reg_ratr_trap_action_set(ratr_pl,
+ MLXSW_REG_RATR_TRAP_ACTION_TRAP);
+ mlxsw_reg_ratr_trap_id_set(ratr_pl, MLXSW_TRAP_ID_RTR_EGRESS0);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
if (nh->counter_valid)
mlxsw_reg_ratr_counter_pack(ratr_pl, nh->counter_index, true);
else
@@ -3425,15 +3454,16 @@ static int __mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
}
-int mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
- struct mlxsw_sp_nexthop *nh)
+int mlxsw_sp_nexthop_eth_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
+ struct mlxsw_sp_nexthop *nh)
{
int i;
for (i = 0; i < nh->num_adj_entries; i++) {
int err;
- err = __mlxsw_sp_nexthop_update(mlxsw_sp, adj_index + i, nh);
+ err = __mlxsw_sp_nexthop_eth_update(mlxsw_sp, adj_index + i,
+ nh);
if (err)
return err;
}
@@ -3469,6 +3499,20 @@ static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp,
return 0;
}
+static int mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
+ struct mlxsw_sp_nexthop *nh)
+{
+ /* When action is discard or trap, the nexthop must be
+ * programmed as an Ethernet nexthop.
+ */
+ if (nh->type == MLXSW_SP_NEXTHOP_TYPE_ETH ||
+ nh->action == MLXSW_SP_NEXTHOP_ACTION_DISCARD ||
+ nh->action == MLXSW_SP_NEXTHOP_ACTION_TRAP)
+ return mlxsw_sp_nexthop_eth_update(mlxsw_sp, adj_index, nh);
+ else
+ return mlxsw_sp_nexthop_ipip_update(mlxsw_sp, adj_index, nh);
+}
+
static int
mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop_group_info *nhgi,
@@ -3489,16 +3533,7 @@ mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp,
if (nh->update || reallocate) {
int err = 0;
- switch (nh->type) {
- case MLXSW_SP_NEXTHOP_TYPE_ETH:
- err = mlxsw_sp_nexthop_update
- (mlxsw_sp, adj_index, nh);
- break;
- case MLXSW_SP_NEXTHOP_TYPE_IPIP:
- err = mlxsw_sp_nexthop_ipip_update
- (mlxsw_sp, adj_index, nh);
- break;
- }
+ err = mlxsw_sp_nexthop_update(mlxsw_sp, adj_index, nh);
if (err)
return err;
nh->update = 0;
@@ -3524,34 +3559,69 @@ mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp,
return 0;
}
-static void mlxsw_sp_adj_grp_size_round_up(u16 *p_adj_grp_size)
+struct mlxsw_sp_adj_grp_size_range {
+ u16 start; /* Inclusive */
+ u16 end; /* Inclusive */
+};
+
+/* Ordered by range start value */
+static const struct mlxsw_sp_adj_grp_size_range
+mlxsw_sp1_adj_grp_size_ranges[] = {
+ { .start = 1, .end = 64 },
+ { .start = 512, .end = 512 },
+ { .start = 1024, .end = 1024 },
+ { .start = 2048, .end = 2048 },
+ { .start = 4096, .end = 4096 },
+};
+
+/* Ordered by range start value */
+static const struct mlxsw_sp_adj_grp_size_range
+mlxsw_sp2_adj_grp_size_ranges[] = {
+ { .start = 1, .end = 128 },
+ { .start = 256, .end = 256 },
+ { .start = 512, .end = 512 },
+ { .start = 1024, .end = 1024 },
+ { .start = 2048, .end = 2048 },
+ { .start = 4096, .end = 4096 },
+};
+
+static void mlxsw_sp_adj_grp_size_round_up(const struct mlxsw_sp *mlxsw_sp,
+ u16 *p_adj_grp_size)
{
- /* Valid sizes for an adjacency group are:
- * 1-64, 512, 1024, 2048 and 4096.
- */
- if (*p_adj_grp_size <= 64)
- return;
- else if (*p_adj_grp_size <= 512)
- *p_adj_grp_size = 512;
- else if (*p_adj_grp_size <= 1024)
- *p_adj_grp_size = 1024;
- else if (*p_adj_grp_size <= 2048)
- *p_adj_grp_size = 2048;
- else
- *p_adj_grp_size = 4096;
+ int i;
+
+ for (i = 0; i < mlxsw_sp->router->adj_grp_size_ranges_count; i++) {
+ const struct mlxsw_sp_adj_grp_size_range *size_range;
+
+ size_range = &mlxsw_sp->router->adj_grp_size_ranges[i];
+
+ if (*p_adj_grp_size >= size_range->start &&
+ *p_adj_grp_size <= size_range->end)
+ return;
+
+ if (*p_adj_grp_size <= size_range->end) {
+ *p_adj_grp_size = size_range->end;
+ return;
+ }
+ }
}
-static void mlxsw_sp_adj_grp_size_round_down(u16 *p_adj_grp_size,
+static void mlxsw_sp_adj_grp_size_round_down(const struct mlxsw_sp *mlxsw_sp,
+ u16 *p_adj_grp_size,
unsigned int alloc_size)
{
- if (alloc_size >= 4096)
- *p_adj_grp_size = 4096;
- else if (alloc_size >= 2048)
- *p_adj_grp_size = 2048;
- else if (alloc_size >= 1024)
- *p_adj_grp_size = 1024;
- else if (alloc_size >= 512)
- *p_adj_grp_size = 512;
+ int i;
+
+ for (i = mlxsw_sp->router->adj_grp_size_ranges_count - 1; i >= 0; i--) {
+ const struct mlxsw_sp_adj_grp_size_range *size_range;
+
+ size_range = &mlxsw_sp->router->adj_grp_size_ranges[i];
+
+ if (alloc_size >= size_range->end) {
+ *p_adj_grp_size = size_range->end;
+ return;
+ }
+ }
}
static int mlxsw_sp_fix_adj_grp_size(struct mlxsw_sp *mlxsw_sp,
@@ -3563,7 +3633,7 @@ static int mlxsw_sp_fix_adj_grp_size(struct mlxsw_sp *mlxsw_sp,
/* Round up the requested group size to the next size supported
* by the device and make sure the request can be satisfied.
*/
- mlxsw_sp_adj_grp_size_round_up(p_adj_grp_size);
+ mlxsw_sp_adj_grp_size_round_up(mlxsw_sp, p_adj_grp_size);
err = mlxsw_sp_kvdl_alloc_count_query(mlxsw_sp,
MLXSW_SP_KVDL_ENTRY_TYPE_ADJ,
*p_adj_grp_size, &alloc_size);
@@ -3573,7 +3643,7 @@ static int mlxsw_sp_fix_adj_grp_size(struct mlxsw_sp *mlxsw_sp,
* entries than requested. Try to use as much of them as
* possible.
*/
- mlxsw_sp_adj_grp_size_round_down(p_adj_grp_size, alloc_size);
+ mlxsw_sp_adj_grp_size_round_down(mlxsw_sp, p_adj_grp_size, alloc_size);
return 0;
}
@@ -3832,10 +3902,12 @@ set_trap:
static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
bool removing)
{
- if (!removing)
+ if (!removing) {
+ nh->action = MLXSW_SP_NEXTHOP_ACTION_FORWARD;
nh->should_offload = 1;
- else
+ } else {
nh->should_offload = 0;
+ }
nh->update = 1;
}
@@ -4268,6 +4340,29 @@ mlxsw_sp_nexthop_obj_single_validate(struct mlxsw_sp *mlxsw_sp,
}
static int
+mlxsw_sp_nexthop_obj_group_entry_validate(struct mlxsw_sp *mlxsw_sp,
+ const struct nh_notifier_single_info *nh,
+ struct netlink_ext_ack *extack)
+{
+ int err;
+
+ err = mlxsw_sp_nexthop_obj_single_validate(mlxsw_sp, nh, extack);
+ if (err)
+ return err;
+
+ /* Device only nexthops with an IPIP device are programmed as
+ * encapsulating adjacency entries.
+ */
+ if (!nh->gw_family && !nh->is_reject &&
+ !mlxsw_sp_netdev_ipip_type(mlxsw_sp, nh->dev, NULL)) {
+ NL_SET_ERR_MSG_MOD(extack, "Nexthop group entry does not have a gateway");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
mlxsw_sp_nexthop_obj_group_validate(struct mlxsw_sp *mlxsw_sp,
const struct nh_notifier_grp_info *nh_grp,
struct netlink_ext_ack *extack)
@@ -4284,19 +4379,10 @@ mlxsw_sp_nexthop_obj_group_validate(struct mlxsw_sp *mlxsw_sp,
int err;
nh = &nh_grp->nh_entries[i].nh;
- err = mlxsw_sp_nexthop_obj_single_validate(mlxsw_sp, nh,
- extack);
+ err = mlxsw_sp_nexthop_obj_group_entry_validate(mlxsw_sp, nh,
+ extack);
if (err)
return err;
-
- /* Device only nexthops with an IPIP device are programmed as
- * encapsulating adjacency entries.
- */
- if (!nh->gw_family && !nh->is_reject &&
- !mlxsw_sp_netdev_ipip_type(mlxsw_sp, nh->dev, NULL)) {
- NL_SET_ERR_MSG_MOD(extack, "Nexthop group entry does not have a gateway");
- return -EINVAL;
- }
}
return 0;
@@ -4346,7 +4432,7 @@ static void mlxsw_sp_nexthop_obj_blackhole_init(struct mlxsw_sp *mlxsw_sp,
{
u16 lb_rif_index = mlxsw_sp->router->lb_rif_index;
- nh->discard = 1;
+ nh->action = MLXSW_SP_NEXTHOP_ACTION_DISCARD;
nh->should_offload = 1;
/* While nexthops that discard packets do not forward packets
* via an egress RIF, they still need to be programmed using a
@@ -4409,7 +4495,7 @@ err_type_init:
static void mlxsw_sp_nexthop_obj_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop *nh)
{
- if (nh->discard)
+ if (nh->action == MLXSW_SP_NEXTHOP_ACTION_DISCARD)
mlxsw_sp_nexthop_obj_blackhole_fini(mlxsw_sp, nh);
mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
list_del(&nh->router_list_node);
@@ -4699,8 +4785,6 @@ static int mlxsw_sp_nexthop_obj_event(struct notifier_block *nb,
mutex_lock(&router->lock);
- ASSERT_RTNL();
-
switch (event) {
case NEXTHOP_EVENT_REPLACE:
err = mlxsw_sp_nexthop_obj_new(router->mlxsw_sp, info);
@@ -7667,7 +7751,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
int i, err;
type = mlxsw_sp_dev_rif_type(mlxsw_sp, params->dev);
- ops = mlxsw_sp->rif_ops_arr[type];
+ ops = mlxsw_sp->router->rif_ops_arr[type];
vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN, extack);
if (IS_ERR(vr))
@@ -8865,7 +8949,7 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp1_rif_ipip_lb_ops = {
.deconfigure = mlxsw_sp1_rif_ipip_lb_deconfigure,
};
-const struct mlxsw_sp_rif_ops *mlxsw_sp1_rif_ops_arr[] = {
+static const struct mlxsw_sp_rif_ops *mlxsw_sp1_rif_ops_arr[] = {
[MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
[MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_emu_ops,
[MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
@@ -9050,7 +9134,7 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp2_rif_ipip_lb_ops = {
.deconfigure = mlxsw_sp2_rif_ipip_lb_deconfigure,
};
-const struct mlxsw_sp_rif_ops *mlxsw_sp2_rif_ops_arr[] = {
+static const struct mlxsw_sp_rif_ops *mlxsw_sp2_rif_ops_arr[] = {
[MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops,
[MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_emu_ops,
[MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops,
@@ -9302,6 +9386,36 @@ static void mlxsw_sp_lb_rif_fini(struct mlxsw_sp *mlxsw_sp)
mlxsw_sp_router_ul_rif_put(mlxsw_sp, mlxsw_sp->router->lb_rif_index);
}
+static int mlxsw_sp1_router_init(struct mlxsw_sp *mlxsw_sp)
+{
+ size_t size_ranges_count = ARRAY_SIZE(mlxsw_sp1_adj_grp_size_ranges);
+
+ mlxsw_sp->router->rif_ops_arr = mlxsw_sp1_rif_ops_arr;
+ mlxsw_sp->router->adj_grp_size_ranges = mlxsw_sp1_adj_grp_size_ranges;
+ mlxsw_sp->router->adj_grp_size_ranges_count = size_ranges_count;
+
+ return 0;
+}
+
+const struct mlxsw_sp_router_ops mlxsw_sp1_router_ops = {
+ .init = mlxsw_sp1_router_init,
+};
+
+static int mlxsw_sp2_router_init(struct mlxsw_sp *mlxsw_sp)
+{
+ size_t size_ranges_count = ARRAY_SIZE(mlxsw_sp2_adj_grp_size_ranges);
+
+ mlxsw_sp->router->rif_ops_arr = mlxsw_sp2_rif_ops_arr;
+ mlxsw_sp->router->adj_grp_size_ranges = mlxsw_sp2_adj_grp_size_ranges;
+ mlxsw_sp->router->adj_grp_size_ranges_count = size_ranges_count;
+
+ return 0;
+}
+
+const struct mlxsw_sp_router_ops mlxsw_sp2_router_ops = {
+ .init = mlxsw_sp2_router_init,
+};
+
int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
struct netlink_ext_ack *extack)
{
@@ -9315,6 +9429,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp->router = router;
router->mlxsw_sp = mlxsw_sp;
+ err = mlxsw_sp->router_ops->init(mlxsw_sp);
+ if (err)
+ goto err_router_ops_init;
+
err = mlxsw_sp_router_xm_init(mlxsw_sp);
if (err)
goto err_xm_init;
@@ -9455,6 +9573,7 @@ err_router_init:
err_ll_op_ctx_init:
mlxsw_sp_router_xm_fini(mlxsw_sp);
err_xm_init:
+err_router_ops_init:
mutex_destroy(&mlxsw_sp->router->lock);
kfree(mlxsw_sp->router);
return err;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
index 2875ee8ec537..01fd9a3d5944 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h
@@ -78,6 +78,8 @@ struct mlxsw_sp_router {
struct mlxsw_sp_fib_entry_op_ctx *ll_op_ctx;
u16 lb_rif_index;
struct mlxsw_sp_router_xm *xm;
+ const struct mlxsw_sp_adj_grp_size_range *adj_grp_size_ranges;
+ size_t adj_grp_size_ranges_count;
};
struct mlxsw_sp_fib_entry_priv {
@@ -195,20 +197,19 @@ mlxsw_sp_ipip_demote_tunnel_by_saddr(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_ipip_entry *except);
struct mlxsw_sp_nexthop *mlxsw_sp_nexthop_next(struct mlxsw_sp_router *router,
struct mlxsw_sp_nexthop *nh);
-bool mlxsw_sp_nexthop_offload(struct mlxsw_sp_nexthop *nh);
+bool mlxsw_sp_nexthop_is_forward(const struct mlxsw_sp_nexthop *nh);
unsigned char *mlxsw_sp_nexthop_ha(struct mlxsw_sp_nexthop *nh);
int mlxsw_sp_nexthop_indexes(struct mlxsw_sp_nexthop *nh, u32 *p_adj_index,
u32 *p_adj_size, u32 *p_adj_hash_index);
struct mlxsw_sp_rif *mlxsw_sp_nexthop_rif(struct mlxsw_sp_nexthop *nh);
bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh);
-bool mlxsw_sp_nexthop_is_discard(const struct mlxsw_sp_nexthop *nh);
#define mlxsw_sp_nexthop_for_each(nh, router) \
for (nh = mlxsw_sp_nexthop_next(router, NULL); nh; \
nh = mlxsw_sp_nexthop_next(router, nh))
int mlxsw_sp_nexthop_counter_get(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop *nh, u64 *p_counter);
-int mlxsw_sp_nexthop_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
- struct mlxsw_sp_nexthop *nh);
+int mlxsw_sp_nexthop_eth_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
+ struct mlxsw_sp_nexthop *nh);
void mlxsw_sp_nexthop_counter_alloc(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nexthop *nh);
void mlxsw_sp_nexthop_counter_free(struct mlxsw_sp *mlxsw_sp,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index a70b0bc84b2a..5a1e018884e6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -704,7 +704,6 @@ int stmmac_pltfr_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_remove);
-#ifdef CONFIG_PM_SLEEP
/**
* stmmac_pltfr_suspend
* @dev: device pointer
@@ -712,7 +711,7 @@ EXPORT_SYMBOL_GPL(stmmac_pltfr_remove);
* call the main suspend function and then, if required, on some platform, it
* can call an exit helper.
*/
-static int stmmac_pltfr_suspend(struct device *dev)
+static int __maybe_unused stmmac_pltfr_suspend(struct device *dev)
{
int ret;
struct net_device *ndev = dev_get_drvdata(dev);
@@ -733,7 +732,7 @@ static int stmmac_pltfr_suspend(struct device *dev)
* the main resume function, on some platforms, it can call own init helper
* if required.
*/
-static int stmmac_pltfr_resume(struct device *dev)
+static int __maybe_unused stmmac_pltfr_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
@@ -745,7 +744,7 @@ static int stmmac_pltfr_resume(struct device *dev)
return stmmac_resume(dev);
}
-static int stmmac_runtime_suspend(struct device *dev)
+static int __maybe_unused stmmac_runtime_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
@@ -755,14 +754,13 @@ static int stmmac_runtime_suspend(struct device *dev)
return 0;
}
-static int stmmac_runtime_resume(struct device *dev)
+static int __maybe_unused stmmac_runtime_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
return stmmac_bus_clks_config(priv, true);
}
-#endif /* CONFIG_PM_SLEEP */
const struct dev_pm_ops stmmac_pltfr_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(stmmac_pltfr_suspend, stmmac_pltfr_resume)
diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c
index 824f2da137a6..ee2baa2b2fcf 100644
--- a/drivers/nfc/fdp/fdp.c
+++ b/drivers/nfc/fdp/fdp.c
@@ -176,7 +176,7 @@ static void fdp_nci_set_data_pkt_counter(struct nci_dev *ndev,
*
* The firmware will be analyzed and applied when we send NCI_OP_PROP_PATCH_CMD
* command with NCI_PATCH_TYPE_EOT parameter. The device will send a
- * NFCC_PATCH_NTF packaet and a NCI_OP_CORE_RESET_NTF packet.
+ * NFCC_PATCH_NTF packet and a NCI_OP_CORE_RESET_NTF packet.
*/
static int fdp_nci_send_patch(struct nci_dev *ndev, u8 conn_id, u8 type)
{
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 8f003955c485..e4a503288d9b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3447,6 +3447,24 @@ netif_xmit_frozen_or_drv_stopped(const struct netdev_queue *dev_queue)
}
/**
+ * netdev_queue_set_dql_min_limit - set dql minimum limit
+ * @dev_queue: pointer to transmit queue
+ * @min_limit: dql minimum limit
+ *
+ * Forces xmit_more() to return true until the minimum threshold
+ * defined by @min_limit is reached (or until the tx queue is
+ * empty). Warning: to be use with care, misuse will impact the
+ * latency.
+ */
+static inline void netdev_queue_set_dql_min_limit(struct netdev_queue *dev_queue,
+ unsigned int min_limit)
+{
+#ifdef CONFIG_BQL
+ dev_queue->dql.min_limit = min_limit;
+#endif
+}
+
+/**
* netdev_txq_bql_enqueue_prefetchw - prefetch bql data for write
* @dev_queue: pointer to transmit queue
*
@@ -5318,6 +5336,9 @@ do { \
#define PTYPE_HASH_SIZE (16)
#define PTYPE_HASH_MASK (PTYPE_HASH_SIZE - 1)
+extern struct list_head ptype_all __read_mostly;
+extern struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
+
extern struct net_device *blackhole_netdev;
#endif /* _LINUX_NETDEVICE_H */
diff --git a/include/linux/qed/qed_chain.h b/include/linux/qed/qed_chain.h
index e339b48de32d..f34dbd0db795 100644
--- a/include/linux/qed/qed_chain.h
+++ b/include/linux/qed/qed_chain.h
@@ -19,7 +19,7 @@ enum qed_chain_mode {
/* Each Page contains a next pointer at its end */
QED_CHAIN_MODE_NEXT_PTR,
- /* Chain is a single page (next ptr) is unrequired */
+ /* Chain is a single page (next ptr) is not required */
QED_CHAIN_MODE_SINGLE,
/* Page pointers are located in a side list */
@@ -56,13 +56,13 @@ struct qed_chain_pbl_u32 {
};
struct qed_chain_u16 {
- /* Cyclic index of next element to produce/consme */
+ /* Cyclic index of next element to produce/consume */
u16 prod_idx;
u16 cons_idx;
};
struct qed_chain_u32 {
- /* Cyclic index of next element to produce/consme */
+ /* Cyclic index of next element to produce/consume */
u32 prod_idx;
u32 cons_idx;
};
@@ -270,7 +270,7 @@ static inline dma_addr_t qed_chain_get_pbl_phys(const struct qed_chain *chain)
/**
* @brief qed_chain_advance_page -
*
- * Advance the next element accros pages for a linked chain
+ * Advance the next element across pages for a linked chain
*
* @param p_chain
* @param p_next_elem
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index dfec65eca8a6..3db1def4437b 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -160,7 +160,9 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
if (br_opt_get(br, BROPT_NEIGH_SUPPRESS_ENABLED)) {
if (p && (p->flags & BR_NEIGH_SUPPRESS))
return;
- if (ipv4_is_zeronet(sip) || sip == tip) {
+ if (parp->ar_op != htons(ARPOP_RREQUEST) &&
+ parp->ar_op != htons(ARPOP_RREPLY) &&
+ (ipv4_is_zeronet(sip) || sip == tip)) {
/* prevent flooding to neigh suppress ports */
BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1;
return;
diff --git a/net/bridge/br_vlan_tunnel.c b/net/bridge/br_vlan_tunnel.c
index 169e005fbda2..0d3a8c01552e 100644
--- a/net/bridge/br_vlan_tunnel.c
+++ b/net/bridge/br_vlan_tunnel.c
@@ -35,7 +35,7 @@ static const struct rhashtable_params br_vlan_tunnel_rht_params = {
};
static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
- u64 tunnel_id)
+ __be64 tunnel_id)
{
return rhashtable_lookup_fast(tbl, &tunnel_id,
br_vlan_tunnel_rht_params);
diff --git a/net/core/dev.c b/net/core/dev.c
index be941ed754ac..ffab3928eeeb 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2451,10 +2451,8 @@ int netdev_txq_to_tc(struct net_device *dev, unsigned int txq)
EXPORT_SYMBOL(netdev_txq_to_tc);
#ifdef CONFIG_XPS
-struct static_key xps_needed __read_mostly;
-EXPORT_SYMBOL(xps_needed);
-struct static_key xps_rxqs_needed __read_mostly;
-EXPORT_SYMBOL(xps_rxqs_needed);
+static struct static_key xps_needed __read_mostly;
+static struct static_key xps_rxqs_needed __read_mostly;
static DEFINE_MUTEX(xps_map_mutex);
#define xmap_dereference(P) \
rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex))
diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c
index c714e6a9dad4..d8b9dbabd4a4 100644
--- a/net/core/net-procfs.c
+++ b/net/core/net-procfs.c
@@ -10,9 +10,6 @@
#define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1))
#define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o))
-extern struct list_head ptype_all __read_mostly;
-extern struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
-
static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos)
{
struct net *net = seq_file_net(seq);
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 562a42fcd437..f6197774048b 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -1378,7 +1378,7 @@ static ssize_t xps_queue_show(struct net_device *dev, unsigned int index,
nr_ids = dev_maps ? dev_maps->nr_ids :
(type == XPS_CPUS ? nr_cpu_ids : dev->num_rx_queues);
- mask = bitmap_zalloc(nr_ids, GFP_KERNEL);
+ mask = bitmap_zalloc(nr_ids, GFP_NOWAIT);
if (!mask) {
rcu_read_unlock();
return -ENOMEM;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index aaf8a452fd5b..8746b07668ae 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -1,15 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
-config HAVE_NET_DSA
- def_bool y
- depends on INET && NETDEVICES && !S390
-
-# Drivers must select NET_DSA and the appropriate tagging format
menuconfig NET_DSA
tristate "Distributed Switch Architecture"
- depends on HAVE_NET_DSA
depends on BRIDGE || BRIDGE=n
depends on HSR || HSR=n
+ depends on INET && NETDEVICES
select GRO_CELLS
select NET_SWITCHDEV
select PHYLINK
@@ -20,7 +15,8 @@ menuconfig NET_DSA
if NET_DSA
-# tagging formats
+# Drivers must select the appropriate tagging format(s)
+
config NET_DSA_TAG_8021Q
tristate
select VLAN_8021Q
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 203890e378cb..2ee20743cb41 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -802,7 +802,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
u16 version;
int length;
- /* UDP has verifed checksum */
+ /* UDP has verified checksum */
/* UDP always verifies the packet length. */
__skb_pull(skb, sizeof(struct udphdr));
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index 1eb7495ac5b4..8a930ca6d6b1 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -20,7 +20,7 @@
struct vport;
struct vport_parms;
-/* The following definitions are for users of the vport subsytem: */
+/* The following definitions are for users of the vport subsystem: */
int ovs_vport_init(void);
void ovs_vport_exit(void);
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index d097b5c15faa..9736df97e04d 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -209,16 +209,16 @@ static bool fl_range_port_dst_cmp(struct cls_fl_filter *filter,
struct fl_flow_key *key,
struct fl_flow_key *mkey)
{
- __be16 min_mask, max_mask, min_val, max_val;
+ u16 min_mask, max_mask, min_val, max_val;
- min_mask = htons(filter->mask->key.tp_range.tp_min.dst);
- max_mask = htons(filter->mask->key.tp_range.tp_max.dst);
- min_val = htons(filter->key.tp_range.tp_min.dst);
- max_val = htons(filter->key.tp_range.tp_max.dst);
+ min_mask = ntohs(filter->mask->key.tp_range.tp_min.dst);
+ max_mask = ntohs(filter->mask->key.tp_range.tp_max.dst);
+ min_val = ntohs(filter->key.tp_range.tp_min.dst);
+ max_val = ntohs(filter->key.tp_range.tp_max.dst);
if (min_mask && max_mask) {
- if (htons(key->tp_range.tp.dst) < min_val ||
- htons(key->tp_range.tp.dst) > max_val)
+ if (ntohs(key->tp_range.tp.dst) < min_val ||
+ ntohs(key->tp_range.tp.dst) > max_val)
return false;
/* skb does not have min and max values */
@@ -232,16 +232,16 @@ static bool fl_range_port_src_cmp(struct cls_fl_filter *filter,
struct fl_flow_key *key,
struct fl_flow_key *mkey)
{
- __be16 min_mask, max_mask, min_val, max_val;
+ u16 min_mask, max_mask, min_val, max_val;
- min_mask = htons(filter->mask->key.tp_range.tp_min.src);
- max_mask = htons(filter->mask->key.tp_range.tp_max.src);
- min_val = htons(filter->key.tp_range.tp_min.src);
- max_val = htons(filter->key.tp_range.tp_max.src);
+ min_mask = ntohs(filter->mask->key.tp_range.tp_min.src);
+ max_mask = ntohs(filter->mask->key.tp_range.tp_max.src);
+ min_val = ntohs(filter->key.tp_range.tp_min.src);
+ max_val = ntohs(filter->key.tp_range.tp_max.src);
if (min_mask && max_mask) {
- if (htons(key->tp_range.tp.src) < min_val ||
- htons(key->tp_range.tp.src) > max_val)
+ if (ntohs(key->tp_range.tp.src) < min_val ||
+ ntohs(key->tp_range.tp.src) > max_val)
return false;
/* skb does not have min and max values */
@@ -783,16 +783,16 @@ static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key,
TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_max.src));
if (mask->tp_range.tp_min.dst && mask->tp_range.tp_max.dst &&
- htons(key->tp_range.tp_max.dst) <=
- htons(key->tp_range.tp_min.dst)) {
+ ntohs(key->tp_range.tp_max.dst) <=
+ ntohs(key->tp_range.tp_min.dst)) {
NL_SET_ERR_MSG_ATTR(extack,
tb[TCA_FLOWER_KEY_PORT_DST_MIN],
"Invalid destination port range (min must be strictly smaller than max)");
return -EINVAL;
}
if (mask->tp_range.tp_min.src && mask->tp_range.tp_max.src &&
- htons(key->tp_range.tp_max.src) <=
- htons(key->tp_range.tp_min.src)) {
+ ntohs(key->tp_range.tp_max.src) <=
+ ntohs(key->tp_range.tp_min.src)) {
NL_SET_ERR_MSG_ATTR(extack,
tb[TCA_FLOWER_KEY_PORT_SRC_MIN],
"Invalid source port range (min must be strictly smaller than max)");
@@ -1044,8 +1044,8 @@ static int fl_set_key_flags(struct nlattr **tb, u32 *flags_key,
return -EINVAL;
}
- key = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS]));
- mask = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS_MASK]));
+ key = be32_to_cpu(nla_get_be32(tb[TCA_FLOWER_KEY_FLAGS]));
+ mask = be32_to_cpu(nla_get_be32(tb[TCA_FLOWER_KEY_FLAGS_MASK]));
*flags_key = 0;
*flags_mask = 0;