mvebu: backport upstream ethernet driver improvements and enable buffer manager support
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
e90245237c
commit
df98acc6a1
29 changed files with 4334 additions and 6 deletions
|
@ -209,6 +209,7 @@ CONFIG_HAVE_UID16=y
|
|||
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
|
||||
CONFIG_HIGHMEM=y
|
||||
# CONFIG_HIGHPTE is not set
|
||||
CONFIG_HWBM=y
|
||||
CONFIG_HWMON=y
|
||||
CONFIG_HZ_FIXED=0
|
||||
CONFIG_HZ_PERIODIC=y
|
||||
|
@ -274,6 +275,7 @@ CONFIG_MVEBU_DEVBUS=y
|
|||
CONFIG_MVEBU_MBUS=y
|
||||
CONFIG_MVMDIO=y
|
||||
CONFIG_MVNETA=y
|
||||
CONFIG_MVNETA_BM=y
|
||||
CONFIG_MVSW61XX_PHY=y
|
||||
CONFIG_MV_XOR=y
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
|
|
|
@ -31,7 +31,7 @@ Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
|||
|
||||
--- a/drivers/mtd/nand/pxa3xx_nand.c
|
||||
+++ b/drivers/mtd/nand/pxa3xx_nand.c
|
||||
@@ -1636,7 +1636,7 @@ static int pxa3xx_nand_scan(struct mtd_i
|
||||
@@ -1637,7 +1637,7 @@ static int pxa3xx_nand_scan(struct mtd_i
|
||||
goto KEEP_CONFIG;
|
||||
|
||||
/* Set a default chunk size */
|
||||
|
|
|
@ -19,7 +19,7 @@ Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
|||
|
||||
--- a/drivers/mtd/nand/pxa3xx_nand.c
|
||||
+++ b/drivers/mtd/nand/pxa3xx_nand.c
|
||||
@@ -1419,34 +1419,43 @@ static int pxa3xx_nand_waitfunc(struct m
|
||||
@@ -1420,34 +1420,43 @@ static int pxa3xx_nand_waitfunc(struct m
|
||||
return NAND_STATUS_READY;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
|||
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
|
||||
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
|
||||
return 0;
|
||||
@@ -1635,10 +1644,7 @@ static int pxa3xx_nand_scan(struct mtd_i
|
||||
@@ -1636,10 +1645,7 @@ static int pxa3xx_nand_scan(struct mtd_i
|
||||
if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
|
||||
goto KEEP_CONFIG;
|
||||
|
||||
|
@ -83,7 +83,7 @@ Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1651,7 +1657,6 @@ static int pxa3xx_nand_scan(struct mtd_i
|
||||
@@ -1652,7 +1658,6 @@ static int pxa3xx_nand_scan(struct mtd_i
|
||||
}
|
||||
|
||||
KEEP_CONFIG:
|
||||
|
@ -91,7 +91,7 @@ Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
|||
if (info->reg_ndcr & NDCR_DWIDTH_M)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
@@ -1736,6 +1741,10 @@ KEEP_CONFIG:
|
||||
@@ -1737,6 +1742,10 @@ KEEP_CONFIG:
|
||||
host->row_addr_cycles = 3;
|
||||
else
|
||||
host->row_addr_cycles = 2;
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
From: Marcin Wojtas <mw@semihalf.com>
|
||||
Date: Mon, 14 Mar 2016 09:39:02 +0100
|
||||
Subject: [PATCH] bus: mvebu-mbus: provide api for obtaining IO and DRAM window
|
||||
information
|
||||
|
||||
This commit enables finding appropriate mbus window and obtaining its
|
||||
target id and attribute for given physical address in two separate
|
||||
routines, both for IO and DRAM windows. This functionality
|
||||
is needed for Armada XP/38x Network Controller's Buffer Manager and
|
||||
PnC configuration.
|
||||
|
||||
[gregory.clement@free-electrons.com: Fix size test for
|
||||
mvebu_mbus_get_dram_win_info]
|
||||
|
||||
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
|
||||
[DRAM window information reference in LKv3.10]
|
||||
Signed-off-by: Evan Wang <xswang@marvell.com>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/bus/mvebu-mbus.c
|
||||
+++ b/drivers/bus/mvebu-mbus.c
|
||||
@@ -948,6 +948,58 @@ void mvebu_mbus_get_pcie_io_aperture(str
|
||||
*res = mbus_state.pcie_io_aperture;
|
||||
}
|
||||
|
||||
+int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr)
|
||||
+{
|
||||
+ const struct mbus_dram_target_info *dram;
|
||||
+ int i;
|
||||
+
|
||||
+ /* Get dram info */
|
||||
+ dram = mv_mbus_dram_info();
|
||||
+ if (!dram) {
|
||||
+ pr_err("missing DRAM information\n");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ /* Try to find matching DRAM window for phyaddr */
|
||||
+ for (i = 0; i < dram->num_cs; i++) {
|
||||
+ const struct mbus_dram_window *cs = dram->cs + i;
|
||||
+
|
||||
+ if (cs->base <= phyaddr &&
|
||||
+ phyaddr <= (cs->base + cs->size - 1)) {
|
||||
+ *target = dram->mbus_dram_target_id;
|
||||
+ *attr = cs->mbus_attr;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pr_err("invalid dram address 0x%x\n", phyaddr);
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(mvebu_mbus_get_dram_win_info);
|
||||
+
|
||||
+int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target,
|
||||
+ u8 *attr)
|
||||
+{
|
||||
+ int win;
|
||||
+
|
||||
+ for (win = 0; win < mbus_state.soc->num_wins; win++) {
|
||||
+ u64 wbase;
|
||||
+ int enabled;
|
||||
+
|
||||
+ mvebu_mbus_read_window(&mbus_state, win, &enabled, &wbase,
|
||||
+ size, target, attr, NULL);
|
||||
+
|
||||
+ if (!enabled)
|
||||
+ continue;
|
||||
+
|
||||
+ if (wbase <= phyaddr && phyaddr <= wbase + *size)
|
||||
+ return win;
|
||||
+ }
|
||||
+
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(mvebu_mbus_get_io_win_info);
|
||||
+
|
||||
static __init int mvebu_mbus_debugfs_init(void)
|
||||
{
|
||||
struct mvebu_mbus_state *s = &mbus_state;
|
||||
--- a/include/linux/mbus.h
|
||||
+++ b/include/linux/mbus.h
|
||||
@@ -69,6 +69,9 @@ static inline const struct mbus_dram_tar
|
||||
int mvebu_mbus_save_cpu_target(u32 *store_addr);
|
||||
void mvebu_mbus_get_pcie_mem_aperture(struct resource *res);
|
||||
void mvebu_mbus_get_pcie_io_aperture(struct resource *res);
|
||||
+int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr);
|
||||
+int mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target,
|
||||
+ u8 *attr);
|
||||
int mvebu_mbus_add_window_remap_by_id(unsigned int target,
|
||||
unsigned int attribute,
|
||||
phys_addr_t base, size_t size,
|
|
@ -0,0 +1,55 @@
|
|||
From: Stas Sergeev <stsp@list.ru>
|
||||
Date: Wed, 2 Dec 2015 20:33:56 +0300
|
||||
Subject: [PATCH] mvneta: consolidate autoneg enabling
|
||||
|
||||
This moves autoneg-related bit manipulations to the single place.
|
||||
|
||||
CC: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
CC: netdev@vger.kernel.org
|
||||
CC: linux-kernel@vger.kernel.org
|
||||
|
||||
Signed-off-by: Stas Sergeev <stsp@users.sourceforge.net>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -1067,15 +1067,28 @@ static void mvneta_defaults_set(struct m
|
||||
MVNETA_GMAC_AN_SPEED_EN |
|
||||
MVNETA_GMAC_AN_DUPLEX_EN;
|
||||
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
||||
+
|
||||
val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
||||
val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
||||
mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
|
||||
+
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
||||
+ val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
|
||||
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
|
||||
} else {
|
||||
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
||||
val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
|
||||
MVNETA_GMAC_AN_SPEED_EN |
|
||||
MVNETA_GMAC_AN_DUPLEX_EN);
|
||||
mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
||||
+
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
||||
+ val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
||||
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
|
||||
+
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
||||
+ val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
|
||||
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
|
||||
}
|
||||
|
||||
mvneta_set_ucast_table(pp, -1);
|
||||
@@ -3230,9 +3243,6 @@ static int mvneta_port_power_up(struct m
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
- if (pp->use_inband_status)
|
||||
- ctrl |= MVNETA_GMAC2_INBAND_AN_ENABLE;
|
||||
-
|
||||
/* Cancel Port Reset */
|
||||
ctrl &= ~MVNETA_GMAC2_PORT_RESET;
|
||||
mvreg_write(pp, MVNETA_GMAC_CTRL_2, ctrl);
|
|
@ -0,0 +1,165 @@
|
|||
From: Stas Sergeev <stsp@list.ru>
|
||||
Date: Wed, 2 Dec 2015 20:35:11 +0300
|
||||
Subject: [PATCH] mvneta: implement ethtool autonegotiation control
|
||||
|
||||
This patch allows to do
|
||||
ethtool -s eth0 autoneg off
|
||||
ethtool -s eth0 autoneg on
|
||||
to disable or enable autonegotiation at run-time.
|
||||
Without that functionality, the only way to control the autonegotiation
|
||||
is to modify the device tree.
|
||||
|
||||
This is needed if you plan to use the same kernel with
|
||||
different ethernet switches, the ones that support the in-band
|
||||
status and the ones that not.
|
||||
|
||||
CC: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
CC: netdev@vger.kernel.org
|
||||
CC: linux-kernel@vger.kernel.org
|
||||
|
||||
Signed-off-by: Stas Sergeev <stsp@users.sourceforge.net>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -371,7 +371,7 @@ struct mvneta_port {
|
||||
unsigned int duplex;
|
||||
unsigned int speed;
|
||||
unsigned int tx_csum_limit;
|
||||
- int use_inband_status:1;
|
||||
+ unsigned int use_inband_status:1;
|
||||
|
||||
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
|
||||
};
|
||||
@@ -973,6 +973,44 @@ static void mvneta_set_other_mcast_table
|
||||
mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
|
||||
}
|
||||
|
||||
+static void mvneta_set_autoneg(struct mvneta_port *pp, int enable)
|
||||
+{
|
||||
+ u32 val;
|
||||
+
|
||||
+ if (enable) {
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
||||
+ val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
|
||||
+ MVNETA_GMAC_FORCE_LINK_DOWN |
|
||||
+ MVNETA_GMAC_AN_FLOW_CTRL_EN);
|
||||
+ val |= MVNETA_GMAC_INBAND_AN_ENABLE |
|
||||
+ MVNETA_GMAC_AN_SPEED_EN |
|
||||
+ MVNETA_GMAC_AN_DUPLEX_EN;
|
||||
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
||||
+
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
||||
+ val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
||||
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
|
||||
+
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
||||
+ val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
|
||||
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
|
||||
+ } else {
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
||||
+ val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
|
||||
+ MVNETA_GMAC_AN_SPEED_EN |
|
||||
+ MVNETA_GMAC_AN_DUPLEX_EN);
|
||||
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
||||
+
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
||||
+ val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
||||
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
|
||||
+
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
||||
+ val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
|
||||
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* This method sets defaults to the NETA port:
|
||||
* Clears interrupt Cause and Mask registers.
|
||||
* Clears all MAC tables.
|
||||
@@ -1058,39 +1096,7 @@ static void mvneta_defaults_set(struct m
|
||||
val &= ~MVNETA_PHY_POLLING_ENABLE;
|
||||
mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
|
||||
|
||||
- if (pp->use_inband_status) {
|
||||
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
||||
- val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
|
||||
- MVNETA_GMAC_FORCE_LINK_DOWN |
|
||||
- MVNETA_GMAC_AN_FLOW_CTRL_EN);
|
||||
- val |= MVNETA_GMAC_INBAND_AN_ENABLE |
|
||||
- MVNETA_GMAC_AN_SPEED_EN |
|
||||
- MVNETA_GMAC_AN_DUPLEX_EN;
|
||||
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
||||
-
|
||||
- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
||||
- val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
||||
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
|
||||
-
|
||||
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
||||
- val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
|
||||
- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
|
||||
- } else {
|
||||
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
||||
- val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
|
||||
- MVNETA_GMAC_AN_SPEED_EN |
|
||||
- MVNETA_GMAC_AN_DUPLEX_EN);
|
||||
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
||||
-
|
||||
- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
|
||||
- val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
|
||||
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
|
||||
-
|
||||
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
|
||||
- val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
|
||||
- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
|
||||
- }
|
||||
-
|
||||
+ mvneta_set_autoneg(pp, pp->use_inband_status);
|
||||
mvneta_set_ucast_table(pp, -1);
|
||||
mvneta_set_special_mcast_table(pp, -1);
|
||||
mvneta_set_other_mcast_table(pp, -1);
|
||||
@@ -2956,10 +2962,43 @@ int mvneta_ethtool_get_settings(struct n
|
||||
int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct mvneta_port *pp = netdev_priv(dev);
|
||||
+ struct phy_device *phydev = pp->phy_dev;
|
||||
|
||||
- if (!pp->phy_dev)
|
||||
+ if (!phydev)
|
||||
return -ENODEV;
|
||||
|
||||
+ if ((cmd->autoneg == AUTONEG_ENABLE) != pp->use_inband_status) {
|
||||
+ u32 val;
|
||||
+
|
||||
+ mvneta_set_autoneg(pp, cmd->autoneg == AUTONEG_ENABLE);
|
||||
+
|
||||
+ if (cmd->autoneg == AUTONEG_DISABLE) {
|
||||
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
|
||||
+ val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
|
||||
+ MVNETA_GMAC_CONFIG_GMII_SPEED |
|
||||
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX);
|
||||
+
|
||||
+ if (phydev->duplex)
|
||||
+ val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
|
||||
+
|
||||
+ if (phydev->speed == SPEED_1000)
|
||||
+ val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
|
||||
+ else if (phydev->speed == SPEED_100)
|
||||
+ val |= MVNETA_GMAC_CONFIG_MII_SPEED;
|
||||
+
|
||||
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
|
||||
+ }
|
||||
+
|
||||
+ pp->use_inband_status = (cmd->autoneg == AUTONEG_ENABLE);
|
||||
+ netdev_info(pp->dev, "autoneg status set to %i\n",
|
||||
+ pp->use_inband_status);
|
||||
+
|
||||
+ if (netif_running(dev)) {
|
||||
+ mvneta_port_down(pp);
|
||||
+ mvneta_port_up(pp);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return phy_ethtool_sset(pp->phy_dev, cmd);
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Wed, 9 Dec 2015 18:23:48 +0100
|
||||
Subject: [PATCH] net: mvneta: Make the default queue related for each port
|
||||
|
||||
Instead of using the same default queue for all the port. Move it in the
|
||||
port struct. It will allow have a different default queue for each port.
|
||||
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -356,6 +356,7 @@ struct mvneta_port {
|
||||
struct mvneta_tx_queue *txqs;
|
||||
struct net_device *dev;
|
||||
struct notifier_block cpu_notifier;
|
||||
+ int rxq_def;
|
||||
|
||||
/* Core clock */
|
||||
struct clk *clk;
|
||||
@@ -819,7 +820,7 @@ static void mvneta_port_up(struct mvneta
|
||||
mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
|
||||
|
||||
/* Enable all initialized RXQs. */
|
||||
- mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def));
|
||||
+ mvreg_write(pp, MVNETA_RXQ_CMD, BIT(pp->rxq_def));
|
||||
}
|
||||
|
||||
/* Stop the Ethernet port activity */
|
||||
@@ -1067,7 +1068,7 @@ static void mvneta_defaults_set(struct m
|
||||
mvreg_write(pp, MVNETA_ACC_MODE, val);
|
||||
|
||||
/* Update val of portCfg register accordingly with all RxQueue types */
|
||||
- val = MVNETA_PORT_CONFIG_DEFL_VALUE(rxq_def);
|
||||
+ val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def);
|
||||
mvreg_write(pp, MVNETA_PORT_CONFIG, val);
|
||||
|
||||
val = 0;
|
||||
@@ -2101,19 +2102,19 @@ static void mvneta_set_rx_mode(struct ne
|
||||
if (dev->flags & IFF_PROMISC) {
|
||||
/* Accept all: Multicast + Unicast */
|
||||
mvneta_rx_unicast_promisc_set(pp, 1);
|
||||
- mvneta_set_ucast_table(pp, rxq_def);
|
||||
- mvneta_set_special_mcast_table(pp, rxq_def);
|
||||
- mvneta_set_other_mcast_table(pp, rxq_def);
|
||||
+ mvneta_set_ucast_table(pp, pp->rxq_def);
|
||||
+ mvneta_set_special_mcast_table(pp, pp->rxq_def);
|
||||
+ mvneta_set_other_mcast_table(pp, pp->rxq_def);
|
||||
} else {
|
||||
/* Accept single Unicast */
|
||||
mvneta_rx_unicast_promisc_set(pp, 0);
|
||||
mvneta_set_ucast_table(pp, -1);
|
||||
- mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
|
||||
+ mvneta_mac_addr_set(pp, dev->dev_addr, pp->rxq_def);
|
||||
|
||||
if (dev->flags & IFF_ALLMULTI) {
|
||||
/* Accept all multicast */
|
||||
- mvneta_set_special_mcast_table(pp, rxq_def);
|
||||
- mvneta_set_other_mcast_table(pp, rxq_def);
|
||||
+ mvneta_set_special_mcast_table(pp, pp->rxq_def);
|
||||
+ mvneta_set_other_mcast_table(pp, pp->rxq_def);
|
||||
} else {
|
||||
/* Accept only initialized multicast */
|
||||
mvneta_set_special_mcast_table(pp, -1);
|
||||
@@ -2122,7 +2123,7 @@ static void mvneta_set_rx_mode(struct ne
|
||||
if (!netdev_mc_empty(dev)) {
|
||||
netdev_for_each_mc_addr(ha, dev) {
|
||||
mvneta_mcast_addr_set(pp, ha->addr,
|
||||
- rxq_def);
|
||||
+ pp->rxq_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2205,7 +2206,7 @@ static int mvneta_poll(struct napi_struc
|
||||
* RX packets
|
||||
*/
|
||||
cause_rx_tx |= port->cause_rx_tx;
|
||||
- rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]);
|
||||
+ rx_done = mvneta_rx(pp, budget, &pp->rxqs[pp->rxq_def]);
|
||||
budget -= rx_done;
|
||||
|
||||
if (budget > 0) {
|
||||
@@ -2418,17 +2419,17 @@ static void mvneta_cleanup_txqs(struct m
|
||||
/* Cleanup all Rx queues */
|
||||
static void mvneta_cleanup_rxqs(struct mvneta_port *pp)
|
||||
{
|
||||
- mvneta_rxq_deinit(pp, &pp->rxqs[rxq_def]);
|
||||
+ mvneta_rxq_deinit(pp, &pp->rxqs[pp->rxq_def]);
|
||||
}
|
||||
|
||||
|
||||
/* Init all Rx queues */
|
||||
static int mvneta_setup_rxqs(struct mvneta_port *pp)
|
||||
{
|
||||
- int err = mvneta_rxq_init(pp, &pp->rxqs[rxq_def]);
|
||||
+ int err = mvneta_rxq_init(pp, &pp->rxqs[pp->rxq_def]);
|
||||
if (err) {
|
||||
netdev_err(pp->dev, "%s: can't create rxq=%d\n",
|
||||
- __func__, rxq_def);
|
||||
+ __func__, pp->rxq_def);
|
||||
mvneta_cleanup_rxqs(pp);
|
||||
return err;
|
||||
}
|
||||
@@ -2634,7 +2635,7 @@ static int mvneta_set_mac_addr(struct ne
|
||||
mvneta_mac_addr_set(pp, dev->dev_addr, -1);
|
||||
|
||||
/* Set new addr in hw */
|
||||
- mvneta_mac_addr_set(pp, sockaddr->sa_data, rxq_def);
|
||||
+ mvneta_mac_addr_set(pp, sockaddr->sa_data, pp->rxq_def);
|
||||
|
||||
eth_commit_mac_addr_change(dev, addr);
|
||||
return 0;
|
||||
@@ -2753,7 +2754,7 @@ static void mvneta_percpu_elect(struct m
|
||||
{
|
||||
int online_cpu_idx, cpu, i = 0;
|
||||
|
||||
- online_cpu_idx = rxq_def % num_online_cpus();
|
||||
+ online_cpu_idx = pp->rxq_def % num_online_cpus();
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
if (i == online_cpu_idx)
|
||||
@@ -3363,6 +3364,8 @@ static int mvneta_probe(struct platform_
|
||||
strcmp(managed, "in-band-status") == 0);
|
||||
pp->cpu_notifier.notifier_call = mvneta_percpu_notifier;
|
||||
|
||||
+ pp->rxq_def = rxq_def;
|
||||
+
|
||||
pp->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pp->clk)) {
|
||||
err = PTR_ERR(pp->clk);
|
|
@ -0,0 +1,278 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Wed, 9 Dec 2015 18:23:49 +0100
|
||||
Subject: [PATCH] net: mvneta: Associate RX queues with each CPU
|
||||
|
||||
We enable the percpu interrupt for all the CPU and we just associate a
|
||||
CPU to a few queue at the neta level. The mapping between the CPUs and
|
||||
the queues is static. The queues are associated to the CPU module the
|
||||
number of CPUs. However currently we only use on RX queue for a given
|
||||
Ethernet port.
|
||||
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -110,9 +110,16 @@
|
||||
#define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2))
|
||||
#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff
|
||||
#define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00
|
||||
+#define MVNETA_CPU_RXQ_ACCESS(rxq) BIT(rxq)
|
||||
#define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2))
|
||||
|
||||
-/* Exception Interrupt Port/Queue Cause register */
|
||||
+/* Exception Interrupt Port/Queue Cause register
|
||||
+ *
|
||||
+ * Their behavior depend of the mapping done using the PCPX2Q
|
||||
+ * registers. For a given CPU if the bit associated to a queue is not
|
||||
+ * set, then for the register a read from this CPU will always return
|
||||
+ * 0 and a write won't do anything
|
||||
+ */
|
||||
|
||||
#define MVNETA_INTR_NEW_CAUSE 0x25a0
|
||||
#define MVNETA_INTR_NEW_MASK 0x25a4
|
||||
@@ -820,7 +827,13 @@ static void mvneta_port_up(struct mvneta
|
||||
mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
|
||||
|
||||
/* Enable all initialized RXQs. */
|
||||
- mvreg_write(pp, MVNETA_RXQ_CMD, BIT(pp->rxq_def));
|
||||
+ for (queue = 0; queue < rxq_number; queue++) {
|
||||
+ struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
|
||||
+
|
||||
+ if (rxq->descs != NULL)
|
||||
+ q_map |= (1 << queue);
|
||||
+ }
|
||||
+ mvreg_write(pp, MVNETA_RXQ_CMD, q_map);
|
||||
}
|
||||
|
||||
/* Stop the Ethernet port activity */
|
||||
@@ -1026,6 +1039,7 @@ static void mvneta_defaults_set(struct m
|
||||
int cpu;
|
||||
int queue;
|
||||
u32 val;
|
||||
+ int max_cpu = num_present_cpus();
|
||||
|
||||
/* Clear all Cause registers */
|
||||
mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0);
|
||||
@@ -1041,13 +1055,23 @@ static void mvneta_defaults_set(struct m
|
||||
/* Enable MBUS Retry bit16 */
|
||||
mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20);
|
||||
|
||||
- /* Set CPU queue access map - all CPUs have access to all RX
|
||||
- * queues and to all TX queues
|
||||
+ /* Set CPU queue access map. CPUs are assigned to the RX
|
||||
+ * queues modulo their number and all the TX queues are
|
||||
+ * assigned to the CPU associated to the default RX queue.
|
||||
*/
|
||||
- for_each_present_cpu(cpu)
|
||||
- mvreg_write(pp, MVNETA_CPU_MAP(cpu),
|
||||
- (MVNETA_CPU_RXQ_ACCESS_ALL_MASK |
|
||||
- MVNETA_CPU_TXQ_ACCESS_ALL_MASK));
|
||||
+ for_each_present_cpu(cpu) {
|
||||
+ int rxq_map = 0, txq_map = 0;
|
||||
+ int rxq;
|
||||
+
|
||||
+ for (rxq = 0; rxq < rxq_number; rxq++)
|
||||
+ if ((rxq % max_cpu) == cpu)
|
||||
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
|
||||
+
|
||||
+ if (cpu == rxq_def)
|
||||
+ txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
|
||||
+
|
||||
+ mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
|
||||
+ }
|
||||
|
||||
/* Reset RX and TX DMAs */
|
||||
mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET);
|
||||
@@ -2174,6 +2198,7 @@ static int mvneta_poll(struct napi_struc
|
||||
{
|
||||
int rx_done = 0;
|
||||
u32 cause_rx_tx;
|
||||
+ int rx_queue;
|
||||
struct mvneta_port *pp = netdev_priv(napi->dev);
|
||||
struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
|
||||
|
||||
@@ -2205,8 +2230,15 @@ static int mvneta_poll(struct napi_struc
|
||||
/* For the case where the last mvneta_poll did not process all
|
||||
* RX packets
|
||||
*/
|
||||
+ rx_queue = fls(((cause_rx_tx >> 8) & 0xff));
|
||||
+
|
||||
cause_rx_tx |= port->cause_rx_tx;
|
||||
- rx_done = mvneta_rx(pp, budget, &pp->rxqs[pp->rxq_def]);
|
||||
+
|
||||
+ if (rx_queue) {
|
||||
+ rx_queue = rx_queue - 1;
|
||||
+ rx_done = mvneta_rx(pp, budget, &pp->rxqs[rx_queue]);
|
||||
+ }
|
||||
+
|
||||
budget -= rx_done;
|
||||
|
||||
if (budget > 0) {
|
||||
@@ -2419,19 +2451,27 @@ static void mvneta_cleanup_txqs(struct m
|
||||
/* Cleanup all Rx queues */
|
||||
static void mvneta_cleanup_rxqs(struct mvneta_port *pp)
|
||||
{
|
||||
- mvneta_rxq_deinit(pp, &pp->rxqs[pp->rxq_def]);
|
||||
+ int queue;
|
||||
+
|
||||
+ for (queue = 0; queue < txq_number; queue++)
|
||||
+ mvneta_rxq_deinit(pp, &pp->rxqs[queue]);
|
||||
}
|
||||
|
||||
|
||||
/* Init all Rx queues */
|
||||
static int mvneta_setup_rxqs(struct mvneta_port *pp)
|
||||
{
|
||||
- int err = mvneta_rxq_init(pp, &pp->rxqs[pp->rxq_def]);
|
||||
- if (err) {
|
||||
- netdev_err(pp->dev, "%s: can't create rxq=%d\n",
|
||||
- __func__, pp->rxq_def);
|
||||
- mvneta_cleanup_rxqs(pp);
|
||||
- return err;
|
||||
+ int queue;
|
||||
+
|
||||
+ for (queue = 0; queue < rxq_number; queue++) {
|
||||
+ int err = mvneta_rxq_init(pp, &pp->rxqs[queue]);
|
||||
+
|
||||
+ if (err) {
|
||||
+ netdev_err(pp->dev, "%s: can't create rxq=%d\n",
|
||||
+ __func__, queue);
|
||||
+ mvneta_cleanup_rxqs(pp);
|
||||
+ return err;
|
||||
+ }
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2455,6 +2495,19 @@ static int mvneta_setup_txqs(struct mvne
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void mvneta_percpu_unmask_interrupt(void *arg)
|
||||
+{
|
||||
+ struct mvneta_port *pp = arg;
|
||||
+
|
||||
+ /* All the queue are unmasked, but actually only the ones
|
||||
+ * maped to this CPU will be unmasked
|
||||
+ */
|
||||
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
||||
+ MVNETA_RX_INTR_MASK_ALL |
|
||||
+ MVNETA_TX_INTR_MASK_ALL |
|
||||
+ MVNETA_MISCINTR_INTR_MASK);
|
||||
+}
|
||||
+
|
||||
static void mvneta_start_dev(struct mvneta_port *pp)
|
||||
{
|
||||
unsigned int cpu;
|
||||
@@ -2472,11 +2525,10 @@ static void mvneta_start_dev(struct mvne
|
||||
napi_enable(&port->napi);
|
||||
}
|
||||
|
||||
- /* Unmask interrupts */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
||||
- MVNETA_RX_INTR_MASK(rxq_number) |
|
||||
- MVNETA_TX_INTR_MASK(txq_number) |
|
||||
- MVNETA_MISCINTR_INTR_MASK);
|
||||
+ /* Unmask interrupts. It has to be done from each CPU */
|
||||
+ for_each_online_cpu(cpu)
|
||||
+ smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt,
|
||||
+ pp, true);
|
||||
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
|
||||
MVNETA_CAUSE_PHY_STATUS_CHANGE |
|
||||
MVNETA_CAUSE_LINK_CHANGE |
|
||||
@@ -2752,22 +2804,35 @@ static void mvneta_percpu_disable(void *
|
||||
|
||||
static void mvneta_percpu_elect(struct mvneta_port *pp)
|
||||
{
|
||||
- int online_cpu_idx, cpu, i = 0;
|
||||
+ int online_cpu_idx, max_cpu, cpu, i = 0;
|
||||
|
||||
online_cpu_idx = pp->rxq_def % num_online_cpus();
|
||||
+ max_cpu = num_present_cpus();
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
- if (i == online_cpu_idx)
|
||||
- /* Enable per-CPU interrupt on the one CPU we
|
||||
- * just elected
|
||||
+ int rxq_map = 0, txq_map = 0;
|
||||
+ int rxq;
|
||||
+
|
||||
+ for (rxq = 0; rxq < rxq_number; rxq++)
|
||||
+ if ((rxq % max_cpu) == cpu)
|
||||
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
|
||||
+
|
||||
+ if (i == online_cpu_idx) {
|
||||
+ /* Map the default receive queue and transmit
|
||||
+ * queue to the elected CPU
|
||||
*/
|
||||
- smp_call_function_single(cpu, mvneta_percpu_enable,
|
||||
- pp, true);
|
||||
- else
|
||||
- /* Disable per-CPU interrupt on all the other CPU */
|
||||
- smp_call_function_single(cpu, mvneta_percpu_disable,
|
||||
- pp, true);
|
||||
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(pp->rxq_def);
|
||||
+ txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
|
||||
+ }
|
||||
+ mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
|
||||
+
|
||||
+ /* Update the interrupt mask on each CPU according the
|
||||
+ * new mapping
|
||||
+ */
|
||||
+ smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt,
|
||||
+ pp, true);
|
||||
i++;
|
||||
+
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2802,12 +2867,22 @@ static int mvneta_percpu_notifier(struct
|
||||
mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
|
||||
napi_enable(&port->napi);
|
||||
|
||||
+
|
||||
+ /* Enable per-CPU interrupts on the CPU that is
|
||||
+ * brought up.
|
||||
+ */
|
||||
+ smp_call_function_single(cpu, mvneta_percpu_enable,
|
||||
+ pp, true);
|
||||
+
|
||||
/* Enable per-CPU interrupt on the one CPU we care
|
||||
* about.
|
||||
*/
|
||||
mvneta_percpu_elect(pp);
|
||||
|
||||
- /* Unmask all ethernet port interrupts */
|
||||
+ /* Unmask all ethernet port interrupts, as this
|
||||
+ * notifier is called for each CPU then the CPU to
|
||||
+ * Queue mapping is applied
|
||||
+ */
|
||||
mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
||||
MVNETA_RX_INTR_MASK(rxq_number) |
|
||||
MVNETA_TX_INTR_MASK(txq_number) |
|
||||
@@ -2858,7 +2933,7 @@ static int mvneta_percpu_notifier(struct
|
||||
static int mvneta_open(struct net_device *dev)
|
||||
{
|
||||
struct mvneta_port *pp = netdev_priv(dev);
|
||||
- int ret;
|
||||
+ int ret, cpu;
|
||||
|
||||
pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
|
||||
pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
|
||||
@@ -2888,8 +2963,13 @@ static int mvneta_open(struct net_device
|
||||
*/
|
||||
mvneta_percpu_disable(pp);
|
||||
|
||||
- /* Elect a CPU to handle our RX queue interrupt */
|
||||
- mvneta_percpu_elect(pp);
|
||||
+ /* Enable per-CPU interrupt on all the CPU to handle our RX
|
||||
+ * queue interrupts
|
||||
+ */
|
||||
+ for_each_online_cpu(cpu)
|
||||
+ smp_call_function_single(cpu, mvneta_percpu_enable,
|
||||
+ pp, true);
|
||||
+
|
||||
|
||||
/* Register a CPU notifier to handle the case where our CPU
|
||||
* might be taken offline.
|
|
@ -0,0 +1,191 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Wed, 9 Dec 2015 18:23:50 +0100
|
||||
Subject: [PATCH] net: mvneta: Add naive RSS support
|
||||
|
||||
This patch adds the support for the RSS related ethtool
|
||||
function. Currently it only uses one entry in the indirection table which
|
||||
allows associating an mvneta interface to a given CPU.
|
||||
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Tested-by: Marcin Wojtas <mw@semihalf.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -261,6 +261,11 @@
|
||||
|
||||
#define MVNETA_TX_MTU_MAX 0x3ffff
|
||||
|
||||
+/* The RSS lookup table actually has 256 entries but we do not use
|
||||
+ * them yet
|
||||
+ */
|
||||
+#define MVNETA_RSS_LU_TABLE_SIZE 1
|
||||
+
|
||||
/* TSO header size */
|
||||
#define TSO_HEADER_SIZE 128
|
||||
|
||||
@@ -382,6 +387,8 @@ struct mvneta_port {
|
||||
unsigned int use_inband_status:1;
|
||||
|
||||
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
|
||||
+
|
||||
+ u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
|
||||
};
|
||||
|
||||
/* The mvneta_tx_desc and mvneta_rx_desc structures describe the
|
||||
@@ -1067,7 +1074,7 @@ static void mvneta_defaults_set(struct m
|
||||
if ((rxq % max_cpu) == cpu)
|
||||
rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
|
||||
|
||||
- if (cpu == rxq_def)
|
||||
+ if (cpu == pp->rxq_def)
|
||||
txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
|
||||
|
||||
mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
|
||||
@@ -2508,6 +2515,18 @@ static void mvneta_percpu_unmask_interru
|
||||
MVNETA_MISCINTR_INTR_MASK);
|
||||
}
|
||||
|
||||
+static void mvneta_percpu_mask_interrupt(void *arg)
|
||||
+{
|
||||
+ struct mvneta_port *pp = arg;
|
||||
+
|
||||
+ /* All the queue are masked, but actually only the ones
|
||||
+ * maped to this CPU will be masked
|
||||
+ */
|
||||
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
|
||||
+ mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
|
||||
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
|
||||
+}
|
||||
+
|
||||
static void mvneta_start_dev(struct mvneta_port *pp)
|
||||
{
|
||||
unsigned int cpu;
|
||||
@@ -3231,6 +3250,106 @@ static int mvneta_ethtool_get_sset_count
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
+static u32 mvneta_ethtool_get_rxfh_indir_size(struct net_device *dev)
|
||||
+{
|
||||
+ return MVNETA_RSS_LU_TABLE_SIZE;
|
||||
+}
|
||||
+
|
||||
+static int mvneta_ethtool_get_rxnfc(struct net_device *dev,
|
||||
+ struct ethtool_rxnfc *info,
|
||||
+ u32 *rules __always_unused)
|
||||
+{
|
||||
+ switch (info->cmd) {
|
||||
+ case ETHTOOL_GRXRINGS:
|
||||
+ info->data = rxq_number;
|
||||
+ return 0;
|
||||
+ case ETHTOOL_GRXFH:
|
||||
+ return -EOPNOTSUPP;
|
||||
+ default:
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int mvneta_config_rss(struct mvneta_port *pp)
|
||||
+{
|
||||
+ int cpu;
|
||||
+ u32 val;
|
||||
+
|
||||
+ netif_tx_stop_all_queues(pp->dev);
|
||||
+
|
||||
+ for_each_online_cpu(cpu)
|
||||
+ smp_call_function_single(cpu, mvneta_percpu_mask_interrupt,
|
||||
+ pp, true);
|
||||
+
|
||||
+ /* We have to synchronise on the napi of each CPU */
|
||||
+ for_each_online_cpu(cpu) {
|
||||
+ struct mvneta_pcpu_port *pcpu_port =
|
||||
+ per_cpu_ptr(pp->ports, cpu);
|
||||
+
|
||||
+ napi_synchronize(&pcpu_port->napi);
|
||||
+ napi_disable(&pcpu_port->napi);
|
||||
+ }
|
||||
+
|
||||
+ pp->rxq_def = pp->indir[0];
|
||||
+
|
||||
+ /* Update unicast mapping */
|
||||
+ mvneta_set_rx_mode(pp->dev);
|
||||
+
|
||||
+ /* Update val of portCfg register accordingly with all RxQueue types */
|
||||
+ val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def);
|
||||
+ mvreg_write(pp, MVNETA_PORT_CONFIG, val);
|
||||
+
|
||||
+ /* Update the elected CPU matching the new rxq_def */
|
||||
+ mvneta_percpu_elect(pp);
|
||||
+
|
||||
+ /* We have to synchronise on the napi of each CPU */
|
||||
+ for_each_online_cpu(cpu) {
|
||||
+ struct mvneta_pcpu_port *pcpu_port =
|
||||
+ per_cpu_ptr(pp->ports, cpu);
|
||||
+
|
||||
+ napi_enable(&pcpu_port->napi);
|
||||
+ }
|
||||
+
|
||||
+ netif_tx_start_all_queues(pp->dev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mvneta_ethtool_set_rxfh(struct net_device *dev, const u32 *indir,
|
||||
+ const u8 *key, const u8 hfunc)
|
||||
+{
|
||||
+ struct mvneta_port *pp = netdev_priv(dev);
|
||||
+ /* We require at least one supported parameter to be changed
|
||||
+ * and no change in any of the unsupported parameters
|
||||
+ */
|
||||
+ if (key ||
|
||||
+ (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ if (!indir)
|
||||
+ return 0;
|
||||
+
|
||||
+ memcpy(pp->indir, indir, MVNETA_RSS_LU_TABLE_SIZE);
|
||||
+
|
||||
+ return mvneta_config_rss(pp);
|
||||
+}
|
||||
+
|
||||
+static int mvneta_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
|
||||
+ u8 *hfunc)
|
||||
+{
|
||||
+ struct mvneta_port *pp = netdev_priv(dev);
|
||||
+
|
||||
+ if (hfunc)
|
||||
+ *hfunc = ETH_RSS_HASH_TOP;
|
||||
+
|
||||
+ if (!indir)
|
||||
+ return 0;
|
||||
+
|
||||
+ memcpy(indir, pp->indir, MVNETA_RSS_LU_TABLE_SIZE);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static const struct net_device_ops mvneta_netdev_ops = {
|
||||
.ndo_open = mvneta_open,
|
||||
.ndo_stop = mvneta_stop,
|
||||
@@ -3255,6 +3374,10 @@ const struct ethtool_ops mvneta_eth_tool
|
||||
.get_strings = mvneta_ethtool_get_strings,
|
||||
.get_ethtool_stats = mvneta_ethtool_get_stats,
|
||||
.get_sset_count = mvneta_ethtool_get_sset_count,
|
||||
+ .get_rxfh_indir_size = mvneta_ethtool_get_rxfh_indir_size,
|
||||
+ .get_rxnfc = mvneta_ethtool_get_rxnfc,
|
||||
+ .get_rxfh = mvneta_ethtool_get_rxfh,
|
||||
+ .set_rxfh = mvneta_ethtool_set_rxfh,
|
||||
};
|
||||
|
||||
/* Initialize hw */
|
||||
@@ -3446,6 +3569,8 @@ static int mvneta_probe(struct platform_
|
||||
|
||||
pp->rxq_def = rxq_def;
|
||||
|
||||
+ pp->indir[0] = rxq_def;
|
||||
+
|
||||
pp->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pp->clk)) {
|
||||
err = PTR_ERR(pp->clk);
|
|
@ -0,0 +1,124 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Wed, 9 Dec 2015 18:23:51 +0100
|
||||
Subject: [PATCH] net: mvneta: Configure XPS support
|
||||
|
||||
With this patch each CPU is associated with its own set of TX queues.
|
||||
|
||||
It also setup the XPS with an initial configuration which set the
|
||||
affinity matching the hardware configuration.
|
||||
|
||||
Suggested-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -111,6 +111,7 @@
|
||||
#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff
|
||||
#define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00
|
||||
#define MVNETA_CPU_RXQ_ACCESS(rxq) BIT(rxq)
|
||||
+#define MVNETA_CPU_TXQ_ACCESS(txq) BIT(txq + 8)
|
||||
#define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2))
|
||||
|
||||
/* Exception Interrupt Port/Queue Cause register
|
||||
@@ -514,6 +515,9 @@ struct mvneta_tx_queue {
|
||||
|
||||
/* DMA address of TSO headers */
|
||||
dma_addr_t tso_hdrs_phys;
|
||||
+
|
||||
+ /* Affinity mask for CPUs*/
|
||||
+ cpumask_t affinity_mask;
|
||||
};
|
||||
|
||||
struct mvneta_rx_queue {
|
||||
@@ -1062,20 +1066,30 @@ static void mvneta_defaults_set(struct m
|
||||
/* Enable MBUS Retry bit16 */
|
||||
mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20);
|
||||
|
||||
- /* Set CPU queue access map. CPUs are assigned to the RX
|
||||
- * queues modulo their number and all the TX queues are
|
||||
- * assigned to the CPU associated to the default RX queue.
|
||||
+ /* Set CPU queue access map. CPUs are assigned to the RX and
|
||||
+ * TX queues modulo their number. If there is only one TX
|
||||
+ * queue then it is assigned to the CPU associated to the
|
||||
+ * default RX queue.
|
||||
*/
|
||||
for_each_present_cpu(cpu) {
|
||||
int rxq_map = 0, txq_map = 0;
|
||||
- int rxq;
|
||||
+ int rxq, txq;
|
||||
|
||||
for (rxq = 0; rxq < rxq_number; rxq++)
|
||||
if ((rxq % max_cpu) == cpu)
|
||||
rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
|
||||
|
||||
- if (cpu == pp->rxq_def)
|
||||
- txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
|
||||
+ for (txq = 0; txq < txq_number; txq++)
|
||||
+ if ((txq % max_cpu) == cpu)
|
||||
+ txq_map |= MVNETA_CPU_TXQ_ACCESS(txq);
|
||||
+
|
||||
+ /* With only one TX queue we configure a special case
|
||||
+ * which will allow to get all the irq on a single
|
||||
+ * CPU
|
||||
+ */
|
||||
+ if (txq_number == 1)
|
||||
+ txq_map = (cpu == pp->rxq_def) ?
|
||||
+ MVNETA_CPU_TXQ_ACCESS(1) : 0;
|
||||
|
||||
mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
|
||||
}
|
||||
@@ -2362,6 +2376,8 @@ static void mvneta_rxq_deinit(struct mvn
|
||||
static int mvneta_txq_init(struct mvneta_port *pp,
|
||||
struct mvneta_tx_queue *txq)
|
||||
{
|
||||
+ int cpu;
|
||||
+
|
||||
txq->size = pp->tx_ring_size;
|
||||
|
||||
/* A queue must always have room for at least one skb.
|
||||
@@ -2414,6 +2430,14 @@ static int mvneta_txq_init(struct mvneta
|
||||
}
|
||||
mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
|
||||
|
||||
+ /* Setup XPS mapping */
|
||||
+ if (txq_number > 1)
|
||||
+ cpu = txq->id % num_present_cpus();
|
||||
+ else
|
||||
+ cpu = pp->rxq_def % num_present_cpus();
|
||||
+ cpumask_set_cpu(cpu, &txq->affinity_mask);
|
||||
+ netif_set_xps_queue(pp->dev, &txq->affinity_mask, txq->id);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2836,13 +2860,23 @@ static void mvneta_percpu_elect(struct m
|
||||
if ((rxq % max_cpu) == cpu)
|
||||
rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
|
||||
|
||||
- if (i == online_cpu_idx) {
|
||||
- /* Map the default receive queue and transmit
|
||||
- * queue to the elected CPU
|
||||
+ if (i == online_cpu_idx)
|
||||
+ /* Map the default receive queue queue to the
|
||||
+ * elected CPU
|
||||
*/
|
||||
rxq_map |= MVNETA_CPU_RXQ_ACCESS(pp->rxq_def);
|
||||
- txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
|
||||
- }
|
||||
+
|
||||
+ /* We update the TX queue map only if we have one
|
||||
+ * queue. In this case we associate the TX queue to
|
||||
+ * the CPU bound to the default RX queue
|
||||
+ */
|
||||
+ if (txq_number == 1)
|
||||
+ txq_map = (i == online_cpu_idx) ?
|
||||
+ MVNETA_CPU_TXQ_ACCESS(1) : 0;
|
||||
+ else
|
||||
+ txq_map = mvreg_read(pp, MVNETA_CPU_MAP(cpu)) &
|
||||
+ MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
|
||||
+
|
||||
mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
|
||||
|
||||
/* Update the interrupt mask on each CPU according the
|
|
@ -0,0 +1,46 @@
|
|||
From: Jisheng Zhang <jszhang@marvell.com>
|
||||
Date: Wed, 20 Jan 2016 16:36:25 +0800
|
||||
Subject: [PATCH] net: mvneta: fix trivial cut-off issue in
|
||||
mvneta_ethtool_update_stats
|
||||
|
||||
When s->type is T_REG_64, the high 32bits are lost in val. This patch
|
||||
fixes this trivial issue.
|
||||
|
||||
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
|
||||
Fixes: 9b0cdefa4cd5 ("net: mvneta: add ethtool statistics")
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -3242,26 +3242,25 @@ static void mvneta_ethtool_update_stats(
|
||||
const struct mvneta_statistic *s;
|
||||
void __iomem *base = pp->base;
|
||||
u32 high, low, val;
|
||||
+ u64 val64;
|
||||
int i;
|
||||
|
||||
for (i = 0, s = mvneta_statistics;
|
||||
s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics);
|
||||
s++, i++) {
|
||||
- val = 0;
|
||||
-
|
||||
switch (s->type) {
|
||||
case T_REG_32:
|
||||
val = readl_relaxed(base + s->offset);
|
||||
+ pp->ethtool_stats[i] += val;
|
||||
break;
|
||||
case T_REG_64:
|
||||
/* Docs say to read low 32-bit then high */
|
||||
low = readl_relaxed(base + s->offset);
|
||||
high = readl_relaxed(base + s->offset + 4);
|
||||
- val = (u64)high << 32 | low;
|
||||
+ val64 = (u64)high << 32 | low;
|
||||
+ pp->ethtool_stats[i] += val64;
|
||||
break;
|
||||
}
|
||||
-
|
||||
- pp->ethtool_stats[i] += val;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Thu, 4 Feb 2016 22:09:23 +0100
|
||||
Subject: [PATCH] net: mvneta: Fix for_each_present_cpu usage
|
||||
|
||||
This patch convert the for_each_present in on_each_cpu, instead of
|
||||
applying on the present cpus it will be applied only on the online cpus.
|
||||
This fix a bug reported on
|
||||
http://thread.gmane.org/gmane.linux.ports.arm.kernel/468173.
|
||||
|
||||
Using the macro on_each_cpu (instead of a for_each_* loop) also ensures
|
||||
that all the calls will be done all at once.
|
||||
|
||||
Fixes: f86428854480 ("net: mvneta: Statically assign queues to CPUs")
|
||||
Reported-by: Stefan Roese <stefan.roese@gmail.com>
|
||||
Suggested-by: Jisheng Zhang <jszhang@marvell.com>
|
||||
Suggested-by: Russell King <rmk+kernel@arm.linux.org.uk>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -2562,7 +2562,7 @@ static void mvneta_start_dev(struct mvne
|
||||
mvneta_port_enable(pp);
|
||||
|
||||
/* Enable polling on the port */
|
||||
- for_each_present_cpu(cpu) {
|
||||
+ for_each_online_cpu(cpu) {
|
||||
struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
|
||||
|
||||
napi_enable(&port->napi);
|
||||
@@ -2587,7 +2587,7 @@ static void mvneta_stop_dev(struct mvnet
|
||||
|
||||
phy_stop(pp->phy_dev);
|
||||
|
||||
- for_each_present_cpu(cpu) {
|
||||
+ for_each_online_cpu(cpu) {
|
||||
struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
|
||||
|
||||
napi_disable(&port->napi);
|
||||
@@ -3055,13 +3055,11 @@ err_cleanup_rxqs:
|
||||
static int mvneta_stop(struct net_device *dev)
|
||||
{
|
||||
struct mvneta_port *pp = netdev_priv(dev);
|
||||
- int cpu;
|
||||
|
||||
mvneta_stop_dev(pp);
|
||||
mvneta_mdio_remove(pp);
|
||||
unregister_cpu_notifier(&pp->cpu_notifier);
|
||||
- for_each_present_cpu(cpu)
|
||||
- smp_call_function_single(cpu, mvneta_percpu_disable, pp, true);
|
||||
+ on_each_cpu(mvneta_percpu_disable, pp, true);
|
||||
free_percpu_irq(dev->irq, pp->ports);
|
||||
mvneta_cleanup_rxqs(pp);
|
||||
mvneta_cleanup_txqs(pp);
|
|
@ -0,0 +1,57 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Thu, 4 Feb 2016 22:09:24 +0100
|
||||
Subject: [PATCH] net: mvneta: Fix the CPU choice in mvneta_percpu_elect
|
||||
|
||||
When passing to the management of multiple RX queue, the
|
||||
mvneta_percpu_elect function was broken. The use of the modulo can lead
|
||||
to elect the wrong cpu. For example with rxq_def=2, if the CPU 2 goes
|
||||
offline and then online, we ended with the third RX queue activated in
|
||||
the same time on CPU 0 and CPU2, which lead to a kernel crash.
|
||||
|
||||
With this fix, we don't try to get "the closer" CPU if the default CPU is
|
||||
gone, now we just use CPU 0 which always be there. Thanks to this, the
|
||||
code becomes more readable, easier to maintain and more predicable.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Fixes: 2dcf75e2793c ("net: mvneta: Associate RX queues with each CPU")
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -2847,9 +2847,14 @@ static void mvneta_percpu_disable(void *
|
||||
|
||||
static void mvneta_percpu_elect(struct mvneta_port *pp)
|
||||
{
|
||||
- int online_cpu_idx, max_cpu, cpu, i = 0;
|
||||
+ int elected_cpu = 0, max_cpu, cpu, i = 0;
|
||||
+
|
||||
+ /* Use the cpu associated to the rxq when it is online, in all
|
||||
+ * the other cases, use the cpu 0 which can't be offline.
|
||||
+ */
|
||||
+ if (cpu_online(pp->rxq_def))
|
||||
+ elected_cpu = pp->rxq_def;
|
||||
|
||||
- online_cpu_idx = pp->rxq_def % num_online_cpus();
|
||||
max_cpu = num_present_cpus();
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
@@ -2860,7 +2865,7 @@ static void mvneta_percpu_elect(struct m
|
||||
if ((rxq % max_cpu) == cpu)
|
||||
rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
|
||||
|
||||
- if (i == online_cpu_idx)
|
||||
+ if (cpu == elected_cpu)
|
||||
/* Map the default receive queue queue to the
|
||||
* elected CPU
|
||||
*/
|
||||
@@ -2871,7 +2876,7 @@ static void mvneta_percpu_elect(struct m
|
||||
* the CPU bound to the default RX queue
|
||||
*/
|
||||
if (txq_number == 1)
|
||||
- txq_map = (i == online_cpu_idx) ?
|
||||
+ txq_map = (cpu == elected_cpu) ?
|
||||
MVNETA_CPU_TXQ_ACCESS(1) : 0;
|
||||
else
|
||||
txq_map = mvreg_read(pp, MVNETA_CPU_MAP(cpu)) &
|
|
@ -0,0 +1,68 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Thu, 4 Feb 2016 22:09:25 +0100
|
||||
Subject: [PATCH] net: mvneta: Use on_each_cpu when possible
|
||||
|
||||
Instead of using a for_each_* loop in which we just call the
|
||||
smp_call_function_single macro, it is more simple to directly use the
|
||||
on_each_cpu macro. Moreover, this macro ensures that the calls will be
|
||||
done all at once.
|
||||
|
||||
Suggested-by: Russell King <rmk+kernel@arm.linux.org.uk>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -2553,7 +2553,7 @@ static void mvneta_percpu_mask_interrupt
|
||||
|
||||
static void mvneta_start_dev(struct mvneta_port *pp)
|
||||
{
|
||||
- unsigned int cpu;
|
||||
+ int cpu;
|
||||
|
||||
mvneta_max_rx_size_set(pp, pp->pkt_size);
|
||||
mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
|
||||
@@ -2569,9 +2569,8 @@ static void mvneta_start_dev(struct mvne
|
||||
}
|
||||
|
||||
/* Unmask interrupts. It has to be done from each CPU */
|
||||
- for_each_online_cpu(cpu)
|
||||
- smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt,
|
||||
- pp, true);
|
||||
+ on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
|
||||
+
|
||||
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
|
||||
MVNETA_CAUSE_PHY_STATUS_CHANGE |
|
||||
MVNETA_CAUSE_LINK_CHANGE |
|
||||
@@ -2991,7 +2990,7 @@ static int mvneta_percpu_notifier(struct
|
||||
static int mvneta_open(struct net_device *dev)
|
||||
{
|
||||
struct mvneta_port *pp = netdev_priv(dev);
|
||||
- int ret, cpu;
|
||||
+ int ret;
|
||||
|
||||
pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
|
||||
pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
|
||||
@@ -3024,9 +3023,7 @@ static int mvneta_open(struct net_device
|
||||
/* Enable per-CPU interrupt on all the CPU to handle our RX
|
||||
* queue interrupts
|
||||
*/
|
||||
- for_each_online_cpu(cpu)
|
||||
- smp_call_function_single(cpu, mvneta_percpu_enable,
|
||||
- pp, true);
|
||||
+ on_each_cpu(mvneta_percpu_enable, pp, true);
|
||||
|
||||
|
||||
/* Register a CPU notifier to handle the case where our CPU
|
||||
@@ -3313,9 +3310,7 @@ static int mvneta_config_rss(struct mvn
|
||||
|
||||
netif_tx_stop_all_queues(pp->dev);
|
||||
|
||||
- for_each_online_cpu(cpu)
|
||||
- smp_call_function_single(cpu, mvneta_percpu_mask_interrupt,
|
||||
- pp, true);
|
||||
+ on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
|
||||
|
||||
/* We have to synchronise on the napi of each CPU */
|
||||
for_each_online_cpu(cpu) {
|
|
@ -0,0 +1,179 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Thu, 4 Feb 2016 22:09:27 +0100
|
||||
Subject: [PATCH] net: mvneta: Modify the queue related fields from each cpu
|
||||
|
||||
In the MVNETA_INTR_* registers, the queues related fields are per cpu,
|
||||
according to the datasheet (comment in [] are added by me):
|
||||
"In a multi-CPU system, bits of RX[or TX] queues for which the access by
|
||||
the reading[or writing] CPU is disabled are read as 0, and cannot be
|
||||
cleared[or written]."
|
||||
|
||||
That means that each time we want to manipulate these bits we had to do
|
||||
it on each cpu and not only on the current cpu.
|
||||
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -1036,6 +1036,43 @@ static void mvneta_set_autoneg(struct mv
|
||||
}
|
||||
}
|
||||
|
||||
+static void mvneta_percpu_unmask_interrupt(void *arg)
|
||||
+{
|
||||
+ struct mvneta_port *pp = arg;
|
||||
+
|
||||
+ /* All the queue are unmasked, but actually only the ones
|
||||
+ * mapped to this CPU will be unmasked
|
||||
+ */
|
||||
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
||||
+ MVNETA_RX_INTR_MASK_ALL |
|
||||
+ MVNETA_TX_INTR_MASK_ALL |
|
||||
+ MVNETA_MISCINTR_INTR_MASK);
|
||||
+}
|
||||
+
|
||||
+static void mvneta_percpu_mask_interrupt(void *arg)
|
||||
+{
|
||||
+ struct mvneta_port *pp = arg;
|
||||
+
|
||||
+ /* All the queue are masked, but actually only the ones
|
||||
+ * mapped to this CPU will be masked
|
||||
+ */
|
||||
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
|
||||
+ mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
|
||||
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
|
||||
+}
|
||||
+
|
||||
+static void mvneta_percpu_clear_intr_cause(void *arg)
|
||||
+{
|
||||
+ struct mvneta_port *pp = arg;
|
||||
+
|
||||
+ /* All the queue are cleared, but actually only the ones
|
||||
+ * mapped to this CPU will be cleared
|
||||
+ */
|
||||
+ mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0);
|
||||
+ mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
|
||||
+ mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0);
|
||||
+}
|
||||
+
|
||||
/* This method sets defaults to the NETA port:
|
||||
* Clears interrupt Cause and Mask registers.
|
||||
* Clears all MAC tables.
|
||||
@@ -1053,14 +1090,10 @@ static void mvneta_defaults_set(struct m
|
||||
int max_cpu = num_present_cpus();
|
||||
|
||||
/* Clear all Cause registers */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
|
||||
+ on_each_cpu(mvneta_percpu_clear_intr_cause, pp, true);
|
||||
|
||||
/* Mask all interrupts */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
|
||||
+ on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
|
||||
mvreg_write(pp, MVNETA_INTR_ENABLE, 0);
|
||||
|
||||
/* Enable MBUS Retry bit16 */
|
||||
@@ -2526,31 +2559,6 @@ static int mvneta_setup_txqs(struct mvne
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void mvneta_percpu_unmask_interrupt(void *arg)
|
||||
-{
|
||||
- struct mvneta_port *pp = arg;
|
||||
-
|
||||
- /* All the queue are unmasked, but actually only the ones
|
||||
- * maped to this CPU will be unmasked
|
||||
- */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
||||
- MVNETA_RX_INTR_MASK_ALL |
|
||||
- MVNETA_TX_INTR_MASK_ALL |
|
||||
- MVNETA_MISCINTR_INTR_MASK);
|
||||
-}
|
||||
-
|
||||
-static void mvneta_percpu_mask_interrupt(void *arg)
|
||||
-{
|
||||
- struct mvneta_port *pp = arg;
|
||||
-
|
||||
- /* All the queue are masked, but actually only the ones
|
||||
- * maped to this CPU will be masked
|
||||
- */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
|
||||
-}
|
||||
-
|
||||
static void mvneta_start_dev(struct mvneta_port *pp)
|
||||
{
|
||||
int cpu;
|
||||
@@ -2601,13 +2609,10 @@ static void mvneta_stop_dev(struct mvnet
|
||||
mvneta_port_disable(pp);
|
||||
|
||||
/* Clear all ethernet port interrupts */
|
||||
- mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0);
|
||||
+ on_each_cpu(mvneta_percpu_clear_intr_cause, pp, true);
|
||||
|
||||
/* Mask all ethernet port interrupts */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
|
||||
+ on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
|
||||
|
||||
mvneta_tx_reset(pp);
|
||||
mvneta_rx_reset(pp);
|
||||
@@ -2919,9 +2924,7 @@ static int mvneta_percpu_notifier(struct
|
||||
}
|
||||
|
||||
/* Mask all ethernet port interrupts */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
|
||||
+ on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
|
||||
napi_enable(&port->napi);
|
||||
|
||||
|
||||
@@ -2936,14 +2939,8 @@ static int mvneta_percpu_notifier(struct
|
||||
*/
|
||||
mvneta_percpu_elect(pp);
|
||||
|
||||
- /* Unmask all ethernet port interrupts, as this
|
||||
- * notifier is called for each CPU then the CPU to
|
||||
- * Queue mapping is applied
|
||||
- */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
||||
- MVNETA_RX_INTR_MASK(rxq_number) |
|
||||
- MVNETA_TX_INTR_MASK(txq_number) |
|
||||
- MVNETA_MISCINTR_INTR_MASK);
|
||||
+ /* Unmask all ethernet port interrupts */
|
||||
+ on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
|
||||
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
|
||||
MVNETA_CAUSE_PHY_STATUS_CHANGE |
|
||||
MVNETA_CAUSE_LINK_CHANGE |
|
||||
@@ -2954,9 +2951,7 @@ static int mvneta_percpu_notifier(struct
|
||||
case CPU_DOWN_PREPARE_FROZEN:
|
||||
netif_tx_stop_all_queues(pp->dev);
|
||||
/* Mask all ethernet port interrupts */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
|
||||
- mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
|
||||
+ on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
|
||||
|
||||
napi_synchronize(&port->napi);
|
||||
napi_disable(&port->napi);
|
||||
@@ -2972,10 +2967,7 @@ static int mvneta_percpu_notifier(struct
|
||||
/* Check if a new CPU must be elected now this on is down */
|
||||
mvneta_percpu_elect(pp);
|
||||
/* Unmask all ethernet port interrupts */
|
||||
- mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
||||
- MVNETA_RX_INTR_MASK(rxq_number) |
|
||||
- MVNETA_TX_INTR_MASK(txq_number) |
|
||||
- MVNETA_MISCINTR_INTR_MASK);
|
||||
+ on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
|
||||
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
|
||||
MVNETA_CAUSE_PHY_STATUS_CHANGE |
|
||||
MVNETA_CAUSE_LINK_CHANGE |
|
|
@ -0,0 +1,68 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Thu, 4 Feb 2016 22:09:28 +0100
|
||||
Subject: [PATCH] net: mvneta: The mvneta_percpu_elect function should be
|
||||
atomic
|
||||
|
||||
Electing a CPU must be done in an atomic way: it should be done after or
|
||||
before the removal/insertion of a CPU and this function is not reentrant.
|
||||
|
||||
During the loop of mvneta_percpu_elect we associates the queues to the
|
||||
CPUs, if there is a topology change during this loop, then the mapping
|
||||
between the CPUs and the queues could be wrong. During this loop the
|
||||
interrupt mask is also updating for each CPUs, It should not be changed
|
||||
in the same time by other part of the driver.
|
||||
|
||||
This patch adds spinlock to create the needed critical sections.
|
||||
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -370,6 +370,10 @@ struct mvneta_port {
|
||||
struct net_device *dev;
|
||||
struct notifier_block cpu_notifier;
|
||||
int rxq_def;
|
||||
+ /* Protect the access to the percpu interrupt registers,
|
||||
+ * ensuring that the configuration remains coherent.
|
||||
+ */
|
||||
+ spinlock_t lock;
|
||||
|
||||
/* Core clock */
|
||||
struct clk *clk;
|
||||
@@ -2853,6 +2857,12 @@ static void mvneta_percpu_elect(struct m
|
||||
{
|
||||
int elected_cpu = 0, max_cpu, cpu, i = 0;
|
||||
|
||||
+ /* Electing a CPU must be done in an atomic way: it should be
|
||||
+ * done after or before the removal/insertion of a CPU and
|
||||
+ * this function is not reentrant.
|
||||
+ */
|
||||
+ spin_lock(&pp->lock);
|
||||
+
|
||||
/* Use the cpu associated to the rxq when it is online, in all
|
||||
* the other cases, use the cpu 0 which can't be offline.
|
||||
*/
|
||||
@@ -2896,6 +2906,7 @@ static void mvneta_percpu_elect(struct m
|
||||
i++;
|
||||
|
||||
}
|
||||
+ spin_unlock(&pp->lock);
|
||||
};
|
||||
|
||||
static int mvneta_percpu_notifier(struct notifier_block *nfb,
|
||||
@@ -2950,8 +2961,13 @@ static int mvneta_percpu_notifier(struct
|
||||
case CPU_DOWN_PREPARE:
|
||||
case CPU_DOWN_PREPARE_FROZEN:
|
||||
netif_tx_stop_all_queues(pp->dev);
|
||||
+ /* Thanks to this lock we are sure that any pending
|
||||
+ * cpu election is done
|
||||
+ */
|
||||
+ spin_lock(&pp->lock);
|
||||
/* Mask all ethernet port interrupts */
|
||||
on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
|
||||
+ spin_unlock(&pp->lock);
|
||||
|
||||
napi_synchronize(&port->napi);
|
||||
napi_disable(&port->napi);
|
|
@ -0,0 +1,128 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Thu, 4 Feb 2016 22:09:29 +0100
|
||||
Subject: [PATCH] net: mvneta: Fix race condition during stopping
|
||||
|
||||
When stopping the port, the CPU notifier are still there whereas the
|
||||
mvneta_stop_dev function calls mvneta_percpu_disable() on each CPUs.
|
||||
It was possible to have a new CPU coming at this point which could be
|
||||
racy.
|
||||
|
||||
This patch adds a flag preventing executing the code notifier for a new
|
||||
CPU when the port is stopping. It also uses the spinlock introduces
|
||||
previously. To avoid the deadlock, the lock has been moved outside the
|
||||
mvneta_percpu_elect function.
|
||||
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -374,6 +374,7 @@ struct mvneta_port {
|
||||
* ensuring that the configuration remains coherent.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
+ bool is_stopped;
|
||||
|
||||
/* Core clock */
|
||||
struct clk *clk;
|
||||
@@ -2853,16 +2854,14 @@ static void mvneta_percpu_disable(void *
|
||||
disable_percpu_irq(pp->dev->irq);
|
||||
}
|
||||
|
||||
+/* Electing a CPU must be done in an atomic way: it should be done
|
||||
+ * after or before the removal/insertion of a CPU and this function is
|
||||
+ * not reentrant.
|
||||
+ */
|
||||
static void mvneta_percpu_elect(struct mvneta_port *pp)
|
||||
{
|
||||
int elected_cpu = 0, max_cpu, cpu, i = 0;
|
||||
|
||||
- /* Electing a CPU must be done in an atomic way: it should be
|
||||
- * done after or before the removal/insertion of a CPU and
|
||||
- * this function is not reentrant.
|
||||
- */
|
||||
- spin_lock(&pp->lock);
|
||||
-
|
||||
/* Use the cpu associated to the rxq when it is online, in all
|
||||
* the other cases, use the cpu 0 which can't be offline.
|
||||
*/
|
||||
@@ -2906,7 +2905,6 @@ static void mvneta_percpu_elect(struct m
|
||||
i++;
|
||||
|
||||
}
|
||||
- spin_unlock(&pp->lock);
|
||||
};
|
||||
|
||||
static int mvneta_percpu_notifier(struct notifier_block *nfb,
|
||||
@@ -2920,6 +2918,14 @@ static int mvneta_percpu_notifier(struct
|
||||
switch (action) {
|
||||
case CPU_ONLINE:
|
||||
case CPU_ONLINE_FROZEN:
|
||||
+ spin_lock(&pp->lock);
|
||||
+ /* Configuring the driver for a new CPU while the
|
||||
+ * driver is stopping is racy, so just avoid it.
|
||||
+ */
|
||||
+ if (pp->is_stopped) {
|
||||
+ spin_unlock(&pp->lock);
|
||||
+ break;
|
||||
+ }
|
||||
netif_tx_stop_all_queues(pp->dev);
|
||||
|
||||
/* We have to synchronise on tha napi of each CPU
|
||||
@@ -2957,6 +2963,7 @@ static int mvneta_percpu_notifier(struct
|
||||
MVNETA_CAUSE_LINK_CHANGE |
|
||||
MVNETA_CAUSE_PSC_SYNC_CHANGE);
|
||||
netif_tx_start_all_queues(pp->dev);
|
||||
+ spin_unlock(&pp->lock);
|
||||
break;
|
||||
case CPU_DOWN_PREPARE:
|
||||
case CPU_DOWN_PREPARE_FROZEN:
|
||||
@@ -2981,7 +2988,9 @@ static int mvneta_percpu_notifier(struct
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
/* Check if a new CPU must be elected now this on is down */
|
||||
+ spin_lock(&pp->lock);
|
||||
mvneta_percpu_elect(pp);
|
||||
+ spin_unlock(&pp->lock);
|
||||
/* Unmask all ethernet port interrupts */
|
||||
on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
|
||||
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
|
||||
@@ -3033,7 +3042,7 @@ static int mvneta_open(struct net_device
|
||||
*/
|
||||
on_each_cpu(mvneta_percpu_enable, pp, true);
|
||||
|
||||
-
|
||||
+ pp->is_stopped = false;
|
||||
/* Register a CPU notifier to handle the case where our CPU
|
||||
* might be taken offline.
|
||||
*/
|
||||
@@ -3066,9 +3075,18 @@ static int mvneta_stop(struct net_device
|
||||
{
|
||||
struct mvneta_port *pp = netdev_priv(dev);
|
||||
|
||||
+ /* Inform that we are stopping so we don't want to setup the
|
||||
+ * driver for new CPUs in the notifiers
|
||||
+ */
|
||||
+ spin_lock(&pp->lock);
|
||||
+ pp->is_stopped = true;
|
||||
mvneta_stop_dev(pp);
|
||||
mvneta_mdio_remove(pp);
|
||||
unregister_cpu_notifier(&pp->cpu_notifier);
|
||||
+ /* Now that the notifier are unregistered, we can release le
|
||||
+ * lock
|
||||
+ */
|
||||
+ spin_unlock(&pp->lock);
|
||||
on_each_cpu(mvneta_percpu_disable, pp, true);
|
||||
free_percpu_irq(dev->irq, pp->ports);
|
||||
mvneta_cleanup_rxqs(pp);
|
||||
@@ -3339,7 +3357,9 @@ static int mvneta_config_rss(struct mvn
|
||||
mvreg_write(pp, MVNETA_PORT_CONFIG, val);
|
||||
|
||||
/* Update the elected CPU matching the new rxq_def */
|
||||
+ spin_lock(&pp->lock);
|
||||
mvneta_percpu_elect(pp);
|
||||
+ spin_unlock(&pp->lock);
|
||||
|
||||
/* We have to synchronise on the napi of each CPU */
|
||||
for_each_online_cpu(cpu) {
|
|
@ -0,0 +1,56 @@
|
|||
From: Jisheng Zhang <jszhang@marvell.com>
|
||||
Date: Wed, 20 Jan 2016 19:27:22 +0800
|
||||
Subject: [PATCH] net: mvneta: sort the headers in alphabetic order
|
||||
|
||||
Sorting the headers in alphabetic order will help to reduce the conflict
|
||||
when adding new headers in the future.
|
||||
|
||||
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
|
||||
Acked-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -11,28 +11,28 @@
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
-#include <linux/kernel.h>
|
||||
-#include <linux/netdevice.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/cpu.h>
|
||||
#include <linux/etherdevice.h>
|
||||
-#include <linux/platform_device.h>
|
||||
-#include <linux/skbuff.h>
|
||||
+#include <linux/if_vlan.h>
|
||||
#include <linux/inetdevice.h>
|
||||
-#include <linux/mbus.h>
|
||||
-#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
-#include <linux/if_vlan.h>
|
||||
-#include <net/ip.h>
|
||||
-#include <net/ipv6.h>
|
||||
#include <linux/io.h>
|
||||
-#include <net/tso.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/mbus.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
+#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/of_net.h>
|
||||
-#include <linux/of_address.h>
|
||||
#include <linux/phy.h>
|
||||
-#include <linux/clk.h>
|
||||
-#include <linux/cpu.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <net/ip.h>
|
||||
+#include <net/ipv6.h>
|
||||
+#include <net/tso.h>
|
||||
|
||||
/* Registers */
|
||||
#define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2))
|
|
@ -0,0 +1,159 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Mon, 14 Mar 2016 09:39:04 +0100
|
||||
Subject: [PATCH] net: add a hardware buffer management helper API
|
||||
|
||||
This basic implementation allows to share code between driver using
|
||||
hardware buffer management. As the code is hardware agnostic, there is
|
||||
few helpers, most of the optimization brought by the an HW BM has to be
|
||||
done at driver level.
|
||||
|
||||
Tested-by: Sebastian Careba <nitroshift@yahoo.com>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
create mode 100644 include/net/hwbm.h
|
||||
create mode 100644 net/core/hwbm.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/include/net/hwbm.h
|
||||
@@ -0,0 +1,28 @@
|
||||
+#ifndef _HWBM_H
|
||||
+#define _HWBM_H
|
||||
+
|
||||
+struct hwbm_pool {
|
||||
+ /* Capacity of the pool */
|
||||
+ int size;
|
||||
+ /* Size of the buffers managed */
|
||||
+ int frag_size;
|
||||
+ /* Number of buffers currently used by this pool */
|
||||
+ int buf_num;
|
||||
+ /* constructor called during alocation */
|
||||
+ int (*construct)(struct hwbm_pool *bm_pool, void *buf);
|
||||
+ /* protect acces to the buffer counter*/
|
||||
+ spinlock_t lock;
|
||||
+ /* private data */
|
||||
+ void *priv;
|
||||
+};
|
||||
+#ifdef CONFIG_HWBM
|
||||
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf);
|
||||
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp);
|
||||
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp);
|
||||
+#else
|
||||
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf) {}
|
||||
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp) { return 0; }
|
||||
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
|
||||
+{ return 0; }
|
||||
+#endif /* CONFIG_HWBM */
|
||||
+#endif /* _HWBM_H */
|
||||
--- a/net/Kconfig
|
||||
+++ b/net/Kconfig
|
||||
@@ -259,6 +259,9 @@ config XPS
|
||||
depends on SMP
|
||||
default y
|
||||
|
||||
+config HWBM
|
||||
+ bool
|
||||
+
|
||||
config CGROUP_NET_PRIO
|
||||
bool "Network priority cgroup"
|
||||
depends on CGROUPS
|
||||
--- a/net/core/Makefile
|
||||
+++ b/net/core/Makefile
|
||||
@@ -14,6 +14,7 @@ obj-y += dev.o ethtool.o dev_addr_
|
||||
obj-$(CONFIG_SOCK_DIAG) += sock_diag.o
|
||||
obj-$(CONFIG_XFRM) += flow.o
|
||||
obj-y += net-sysfs.o
|
||||
+obj-$(CONFIG_HWBM) += hwbm.o
|
||||
obj-$(CONFIG_PROC_FS) += net-procfs.o
|
||||
obj-$(CONFIG_NET_PKTGEN) += pktgen.o
|
||||
obj-$(CONFIG_NETPOLL) += netpoll.o
|
||||
--- /dev/null
|
||||
+++ b/net/core/hwbm.c
|
||||
@@ -0,0 +1,87 @@
|
||||
+/* Support for hardware buffer manager.
|
||||
+ *
|
||||
+ * Copyright (C) 2016 Marvell
|
||||
+ *
|
||||
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ */
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/printk.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <net/hwbm.h>
|
||||
+
|
||||
+void hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf)
|
||||
+{
|
||||
+ if (likely(bm_pool->frag_size <= PAGE_SIZE))
|
||||
+ skb_free_frag(buf);
|
||||
+ else
|
||||
+ kfree(buf);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(hwbm_buf_free);
|
||||
+
|
||||
+/* Refill processing for HW buffer management */
|
||||
+int hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp)
|
||||
+{
|
||||
+ int frag_size = bm_pool->frag_size;
|
||||
+ void *buf;
|
||||
+
|
||||
+ if (likely(frag_size <= PAGE_SIZE))
|
||||
+ buf = netdev_alloc_frag(frag_size);
|
||||
+ else
|
||||
+ buf = kmalloc(frag_size, gfp);
|
||||
+
|
||||
+ if (!buf)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ if (bm_pool->construct)
|
||||
+ if (bm_pool->construct(bm_pool, buf)) {
|
||||
+ hwbm_buf_free(bm_pool, buf);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(hwbm_pool_refill);
|
||||
+
|
||||
+int hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num, gfp_t gfp)
|
||||
+{
|
||||
+ int err, i;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&bm_pool->lock, flags);
|
||||
+ if (bm_pool->buf_num == bm_pool->size) {
|
||||
+ pr_warn("pool already filled\n");
|
||||
+ return bm_pool->buf_num;
|
||||
+ }
|
||||
+
|
||||
+ if (buf_num + bm_pool->buf_num > bm_pool->size) {
|
||||
+ pr_warn("cannot allocate %d buffers for pool\n",
|
||||
+ buf_num);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ if ((buf_num + bm_pool->buf_num) < bm_pool->buf_num) {
|
||||
+ pr_warn("Adding %d buffers to the %d current buffers will overflow\n",
|
||||
+ buf_num, bm_pool->buf_num);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < buf_num; i++) {
|
||||
+ err = hwbm_pool_refill(bm_pool, gfp);
|
||||
+ if (err < 0)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Update BM driver with number of buffers added to pool */
|
||||
+ bm_pool->buf_num += i;
|
||||
+
|
||||
+ pr_debug("hwpm pool: %d of %d buffers added\n", i, buf_num);
|
||||
+ spin_unlock_irqrestore(&bm_pool->lock, flags);
|
||||
+
|
||||
+ return i;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(hwbm_pool_add);
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,359 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Mon, 14 Mar 2016 09:39:05 +0100
|
||||
Subject: [PATCH] net: mvneta: Use the new hwbm framework
|
||||
|
||||
Now that the hardware buffer management framework had been introduced,
|
||||
let's use it.
|
||||
|
||||
Tested-by: Sebastian Careba <nitroshift@yahoo.com>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/Kconfig
|
||||
+++ b/drivers/net/ethernet/marvell/Kconfig
|
||||
@@ -43,6 +43,7 @@ config MVMDIO
|
||||
config MVNETA_BM
|
||||
tristate "Marvell Armada 38x/XP network interface BM support"
|
||||
depends on MVNETA
|
||||
+ select HWBM
|
||||
---help---
|
||||
This driver supports auxiliary block of the network
|
||||
interface units in the Marvell ARMADA XP and ARMADA 38x SoC
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <linux/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/skbuff.h>
|
||||
+#include <net/hwbm.h>
|
||||
#include "mvneta_bm.h"
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
@@ -1024,11 +1025,12 @@ static int mvneta_bm_port_init(struct pl
|
||||
static void mvneta_bm_update_mtu(struct mvneta_port *pp, int mtu)
|
||||
{
|
||||
struct mvneta_bm_pool *bm_pool = pp->pool_long;
|
||||
+ struct hwbm_pool *hwbm_pool = &bm_pool->hwbm_pool;
|
||||
int num;
|
||||
|
||||
/* Release all buffers from long pool */
|
||||
mvneta_bm_bufs_free(pp->bm_priv, bm_pool, 1 << pp->id);
|
||||
- if (bm_pool->buf_num) {
|
||||
+ if (hwbm_pool->buf_num) {
|
||||
WARN(1, "cannot free all buffers in pool %d\n",
|
||||
bm_pool->id);
|
||||
goto bm_mtu_err;
|
||||
@@ -1036,14 +1038,14 @@ static void mvneta_bm_update_mtu(struct
|
||||
|
||||
bm_pool->pkt_size = MVNETA_RX_PKT_SIZE(mtu);
|
||||
bm_pool->buf_size = MVNETA_RX_BUF_SIZE(bm_pool->pkt_size);
|
||||
- bm_pool->frag_size = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) +
|
||||
- SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(bm_pool->pkt_size));
|
||||
+ hwbm_pool->frag_size = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) +
|
||||
+ SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(bm_pool->pkt_size));
|
||||
|
||||
/* Fill entire long pool */
|
||||
- num = mvneta_bm_bufs_add(pp->bm_priv, bm_pool, bm_pool->size);
|
||||
- if (num != bm_pool->size) {
|
||||
+ num = hwbm_pool_add(hwbm_pool, hwbm_pool->size, GFP_ATOMIC);
|
||||
+ if (num != hwbm_pool->size) {
|
||||
WARN(1, "pool %d: %d of %d allocated\n",
|
||||
- bm_pool->id, num, bm_pool->size);
|
||||
+ bm_pool->id, num, hwbm_pool->size);
|
||||
goto bm_mtu_err;
|
||||
}
|
||||
mvneta_bm_pool_bufsize_set(pp, bm_pool->buf_size, bm_pool->id);
|
||||
@@ -2064,14 +2066,14 @@ err_drop_frame:
|
||||
}
|
||||
|
||||
/* Refill processing */
|
||||
- err = mvneta_bm_pool_refill(pp->bm_priv, bm_pool);
|
||||
+ err = hwbm_pool_refill(&bm_pool->hwbm_pool, GFP_ATOMIC);
|
||||
if (err) {
|
||||
netdev_err(dev, "Linux processing - Can't refill\n");
|
||||
rxq->missed++;
|
||||
goto err_drop_frame_ret_pool;
|
||||
}
|
||||
|
||||
- frag_size = bm_pool->frag_size;
|
||||
+ frag_size = bm_pool->hwbm_pool.frag_size;
|
||||
|
||||
skb = build_skb(data, frag_size > PAGE_SIZE ? 0 : frag_size);
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta_bm.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta_bm.c
|
||||
@@ -10,16 +10,17 @@
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
-#include <linux/kernel.h>
|
||||
+#include <linux/clk.h>
|
||||
#include <linux/genalloc.h>
|
||||
-#include <linux/platform_device.h>
|
||||
-#include <linux/netdevice.h>
|
||||
-#include <linux/skbuff.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/kernel.h>
|
||||
#include <linux/mbus.h>
|
||||
#include <linux/module.h>
|
||||
-#include <linux/io.h>
|
||||
+#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
-#include <linux/clk.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <net/hwbm.h>
|
||||
#include "mvneta_bm.h"
|
||||
|
||||
#define MVNETA_BM_DRIVER_NAME "mvneta_bm"
|
||||
@@ -88,17 +89,13 @@ static void mvneta_bm_pool_target_set(st
|
||||
mvneta_bm_write(priv, MVNETA_BM_XBAR_POOL_REG(pool_id), val);
|
||||
}
|
||||
|
||||
-/* Allocate skb for BM pool */
|
||||
-void *mvneta_buf_alloc(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
|
||||
- dma_addr_t *buf_phys_addr)
|
||||
+int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf)
|
||||
{
|
||||
- void *buf;
|
||||
+ struct mvneta_bm_pool *bm_pool =
|
||||
+ (struct mvneta_bm_pool *)hwbm_pool->priv;
|
||||
+ struct mvneta_bm *priv = bm_pool->priv;
|
||||
dma_addr_t phys_addr;
|
||||
|
||||
- buf = mvneta_frag_alloc(bm_pool->frag_size);
|
||||
- if (!buf)
|
||||
- return NULL;
|
||||
-
|
||||
/* In order to update buf_cookie field of RX descriptor properly,
|
||||
* BM hardware expects buf virtual address to be placed in the
|
||||
* first four bytes of mapped buffer.
|
||||
@@ -106,75 +103,13 @@ void *mvneta_buf_alloc(struct mvneta_bm
|
||||
*(u32 *)buf = (u32)buf;
|
||||
phys_addr = dma_map_single(&priv->pdev->dev, buf, bm_pool->buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
- if (unlikely(dma_mapping_error(&priv->pdev->dev, phys_addr))) {
|
||||
- mvneta_frag_free(bm_pool->frag_size, buf);
|
||||
- return NULL;
|
||||
- }
|
||||
- *buf_phys_addr = phys_addr;
|
||||
-
|
||||
- return buf;
|
||||
-}
|
||||
-
|
||||
-/* Refill processing for HW buffer management */
|
||||
-int mvneta_bm_pool_refill(struct mvneta_bm *priv,
|
||||
- struct mvneta_bm_pool *bm_pool)
|
||||
-{
|
||||
- dma_addr_t buf_phys_addr;
|
||||
- void *buf;
|
||||
-
|
||||
- buf = mvneta_buf_alloc(priv, bm_pool, &buf_phys_addr);
|
||||
- if (!buf)
|
||||
+ if (unlikely(dma_mapping_error(&priv->pdev->dev, phys_addr)))
|
||||
return -ENOMEM;
|
||||
|
||||
- mvneta_bm_pool_put_bp(priv, bm_pool, buf_phys_addr);
|
||||
-
|
||||
+ mvneta_bm_pool_put_bp(priv, bm_pool, phys_addr);
|
||||
return 0;
|
||||
}
|
||||
-EXPORT_SYMBOL_GPL(mvneta_bm_pool_refill);
|
||||
-
|
||||
-/* Allocate buffers for the pool */
|
||||
-int mvneta_bm_bufs_add(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
|
||||
- int buf_num)
|
||||
-{
|
||||
- int err, i;
|
||||
-
|
||||
- if (bm_pool->buf_num == bm_pool->size) {
|
||||
- dev_dbg(&priv->pdev->dev, "pool %d already filled\n",
|
||||
- bm_pool->id);
|
||||
- return bm_pool->buf_num;
|
||||
- }
|
||||
-
|
||||
- if (buf_num < 0 ||
|
||||
- (buf_num + bm_pool->buf_num > bm_pool->size)) {
|
||||
- dev_err(&priv->pdev->dev,
|
||||
- "cannot allocate %d buffers for pool %d\n",
|
||||
- buf_num, bm_pool->id);
|
||||
- return 0;
|
||||
- }
|
||||
-
|
||||
- for (i = 0; i < buf_num; i++) {
|
||||
- err = mvneta_bm_pool_refill(priv, bm_pool);
|
||||
- if (err < 0)
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- /* Update BM driver with number of buffers added to pool */
|
||||
- bm_pool->buf_num += i;
|
||||
-
|
||||
- dev_dbg(&priv->pdev->dev,
|
||||
- "%s pool %d: pkt_size=%4d, buf_size=%4d, frag_size=%4d\n",
|
||||
- bm_pool->type == MVNETA_BM_SHORT ? "short" : "long",
|
||||
- bm_pool->id, bm_pool->pkt_size, bm_pool->buf_size,
|
||||
- bm_pool->frag_size);
|
||||
-
|
||||
- dev_dbg(&priv->pdev->dev,
|
||||
- "%s pool %d: %d of %d buffers added\n",
|
||||
- bm_pool->type == MVNETA_BM_SHORT ? "short" : "long",
|
||||
- bm_pool->id, i, buf_num);
|
||||
-
|
||||
- return i;
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(mvneta_bm_bufs_add);
|
||||
+EXPORT_SYMBOL_GPL(mvneta_bm_construct);
|
||||
|
||||
/* Create pool */
|
||||
static int mvneta_bm_pool_create(struct mvneta_bm *priv,
|
||||
@@ -183,8 +118,7 @@ static int mvneta_bm_pool_create(struct
|
||||
struct platform_device *pdev = priv->pdev;
|
||||
u8 target_id, attr;
|
||||
int size_bytes, err;
|
||||
-
|
||||
- size_bytes = sizeof(u32) * bm_pool->size;
|
||||
+ size_bytes = sizeof(u32) * bm_pool->hwbm_pool.size;
|
||||
bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, size_bytes,
|
||||
&bm_pool->phys_addr,
|
||||
GFP_KERNEL);
|
||||
@@ -245,11 +179,16 @@ struct mvneta_bm_pool *mvneta_bm_pool_us
|
||||
|
||||
/* Allocate buffers in case BM pool hasn't been used yet */
|
||||
if (new_pool->type == MVNETA_BM_FREE) {
|
||||
+ struct hwbm_pool *hwbm_pool = &new_pool->hwbm_pool;
|
||||
+
|
||||
+ new_pool->priv = priv;
|
||||
new_pool->type = type;
|
||||
new_pool->buf_size = MVNETA_RX_BUF_SIZE(new_pool->pkt_size);
|
||||
- new_pool->frag_size =
|
||||
+ hwbm_pool->frag_size =
|
||||
SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(new_pool->pkt_size)) +
|
||||
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
||||
+ hwbm_pool->construct = mvneta_bm_construct;
|
||||
+ hwbm_pool->priv = new_pool;
|
||||
|
||||
/* Create new pool */
|
||||
err = mvneta_bm_pool_create(priv, new_pool);
|
||||
@@ -260,10 +199,10 @@ struct mvneta_bm_pool *mvneta_bm_pool_us
|
||||
}
|
||||
|
||||
/* Allocate buffers for this pool */
|
||||
- num = mvneta_bm_bufs_add(priv, new_pool, new_pool->size);
|
||||
- if (num != new_pool->size) {
|
||||
+ num = hwbm_pool_add(hwbm_pool, hwbm_pool->size, GFP_ATOMIC);
|
||||
+ if (num != hwbm_pool->size) {
|
||||
WARN(1, "pool %d: %d of %d allocated\n",
|
||||
- new_pool->id, num, new_pool->size);
|
||||
+ new_pool->id, num, hwbm_pool->size);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -284,7 +223,7 @@ void mvneta_bm_bufs_free(struct mvneta_b
|
||||
|
||||
mvneta_bm_config_set(priv, MVNETA_BM_EMPTY_LIMIT_MASK);
|
||||
|
||||
- for (i = 0; i < bm_pool->buf_num; i++) {
|
||||
+ for (i = 0; i < bm_pool->hwbm_pool.buf_num; i++) {
|
||||
dma_addr_t buf_phys_addr;
|
||||
u32 *vaddr;
|
||||
|
||||
@@ -303,13 +242,13 @@ void mvneta_bm_bufs_free(struct mvneta_b
|
||||
|
||||
dma_unmap_single(&priv->pdev->dev, buf_phys_addr,
|
||||
bm_pool->buf_size, DMA_FROM_DEVICE);
|
||||
- mvneta_frag_free(bm_pool->frag_size, vaddr);
|
||||
+ hwbm_buf_free(&bm_pool->hwbm_pool, vaddr);
|
||||
}
|
||||
|
||||
mvneta_bm_config_clear(priv, MVNETA_BM_EMPTY_LIMIT_MASK);
|
||||
|
||||
/* Update BM driver with number of buffers removed from pool */
|
||||
- bm_pool->buf_num -= i;
|
||||
+ bm_pool->hwbm_pool.buf_num -= i;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mvneta_bm_bufs_free);
|
||||
|
||||
@@ -317,6 +256,7 @@ EXPORT_SYMBOL_GPL(mvneta_bm_bufs_free);
|
||||
void mvneta_bm_pool_destroy(struct mvneta_bm *priv,
|
||||
struct mvneta_bm_pool *bm_pool, u8 port_map)
|
||||
{
|
||||
+ struct hwbm_pool *hwbm_pool = &bm_pool->hwbm_pool;
|
||||
bm_pool->port_map &= ~port_map;
|
||||
if (bm_pool->port_map)
|
||||
return;
|
||||
@@ -324,11 +264,12 @@ void mvneta_bm_pool_destroy(struct mvnet
|
||||
bm_pool->type = MVNETA_BM_FREE;
|
||||
|
||||
mvneta_bm_bufs_free(priv, bm_pool, port_map);
|
||||
- if (bm_pool->buf_num)
|
||||
+ if (hwbm_pool->buf_num)
|
||||
WARN(1, "cannot free all buffers in pool %d\n", bm_pool->id);
|
||||
|
||||
if (bm_pool->virt_addr) {
|
||||
- dma_free_coherent(&priv->pdev->dev, sizeof(u32) * bm_pool->size,
|
||||
+ dma_free_coherent(&priv->pdev->dev,
|
||||
+ sizeof(u32) * hwbm_pool->size,
|
||||
bm_pool->virt_addr, bm_pool->phys_addr);
|
||||
bm_pool->virt_addr = NULL;
|
||||
}
|
||||
@@ -381,10 +322,10 @@ static void mvneta_bm_pools_init(struct
|
||||
MVNETA_BM_POOL_CAP_ALIGN));
|
||||
size = ALIGN(size, MVNETA_BM_POOL_CAP_ALIGN);
|
||||
}
|
||||
- bm_pool->size = size;
|
||||
+ bm_pool->hwbm_pool.size = size;
|
||||
|
||||
mvneta_bm_write(priv, MVNETA_BM_POOL_SIZE_REG(i),
|
||||
- bm_pool->size);
|
||||
+ bm_pool->hwbm_pool.size);
|
||||
|
||||
/* Obtain custom pkt_size from DT */
|
||||
sprintf(prop, "pool%d,pkt-size", i);
|
||||
--- a/drivers/net/ethernet/marvell/mvneta_bm.h
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta_bm.h
|
||||
@@ -108,20 +108,15 @@ struct mvneta_bm {
|
||||
};
|
||||
|
||||
struct mvneta_bm_pool {
|
||||
+ struct hwbm_pool hwbm_pool;
|
||||
/* Pool number in the range 0-3 */
|
||||
u8 id;
|
||||
enum mvneta_bm_type type;
|
||||
|
||||
- /* Buffer Pointers Pool External (BPPE) size in number of bytes */
|
||||
- int size;
|
||||
- /* Number of buffers used by this pool */
|
||||
- int buf_num;
|
||||
- /* Pool buffer size */
|
||||
- int buf_size;
|
||||
/* Packet size */
|
||||
int pkt_size;
|
||||
- /* Single frag size */
|
||||
- u32 frag_size;
|
||||
+ /* Size of the buffer acces through DMA*/
|
||||
+ u32 buf_size;
|
||||
|
||||
/* BPPE virtual base address */
|
||||
u32 *virt_addr;
|
||||
@@ -143,8 +138,7 @@ void mvneta_bm_pool_destroy(struct mvnet
|
||||
struct mvneta_bm_pool *bm_pool, u8 port_map);
|
||||
void mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
|
||||
u8 port_map);
|
||||
-int mvneta_bm_bufs_add(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
|
||||
- int buf_num);
|
||||
+int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf);
|
||||
int mvneta_bm_pool_refill(struct mvneta_bm *priv,
|
||||
struct mvneta_bm_pool *bm_pool);
|
||||
struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
|
||||
@@ -170,8 +164,7 @@ void mvneta_bm_pool_destroy(struct mvnet
|
||||
struct mvneta_bm_pool *bm_pool, u8 port_map) {}
|
||||
void mvneta_bm_bufs_free(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
|
||||
u8 port_map) {}
|
||||
-int mvneta_bm_bufs_add(struct mvneta_bm *priv, struct mvneta_bm_pool *bm_pool,
|
||||
- int buf_num) { return 0; }
|
||||
+int mvneta_bm_construct(struct hwbm_pool *hwbm_pool, void *buf) { return 0; }
|
||||
int mvneta_bm_pool_refill(struct mvneta_bm *priv,
|
||||
struct mvneta_bm_pool *bm_pool) {return 0; }
|
||||
struct mvneta_bm_pool *mvneta_bm_pool_use(struct mvneta_bm *priv, u8 pool_id,
|
|
@ -0,0 +1,52 @@
|
|||
From: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Date: Sat, 12 Mar 2016 18:44:17 +0100
|
||||
Subject: [PATCH] net: mvneta: Fix spinlock usage
|
||||
|
||||
In the previous patch, the spinlock was not initialized. While it didn't
|
||||
cause any trouble yet it could be a problem to use it uninitialized.
|
||||
|
||||
The most annoying part was the critical section protected by the spinlock
|
||||
in mvneta_stop(). Some of the functions could sleep as pointed when
|
||||
activated CONFIG_DEBUG_ATOMIC_SLEEP. Actually, in mvneta_stop() we only
|
||||
need to protect the is_stopped flagged, indeed the code of the notifier
|
||||
for CPU online is protected by the same spinlock, so when we get the
|
||||
lock, the notifer work is done.
|
||||
|
||||
Reported-by: Patrick Uiterwijk <patrick@puiterwijk.org>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -3484,17 +3484,17 @@ static int mvneta_stop(struct net_device
|
||||
struct mvneta_port *pp = netdev_priv(dev);
|
||||
|
||||
/* Inform that we are stopping so we don't want to setup the
|
||||
- * driver for new CPUs in the notifiers
|
||||
+ * driver for new CPUs in the notifiers. The code of the
|
||||
+ * notifier for CPU online is protected by the same spinlock,
|
||||
+ * so when we get the lock, the notifer work is done.
|
||||
*/
|
||||
spin_lock(&pp->lock);
|
||||
pp->is_stopped = true;
|
||||
+ spin_unlock(&pp->lock);
|
||||
+
|
||||
mvneta_stop_dev(pp);
|
||||
mvneta_mdio_remove(pp);
|
||||
unregister_cpu_notifier(&pp->cpu_notifier);
|
||||
- /* Now that the notifier are unregistered, we can release le
|
||||
- * lock
|
||||
- */
|
||||
- spin_unlock(&pp->lock);
|
||||
on_each_cpu(mvneta_percpu_disable, pp, true);
|
||||
free_percpu_irq(dev->irq, pp->ports);
|
||||
mvneta_cleanup_rxqs(pp);
|
||||
@@ -4027,6 +4027,7 @@ static int mvneta_probe(struct platform_
|
||||
dev->ethtool_ops = &mvneta_eth_tool_ops;
|
||||
|
||||
pp = netdev_priv(dev);
|
||||
+ spin_lock_init(&pp->lock);
|
||||
pp->phy_node = phy_node;
|
||||
pp->phy_interface = phy_mode;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
From: Dmitri Epshtein <dima@marvell.com>
|
||||
Date: Sat, 12 Mar 2016 18:44:19 +0100
|
||||
Subject: [PATCH] net: mvneta: fix error messages in mvneta_port_down function
|
||||
|
||||
This commit corrects error printing when shutting down the port.
|
||||
|
||||
[gregory.clement@free-electrons.com: split initial commit in two
|
||||
individual changes]
|
||||
Signed-off-by: Dmitri Epshtein <dima@marvell.com>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -1105,7 +1105,7 @@ static void mvneta_port_down(struct mvne
|
||||
do {
|
||||
if (count++ >= MVNETA_RX_DISABLE_TIMEOUT_MSEC) {
|
||||
netdev_warn(pp->dev,
|
||||
- "TIMEOUT for RX stopped ! rx_queue_cmd: 0x08%x\n",
|
||||
+ "TIMEOUT for RX stopped ! rx_queue_cmd: 0x%08x\n",
|
||||
val);
|
||||
break;
|
||||
}
|
||||
@@ -1144,7 +1144,7 @@ static void mvneta_port_down(struct mvne
|
||||
do {
|
||||
if (count++ >= MVNETA_TX_FIFO_EMPTY_TIMEOUT) {
|
||||
netdev_warn(pp->dev,
|
||||
- "TX FIFO empty timeout status=0x08%x\n",
|
||||
+ "TX FIFO empty timeout status=0x%08x\n",
|
||||
val);
|
||||
break;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
From: Jisheng Zhang <jszhang@marvell.com>
|
||||
Date: Wed, 30 Mar 2016 19:55:21 +0800
|
||||
Subject: [PATCH] net: mvneta: replace MVNETA_CPU_D_CACHE_LINE_SIZE with
|
||||
L1_CACHE_BYTES
|
||||
|
||||
The mvneta is also used in some Marvell berlin family SoCs which may
|
||||
have 64bytes cacheline size. Replace the MVNETA_CPU_D_CACHE_LINE_SIZE
|
||||
usage with L1_CACHE_BYTES.
|
||||
|
||||
And since dma_alloc_coherent() is always cacheline size aligned, so
|
||||
remove the align checks.
|
||||
|
||||
Signed-off-by: Jisheng Zhang <jszhang@marvell.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -260,7 +260,6 @@
|
||||
|
||||
#define MVNETA_VLAN_TAG_LEN 4
|
||||
|
||||
-#define MVNETA_CPU_D_CACHE_LINE_SIZE 32
|
||||
#define MVNETA_TX_CSUM_DEF_SIZE 1600
|
||||
#define MVNETA_TX_CSUM_MAX_SIZE 9800
|
||||
#define MVNETA_ACC_MODE_EXT1 1
|
||||
@@ -300,7 +299,7 @@
|
||||
#define MVNETA_RX_PKT_SIZE(mtu) \
|
||||
ALIGN((mtu) + MVNETA_MH_SIZE + MVNETA_VLAN_TAG_LEN + \
|
||||
ETH_HLEN + ETH_FCS_LEN, \
|
||||
- MVNETA_CPU_D_CACHE_LINE_SIZE)
|
||||
+ L1_CACHE_BYTES)
|
||||
|
||||
#define IS_TSO_HEADER(txq, addr) \
|
||||
((addr >= txq->tso_hdrs_phys) && \
|
||||
@@ -2762,9 +2761,6 @@ static int mvneta_rxq_init(struct mvneta
|
||||
if (rxq->descs == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
- BUG_ON(rxq->descs !=
|
||||
- PTR_ALIGN(rxq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
|
||||
-
|
||||
rxq->last_desc = rxq->size - 1;
|
||||
|
||||
/* Set Rx descriptors queue starting address */
|
||||
@@ -2835,10 +2831,6 @@ static int mvneta_txq_init(struct mvneta
|
||||
if (txq->descs == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
- /* Make sure descriptor address is cache line size aligned */
|
||||
- BUG_ON(txq->descs !=
|
||||
- PTR_ALIGN(txq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
|
||||
-
|
||||
txq->last_desc = txq->size - 1;
|
||||
|
||||
/* Set maximum bandwidth for enabled TXQs */
|
|
@ -0,0 +1,75 @@
|
|||
From: Marcin Wojtas <mw@semihalf.com>
|
||||
Date: Fri, 1 Apr 2016 15:21:18 +0200
|
||||
Subject: [PATCH] net: mvneta: fix changing MTU when using per-cpu processing
|
||||
|
||||
After enabling per-cpu processing it appeared that under heavy load
|
||||
changing MTU can result in blocking all port's interrupts and
|
||||
transmitting data is not possible after the change.
|
||||
|
||||
This commit fixes above issue by disabling percpu interrupts for the
|
||||
time, when TXQs and RXQs are reconfigured.
|
||||
|
||||
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/drivers/net/ethernet/marvell/mvneta.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
||||
@@ -3040,6 +3040,20 @@ static int mvneta_check_mtu_valid(struct
|
||||
return mtu;
|
||||
}
|
||||
|
||||
+static void mvneta_percpu_enable(void *arg)
|
||||
+{
|
||||
+ struct mvneta_port *pp = arg;
|
||||
+
|
||||
+ enable_percpu_irq(pp->dev->irq, IRQ_TYPE_NONE);
|
||||
+}
|
||||
+
|
||||
+static void mvneta_percpu_disable(void *arg)
|
||||
+{
|
||||
+ struct mvneta_port *pp = arg;
|
||||
+
|
||||
+ disable_percpu_irq(pp->dev->irq);
|
||||
+}
|
||||
+
|
||||
/* Change the device mtu */
|
||||
static int mvneta_change_mtu(struct net_device *dev, int mtu)
|
||||
{
|
||||
@@ -3064,6 +3078,7 @@ static int mvneta_change_mtu(struct net_
|
||||
* reallocation of the queues
|
||||
*/
|
||||
mvneta_stop_dev(pp);
|
||||
+ on_each_cpu(mvneta_percpu_disable, pp, true);
|
||||
|
||||
mvneta_cleanup_txqs(pp);
|
||||
mvneta_cleanup_rxqs(pp);
|
||||
@@ -3087,6 +3102,7 @@ static int mvneta_change_mtu(struct net_
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ on_each_cpu(mvneta_percpu_enable, pp, true);
|
||||
mvneta_start_dev(pp);
|
||||
mvneta_port_up(pp);
|
||||
|
||||
@@ -3240,20 +3256,6 @@ static void mvneta_mdio_remove(struct mv
|
||||
pp->phy_dev = NULL;
|
||||
}
|
||||
|
||||
-static void mvneta_percpu_enable(void *arg)
|
||||
-{
|
||||
- struct mvneta_port *pp = arg;
|
||||
-
|
||||
- enable_percpu_irq(pp->dev->irq, IRQ_TYPE_NONE);
|
||||
-}
|
||||
-
|
||||
-static void mvneta_percpu_disable(void *arg)
|
||||
-{
|
||||
- struct mvneta_port *pp = arg;
|
||||
-
|
||||
- disable_percpu_irq(pp->dev->irq);
|
||||
-}
|
||||
-
|
||||
/* Electing a CPU must be done in an atomic way: it should be done
|
||||
* after or before the removal/insertion of a CPU and this function is
|
||||
* not reentrant.
|
|
@ -0,0 +1,53 @@
|
|||
From: Marcin Wojtas <mw@semihalf.com>
|
||||
Date: Mon, 14 Mar 2016 09:38:57 +0100
|
||||
Subject: [PATCH] ARM: dts: armada-38x: add buffer manager nodes
|
||||
|
||||
Armada 38x network controller supports hardware buffer management (BM).
|
||||
Since it is now enabled in mvneta driver, appropriate nodes can be added
|
||||
to armada-38x.dtsi - for the actual common BM unit (bm@c8000) and its
|
||||
internal SRAM (bm-bppi), which is used for indirect access to buffer
|
||||
pointer ring residing in DRAM.
|
||||
|
||||
Pools - ports mapping, bm-bppi entry in 'soc' node's ranges and optional
|
||||
parameters are supposed to be set in board files.
|
||||
|
||||
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-38x.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-38x.dtsi
|
||||
@@ -540,6 +540,14 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
+ bm: bm@c8000 {
|
||||
+ compatible = "marvell,armada-380-neta-bm";
|
||||
+ reg = <0xc8000 0xac>;
|
||||
+ clocks = <&gateclk 13>;
|
||||
+ internal-mem = <&bm_bppi>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
sata@e0000 {
|
||||
compatible = "marvell,armada-380-ahci";
|
||||
reg = <0xe0000 0x2000>;
|
||||
@@ -618,6 +626,17 @@
|
||||
#size-cells = <1>;
|
||||
ranges = <0 MBUS_ID(0x09, 0x15) 0 0x800>;
|
||||
};
|
||||
+
|
||||
+ bm_bppi: bm-bppi {
|
||||
+ compatible = "mmio-sram";
|
||||
+ reg = <MBUS_ID(0x0c, 0x04) 0 0x100000>;
|
||||
+ ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>;
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ clocks = <&gateclk 13>;
|
||||
+ no-memory-wc;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
};
|
||||
|
||||
clocks {
|
|
@ -0,0 +1,53 @@
|
|||
From: Marcin Wojtas <mw@semihalf.com>
|
||||
Date: Mon, 14 Mar 2016 09:38:59 +0100
|
||||
Subject: [PATCH] ARM: dts: armada-xp: add buffer manager nodes
|
||||
|
||||
Armada XP network controller supports hardware buffer management (BM).
|
||||
Since it is now enabled in mvneta driver, appropriate nodes can be added
|
||||
to armada-xp.dtsi - for the actual common BM unit (bm@c0000) and its
|
||||
internal SRAM (bm-bppi), which is used for indirect access to buffer
|
||||
pointer ring residing in DRAM.
|
||||
|
||||
Pools - ports mapping, bm-bppi entry in 'soc' node's ranges and optional
|
||||
parameters are supposed to be set in board files.
|
||||
|
||||
Signed-off-by: Marcin Wojtas <mw@semihalf.com>
|
||||
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-xp.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-xp.dtsi
|
||||
@@ -253,6 +253,14 @@
|
||||
marvell,crypto-sram-size = <0x800>;
|
||||
};
|
||||
|
||||
+ bm: bm@c0000 {
|
||||
+ compatible = "marvell,armada-380-neta-bm";
|
||||
+ reg = <0xc0000 0xac>;
|
||||
+ clocks = <&gateclk 13>;
|
||||
+ internal-mem = <&bm_bppi>;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
+
|
||||
xor@f0900 {
|
||||
compatible = "marvell,orion-xor";
|
||||
reg = <0xF0900 0x100
|
||||
@@ -291,6 +299,17 @@
|
||||
#size-cells = <1>;
|
||||
ranges = <0 MBUS_ID(0x09, 0x05) 0 0x800>;
|
||||
};
|
||||
+
|
||||
+ bm_bppi: bm-bppi {
|
||||
+ compatible = "mmio-sram";
|
||||
+ reg = <MBUS_ID(0x0c, 0x04) 0 0x100000>;
|
||||
+ ranges = <0 MBUS_ID(0x0c, 0x04) 0 0x100000>;
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <1>;
|
||||
+ clocks = <&gateclk 13>;
|
||||
+ no-memory-wc;
|
||||
+ status = "disabled";
|
||||
+ };
|
||||
};
|
||||
|
||||
clocks {
|
|
@ -0,0 +1,107 @@
|
|||
--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
|
||||
@@ -59,7 +59,8 @@
|
||||
ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
|
||||
MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
|
||||
MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
|
||||
- MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
|
||||
+ MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
|
||||
+ MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
|
||||
|
||||
internal-regs {
|
||||
|
||||
@@ -93,6 +94,9 @@
|
||||
ethernet@70000 {
|
||||
status = "okay";
|
||||
phy-mode = "rgmii-id";
|
||||
+ buffer-manager = <&bm>;
|
||||
+ bm,pool-long = <0>;
|
||||
+ bm,pool-short = <3>;
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
@@ -102,6 +106,9 @@
|
||||
ethernet@34000 {
|
||||
status = "okay";
|
||||
phy-mode = "sgmii";
|
||||
+ buffer-manager = <&bm>;
|
||||
+ bm,pool-long = <2>;
|
||||
+ bm,pool-short = <3>;
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
@@ -112,6 +119,10 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
+ bm@c8000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+
|
||||
sata@a8000 {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -198,6 +209,10 @@
|
||||
};
|
||||
};
|
||||
|
||||
+ bm-bppi {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+
|
||||
pcie-controller {
|
||||
status = "okay";
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
|
||||
+++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
|
||||
@@ -71,7 +71,8 @@
|
||||
ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
|
||||
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
|
||||
MBUS_ID(0x09, 0x09) 0 0 0xf1100000 0x10000
|
||||
- MBUS_ID(0x09, 0x05) 0 0 0xf1110000 0x10000>;
|
||||
+ MBUS_ID(0x09, 0x05) 0 0 0xf1110000 0x10000
|
||||
+ MBUS_ID(0x0c, 0x04) 0 0 0xf1200000 0x100000>;
|
||||
|
||||
pcie-controller {
|
||||
status = "okay";
|
||||
@@ -205,6 +206,9 @@
|
||||
pinctrl-names = "default";
|
||||
status = "okay";
|
||||
phy-mode = "rgmii-id";
|
||||
+ buffer-manager = <&bm>;
|
||||
+ bm,pool-long = <0>;
|
||||
+ bm,pool-short = <3>;
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
@@ -216,12 +220,19 @@
|
||||
pinctrl-names = "default";
|
||||
status = "okay";
|
||||
phy-mode = "rgmii-id";
|
||||
+ buffer-manager = <&bm>;
|
||||
+ bm,pool-long = <1>;
|
||||
+ bm,pool-short = <3>;
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
|
||||
+ bm@c0000 {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
+
|
||||
/* USB part of the eSATA/USB 2.0 port */
|
||||
usb@50000 {
|
||||
status = "okay";
|
||||
@@ -379,6 +390,10 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
+
|
||||
+ bm-bppi {
|
||||
+ status = "okay";
|
||||
+ };
|
||||
};
|
||||
|
||||
gpio_keys {
|
|
@ -9,7 +9,7 @@ Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
|||
|
||||
--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
|
||||
+++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
|
||||
@@ -414,13 +414,11 @@
|
||||
@@ -428,13 +428,11 @@
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue