* [RFC PATCH net-next 19/20] net: dsa: mv88e6xxx: factorize tag protocol
From: Vivien Didelot @ 2016-05-05 22:41 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
Andrew Lunn, Vivien Didelot
In-Reply-To: <1462488064-1841-1-git-send-email-vivien.didelot@savoirfairelinux.com>
6131 is the only driver to set the tag protocol to DSA_TAG_PROTO_DSA.
Since it works fine with DSA_TAG_PROTO_EDSA, change its value, like all
other mv88e6xxx drivers.
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
drivers/net/dsa/Kconfig | 2 +-
drivers/net/dsa/mv88e6131.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4aaadce..7e01dce 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -17,7 +17,7 @@ config NET_DSA_MV88E6131
tristate "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support"
depends on NET_DSA
select NET_DSA_MV88E6XXX
- select NET_DSA_TAG_DSA
+ select NET_DSA_TAG_EDSA
---help---
This enables support for the Marvell 88E6085/6095/6095F/6131
ethernet switch chips.
diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c
index cf7d710..81640f6 100644
--- a/drivers/net/dsa/mv88e6131.c
+++ b/drivers/net/dsa/mv88e6131.c
@@ -75,7 +75,7 @@ static const char *mv88e6131_drv_probe(struct device *dsa_dev,
}
struct dsa_switch_driver mv88e6131_switch_driver = {
- .tag_protocol = DSA_TAG_PROTO_DSA,
+ .tag_protocol = DSA_TAG_PROTO_EDSA,
.probe = mv88e6131_drv_probe,
.setup = mv88e6xxx_setup,
.set_addr = mv88e6xxx_set_addr,
--
2.8.2
^ permalink raw reply related
* [RFC PATCH net-next 20/20] net: dsa: mv88e6xxx: factorize the switch driver
From: Vivien Didelot @ 2016-05-05 22:41 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
Andrew Lunn, Vivien Didelot
In-Reply-To: <1462488064-1841-1-git-send-email-vivien.didelot@savoirfairelinux.com>
Now that all drivers support the same set of functions and the same
setup code, drop every model-specific DSA switch driver and replace them
with a common mv88e6xxx driver.
This merges the info tables into one, removes the function exports, the
model-specific files, and update the defconfigs.
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
arch/arm/configs/multi_v5_defconfig | 5 +-
arch/arm/configs/mvebu_v7_defconfig | 2 +-
arch/arm/configs/orion5x_defconfig | 3 +-
arch/tile/configs/tilegx_defconfig | 3 +-
arch/tile/configs/tilepro_defconfig | 3 +-
drivers/net/dsa/Kconfig | 34 +--
drivers/net/dsa/Makefile | 15 +-
drivers/net/dsa/mv88e6123.c | 96 --------
drivers/net/dsa/mv88e6131.c | 117 ---------
drivers/net/dsa/mv88e6171.c | 129 ----------
drivers/net/dsa/mv88e6352.c | 181 --------------
drivers/net/dsa/mv88e6xxx.c | 461 +++++++++++++++++++++++++++++-------
drivers/net/dsa/mv88e6xxx.h | 94 ++------
13 files changed, 406 insertions(+), 737 deletions(-)
delete mode 100644 drivers/net/dsa/mv88e6123.c
delete mode 100644 drivers/net/dsa/mv88e6131.c
delete mode 100644 drivers/net/dsa/mv88e6171.c
delete mode 100644 drivers/net/dsa/mv88e6352.c
diff --git a/arch/arm/configs/multi_v5_defconfig b/arch/arm/configs/multi_v5_defconfig
index e11d99d..690352d 100644
--- a/arch/arm/configs/multi_v5_defconfig
+++ b/arch/arm/configs/multi_v5_defconfig
@@ -91,10 +91,7 @@ CONFIG_SATA_AHCI=y
CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y
CONFIG_NET_DSA_MV88E6060=y
-CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123=y
-CONFIG_NET_DSA_MV88E6171=y
-CONFIG_NET_DSA_MV88E6352=y
+CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_MV643XX_ETH=y
CONFIG_R8169=y
CONFIG_MARVELL_PHY=y
diff --git a/arch/arm/configs/mvebu_v7_defconfig b/arch/arm/configs/mvebu_v7_defconfig
index dc5797a..6492407 100644
--- a/arch/arm/configs/mvebu_v7_defconfig
+++ b/arch/arm/configs/mvebu_v7_defconfig
@@ -66,7 +66,7 @@ CONFIG_SATA_AHCI=y
CONFIG_AHCI_MVEBU=y
CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y
-CONFIG_NET_DSA_MV88E6171=y
+CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_MV643XX_ETH=y
CONFIG_MVNETA=y
CONFIG_MVPP2=y
diff --git a/arch/arm/configs/orion5x_defconfig b/arch/arm/configs/orion5x_defconfig
index 6a5bc27..27a70a7 100644
--- a/arch/arm/configs/orion5x_defconfig
+++ b/arch/arm/configs/orion5x_defconfig
@@ -85,8 +85,7 @@ CONFIG_ATA=y
CONFIG_SATA_MV=y
CONFIG_NETDEVICES=y
CONFIG_MII=y
-CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123=y
+CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_MV643XX_ETH=y
CONFIG_MARVELL_PHY=y
# CONFIG_INPUT_MOUSEDEV is not set
diff --git a/arch/tile/configs/tilegx_defconfig b/arch/tile/configs/tilegx_defconfig
index 3f3dfb8..7189055 100644
--- a/arch/tile/configs/tilegx_defconfig
+++ b/arch/tile/configs/tilegx_defconfig
@@ -221,8 +221,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_TUN=y
CONFIG_VETH=m
CONFIG_NET_DSA_MV88E6060=y
-CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123=y
+CONFIG_NET_DSA_MV88E6XXX=y
CONFIG_SKY2=y
CONFIG_PTP_1588_CLOCK_TILEGX=y
# CONFIG_WLAN is not set
diff --git a/arch/tile/configs/tilepro_defconfig b/arch/tile/configs/tilepro_defconfig
index ef9e27e..dc85468 100644
--- a/arch/tile/configs/tilepro_defconfig
+++ b/arch/tile/configs/tilepro_defconfig
@@ -340,8 +340,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_TUN=y
CONFIG_VETH=m
CONFIG_NET_DSA_MV88E6060=y
-CONFIG_NET_DSA_MV88E6131=y
-CONFIG_NET_DSA_MV88E6123=y
+CONFIG_NET_DSA_MV88E6XXX=y
# CONFIG_NET_VENDOR_3COM is not set
CONFIG_E1000E=y
# CONFIG_WLAN is not set
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 7e01dce..b95fa9f 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -1,10 +1,6 @@
menu "Distributed Switch Architecture drivers"
depends on HAVE_NET_DSA
-config NET_DSA_MV88E6XXX
- tristate
- default n
-
config NET_DSA_MV88E6060
tristate "Marvell 88E6060 ethernet switch chip support"
depends on NET_DSA
@@ -13,42 +9,14 @@ config NET_DSA_MV88E6060
This enables support for the Marvell 88E6060 ethernet switch
chip.
-config NET_DSA_MV88E6131
+config NET_DSA_MV88E6XXX
tristate "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support"
depends on NET_DSA
- select NET_DSA_MV88E6XXX
select NET_DSA_TAG_EDSA
---help---
This enables support for the Marvell 88E6085/6095/6095F/6131
ethernet switch chips.
-config NET_DSA_MV88E6123
- tristate "Marvell 88E6123/6161/6165 ethernet switch chip support"
- depends on NET_DSA
- select NET_DSA_MV88E6XXX
- select NET_DSA_TAG_EDSA
- ---help---
- This enables support for the Marvell 88E6123/6161/6165
- ethernet switch chips.
-
-config NET_DSA_MV88E6171
- tristate "Marvell 88E6171/6175/6350/6351 ethernet switch chip support"
- depends on NET_DSA
- select NET_DSA_MV88E6XXX
- select NET_DSA_TAG_EDSA
- ---help---
- This enables support for the Marvell 88E6171/6175/6350/6351
- ethernet switches chips.
-
-config NET_DSA_MV88E6352
- tristate "Marvell 88E6172/6176/6320/6321/6352 ethernet switch chip support"
- depends on NET_DSA
- select NET_DSA_MV88E6XXX
- select NET_DSA_TAG_EDSA
- ---help---
- This enables support for the Marvell 88E6172, 88E6176, 88E6320,
- 88E6321 and 88E6352 ethernet switch chips.
-
config NET_DSA_BCM_SF2
tristate "Broadcom Starfighter 2 Ethernet switch support"
depends on HAS_IOMEM && NET_DSA
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index a6e0993..76b751d 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -1,16 +1,3 @@
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
-obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx_drv.o
-mv88e6xxx_drv-y += mv88e6xxx.o
-ifdef CONFIG_NET_DSA_MV88E6123
-mv88e6xxx_drv-y += mv88e6123.o
-endif
-ifdef CONFIG_NET_DSA_MV88E6131
-mv88e6xxx_drv-y += mv88e6131.o
-endif
-ifdef CONFIG_NET_DSA_MV88E6352
-mv88e6xxx_drv-y += mv88e6352.o
-endif
-ifdef CONFIG_NET_DSA_MV88E6171
-mv88e6xxx_drv-y += mv88e6171.o
-endif
+obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o
diff --git a/drivers/net/dsa/mv88e6123.c b/drivers/net/dsa/mv88e6123.c
deleted file mode 100644
index bad4b8d..0000000
--- a/drivers/net/dsa/mv88e6123.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * net/dsa/mv88e6123_61_65.c - Marvell 88e6123/6161/6165 switch chip support
- * Copyright (c) 2008-2009 Marvell Semiconductor
- *
- * 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/delay.h>
-#include <linux/jiffies.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/phy.h>
-#include <net/dsa.h>
-#include "mv88e6xxx.h"
-
-static const struct mv88e6xxx_info mv88e6123_table[] = {
- {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
- .family = MV88E6XXX_FAMILY_6165,
- .name = "Marvell 88E6123",
- .num_databases = 4096,
- .num_ports = 3,
- .flags = MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
- .family = MV88E6XXX_FAMILY_6165,
- .name = "Marvell 88E6161",
- .num_databases = 4096,
- .num_ports = 6,
- .flags = MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
- .family = MV88E6XXX_FAMILY_6165,
- .name = "Marvell 88E6165",
- .num_databases = 4096,
- .num_ports = 6,
- .flags = MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP,
- }
-};
-
-static const char *mv88e6123_drv_probe(struct device *dsa_dev,
- struct device *host_dev, int sw_addr,
- void **priv)
-{
- return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
- mv88e6123_table,
- ARRAY_SIZE(mv88e6123_table));
-}
-
-struct dsa_switch_driver mv88e6123_switch_driver = {
- .tag_protocol = DSA_TAG_PROTO_EDSA,
- .probe = mv88e6123_drv_probe,
- .setup = mv88e6xxx_setup,
- .set_addr = mv88e6xxx_set_addr,
- .phy_read = mv88e6xxx_phy_read,
- .phy_write = mv88e6xxx_phy_write,
- .set_eee = mv88e6xxx_set_eee,
- .get_eee = mv88e6xxx_get_eee,
- .get_strings = mv88e6xxx_get_strings,
- .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
- .get_sset_count = mv88e6xxx_get_sset_count,
- .adjust_link = mv88e6xxx_adjust_link,
-#ifdef CONFIG_NET_DSA_HWMON
- .get_temp = mv88e6xxx_get_temp,
- .get_temp_limit = mv88e6xxx_get_temp_limit,
- .set_temp_limit = mv88e6xxx_set_temp_limit,
- .get_temp_alarm = mv88e6xxx_get_temp_alarm,
-#endif
- .get_eeprom = mv88e6xxx_get_eeprom,
- .set_eeprom = mv88e6xxx_set_eeprom,
- .get_regs_len = mv88e6xxx_get_regs_len,
- .get_regs = mv88e6xxx_get_regs,
- .port_bridge_join = mv88e6xxx_port_bridge_join,
- .port_bridge_leave = mv88e6xxx_port_bridge_leave,
- .port_stp_state_set = mv88e6xxx_port_stp_state_set,
- .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
- .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
- .port_vlan_add = mv88e6xxx_port_vlan_add,
- .port_vlan_del = mv88e6xxx_port_vlan_del,
- .port_vlan_dump = mv88e6xxx_port_vlan_dump,
- .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
- .port_fdb_add = mv88e6xxx_port_fdb_add,
- .port_fdb_del = mv88e6xxx_port_fdb_del,
- .port_fdb_dump = mv88e6xxx_port_fdb_dump,
-};
-
-MODULE_ALIAS("platform:mv88e6123");
-MODULE_ALIAS("platform:mv88e6161");
-MODULE_ALIAS("platform:mv88e6165");
diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c
deleted file mode 100644
index 81640f6..0000000
--- a/drivers/net/dsa/mv88e6131.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * net/dsa/mv88e6131.c - Marvell 88e6095/6095f/6131 switch chip support
- * Copyright (c) 2008-2009 Marvell Semiconductor
- *
- * 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/delay.h>
-#include <linux/jiffies.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/phy.h>
-#include <net/dsa.h>
-#include "mv88e6xxx.h"
-
-static const struct mv88e6xxx_info mv88e6131_table[] = {
- {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
- .family = MV88E6XXX_FAMILY_6095,
- .name = "Marvell 88E6095/88E6095F",
- .num_databases = 256,
- .num_ports = 11,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_CORE_TAG_TYPE |
- MV88E6XXX_FLAG_PPU |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
- .family = MV88E6XXX_FAMILY_6097,
- .name = "Marvell 88E6085",
- .num_databases = 4096,
- .num_ports = 10,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_CORE_TAG_TYPE |
- MV88E6XXX_FLAG_PPU |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
- .family = MV88E6XXX_FAMILY_6185,
- .name = "Marvell 88E6131",
- .num_databases = 256,
- .num_ports = 8,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_CORE_TAG_TYPE |
- MV88E6XXX_FLAG_PPU |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
- .family = MV88E6XXX_FAMILY_6185,
- .name = "Marvell 88E6185",
- .num_databases = 256,
- .num_ports = 10,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_CORE_TAG_TYPE |
- MV88E6XXX_FLAG_PPU |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }
-};
-
-static const char *mv88e6131_drv_probe(struct device *dsa_dev,
- struct device *host_dev, int sw_addr,
- void **priv)
-{
- return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
- mv88e6131_table,
- ARRAY_SIZE(mv88e6131_table));
-}
-
-struct dsa_switch_driver mv88e6131_switch_driver = {
- .tag_protocol = DSA_TAG_PROTO_EDSA,
- .probe = mv88e6131_drv_probe,
- .setup = mv88e6xxx_setup,
- .set_addr = mv88e6xxx_set_addr,
- .phy_read = mv88e6xxx_phy_read,
- .phy_write = mv88e6xxx_phy_write,
- .set_eee = mv88e6xxx_set_eee,
- .get_eee = mv88e6xxx_get_eee,
- .get_strings = mv88e6xxx_get_strings,
- .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
- .get_sset_count = mv88e6xxx_get_sset_count,
- .get_eeprom = mv88e6xxx_get_eeprom,
- .set_eeprom = mv88e6xxx_set_eeprom,
- .get_regs_len = mv88e6xxx_get_regs_len,
- .get_regs = mv88e6xxx_get_regs,
-#ifdef CONFIG_NET_DSA_HWMON
- .get_temp = mv88e6xxx_get_temp,
- .get_temp_limit = mv88e6xxx_get_temp_limit,
- .set_temp_limit = mv88e6xxx_set_temp_limit,
- .get_temp_alarm = mv88e6xxx_get_temp_alarm,
-#endif
- .adjust_link = mv88e6xxx_adjust_link,
- .port_bridge_join = mv88e6xxx_port_bridge_join,
- .port_bridge_leave = mv88e6xxx_port_bridge_leave,
- .port_stp_state_set = mv88e6xxx_port_stp_state_set,
- .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
- .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
- .port_vlan_add = mv88e6xxx_port_vlan_add,
- .port_vlan_del = mv88e6xxx_port_vlan_del,
- .port_vlan_dump = mv88e6xxx_port_vlan_dump,
- .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
- .port_fdb_add = mv88e6xxx_port_fdb_add,
- .port_fdb_del = mv88e6xxx_port_fdb_del,
- .port_fdb_dump = mv88e6xxx_port_fdb_dump,
-};
-
-MODULE_ALIAS("platform:mv88e6085");
-MODULE_ALIAS("platform:mv88e6095");
-MODULE_ALIAS("platform:mv88e6095f");
-MODULE_ALIAS("platform:mv88e6131");
diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c
deleted file mode 100644
index a5f32dc..0000000
--- a/drivers/net/dsa/mv88e6171.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/* net/dsa/mv88e6171.c - Marvell 88e6171 switch chip support
- * Copyright (c) 2008-2009 Marvell Semiconductor
- * Copyright (c) 2014 Claudio Leite <leitec@staticky.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/delay.h>
-#include <linux/jiffies.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/phy.h>
-#include <net/dsa.h>
-#include "mv88e6xxx.h"
-
-static const struct mv88e6xxx_info mv88e6171_table[] = {
- {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
- .family = MV88E6XXX_FAMILY_6351,
- .name = "Marvell 88E6171",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
- .family = MV88E6XXX_FAMILY_6351,
- .name = "Marvell 88E6175",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
- .family = MV88E6XXX_FAMILY_6351,
- .name = "Marvell 88E6350",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
- .family = MV88E6XXX_FAMILY_6351,
- .name = "Marvell 88E6351",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }
-};
-
-static const char *mv88e6171_drv_probe(struct device *dsa_dev,
- struct device *host_dev, int sw_addr,
- void **priv)
-{
- return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
- mv88e6171_table,
- ARRAY_SIZE(mv88e6171_table));
-}
-
-struct dsa_switch_driver mv88e6171_switch_driver = {
- .tag_protocol = DSA_TAG_PROTO_EDSA,
- .probe = mv88e6171_drv_probe,
- .setup = mv88e6xxx_setup,
- .set_addr = mv88e6xxx_set_addr,
- .phy_read = mv88e6xxx_phy_read,
- .phy_write = mv88e6xxx_phy_write,
- .set_eee = mv88e6xxx_set_eee,
- .get_eee = mv88e6xxx_get_eee,
- .get_strings = mv88e6xxx_get_strings,
- .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
- .get_sset_count = mv88e6xxx_get_sset_count,
- .adjust_link = mv88e6xxx_adjust_link,
-#ifdef CONFIG_NET_DSA_HWMON
- .get_temp = mv88e6xxx_get_temp,
- .get_temp_limit = mv88e6xxx_get_temp_limit,
- .set_temp_limit = mv88e6xxx_set_temp_limit,
- .get_temp_alarm = mv88e6xxx_get_temp_alarm,
-#endif
- .get_eeprom = mv88e6xxx_get_eeprom,
- .set_eeprom = mv88e6xxx_set_eeprom,
- .get_regs_len = mv88e6xxx_get_regs_len,
- .get_regs = mv88e6xxx_get_regs,
- .port_bridge_join = mv88e6xxx_port_bridge_join,
- .port_bridge_leave = mv88e6xxx_port_bridge_leave,
- .port_stp_state_set = mv88e6xxx_port_stp_state_set,
- .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
- .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
- .port_vlan_add = mv88e6xxx_port_vlan_add,
- .port_vlan_del = mv88e6xxx_port_vlan_del,
- .port_vlan_dump = mv88e6xxx_port_vlan_dump,
- .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
- .port_fdb_add = mv88e6xxx_port_fdb_add,
- .port_fdb_del = mv88e6xxx_port_fdb_del,
- .port_fdb_dump = mv88e6xxx_port_fdb_dump,
-};
-
-MODULE_ALIAS("platform:mv88e6171");
-MODULE_ALIAS("platform:mv88e6175");
-MODULE_ALIAS("platform:mv88e6350");
-MODULE_ALIAS("platform:mv88e6351");
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
deleted file mode 100644
index d34eff5..0000000
--- a/drivers/net/dsa/mv88e6352.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * net/dsa/mv88e6352.c - Marvell 88e6352 switch chip support
- *
- * Copyright (c) 2014 Guenter Roeck
- *
- * Derived from mv88e6123_61_65.c
- * Copyright (c) 2008-2009 Marvell Semiconductor
- *
- * 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/delay.h>
-#include <linux/jiffies.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/platform_device.h>
-#include <linux/phy.h>
-#include <net/dsa.h>
-#include "mv88e6xxx.h"
-
-static const struct mv88e6xxx_info mv88e6352_table[] = {
- {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
- .family = MV88E6XXX_FAMILY_6320,
- .name = "Marvell 88E6320",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_EEE |
- MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_TEMP_LIMIT |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
- .family = MV88E6XXX_FAMILY_6320,
- .name = "Marvell 88E6321",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_EEE |
- MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_TEMP_LIMIT |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
- .family = MV88E6XXX_FAMILY_6352,
- .name = "Marvell 88E6172",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_EEE |
- MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_TEMP_LIMIT |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
- .family = MV88E6XXX_FAMILY_6352,
- .name = "Marvell 88E6176",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_EEE |
- MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_TEMP_LIMIT |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
- .family = MV88E6XXX_FAMILY_6352,
- .name = "Marvell 88E6240",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_EEE |
- MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_TEMP_LIMIT |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }, {
- .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
- .family = MV88E6XXX_FAMILY_6352,
- .name = "Marvell 88E6352",
- .num_databases = 4096,
- .num_ports = 7,
- .flags = MV88E6XXX_FLAG_ATU |
- MV88E6XXX_FLAG_EEE |
- MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_PORTSTATE |
- MV88E6XXX_FLAG_PPU_ACTIVE |
- MV88E6XXX_FLAG_SMI_PHY |
- MV88E6XXX_FLAG_SWITCH_MAC |
- MV88E6XXX_FLAG_TEMP |
- MV88E6XXX_FLAG_TEMP_LIMIT |
- MV88E6XXX_FLAG_VLANTABLE |
- MV88E6XXX_FLAG_VTU,
- }
-};
-
-static const char *mv88e6352_drv_probe(struct device *dsa_dev,
- struct device *host_dev, int sw_addr,
- void **priv)
-{
- return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv,
- mv88e6352_table,
- ARRAY_SIZE(mv88e6352_table));
-}
-
-struct dsa_switch_driver mv88e6352_switch_driver = {
- .tag_protocol = DSA_TAG_PROTO_EDSA,
- .probe = mv88e6352_drv_probe,
- .setup = mv88e6xxx_setup,
- .set_addr = mv88e6xxx_set_addr,
- .phy_read = mv88e6xxx_phy_read,
- .phy_write = mv88e6xxx_phy_write,
- .get_strings = mv88e6xxx_get_strings,
- .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
- .get_sset_count = mv88e6xxx_get_sset_count,
- .adjust_link = mv88e6xxx_adjust_link,
- .set_eee = mv88e6xxx_set_eee,
- .get_eee = mv88e6xxx_get_eee,
-#ifdef CONFIG_NET_DSA_HWMON
- .get_temp = mv88e6xxx_get_temp,
- .get_temp_limit = mv88e6xxx_get_temp_limit,
- .set_temp_limit = mv88e6xxx_set_temp_limit,
- .get_temp_alarm = mv88e6xxx_get_temp_alarm,
-#endif
- .get_eeprom = mv88e6xxx_get_eeprom,
- .set_eeprom = mv88e6xxx_set_eeprom,
- .get_regs_len = mv88e6xxx_get_regs_len,
- .get_regs = mv88e6xxx_get_regs,
- .port_bridge_join = mv88e6xxx_port_bridge_join,
- .port_bridge_leave = mv88e6xxx_port_bridge_leave,
- .port_stp_state_set = mv88e6xxx_port_stp_state_set,
- .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
- .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
- .port_vlan_add = mv88e6xxx_port_vlan_add,
- .port_vlan_del = mv88e6xxx_port_vlan_del,
- .port_vlan_dump = mv88e6xxx_port_vlan_dump,
- .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
- .port_fdb_add = mv88e6xxx_port_fdb_add,
- .port_fdb_del = mv88e6xxx_port_fdb_del,
- .port_fdb_dump = mv88e6xxx_port_fdb_dump,
-};
-
-MODULE_ALIAS("platform:mv88e6172");
-MODULE_ALIAS("platform:mv88e6176");
-MODULE_ALIAS("platform:mv88e6320");
-MODULE_ALIAS("platform:mv88e6321");
-MODULE_ALIAS("platform:mv88e6352");
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index ad0513d..fabfbd1 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -386,8 +386,8 @@ static bool mv88e6xxx_has_stu(struct mv88e6xxx_priv_state *ps)
* phy. However, in the case of a fixed link phy, we force the port
* settings from the fixed link settings.
*/
-void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
- struct phy_device *phydev)
+static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
+ struct phy_device *phydev)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u32 reg;
@@ -633,7 +633,8 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps,
return value;
}
-void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
+ uint8_t *data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_hw_stat *stat;
@@ -649,7 +650,7 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
}
}
-int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
+static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_hw_stat *stat;
@@ -663,9 +664,8 @@ int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
return j;
}
-void
-mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
- int port, uint64_t *data)
+static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_hw_stat *stat;
@@ -690,13 +690,13 @@ mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
mutex_unlock(&ps->smi_mutex);
}
-int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
+static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
{
return 32 * sizeof(u16);
}
-void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
- struct ethtool_regs *regs, void *_p)
+static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
+ struct ethtool_regs *regs, void *_p)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 *p = _p;
@@ -846,8 +846,8 @@ static int _mv88e6xxx_get_eeprom(struct mv88e6xxx_priv_state *ps,
return 0;
}
-int mv88e6xxx_get_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
- u8 *data)
+static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
+ struct ethtool_eeprom *eeprom, u8 *data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int err;
@@ -976,8 +976,8 @@ static int _mv88e6xxx_set_eeprom(struct mv88e6xxx_priv_state *ps,
return 0;
}
-int mv88e6xxx_set_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
- u8 *data)
+static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
+ struct ethtool_eeprom *eeprom, u8 *data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int err;
@@ -1031,7 +1031,8 @@ static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps,
return _mv88e6xxx_phy_wait(ps);
}
-int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
+static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
+ struct ethtool_eee *e)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int reg;
@@ -1060,8 +1061,8 @@ out:
return reg;
}
-int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
- struct phy_device *phydev, struct ethtool_eee *e)
+static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
+ struct phy_device *phydev, struct ethtool_eee *e)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int reg;
@@ -1354,7 +1355,8 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
}
-void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
+ u8 state)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int stp_state;
@@ -1577,9 +1579,9 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
return 0;
}
-int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
- struct switchdev_obj_port_vlan *vlan,
- int (*cb)(struct switchdev_obj *obj))
+static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_vlan *vlan,
+ int (*cb)(struct switchdev_obj *obj))
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_vtu_stu_entry next;
@@ -2004,8 +2006,8 @@ static const char * const mv88e6xxx_port_8021q_mode_names[] = {
[PORT_CONTROL_2_8021Q_SECURE] = "Secure",
};
-int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
- bool vlan_filtering)
+static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
+ bool vlan_filtering)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
@@ -2044,9 +2046,9 @@ unlock:
return ret;
}
-int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct switchdev_trans *trans)
+static int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int err;
@@ -2085,9 +2087,9 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
}
-void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct switchdev_trans *trans)
+static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct switchdev_trans *trans)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
@@ -2147,8 +2149,8 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
}
-int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan)
+static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 pvid, vid;
@@ -2261,9 +2263,9 @@ static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
return _mv88e6xxx_atu_load(ps, &entry);
}
-int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb,
- struct switchdev_trans *trans)
+static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
@@ -2276,9 +2278,9 @@ int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
return 0;
}
-void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb,
- struct switchdev_trans *trans)
+static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb,
+ struct switchdev_trans *trans)
{
int state = is_multicast_ether_addr(fdb->addr) ?
GLOBAL_ATU_DATA_STATE_MC_STATIC :
@@ -2294,8 +2296,8 @@ void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
mutex_unlock(&ps->smi_mutex);
}
-int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb)
+static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_fdb *fdb)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
@@ -2397,9 +2399,9 @@ static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
return err;
}
-int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
- struct switchdev_obj_port_fdb *fdb,
- int (*cb)(struct switchdev_obj *obj))
+static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
+ struct switchdev_obj_port_fdb *fdb,
+ int (*cb)(struct switchdev_obj *obj))
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct mv88e6xxx_vtu_stu_entry vlan = {
@@ -2447,8 +2449,8 @@ unlock:
return err;
}
-int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
- struct net_device *bridge)
+static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
+ struct net_device *bridge)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int i, err = 0;
@@ -2474,7 +2476,7 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
return err;
}
-void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
+static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct net_device *bridge = ps->ports[port].bridge_dev;
@@ -3107,7 +3109,7 @@ static int _mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps)
return err;
}
-int mv88e6xxx_setup(struct dsa_switch *ds)
+static int mv88e6xxx_setup(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int err;
@@ -3180,8 +3182,7 @@ static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps,
return -EINVAL;
}
-int
-mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
+static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ps, port);
@@ -3204,8 +3205,8 @@ mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
return ret;
}
-int
-mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
+static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum,
+ u16 val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ps, port);
@@ -3289,7 +3290,7 @@ static int _mv88e63xx_get_temp(struct mv88e6xxx_priv_state *ps, int *temp)
return 0;
}
-int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
+static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int err;
@@ -3309,7 +3310,7 @@ int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
return err;
}
-int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
+static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
@@ -3329,7 +3330,7 @@ int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
return 0;
}
-int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
+static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
@@ -3346,7 +3347,7 @@ int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
(ret & 0xe0ff) | (temp << 8));
}
-int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
+static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
@@ -3367,6 +3368,268 @@ int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
}
#endif /* CONFIG_NET_DSA_HWMON */
+static const struct mv88e6xxx_info mv88e6xxx_table[] = {
+ [MV88E6085] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
+ .family = MV88E6XXX_FAMILY_6097,
+ .name = "Marvell 88E6085",
+ .num_databases = 4096,
+ .num_ports = 10,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_CORE_TAG_TYPE |
+ MV88E6XXX_FLAG_PPU |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6095] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
+ .family = MV88E6XXX_FAMILY_6095,
+ .name = "Marvell 88E6095/88E6095F",
+ .num_databases = 256,
+ .num_ports = 11,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_CORE_TAG_TYPE |
+ MV88E6XXX_FLAG_PPU |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6123] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
+ .family = MV88E6XXX_FAMILY_6165,
+ .name = "Marvell 88E6123",
+ .num_databases = 4096,
+ .num_ports = 3,
+ .flags = MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP,
+ },
+
+ [MV88E6131] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
+ .family = MV88E6XXX_FAMILY_6185,
+ .name = "Marvell 88E6131",
+ .num_databases = 256,
+ .num_ports = 8,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_CORE_TAG_TYPE |
+ MV88E6XXX_FLAG_PPU |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6161] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
+ .family = MV88E6XXX_FAMILY_6165,
+ .name = "Marvell 88E6161",
+ .num_databases = 4096,
+ .num_ports = 6,
+ .flags = MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP,
+ },
+
+ [MV88E6165] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
+ .family = MV88E6XXX_FAMILY_6165,
+ .name = "Marvell 88E6165",
+ .num_databases = 4096,
+ .num_ports = 6,
+ .flags = MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP,
+ },
+
+ [MV88E6171] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
+ .family = MV88E6XXX_FAMILY_6351,
+ .name = "Marvell 88E6171",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6172] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
+ .family = MV88E6XXX_FAMILY_6352,
+ .name = "Marvell 88E6172",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_EEE |
+ MV88E6XXX_FLAG_EEPROM |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6175] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
+ .family = MV88E6XXX_FAMILY_6351,
+ .name = "Marvell 88E6175",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6176] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
+ .family = MV88E6XXX_FAMILY_6352,
+ .name = "Marvell 88E6176",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_EEE |
+ MV88E6XXX_FLAG_EEPROM |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6185] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
+ .family = MV88E6XXX_FAMILY_6185,
+ .name = "Marvell 88E6185",
+ .num_databases = 256,
+ .num_ports = 10,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_CORE_TAG_TYPE |
+ MV88E6XXX_FLAG_PPU |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6240] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
+ .family = MV88E6XXX_FAMILY_6352,
+ .name = "Marvell 88E6240",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_EEE |
+ MV88E6XXX_FLAG_EEPROM |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6320] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
+ .family = MV88E6XXX_FAMILY_6320,
+ .name = "Marvell 88E6320",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_EEE |
+ MV88E6XXX_FLAG_EEPROM |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6321] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
+ .family = MV88E6XXX_FAMILY_6320,
+ .name = "Marvell 88E6321",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_EEE |
+ MV88E6XXX_FLAG_EEPROM |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6350] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
+ .family = MV88E6XXX_FAMILY_6351,
+ .name = "Marvell 88E6350",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6351] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
+ .family = MV88E6XXX_FAMILY_6351,
+ .name = "Marvell 88E6351",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+
+ [MV88E6352] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
+ .family = MV88E6XXX_FAMILY_6352,
+ .name = "Marvell 88E6352",
+ .num_databases = 4096,
+ .num_ports = 7,
+ .flags = MV88E6XXX_FLAG_ATU |
+ MV88E6XXX_FLAG_EEE |
+ MV88E6XXX_FLAG_EEPROM |
+ MV88E6XXX_FLAG_PORTSTATE |
+ MV88E6XXX_FLAG_PPU_ACTIVE |
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_SWITCH_MAC |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT |
+ MV88E6XXX_FLAG_VLANTABLE |
+ MV88E6XXX_FLAG_VTU,
+ },
+};
+
static const struct mv88e6xxx_info *
mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
unsigned int num)
@@ -3380,10 +3643,9 @@ mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
return NULL;
}
-const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
- int sw_addr, void **priv,
- const struct mv88e6xxx_info *table,
- unsigned int num)
+static const char *mv88e6xxx_probe(struct device *dsa_dev,
+ struct device *host_dev, int sw_addr,
+ void **priv)
{
const struct mv88e6xxx_info *info;
struct mv88e6xxx_priv_state *ps;
@@ -3402,7 +3664,8 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
prod_num = (id & 0xfff0) >> 4;
rev = id & 0x000f;
- info = mv88e6xxx_lookup_info(prod_num, table, num);
+ info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
+ ARRAY_SIZE(mv88e6xxx_table));
if (!info)
return NULL;
@@ -3424,41 +3687,73 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
return name;
}
+struct dsa_switch_driver mv88e6xxx_switch_driver = {
+ .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .probe = mv88e6xxx_probe,
+ .setup = mv88e6xxx_setup,
+ .set_addr = mv88e6xxx_set_addr,
+ .phy_read = mv88e6xxx_phy_read,
+ .phy_write = mv88e6xxx_phy_write,
+ .adjust_link = mv88e6xxx_adjust_link,
+ .get_strings = mv88e6xxx_get_strings,
+ .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
+ .get_sset_count = mv88e6xxx_get_sset_count,
+ .set_eee = mv88e6xxx_set_eee,
+ .get_eee = mv88e6xxx_get_eee,
+#ifdef CONFIG_NET_DSA_HWMON
+ .get_temp = mv88e6xxx_get_temp,
+ .get_temp_limit = mv88e6xxx_get_temp_limit,
+ .set_temp_limit = mv88e6xxx_set_temp_limit,
+ .get_temp_alarm = mv88e6xxx_get_temp_alarm,
+#endif
+ .get_eeprom = mv88e6xxx_get_eeprom,
+ .set_eeprom = mv88e6xxx_set_eeprom,
+ .get_regs_len = mv88e6xxx_get_regs_len,
+ .get_regs = mv88e6xxx_get_regs,
+ .port_bridge_join = mv88e6xxx_port_bridge_join,
+ .port_bridge_leave = mv88e6xxx_port_bridge_leave,
+ .port_stp_state_set = mv88e6xxx_port_stp_state_set,
+ .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
+ .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
+ .port_vlan_add = mv88e6xxx_port_vlan_add,
+ .port_vlan_del = mv88e6xxx_port_vlan_del,
+ .port_vlan_dump = mv88e6xxx_port_vlan_dump,
+ .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
+ .port_fdb_add = mv88e6xxx_port_fdb_add,
+ .port_fdb_del = mv88e6xxx_port_fdb_del,
+ .port_fdb_dump = mv88e6xxx_port_fdb_dump,
+};
+
static int __init mv88e6xxx_init(void)
{
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
- register_switch_driver(&mv88e6131_switch_driver);
-#endif
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
- register_switch_driver(&mv88e6123_switch_driver);
-#endif
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
- register_switch_driver(&mv88e6352_switch_driver);
-#endif
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
- register_switch_driver(&mv88e6171_switch_driver);
-#endif
+ register_switch_driver(&mv88e6xxx_switch_driver);
+
return 0;
}
module_init(mv88e6xxx_init);
static void __exit mv88e6xxx_cleanup(void)
{
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
- unregister_switch_driver(&mv88e6171_switch_driver);
-#endif
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
- unregister_switch_driver(&mv88e6352_switch_driver);
-#endif
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
- unregister_switch_driver(&mv88e6123_switch_driver);
-#endif
-#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
- unregister_switch_driver(&mv88e6131_switch_driver);
-#endif
+ unregister_switch_driver(&mv88e6xxx_switch_driver);
}
module_exit(mv88e6xxx_cleanup);
+MODULE_ALIAS("platform:mv88e6085");
+MODULE_ALIAS("platform:mv88e6095");
+MODULE_ALIAS("platform:mv88e6095f");
+MODULE_ALIAS("platform:mv88e6123");
+MODULE_ALIAS("platform:mv88e6131");
+MODULE_ALIAS("platform:mv88e6161");
+MODULE_ALIAS("platform:mv88e6165");
+MODULE_ALIAS("platform:mv88e6171");
+MODULE_ALIAS("platform:mv88e6172");
+MODULE_ALIAS("platform:mv88e6175");
+MODULE_ALIAS("platform:mv88e6176");
+MODULE_ALIAS("platform:mv88e6320");
+MODULE_ALIAS("platform:mv88e6321");
+MODULE_ALIAS("platform:mv88e6350");
+MODULE_ALIAS("platform:mv88e6351");
+MODULE_ALIAS("platform:mv88e6352");
MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index d13e290..25bbf77 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -338,6 +338,27 @@
#define MV88E6XXX_N_FID 4096
+/* List of supported models */
+enum mv88e6xxx_model {
+ MV88E6085,
+ MV88E6095,
+ MV88E6123,
+ MV88E6131,
+ MV88E6161,
+ MV88E6165,
+ MV88E6171,
+ MV88E6172,
+ MV88E6175,
+ MV88E6176,
+ MV88E6185,
+ MV88E6240,
+ MV88E6320,
+ MV88E6321,
+ MV88E6350,
+ MV88E6351,
+ MV88E6352,
+};
+
enum mv88e6xxx_family {
MV88E6XXX_FAMILY_NONE,
MV88E6XXX_FAMILY_6065, /* 6031 6035 6061 6065 */
@@ -532,77 +553,4 @@ static inline bool mv88e6xxx_has(struct mv88e6xxx_priv_state *ps,
return (ps->info->flags & flags) == flags;
}
-const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
- int sw_addr, void **priv,
- const struct mv88e6xxx_info *table,
- unsigned int num);
-
-int mv88e6xxx_setup(struct dsa_switch *ds);
-int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr, int reg);
-int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
- int reg, u16 val);
-int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr);
-int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum);
-int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val);
-void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data);
-void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
- uint64_t *data);
-int mv88e6xxx_get_sset_count(struct dsa_switch *ds);
-int mv88e6xxx_get_sset_count_basic(struct dsa_switch *ds);
-void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
- struct phy_device *phydev);
-int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port);
-void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
- struct ethtool_regs *regs, void *_p);
-int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp);
-int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp);
-int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp);
-int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm);
-int mv88e6xxx_get_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
- u8 *data);
-int mv88e6xxx_set_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
- u8 *data);
-int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum);
-int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
- u16 val);
-int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
-int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
- struct phy_device *phydev, struct ethtool_eee *e);
-int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
- struct net_device *bridge);
-void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port);
-void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
-int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
- bool vlan_filtering);
-int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct switchdev_trans *trans);
-void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct switchdev_trans *trans);
-int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan);
-int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
- struct switchdev_obj_port_vlan *vlan,
- int (*cb)(struct switchdev_obj *obj));
-int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb,
- struct switchdev_trans *trans);
-void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb,
- struct switchdev_trans *trans);
-int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_fdb *fdb);
-int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
- struct switchdev_obj_port_fdb *fdb,
- int (*cb)(struct switchdev_obj *obj));
-int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg);
-int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
- int reg, int val);
-
-extern struct dsa_switch_driver mv88e6131_switch_driver;
-extern struct dsa_switch_driver mv88e6123_switch_driver;
-extern struct dsa_switch_driver mv88e6352_switch_driver;
-extern struct dsa_switch_driver mv88e6171_switch_driver;
-
#endif
--
2.8.2
^ permalink raw reply related
* [RFC PATCH net-next 04/20] net: dsa: mv88e6xxx: factorize temperature access
From: Vivien Didelot @ 2016-05-05 22:40 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
Andrew Lunn, Vivien Didelot
In-Reply-To: <1462488064-1841-1-git-send-email-vivien.didelot@savoirfairelinux.com>
Add MV88E6XXX_FLAG_TEMP and MV88E6XXX_FLAG_TEMP_LIMIT flags to describe
switch models featuring a temperature access. Use them to centralize the
access to the temperature feature.
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
drivers/net/dsa/mv88e6123.c | 6 ++++++
drivers/net/dsa/mv88e6131.c | 6 ++++++
drivers/net/dsa/mv88e6171.c | 17 ++++++++++++-----
drivers/net/dsa/mv88e6352.c | 24 ++++++++++++++++++------
drivers/net/dsa/mv88e6xxx.c | 32 +++++++++++++++++++-------------
drivers/net/dsa/mv88e6xxx.h | 8 ++++++++
6 files changed, 69 insertions(+), 24 deletions(-)
diff --git a/drivers/net/dsa/mv88e6123.c b/drivers/net/dsa/mv88e6123.c
index 1fc8f0d..6f4f719 100644
--- a/drivers/net/dsa/mv88e6123.c
+++ b/drivers/net/dsa/mv88e6123.c
@@ -24,18 +24,21 @@ static const struct mv88e6xxx_info mv88e6123_table[] = {
.name = "Marvell 88E6123",
.num_databases = 4096,
.num_ports = 3,
+ .flags = MV88E6XXX_FLAG_TEMP,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6161",
.num_databases = 4096,
.num_ports = 6,
+ .flags = MV88E6XXX_FLAG_TEMP,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
.family = MV88E6XXX_FAMILY_6165,
.name = "Marvell 88E6165",
.num_databases = 4096,
.num_ports = 6,
+ .flags = MV88E6XXX_FLAG_TEMP,
}
};
@@ -120,6 +123,9 @@ struct dsa_switch_driver mv88e6123_switch_driver = {
.adjust_link = mv88e6xxx_adjust_link,
#ifdef CONFIG_NET_DSA_HWMON
.get_temp = mv88e6xxx_get_temp,
+ .get_temp_limit = mv88e6xxx_get_temp_limit,
+ .set_temp_limit = mv88e6xxx_set_temp_limit,
+ .get_temp_alarm = mv88e6xxx_get_temp_alarm,
#endif
.get_eeprom = mv88e6xxx_get_eeprom,
.set_eeprom = mv88e6xxx_set_eeprom,
diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c
index 3796f6d..e8eb9a6 100644
--- a/drivers/net/dsa/mv88e6131.c
+++ b/drivers/net/dsa/mv88e6131.c
@@ -155,6 +155,12 @@ struct dsa_switch_driver mv88e6131_switch_driver = {
.get_sset_count = mv88e6xxx_get_sset_count,
.get_eeprom = mv88e6xxx_get_eeprom,
.set_eeprom = mv88e6xxx_set_eeprom,
+#ifdef CONFIG_NET_DSA_HWMON
+ .get_temp = mv88e6xxx_get_temp,
+ .get_temp_limit = mv88e6xxx_get_temp_limit,
+ .set_temp_limit = mv88e6xxx_set_temp_limit,
+ .get_temp_alarm = mv88e6xxx_get_temp_alarm,
+#endif
.adjust_link = mv88e6xxx_adjust_link,
.port_bridge_join = mv88e6xxx_port_bridge_join,
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c
index b8b6902..bd2082e 100644
--- a/drivers/net/dsa/mv88e6171.c
+++ b/drivers/net/dsa/mv88e6171.c
@@ -24,28 +24,32 @@ static const struct mv88e6xxx_info mv88e6171_table[] = {
.name = "Marvell 88E6171",
.num_databases = 4096,
.num_ports = 7,
- .flags = MV88E6XXX_FLAG_SMI_PHY,
+ .flags = MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6175",
.num_databases = 4096,
.num_ports = 7,
- .flags = MV88E6XXX_FLAG_SMI_PHY,
+ .flags = MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6350",
.num_databases = 4096,
.num_ports = 7,
- .flags = MV88E6XXX_FLAG_SMI_PHY,
+ .flags = MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
.family = MV88E6XXX_FAMILY_6351,
.name = "Marvell 88E6351",
.num_databases = 4096,
.num_ports = 7,
- .flags = MV88E6XXX_FLAG_SMI_PHY,
+ .flags = MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP,
}
};
@@ -131,7 +135,10 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
.get_sset_count = mv88e6xxx_get_sset_count,
.adjust_link = mv88e6xxx_adjust_link,
#ifdef CONFIG_NET_DSA_HWMON
- .get_temp = mv88e6xxx_get_temp,
+ .get_temp = mv88e6xxx_get_temp,
+ .get_temp_limit = mv88e6xxx_get_temp_limit,
+ .set_temp_limit = mv88e6xxx_set_temp_limit,
+ .get_temp_alarm = mv88e6xxx_get_temp_alarm,
#endif
.get_eeprom = mv88e6xxx_get_eeprom,
.set_eeprom = mv88e6xxx_set_eeprom,
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index e6437de..f588e2f 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -30,7 +30,9 @@ static const struct mv88e6xxx_info mv88e6352_table[] = {
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_SMI_PHY,
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
.family = MV88E6XXX_FAMILY_6320,
@@ -38,7 +40,9 @@ static const struct mv88e6xxx_info mv88e6352_table[] = {
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_SMI_PHY,
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
.family = MV88E6XXX_FAMILY_6352,
@@ -46,7 +50,9 @@ static const struct mv88e6xxx_info mv88e6352_table[] = {
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_SMI_PHY,
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
.family = MV88E6XXX_FAMILY_6352,
@@ -54,7 +60,9 @@ static const struct mv88e6xxx_info mv88e6352_table[] = {
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_SMI_PHY,
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
.family = MV88E6XXX_FAMILY_6352,
@@ -62,7 +70,9 @@ static const struct mv88e6xxx_info mv88e6352_table[] = {
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_SMI_PHY,
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT,
}, {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
.family = MV88E6XXX_FAMILY_6352,
@@ -70,7 +80,9 @@ static const struct mv88e6xxx_info mv88e6352_table[] = {
.num_databases = 4096,
.num_ports = 7,
.flags = MV88E6XXX_FLAG_EEPROM |
- MV88E6XXX_FLAG_SMI_PHY,
+ MV88E6XXX_FLAG_SMI_PHY |
+ MV88E6XXX_FLAG_TEMP |
+ MV88E6XXX_FLAG_TEMP_LIMIT,
}
};
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 233c265..5f7851e 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -3125,16 +3125,13 @@ mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
#ifdef CONFIG_NET_DSA_HWMON
-static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
+static int _mv88e61xx_get_temp(struct mv88e6xxx_priv_state *ps, int *temp)
{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
int val;
*temp = 0;
- mutex_lock(&ps->smi_mutex);
-
ret = _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x6);
if (ret < 0)
goto error;
@@ -3166,19 +3163,18 @@ static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
error:
_mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x0);
- mutex_unlock(&ps->smi_mutex);
+
return ret;
}
-static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
+static int _mv88e63xx_get_temp(struct mv88e6xxx_priv_state *ps, int *temp)
{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
int ret;
*temp = 0;
- ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
+ ret = _mv88e6xxx_phy_page_read(ps, phy, 6, 27);
if (ret < 0)
return ret;
@@ -3190,11 +3186,21 @@ static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ int err;
+
+ if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP))
+ return -EOPNOTSUPP;
+
+ mutex_lock(&ps->smi_mutex);
if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
- return mv88e63xx_get_temp(ds, temp);
+ err = _mv88e63xx_get_temp(ps, temp);
+ else
+ err = _mv88e61xx_get_temp(ps, temp);
- return mv88e61xx_get_temp(ds, temp);
+ mutex_unlock(&ps->smi_mutex);
+
+ return err;
}
int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
@@ -3203,7 +3209,7 @@ int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
int ret;
- if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
+ if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP;
*temp = 0;
@@ -3223,7 +3229,7 @@ int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
int ret;
- if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
+ if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP;
ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
@@ -3240,7 +3246,7 @@ int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
int ret;
- if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
+ if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
return -EOPNOTSUPP;
*alarm = false;
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 08cb5c4..e7c2e45 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -366,12 +366,20 @@ enum mv88e6xxx_cap {
* GLOBAL2_SMI_OP, otherwise direct access to PHY registers is done.
*/
MV88E6XXX_CAP_SMI_PHY,
+
+ /* Internal temperature sensor.
+ * Available from any enabled port's PHY register 26, page 6.
+ */
+ MV88E6XXX_CAP_TEMP,
+ MV88E6XXX_CAP_TEMP_LIMIT,
};
/* Bitmask of capabilities */
#define MV88E6XXX_FLAG_EEPROM BIT(MV88E6XXX_CAP_EEPROM)
#define MV88E6XXX_FLAG_PPU BIT(MV88E6XXX_CAP_PPU)
#define MV88E6XXX_FLAG_SMI_PHY BIT(MV88E6XXX_CAP_SMI_PHY)
+#define MV88E6XXX_FLAG_TEMP BIT(MV88E6XXX_CAP_TEMP)
+#define MV88E6XXX_FLAG_TEMP_LIMIT BIT(MV88E6XXX_CAP_TEMP_LIMIT)
struct mv88e6xxx_info {
enum mv88e6xxx_family family;
--
2.8.2
^ permalink raw reply related
* [RFC PATCH net-next 17/20] net: dsa: mv88e6xxx: factorize frames priorities
From: Vivien Didelot @ 2016-05-05 22:41 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, kernel, David S. Miller, Florian Fainelli,
Andrew Lunn, Vivien Didelot
In-Reply-To: <1462488064-1841-1-git-send-email-vivien.didelot@savoirfairelinux.com>
6131 is the only driver to setup the priority of IGMP/MLD snoop frames
and ARP frames to the highest setting. Drop such change until we figure
out a common configuration for all switch models.
Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
---
drivers/net/dsa/mv88e6131.c | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c
index 14d0722..4ab617a 100644
--- a/drivers/net/dsa/mv88e6131.c
+++ b/drivers/net/dsa/mv88e6131.c
@@ -74,20 +74,6 @@ static const char *mv88e6131_drv_probe(struct device *dsa_dev,
ARRAY_SIZE(mv88e6131_table));
}
-static int mv88e6131_setup_global(struct dsa_switch *ds)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-
- /* Force the priority of IGMP/MLD snoop frames and ARP frames
- * to the highest setting.
- */
- return mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE,
- GLOBAL2_PRIO_OVERRIDE_FORCE_SNOOP |
- 7 << GLOBAL2_PRIO_OVERRIDE_SNOOP_SHIFT |
- GLOBAL2_PRIO_OVERRIDE_FORCE_ARP |
- 7 << GLOBAL2_PRIO_OVERRIDE_ARP_SHIFT);
-}
-
static int mv88e6131_setup(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
@@ -99,10 +85,6 @@ static int mv88e6131_setup(struct dsa_switch *ds)
if (ret < 0)
return ret;
- ret = mv88e6131_setup_global(ds);
- if (ret < 0)
- return ret;
-
return mv88e6xxx_setup_ports(ds);
}
--
2.8.2
^ permalink raw reply related
* Re: [PATCH] tools: bpf_jit_disasm: check for klogctl failure
From: Daniel Borkmann @ 2016-05-05 22:46 UTC (permalink / raw)
To: Colin King, David S . Miller, netdev; +Cc: linux-kernel
In-Reply-To: <1462487973-21712-1-git-send-email-colin.king@canonical.com>
On 05/06/2016 12:39 AM, Colin King wrote:
> From: Colin Ian King <colin.king@canonical.com>
>
> klogctl can fail and return -ve len, so check for this and
> return NULL to avoid passing a (size_t)-1 to malloc.
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
[ would be nice to get Cc'ed in future ... ]
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
^ permalink raw reply
* [PATCH net] netfilter: nf_conntrack: Use net_mutex for helper unregistration.
From: Joe Stringer @ 2016-05-05 22:50 UTC (permalink / raw)
To: netdev; +Cc: Joe Stringer, pablo, netfilter-devel, fw
If a user loads nf_conntrack_ftp, sends FTP traffic through a network
namespace, destroys that namespace then unloads the FTP helper module,
then the kernel will crash.
Florian's assessment of the bug:
AFAIU following happens:
1. ct is created with ftp helper in netns x
2. netns x gets destroyed
3. netns destruction is scheduled
4. netns destruction wq starts, removes netns from global list
5. ftp helper is unloaded, which resets all helpers of the conntracks
... but because netns is already gone from list the for_each_net() loop
doesn't include it, so we do not change any of the conntracks in net
namespaces that are already dead.
6. netns destruction invokes destructor for rmmod'ed helper
Backtrace:
BUG: unable to handle kernel paging request at ffffffffa03d60c8
IP: [<ffffffffa00bc797>] nf_ct_helper_destroy+0x97/0x170 [nf_conntrack]
...
Oops: 0000 [#1] SMP
Workqueue: netns cleanup_net
...
Call Trace:
[<ffffffffa00bc73c>] ? nf_ct_helper_destroy+0x3c/0x170 [nf_conntrack]
[<ffffffffa00b6c9c>] nf_ct_delete+0x3c/0x1e0 [nf_conntrack]
[<ffffffffa00bc9f0>] ? nf_conntrack_helper_fini+0x30/0x30 [nf_conntrack]
[<ffffffffa00b75c8>] nf_ct_iterate_cleanup+0x258/0x270 [nf_conntrack]
[<ffffffffa00bcf0f>] nf_ct_l3proto_pernet_unregister+0x2f/0x60 [nf_conntrack]
[<ffffffffa00370e9>] ipv4_net_exit+0x19/0x50 [nf_conntrack_ipv4]
[<ffffffff81668fa8>] ops_exit_list.isra.4+0x38/0x60
[<ffffffff8166a35e>] cleanup_net+0x1be/0x290
[<ffffffff81085b2c>] process_one_work+0x1dc/0x660
[<ffffffff81085ab1>] ? process_one_work+0x161/0x660
[<ffffffff810860db>] worker_thread+0x12b/0x4a0
[<ffffffff81085fb0>] ? process_one_work+0x660/0x660
[<ffffffff8108ca22>] kthread+0xf2/0x110
[<ffffffff817a6c02>] ret_from_fork+0x22/0x40
[<ffffffff8108c930>] ? kthread_create_on_node+0x220/0x220
Fix the issue by using net_mutex during conntrack helper unregistration.
Suggested-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Joe Stringer <joe@ovn.org>
---
I've boiled it down to a repro script here:
https://gist.github.com/joestringer/465328172ee8960242142572b0ffc6e1
There's nothing special about the FTP server (requires pyftpdlib):
https://github.com/openvswitch/ovs/blob/v2.5.0/tests/test-l7.py
Other script dependencies are conntrack, ip, bridge-utils, wget.
This issue may be mitigated by disabling automatic conntrack helper
assignment.
In regards to affected kernels, I looked back as far as 3.13 and I can
still reproduce the issue with the above script. I haven't looked further.
---
net/core/net_namespace.c | 1 +
net/netfilter/nf_conntrack_helper.c | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 2c2eb1b629b1..cf9058409082 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -28,6 +28,7 @@
static LIST_HEAD(pernet_list);
static struct list_head *first_device = &pernet_list;
DEFINE_MUTEX(net_mutex);
+EXPORT_SYMBOL_GPL(net_mutex);
LIST_HEAD(net_namespace_list);
EXPORT_SYMBOL_GPL(net_namespace_list);
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 3b40ec575cd5..6860b19be406 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -449,10 +449,10 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
*/
synchronize_rcu();
- rtnl_lock();
+ mutex_lock(&net_mutex);
for_each_net(net)
__nf_conntrack_helper_unregister(me, net);
- rtnl_unlock();
+ mutex_unlock(&net_mutex);
}
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
--
2.1.4
^ permalink raw reply related
* Re: [RFC PATCH] gso: Replace SKB_GSO_IPIP and SKB_GSO_SIT with outer L3 types
From: Alex Duyck @ 2016-05-05 22:50 UTC (permalink / raw)
To: Tom Herbert
Cc: Linux Kernel Network Developers, Michael Chan, Alexander Duyck
In-Reply-To: <CALx6S342jKgPp3i+DZaWUxZQRtqL9fhqDv8mdaU1mTaZphiVKQ@mail.gmail.com>
On Thu, May 5, 2016 at 3:34 PM, Tom Herbert <tom@herbertland.com> wrote:
> I'm testing a similar change, it seems like a good idea.
Okay, I wasn't sure if that was the case based on our last discussion.
The bits for the drivers below more or less call out the drivers I am
confident can handle V4 or V6 for tunnels.
> On Thu, May 5, 2016 at 3:12 PM, Alexander Duyck <aduyck@mirantis.com> wrote:
>> This patch removes SKB_GSO_IPIP and SKB_GSO_SIT and instead replaces them
>> with SKB_GSO_IPIPV4 and SKB_GSO_IPIPV6. The idea here is that SKB_GSO_IPIP
>> and SKB_GSO_SIT are actually redundant as TCPV4 and TCPV6 were already
>> representing the inner network header type. By reporting what the outer
>> network header type is we should be able to represent all applicable cases
>> as SKB_GSO_IPIPV4 can be combined with SKB_GSO_TCPV4 or SKB_GSO_TCPV6 and
>> it should then be possible to describe all possible IP in IP tunnel
>> combinations for inner and outer v4/v6.
>>
>> I played it safe with the Broadcom drivers and just merged GSO_IPIP and
>> GSO_SIT into GSO_IPIPV4, if the drivers do support outer IPv6 tunnels I can
>> either update this patch to add them in, or can add a patch to enable that
>> later.
>>
>> In the case of the Intel drivers I know they should be able to handle IPv6
>> outer headers so I went ahead and enabled GSO_IPIPV6 for them.
>>
>> This patch is meant to be applied after "gso: Remove arbitrary checks for
>> unsupported GSO" which can be found at:
>> https://patchwork.ozlabs.org/patch/618757/
>>
>> Signed-off-by: Alexander Duyck <aduyck@mirantis.com>
>> ---
>> drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 5 ++---
>> drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 ++--
>> drivers/net/ethernet/intel/i40e/i40e_main.c | 4 ++--
>> drivers/net/ethernet/intel/i40e/i40e_txrx.c | 4 ++--
>> drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 4 ++--
>> drivers/net/ethernet/intel/i40evf/i40evf_main.c | 4 ++--
>> drivers/net/ethernet/intel/igb/igb_main.c | 4 ++--
>> drivers/net/ethernet/intel/igbvf/netdev.c | 4 ++--
>> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 ++--
>> drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 4 ++--
>> include/linux/netdev_features.h | 12 ++++++------
>> include/linux/netdevice.h | 4 ++--
>> include/linux/skbuff.h | 4 ++--
>> net/core/ethtool.c | 4 ++--
>> net/ipv4/af_inet.c | 16 +++++++++++-----
>> net/ipv4/ipip.c | 2 +-
>> net/ipv6/ip6_offload.c | 7 +++++--
>> net/ipv6/sit.c | 4 ++--
>> net/netfilter/ipvs/ip_vs_xmit.c | 14 +++-----------
>> 19 files changed, 54 insertions(+), 54 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
>> index d465bd721146..fbc713094611 100644
>> --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
>> +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
>> @@ -13259,12 +13259,11 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev,
>> NETIF_F_RXHASH | NETIF_F_HW_VLAN_CTAG_TX;
>> if (!chip_is_e1x) {
>> dev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL |
>> - NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT;
>> + NETIF_F_GSO_IPIPV4;
>
> I prefer using IPXIP4 and IPXIP6, especially to be clear that the
> inner protocol is either IP version.
That works for me.
>> dev->hw_enc_features =
>> NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
>> NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 |
>> - NETIF_F_GSO_IPIP |
>> - NETIF_F_GSO_SIT |
>> + NETIF_F_GSO_IPIPV4 |
>> NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL;
>> }
>>
>> diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
>> index fd85b6dd4a6e..b3821fbf8e1b 100644
>> --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
>> +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
>> @@ -6218,7 +6218,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>> dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
>> NETIF_F_TSO | NETIF_F_TSO6 |
>> NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE |
>> - NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT |
>> + NETIF_F_GSO_IPIPV4 |
>> NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM |
>> NETIF_F_GSO_PARTIAL | NETIF_F_RXHASH |
>> NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO;
>> @@ -6228,7 +6228,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>> NETIF_F_TSO | NETIF_F_TSO6 |
>> NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE |
>> NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM |
>> - NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT |
>> + NETIF_F_GSO_IPIPV4 |
>> NETIF_F_GSO_PARTIAL;
>> dev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM |
>> NETIF_F_GSO_GRE_CSUM;
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> index 1cd0ebf7520a..bf1d31c4e310 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> @@ -9083,8 +9083,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
>> NETIF_F_TSO6 |
>> NETIF_F_GSO_GRE |
>> NETIF_F_GSO_GRE_CSUM |
>> - NETIF_F_GSO_IPIP |
>> - NETIF_F_GSO_SIT |
>> + NETIF_F_GSO_IPIPV4 |
>> + NETIF_F_GSO_IPIPV6 |
>> NETIF_F_GSO_UDP_TUNNEL |
>> NETIF_F_GSO_UDP_TUNNEL_CSUM |
>> NETIF_F_GSO_PARTIAL |
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>> index 36a34f7191e3..180d4a889c69 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
>> @@ -2302,8 +2302,8 @@ static int i40e_tso(struct sk_buff *skb, u8 *hdr_len, u64 *cd_type_cmd_tso_mss)
>>
>> if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE |
>> SKB_GSO_GRE_CSUM |
>> - SKB_GSO_IPIP |
>> - SKB_GSO_SIT |
>> + SKB_GSO_IPIPV4 |
>> + SKB_GSO_IPIPV6 |
>> SKB_GSO_UDP_TUNNEL |
>> SKB_GSO_UDP_TUNNEL_CSUM)) {
>> if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) &&
>> diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
>> index fd7dae46c5d8..e2630c663cfb 100644
>> --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
>> +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
>> @@ -1559,8 +1559,8 @@ static int i40e_tso(struct sk_buff *skb, u8 *hdr_len, u64 *cd_type_cmd_tso_mss)
>>
>> if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE |
>> SKB_GSO_GRE_CSUM |
>> - SKB_GSO_IPIP |
>> - SKB_GSO_SIT |
>> + SKB_GSO_IPIPV4 |
>> + SKB_GSO_IPIPV6 |
>> SKB_GSO_UDP_TUNNEL |
>> SKB_GSO_UDP_TUNNEL_CSUM)) {
>> if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) &&
>> diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
>> index 642bb45ed906..3a72568d8175 100644
>> --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
>> +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
>> @@ -2230,8 +2230,8 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
>> NETIF_F_TSO6 |
>> NETIF_F_GSO_GRE |
>> NETIF_F_GSO_GRE_CSUM |
>> - NETIF_F_GSO_IPIP |
>> - NETIF_F_GSO_SIT |
>> + NETIF_F_GSO_IPIPV4 |
>> + NETIF_F_GSO_IPIPV6 |
>> NETIF_F_GSO_UDP_TUNNEL |
>> NETIF_F_GSO_UDP_TUNNEL_CSUM |
>> NETIF_F_GSO_PARTIAL |
>> diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
>> index 21727692bef6..1ba2213ce5d5 100644
>> --- a/drivers/net/ethernet/intel/igb/igb_main.c
>> +++ b/drivers/net/ethernet/intel/igb/igb_main.c
>> @@ -2418,8 +2418,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>>
>> #define IGB_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
>> NETIF_F_GSO_GRE_CSUM | \
>> - NETIF_F_GSO_IPIP | \
>> - NETIF_F_GSO_SIT | \
>> + NETIF_F_GSO_IPIPV4 | \
>> + NETIF_F_GSO_IPIPV6 | \
>> NETIF_F_GSO_UDP_TUNNEL | \
>> NETIF_F_GSO_UDP_TUNNEL_CSUM)
>>
>> diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c
>> index 322a2d7828a5..de1827983096 100644
>> --- a/drivers/net/ethernet/intel/igbvf/netdev.c
>> +++ b/drivers/net/ethernet/intel/igbvf/netdev.c
>> @@ -2763,8 +2763,8 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>>
>> #define IGBVF_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
>> NETIF_F_GSO_GRE_CSUM | \
>> - NETIF_F_GSO_IPIP | \
>> - NETIF_F_GSO_SIT | \
>> + NETIF_F_GSO_IPIPV4 | \
>> + NETIF_F_GSO_IPIPV6 | \
>> NETIF_F_GSO_UDP_TUNNEL | \
>> NETIF_F_GSO_UDP_TUNNEL_CSUM)
>>
>> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> index d08fbcfb9417..9180361a909c 100644
>> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> @@ -9482,8 +9482,8 @@ skip_sriov:
>>
>> #define IXGBE_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
>> NETIF_F_GSO_GRE_CSUM | \
>> - NETIF_F_GSO_IPIP | \
>> - NETIF_F_GSO_SIT | \
>> + NETIF_F_GSO_IPIPV4 | \
>> + NETIF_F_GSO_IPIPV6 | \
>> NETIF_F_GSO_UDP_TUNNEL | \
>> NETIF_F_GSO_UDP_TUNNEL_CSUM)
>>
>> diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
>> index 5e348b125090..622f456ec41e 100644
>> --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
>> +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
>> @@ -4062,8 +4062,8 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>>
>> #define IXGBEVF_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
>> NETIF_F_GSO_GRE_CSUM | \
>> - NETIF_F_GSO_IPIP | \
>> - NETIF_F_GSO_SIT | \
>> + NETIF_F_GSO_IPIPV4 | \
>> + NETIF_F_GSO_IPIPV6 | \
>> NETIF_F_GSO_UDP_TUNNEL | \
>> NETIF_F_GSO_UDP_TUNNEL_CSUM)
>>
>> diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
>> index bc8736266749..f10248df9705 100644
>> --- a/include/linux/netdev_features.h
>> +++ b/include/linux/netdev_features.h
>> @@ -44,8 +44,8 @@ enum {
>> NETIF_F_FSO_BIT, /* ... FCoE segmentation */
>> NETIF_F_GSO_GRE_BIT, /* ... GRE with TSO */
>> NETIF_F_GSO_GRE_CSUM_BIT, /* ... GRE with csum with TSO */
>> - NETIF_F_GSO_IPIP_BIT, /* ... IPIP tunnel with TSO */
>> - NETIF_F_GSO_SIT_BIT, /* ... SIT tunnel with TSO */
>> + NETIF_F_GSO_IPIPV4_BIT, /* ... IP in IPv4 tunnel with TSO */
>> + NETIF_F_GSO_IPIPV6_BIT, /* ... IP in IPv6 tunnel with TSO */
>> NETIF_F_GSO_UDP_TUNNEL_BIT, /* ... UDP TUNNEL with TSO */
>> NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT,/* ... UDP TUNNEL with TSO & CSUM */
>> NETIF_F_GSO_PARTIAL_BIT, /* ... Only segment inner-most L4
>> @@ -121,8 +121,8 @@ enum {
>> #define NETIF_F_RXALL __NETIF_F(RXALL)
>> #define NETIF_F_GSO_GRE __NETIF_F(GSO_GRE)
>> #define NETIF_F_GSO_GRE_CSUM __NETIF_F(GSO_GRE_CSUM)
>> -#define NETIF_F_GSO_IPIP __NETIF_F(GSO_IPIP)
>> -#define NETIF_F_GSO_SIT __NETIF_F(GSO_SIT)
>> +#define NETIF_F_GSO_IPIPV4 __NETIF_F(GSO_IPIPV4)
>> +#define NETIF_F_GSO_IPIPV6 __NETIF_F(GSO_IPIPV6)
>> #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL)
>> #define NETIF_F_GSO_UDP_TUNNEL_CSUM __NETIF_F(GSO_UDP_TUNNEL_CSUM)
>> #define NETIF_F_TSO_MANGLEID __NETIF_F(TSO_MANGLEID)
>> @@ -200,8 +200,8 @@ enum {
>>
>> #define NETIF_F_GSO_ENCAP_ALL (NETIF_F_GSO_GRE | \
>> NETIF_F_GSO_GRE_CSUM | \
>> - NETIF_F_GSO_IPIP | \
>> - NETIF_F_GSO_SIT | \
>> + NETIF_F_GSO_IPIPV4 | \
>> + NETIF_F_GSO_IPIPV6 | \
>> NETIF_F_GSO_UDP_TUNNEL | \
>> NETIF_F_GSO_UDP_TUNNEL_CSUM)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index 63580e6d0df4..d9d05d6b5849 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -4005,8 +4005,8 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type)
>> BUILD_BUG_ON(SKB_GSO_FCOE != (NETIF_F_FSO >> NETIF_F_GSO_SHIFT));
>> BUILD_BUG_ON(SKB_GSO_GRE != (NETIF_F_GSO_GRE >> NETIF_F_GSO_SHIFT));
>> BUILD_BUG_ON(SKB_GSO_GRE_CSUM != (NETIF_F_GSO_GRE_CSUM >> NETIF_F_GSO_SHIFT));
>> - BUILD_BUG_ON(SKB_GSO_IPIP != (NETIF_F_GSO_IPIP >> NETIF_F_GSO_SHIFT));
>> - BUILD_BUG_ON(SKB_GSO_SIT != (NETIF_F_GSO_SIT >> NETIF_F_GSO_SHIFT));
>> + BUILD_BUG_ON(SKB_GSO_IPIPV4 != (NETIF_F_GSO_IPIPV4 >> NETIF_F_GSO_SHIFT));
>> + BUILD_BUG_ON(SKB_GSO_IPIPV6 != (NETIF_F_GSO_IPIPV6 >> NETIF_F_GSO_SHIFT));
>> BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL != (NETIF_F_GSO_UDP_TUNNEL >> NETIF_F_GSO_SHIFT));
>> BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >> NETIF_F_GSO_SHIFT));
>> BUILD_BUG_ON(SKB_GSO_PARTIAL != (NETIF_F_GSO_PARTIAL >> NETIF_F_GSO_SHIFT));
>> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
>> index c413c588a24f..6186a6446d1c 100644
>> --- a/include/linux/skbuff.h
>> +++ b/include/linux/skbuff.h
>> @@ -471,9 +471,9 @@ enum {
>>
>> SKB_GSO_GRE_CSUM = 1 << 8,
>>
>> - SKB_GSO_IPIP = 1 << 9,
>> + SKB_GSO_IPIPV4 = 1 << 9,
>>
>> - SKB_GSO_SIT = 1 << 10,
>> + SKB_GSO_IPIPV6 = 1 << 10,
>>
>> SKB_GSO_UDP_TUNNEL = 1 << 11,
>>
>> diff --git a/net/core/ethtool.c b/net/core/ethtool.c
>> index bdb4013581b1..8771dafe93ec 100644
>> --- a/net/core/ethtool.c
>> +++ b/net/core/ethtool.c
>> @@ -84,8 +84,8 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
>> [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation",
>> [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation",
>> [NETIF_F_GSO_GRE_CSUM_BIT] = "tx-gre-csum-segmentation",
>> - [NETIF_F_GSO_IPIP_BIT] = "tx-ipip-segmentation",
>> - [NETIF_F_GSO_SIT_BIT] = "tx-sit-segmentation",
>> + [NETIF_F_GSO_IPIPV4_BIT] = "tx-ipip4-segmentation",
>> + [NETIF_F_GSO_IPIPV6_BIT] = "tx-ipip6-segmentation",
>> [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
>> [NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT] = "tx-udp_tnl-csum-segmentation",
>> [NETIF_F_GSO_PARTIAL_BIT] = "tx-gso-partial",
>> diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
>> index 7f08d4525981..c62230f1b16b 100644
>> --- a/net/ipv4/af_inet.c
>> +++ b/net/ipv4/af_inet.c
>> @@ -1459,20 +1459,25 @@ static int inet_gro_complete(struct sk_buff *skb, int nhoff)
>>
>> if (skb->encapsulation)
>> skb_set_inner_network_header(skb, nhoff);
>> + else
>> + skb_set_network_header(skb, nhoff);
>
> This seems like an unrelated change.
It was needed for the bits in ipip_gro_complete and sit_gro_complete.
I agree that based on your other comments this is not needed.
>>
>> csum_replace2(&iph->check, iph->tot_len, newlen);
>> iph->tot_len = newlen;
>>
>> rcu_read_lock();
>> - ops = rcu_dereference(inet_offloads[proto]);
>> - if (WARN_ON(!ops || !ops->callbacks.gro_complete))
>> - goto out_unlock;
>>
>> /* Only need to add sizeof(*iph) to get to the next hdr below
>> * because any hdr with option will have been flushed in
>> * inet_gro_receive().
>> */
>> - err = ops->callbacks.gro_complete(skb, nhoff + sizeof(*iph));
>> + nhoff += sizeof(*iph);
>> +
>> + ops = rcu_dereference(inet_offloads[proto]);
>> + if (WARN_ON(!ops || !ops->callbacks.gro_complete))
>> + goto out_unlock;
>> +
>> + err = ops->callbacks.gro_complete(skb, nhoff);
>
> Why this change?
This is a bit of mess left from an experiment I did where I was also
resetting the outer transport header. It turns out we are leaving the
outer transport header pointing at the inner header. I was debating
adding a line after the call to gro_complete that resets the outer
network header using skb_set_transport_header.
>>
>> out_unlock:
>> rcu_read_unlock();
>> @@ -1483,7 +1488,8 @@ out_unlock:
>> static int ipip_gro_complete(struct sk_buff *skb, int nhoff)
>> {
>> skb->encapsulation = 1;
>> - skb_shinfo(skb)->gso_type |= SKB_GSO_IPIP;
>> + skb_shinfo(skb)->gso_type = (ip_hdr(skb)->version == 4) ?
>> + SKB_GSO_IPIPV4 : SKB_GSO_IPIPV6;
>
> I don't think this is necessary. IPIP means IPv4 over IPv4, IPv4 over
> IPv6 can have its own gro_complete
Yeah, I am not all that familiar with IPIP and SIT so I wasn't sure if
they were restricted like that or not. I kind of spaced on the fact
that it is an inet_offload and not something that is used for IPv4 and
IPv6.
The resetting of the network header and transport header is something
we might want to address perhaps for the GRO and bridging case but I
think other people may be working on that as well so for now I might
just go back to watching how this all develops.
Thanks.
- Alex
^ permalink raw reply
* Re: [RFC PATCH net-next 01/20] net: dsa: mv88e6xxx: factorize PHY access with PPU
From: Andrew Lunn @ 2016-05-05 22:54 UTC (permalink / raw)
To: Vivien Didelot
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <1462488064-1841-2-git-send-email-vivien.didelot@savoirfairelinux.com>
On Thu, May 05, 2016 at 06:40:45PM -0400, Vivien Didelot wrote:
> Add a flags bitmap to the mv88e6xxx_info structure to help describing
> features supported or not by a switch model.
>
> Add a MV88E6XXX_FLAG_PPU flag to describe switch models with a PHY
> Polling Unit. This allows to merge PPU specific PHY access code in the
> share code. In the meantime, use unlocked register accesses.
>
> Since the PPU code is shared, also remove NET_DSA_MV88E6XXX_NEED_PPU.
> -#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU
> -static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps)
> +static int _mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps, bool enable)
> {
> - int ret;
The change log does not say anything about refactoring
mv88e6xxx_ppu_disable() and mv88e6xxx_ppu_enable() into one function.
That should be in separate patch. Also, i don't see much value in this
refactoring. The names mv88e6xxx_ppu_disable() and
mv88e6xxx_ppu_enable() are much clearer than having one function which
takes a bool.
Andrew
^ permalink raw reply
* Re: [PATCH nf-next 0/9] netfilter: remove per-netns conntrack tables, part 1
From: Brian Haley @ 2016-05-05 22:55 UTC (permalink / raw)
To: Florian Westphal; +Cc: netfilter-devel, netdev
In-Reply-To: <20160505223632.GB21687@breakpoint.cc>
On 05/05/2016 06:36 PM, Florian Westphal wrote:
> Brian Haley <brian.haley@hpe.com> wrote:
>>>> I've seen cases where certain users are attacked, where the CT table is
>>>> filled such that we start seeing "nf_conntrack: table full, dropping packet"
>>>> messages (as expected). But other users continue to function normally,
>>>> unaffected. Is this still the case - each netns has some limit it can't
>>>> exceed?
>>>
>>> The limit is global, the accounting per namespace.
>>
>> So this is a change from existing.
>
> No, see __nf_conntrack_alloc():
>
> if (nf_conntrack_max &&
> unlikely(atomic_read(&net->ct.count) > nf_conntrack_max)) {
> ...
>
> ct.count is whatever number of entries the namespace has allocated,
> so max number of possible conntracks is always infinite if number
> of net namespaces is unlimited (barring memory constraints, of course).
Ah yes, nf_conntrack_max is a global, thanks for setting me straight. So I
guess the tuning might just include increasing the bucket count in order to try
and keep the number of items in each one small since there will be more entries
in this single table now.
Thanks,
-Brian
^ permalink raw reply
* Re: [RFC PATCH net-next 01/20] net: dsa: mv88e6xxx: factorize PHY access with PPU
From: Andrew Lunn @ 2016-05-05 22:59 UTC (permalink / raw)
To: Vivien Didelot
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <1462488064-1841-2-git-send-email-vivien.didelot@savoirfairelinux.com>
> -static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_priv_state *ps)
> +static int _mv88e6xxx_ppu_access_get(struct mv88e6xxx_priv_state *ps)
Hi Vivien
We agreed to stop adding _ to functions that assume the lock has been
taken. Now that we check the lock is held in lowest level function, it
quickly becomes clear if the locks are wrong. It either deadlocks, or
it prints a warning when there is an error.
Please don't rename these functions.
Andrew
^ permalink raw reply
* Re: [RFC PATCH net-next 01/20] net: dsa: mv88e6xxx: factorize PHY access with PPU
From: Andrew Lunn @ 2016-05-05 23:06 UTC (permalink / raw)
To: Vivien Didelot
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <1462488064-1841-2-git-send-email-vivien.didelot@savoirfairelinux.com>
> {
> struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
> int addr = mv88e6xxx_port_to_phy_addr(ps, port);
> - int ret;
> + int err;
>
> if (addr < 0)
> return 0xffff;
>
> +
We don't want an extra blank line. I'm surprised checkpatch did not
warn about this.
> mutex_lock(&ps->smi_mutex);
> - ret = _mv88e6xxx_phy_write(ps, addr, regnum, val);
> +
> + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
> + err = _mv88e6xxx_phy_write_ppu(ps, addr, regnum, val);
> + else
> + err = _mv88e6xxx_phy_write(ps, addr, regnum, val);
> +
> mutex_unlock(&ps->smi_mutex);
> - return ret;
> +
> + return err;
Please don't rename ret to err. This patch is about phy reading and
writing, and i don't expect to see anything else here. It makes it
harder to review.
Andrew
^ permalink raw reply
* Re: [RFC PATCH net-next 04/20] net: dsa: mv88e6xxx: factorize temperature access
From: Andrew Lunn @ 2016-05-05 23:40 UTC (permalink / raw)
To: Vivien Didelot
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <1462488064-1841-5-git-send-email-vivien.didelot@savoirfairelinux.com>
> int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
> {
> struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
> + int err;
> +
> + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP))
> + return -EOPNOTSUPP;
> +
> + mutex_lock(&ps->smi_mutex);
>
> if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
> - return mv88e63xx_get_temp(ds, temp);
> + err = _mv88e63xx_get_temp(ps, temp);
> + else
> + err = _mv88e61xx_get_temp(ps, temp);
It seems like this should be done with a flag, rather than the family.
However, don't spend too much time on this. I plan to rip it all out
and put it in the correct place, in the Marvell PHY driver. The
temperature sensor is in the PHY, and other Marvell PHYs also have
this temperature sensor.
Andrew
^ permalink raw reply
* Re: [RFC PATCH net-next 13/20] net: dsa: mv88e6xxx: factorize GLOBAL_CONTROL setup
From: Andrew Lunn @ 2016-05-06 0:55 UTC (permalink / raw)
To: Vivien Didelot
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <1462488064-1841-14-git-send-email-vivien.didelot@savoirfairelinux.com>
On Thu, May 05, 2016 at 06:40:57PM -0400, Vivien Didelot wrote:
> All switch models configure the GLOBAL_CONTROL register with slightly
> differences.
And as far as i know, we have no idea why.
> Assume a common setup to enable the PHY Polling Unit if present, discard
> packets with excessive collisions, set the maximum frame size to 1632,
> and mask all interrupt sources.
This is a behaviour change, not just a refactor. It would be good to
say on which families it has been tested, and which have not.
Andrew
^ permalink raw reply
* Re: [RFC PATCH net-next 14/20] net: dsa: mv88e6xxx: factorize VLAN Ethertype
From: Andrew Lunn @ 2016-05-06 0:57 UTC (permalink / raw)
To: Vivien Didelot
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <1462488064-1841-15-git-send-email-vivien.didelot@savoirfairelinux.com>
On Thu, May 05, 2016 at 06:40:58PM -0400, Vivien Didelot wrote:
> Only the 6131 driver was setting the VLAN Ethertype to 0x8100. Set it to
> all models.
>
> Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
> ---
> drivers/net/dsa/mv88e6131.c | 9 ++++-----
> drivers/net/dsa/mv88e6xxx.c | 8 ++++++++
> drivers/net/dsa/mv88e6xxx.h | 7 +++++++
> 3 files changed, 19 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c
> index 013668c..4c0b1e9 100644
> --- a/drivers/net/dsa/mv88e6131.c
> +++ b/drivers/net/dsa/mv88e6131.c
> @@ -25,6 +25,7 @@ static const struct mv88e6xxx_info mv88e6131_table[] = {
> .num_databases = 256,
> .num_ports = 11,
> .flags = MV88E6XXX_FLAG_ATU |
> + MV88E6XXX_FLAG_CORE_TAG_TYPE |
> MV88E6XXX_FLAG_PPU |
> MV88E6XXX_FLAG_VLANTABLE |
> MV88E6XXX_FLAG_VTU,
> @@ -35,6 +36,7 @@ static const struct mv88e6xxx_info mv88e6131_table[] = {
> .num_databases = 4096,
> .num_ports = 10,
> .flags = MV88E6XXX_FLAG_ATU |
> + MV88E6XXX_FLAG_CORE_TAG_TYPE |
> MV88E6XXX_FLAG_PPU |
> MV88E6XXX_FLAG_VLANTABLE |
> MV88E6XXX_FLAG_VTU,
> @@ -45,6 +47,7 @@ static const struct mv88e6xxx_info mv88e6131_table[] = {
> .num_databases = 256,
> .num_ports = 8,
> .flags = MV88E6XXX_FLAG_ATU |
> + MV88E6XXX_FLAG_CORE_TAG_TYPE |
> MV88E6XXX_FLAG_PPU |
> MV88E6XXX_FLAG_VLANTABLE |
> MV88E6XXX_FLAG_VTU,
> @@ -55,6 +58,7 @@ static const struct mv88e6xxx_info mv88e6131_table[] = {
> .num_databases = 256,
> .num_ports = 10,
> .flags = MV88E6XXX_FLAG_ATU |
> + MV88E6XXX_FLAG_CORE_TAG_TYPE |
> MV88E6XXX_FLAG_PPU |
> MV88E6XXX_FLAG_VLANTABLE |
> MV88E6XXX_FLAG_VTU,
Hi Vivien
Rather than repeating these flags again and again, could you add one
#define containing the flags, and then use that to initialise .flags.
Andrew
^ permalink raw reply
* Re: [RFC PATCH net-next 19/20] net: dsa: mv88e6xxx: factorize tag protocol
From: Andrew Lunn @ 2016-05-06 1:09 UTC (permalink / raw)
To: Vivien Didelot
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <1462488064-1841-20-git-send-email-vivien.didelot@savoirfairelinux.com>
On Thu, May 05, 2016 at 06:41:03PM -0400, Vivien Didelot wrote:
> 6131 is the only driver to set the tag protocol to DSA_TAG_PROTO_DSA.
> Since it works fine with DSA_TAG_PROTO_EDSA, change its value, like all
> other mv88e6xxx drivers.
Hi Vivien
You might as well remove net/dsa/tag_dsa.c as well, since it is now
unused. Please do that as a separate patch.
Andrew
^ permalink raw reply
* Re: [RFC PATCH net-next 20/20] net: dsa: mv88e6xxx: factorize the switch driver
From: Andrew Lunn @ 2016-05-06 1:14 UTC (permalink / raw)
To: Vivien Didelot
Cc: netdev, linux-kernel, kernel, David S. Miller, Florian Fainelli
In-Reply-To: <1462488064-1841-21-git-send-email-vivien.didelot@savoirfairelinux.com>
> +config NET_DSA_MV88E6XXX
> tristate "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support"
> depends on NET_DSA
> - select NET_DSA_MV88E6XXX
> select NET_DSA_TAG_EDSA
> ---help---
> This enables support for the Marvell 88E6085/6095/6095F/6131
> ethernet switch chips.
Hi Vivien
You should update the list of devices. We need some text in the
tristate and help which suggests this driver supports everything
except mv88e6060.
Andrew
^ permalink raw reply
* [PATCH net-next 7/7] samples/bpf: add verifier tests
From: Alexei Starovoitov @ 2016-05-06 2:49 UTC (permalink / raw)
To: David S . Miller; +Cc: Daniel Borkmann, netdev, kernel-team
In-Reply-To: <1462502955-1731797-1-git-send-email-ast@fb.com>
add few tests for "pointer to packet" logic of the verifier
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
samples/bpf/test_verifier.c | 80 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c
index 9eba8d1d9dcc..fe2fcec98c1f 100644
--- a/samples/bpf/test_verifier.c
+++ b/samples/bpf/test_verifier.c
@@ -1448,6 +1448,86 @@ static struct bpf_test tests[] = {
.result = ACCEPT,
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
},
+ {
+ "pkt: test1",
+ .insns = {
+ BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
+ offsetof(struct __sk_buff, data)),
+ BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+ offsetof(struct __sk_buff, data_end)),
+ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
+ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
+ BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+ },
+ {
+ "pkt: test2",
+ .insns = {
+ BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1,
+ offsetof(struct __sk_buff, data_end)),
+ BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+ offsetof(struct __sk_buff, data)),
+ BPF_MOV64_REG(BPF_REG_5, BPF_REG_3),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
+ BPF_JMP_REG(BPF_JGT, BPF_REG_5, BPF_REG_4, 15),
+ BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_3, 7),
+ BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_3, 12),
+ BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 14),
+ BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+ offsetof(struct __sk_buff, data)),
+ BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_4),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
+ BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 48),
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_2, 48),
+ BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_2),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_3),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8),
+ BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
+ offsetof(struct __sk_buff, data_end)),
+ BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1),
+ BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_3, 4),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .result = ACCEPT,
+ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+ },
+ {
+ "pkt: test3",
+ .insns = {
+ BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
+ offsetof(struct __sk_buff, data)),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .errstr = "invalid bpf_context access off=76",
+ .result = REJECT,
+ .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+ },
+ {
+ "pkt: test4",
+ .insns = {
+ BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
+ offsetof(struct __sk_buff, data)),
+ BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+ offsetof(struct __sk_buff, data_end)),
+ BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
+ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
+ BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .errstr = "cannot write",
+ .result = REJECT,
+ .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+ },
};
static int probe_filter_length(struct bpf_insn *fp)
--
2.8.0
^ permalink raw reply related
* [PATCH net-next 0/7] bpf: introduce direct packet access
From: Alexei Starovoitov @ 2016-05-06 2:49 UTC (permalink / raw)
To: David S . Miller; +Cc: Daniel Borkmann, netdev, kernel-team
This set of patches introduce 'direct packet access' from
cls_bpf and act_bpf programs (which are root only).
Current bpf programs use LD_ABS, LD_INS instructions which have
to do 'if (off < skb_headlen)' for every packet access.
It's ok for socket filters, but too slow for XDP, since single
LD_ABS insn consumes 3% of cpu. Therefore we have to amortize the cost
of length check over multiple packet accesses via direct access
to skb->data, data_end pointers.
The existing packet parser typically look like:
if (load_half(skb, offsetof(struct ethhdr, h_proto)) != ETH_P_IP)
return 0;
if (load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)) != IPPROTO_UDP ||
load_byte(skb, ETH_HLEN) != 0x45)
return 0;
...
with 'direct packet access' the bpf program becomes:
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct eth_hdr *eth = data;
struct iphdr *iph = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*udp) > data_end)
return 0;
if (eth->h_proto != htons(ETH_P_IP))
return 0;
if (iph->protocol != IPPROTO_UDP || iph->ihl != 5)
return 0;
...
which is more natural to write and significantly faster.
See patch 6 for performance tests:
21Mpps(old) vs 24Mpps(new) with just 5 loads.
For more complex parsers the performance gain is higher.
The other approach implemented in [1] was adding two new instructions
to interpreter and JITs and was too hard to use from llvm side.
The approach presented here doesn't need any instruction changes,
but the verifier has to work harder to check safety of the packet access.
Patch 1 prepares the code and Patch 2 adds new checks for direct
packet access and all of them are gated with 'env->allow_ptr_leaks'
which is true for root only.
Patch 3 improves search pruning for large programs.
Patch 4 wires in verifier's changes with net/core/filter side.
Patch 5 updates docs
Patches 6 and 7 add tests.
[1] https://git.kernel.org/cgit/linux/kernel/git/ast/bpf.git/?h=ld_abs_dw
Alexei Starovoitov (7):
bpf: cleanup verifier code
bpf: direct packet access
bpf: improve verifier state equivalence
bpf: wire in data and data_end for cls_act_bpf
bpf: add documentation for 'direct packet access'
samples/bpf: add 'pointer to packet' tests
samples/bpf: add verifier tests
Documentation/networking/filter.txt | 85 +++++-
include/linux/filter.h | 16 +
include/uapi/linux/bpf.h | 2 +
kernel/bpf/core.c | 5 +
kernel/bpf/verifier.c | 562 +++++++++++++++++++++++++++++++-----
net/core/filter.c | 51 +++-
net/sched/act_bpf.c | 2 +
net/sched/cls_bpf.c | 2 +
samples/bpf/Makefile | 2 +
samples/bpf/parse_ldabs.c | 41 +++
samples/bpf/parse_simple.c | 48 +++
samples/bpf/parse_varlen.c | 153 ++++++++++
samples/bpf/test_cls_bpf.sh | 37 +++
samples/bpf/test_verifier.c | 80 +++++
14 files changed, 1004 insertions(+), 82 deletions(-)
create mode 100644 samples/bpf/parse_ldabs.c
create mode 100644 samples/bpf/parse_simple.c
create mode 100644 samples/bpf/parse_varlen.c
create mode 100755 samples/bpf/test_cls_bpf.sh
--
2.8.0
^ permalink raw reply
* [PATCH net-next 2/7] bpf: direct packet access
From: Alexei Starovoitov @ 2016-05-06 2:49 UTC (permalink / raw)
To: David S . Miller; +Cc: Daniel Borkmann, netdev, kernel-team
In-Reply-To: <1462502955-1731797-1-git-send-email-ast@fb.com>
Extended BPF carried over two instructions from classic to access
packet data: LD_ABS and LD_IND. They're highly optimized in JITs,
but due to their design they have to do length check for every access.
When BPF is processing 20M packets per second single LD_ABS after JIT
is consuming 3% cpu. Hence the need to optimize it further by amortizing
the cost of 'off < skb_headlen' over multiple packet accesses.
One option is to introduce two new eBPF instructions LD_ABS_DW and LD_IND_DW
with similar usage as skb_header_pointer().
The kernel part for interpreter and x64 JIT was implemented in [1], but such
new insns behave like old ld_abs and abort the program with 'return 0' if
access is beyond linear data. Such hidden control flow is hard to workaround
plus changing JITs and rolling out new llvm is incovenient.
Therefore allow cls_bpf/act_bpf program access skb->data directly:
int bpf_prog(struct __sk_buff *skb)
{
struct iphdr *ip;
if (skb->data + sizeof(struct iphdr) + ETH_HLEN > skb->data_end)
/* packet too small */
return 0;
ip = skb->data + ETH_HLEN;
/* access IP header fields with direct loads */
if (ip->version != 4 || ip->saddr == 0x7f000001)
return 1;
[...]
}
This solution avoids introduction of new instructions. llvm stays
the same and all JITs stay the same, but verifier has to work extra hard
to prove safety of the above program.
For XDP the direct store instructions can be allowed as well.
The skb->data is NET_IP_ALIGNED, so for common cases the verifier can check
the alignment. The complex packet parsers where packet pointer is adjusted
incrementally cannot be tracked for alignment, so allow byte access in such cases
and misaligned access on architectures that define efficient_unaligned_access
[1] https://git.kernel.org/cgit/linux/kernel/git/ast/bpf.git/?h=ld_abs_dw
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
---
include/uapi/linux/bpf.h | 2 +
kernel/bpf/core.c | 5 +
kernel/bpf/verifier.c | 441 ++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 440 insertions(+), 8 deletions(-)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index b7b0fb1292e7..406459b935a2 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -370,6 +370,8 @@ struct __sk_buff {
__u32 cb[5];
__u32 hash;
__u32 tc_classid;
+ __u32 data;
+ __u32 data_end;
};
struct bpf_tunnel_key {
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index e4248fe79513..d781b077431f 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -794,6 +794,11 @@ void __weak bpf_int_jit_compile(struct bpf_prog *prog)
{
}
+bool __weak bpf_helper_changes_skb_data(void *func)
+{
+ return false;
+}
+
/* To execute LD_ABS/LD_IND instructions __bpf_prog_run() may call
* skb_copy_bits(), so provide a weak definition of it for NET-less config.
*/
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index afeb62808902..6338c61fc2a1 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1,4 +1,5 @@
/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
+ * Copyright (c) 2016 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -136,13 +137,32 @@ enum bpf_reg_type {
FRAME_PTR, /* reg == frame_pointer */
PTR_TO_STACK, /* reg == frame_pointer + imm */
CONST_IMM, /* constant integer value */
+
+ /* PTR_TO_PACKET represents:
+ * skb->data
+ * skb->data + imm
+ * skb->data + (u16) var
+ * skb->data + (u16) var + imm
+ * if (range > 0) then [ptr, ptr + range - off) is safe to access
+ * if (id > 0) means that some 'var' was added
+ * if (off > 0) menas that 'imm' was added
+ */
+ PTR_TO_PACKET,
+ PTR_TO_PACKET_END, /* skb->data + headlen */
};
struct reg_state {
enum bpf_reg_type type;
union {
- /* valid when type == CONST_IMM | PTR_TO_STACK */
- long imm;
+ /* valid when type == CONST_IMM | PTR_TO_STACK | UNKNOWN_VALUE */
+ s64 imm;
+
+ /* valid when type == PTR_TO_PACKET* */
+ struct {
+ u32 id;
+ u16 off;
+ u16 range;
+ };
/* valid when type == CONST_PTR_TO_MAP | PTR_TO_MAP_VALUE |
* PTR_TO_MAP_VALUE_OR_NULL
@@ -247,6 +267,8 @@ static const char * const reg_type_str[] = {
[FRAME_PTR] = "fp",
[PTR_TO_STACK] = "fp",
[CONST_IMM] = "imm",
+ [PTR_TO_PACKET] = "pkt",
+ [PTR_TO_PACKET_END] = "pkt_end",
};
static void print_verifier_state(struct verifier_state *state)
@@ -262,7 +284,12 @@ static void print_verifier_state(struct verifier_state *state)
continue;
verbose(" R%d=%s", i, reg_type_str[t]);
if (t == CONST_IMM || t == PTR_TO_STACK)
- verbose("%ld", reg->imm);
+ verbose("%lld", reg->imm);
+ else if (t == PTR_TO_PACKET)
+ verbose("(id=%d,off=%d,r=%d)",
+ reg->id, reg->off, reg->range);
+ else if (t == UNKNOWN_VALUE && reg->imm)
+ verbose("%lld", reg->imm);
else if (t == CONST_PTR_TO_MAP || t == PTR_TO_MAP_VALUE ||
t == PTR_TO_MAP_VALUE_OR_NULL)
verbose("(ks=%d,vs=%d)",
@@ -548,6 +575,8 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
case PTR_TO_MAP_VALUE_OR_NULL:
case PTR_TO_STACK:
case PTR_TO_CTX:
+ case PTR_TO_PACKET:
+ case PTR_TO_PACKET_END:
case FRAME_PTR:
case CONST_PTR_TO_MAP:
return true;
@@ -647,6 +676,27 @@ static int check_map_access(struct verifier_env *env, u32 regno, int off,
return 0;
}
+#define MAX_PACKET_OFF 0xffff
+
+static int check_packet_access(struct verifier_env *env, u32 regno, int off,
+ int size)
+{
+ struct reg_state *regs = env->cur_state.regs;
+ struct reg_state *reg = ®s[regno];
+ int linear_size = (int) reg->range - (int) reg->off;
+
+ if (linear_size < 0 || linear_size >= MAX_PACKET_OFF) {
+ verbose("verifier bug\n");
+ return -EFAULT;
+ }
+ if (off < 0 || off + size > linear_size) {
+ verbose("invalid access to packet, off=%d size=%d, allowed=%d\n",
+ off, size, linear_size);
+ return -EACCES;
+ }
+ return 0;
+}
+
/* check access to 'struct bpf_context' fields */
static int check_ctx_access(struct verifier_env *env, int off, int size,
enum bpf_access_type t)
@@ -677,6 +727,45 @@ static bool is_pointer_value(struct verifier_env *env, int regno)
}
}
+static int check_ptr_alignment(struct verifier_env *env, struct reg_state *reg,
+ int off, int size)
+{
+ if (reg->type != PTR_TO_PACKET) {
+ if (off % size != 0) {
+ verbose("misaligned access off %d size %d\n", off, size);
+ return -EACCES;
+ } else {
+ return 0;
+ }
+ }
+
+ switch (env->prog->type) {
+ case BPF_PROG_TYPE_SCHED_CLS:
+ case BPF_PROG_TYPE_SCHED_ACT:
+ break;
+ default:
+ verbose("verifier is misconfigured\n");
+ return -EACCES;
+ }
+
+ if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
+ /* misaligned access to packet is ok on x86,arm,arm64 */
+ return 0;
+
+ if (reg->id && size != 1) {
+ verbose("Unknown packet alignment. Only byte-sized access allowed\n");
+ return -EACCES;
+ }
+
+ /* skb->data is NET_IP_ALIGN-ed */
+ if ((NET_IP_ALIGN + reg->off + off) % size != 0) {
+ verbose("misaligned packet access off %d+%d+%d size %d\n",
+ NET_IP_ALIGN, reg->off, off, size);
+ return -EACCES;
+ }
+ return 0;
+}
+
/* check whether memory at (regno + off) is accessible for t = (read | write)
* if t==write, value_regno is a register which value is stored into memory
* if t==read, value_regno is a register which will receive the value from memory
@@ -698,10 +787,9 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
if (size < 0)
return size;
- if (off % size != 0) {
- verbose("misaligned access off %d size %d\n", off, size);
- return -EACCES;
- }
+ err = check_ptr_alignment(env, reg, off, size);
+ if (err)
+ return err;
if (reg->type == PTR_TO_MAP_VALUE) {
if (t == BPF_WRITE && value_regno >= 0 &&
@@ -720,8 +808,16 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
return -EACCES;
}
err = check_ctx_access(env, off, size, t);
- if (!err && t == BPF_READ && value_regno >= 0)
+ if (!err && t == BPF_READ && value_regno >= 0) {
mark_reg_unknown_value(state->regs, value_regno);
+ if (off == offsetof(struct __sk_buff, data) &&
+ env->allow_ptr_leaks)
+ /* note that reg.[id|off|range] == 0 */
+ state->regs[value_regno].type = PTR_TO_PACKET;
+ else if (off == offsetof(struct __sk_buff, data_end) &&
+ env->allow_ptr_leaks)
+ state->regs[value_regno].type = PTR_TO_PACKET_END;
+ }
} else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) {
if (off >= 0 || off < -MAX_BPF_STACK) {
@@ -739,11 +835,28 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
} else {
err = check_stack_read(state, off, size, value_regno);
}
+ } else if (state->regs[regno].type == PTR_TO_PACKET) {
+ if (t == BPF_WRITE) {
+ verbose("cannot write into packet\n");
+ return -EACCES;
+ }
+ err = check_packet_access(env, regno, off, size);
+ if (!err && t == BPF_READ && value_regno >= 0)
+ mark_reg_unknown_value(state->regs, value_regno);
} else {
verbose("R%d invalid mem access '%s'\n",
regno, reg_type_str[reg->type]);
return -EACCES;
}
+
+ if (!err && size <= 2 && value_regno >= 0 && env->allow_ptr_leaks &&
+ state->regs[value_regno].type == UNKNOWN_VALUE) {
+ /* 1 or 2 byte load zero-extends, determine the number of
+ * zero upper bits. Not doing it fo 4 byte load, since
+ * such values cannot be added to ptr_to_packet anyway.
+ */
+ state->regs[value_regno].imm = 64 - size * 8;
+ }
return err;
}
@@ -1001,6 +1114,29 @@ static int check_raw_mode(const struct bpf_func_proto *fn)
return count > 1 ? -EINVAL : 0;
}
+static void clear_all_pkt_pointers(struct verifier_env *env)
+{
+ struct verifier_state *state = &env->cur_state;
+ struct reg_state *regs = state->regs, *reg;
+ int i;
+
+ for (i = 0; i < MAX_BPF_REG; i++)
+ if (regs[i].type == PTR_TO_PACKET ||
+ regs[i].type == PTR_TO_PACKET_END)
+ mark_reg_unknown_value(regs, i);
+
+ for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) {
+ if (state->stack_slot_type[i] != STACK_SPILL)
+ continue;
+ reg = &state->spilled_regs[i / BPF_REG_SIZE];
+ if (reg->type != PTR_TO_PACKET &&
+ reg->type != PTR_TO_PACKET_END)
+ continue;
+ reg->type = UNKNOWN_VALUE;
+ reg->imm = 0;
+ }
+}
+
static int check_call(struct verifier_env *env, int func_id)
{
struct verifier_state *state = &env->cur_state;
@@ -1008,6 +1144,7 @@ static int check_call(struct verifier_env *env, int func_id)
struct reg_state *regs = state->regs;
struct reg_state *reg;
struct bpf_call_arg_meta meta;
+ bool changes_data;
int i, err;
/* find function prototype */
@@ -1030,6 +1167,8 @@ static int check_call(struct verifier_env *env, int func_id)
return -EINVAL;
}
+ changes_data = bpf_helper_changes_skb_data(fn->func);
+
memset(&meta, 0, sizeof(meta));
/* We only support one arg being in raw mode at the moment, which
@@ -1100,6 +1239,189 @@ static int check_call(struct verifier_env *env, int func_id)
if (err)
return err;
+ if (changes_data)
+ clear_all_pkt_pointers(env);
+ return 0;
+}
+
+static int check_packet_ptr_add(struct verifier_env *env, struct bpf_insn *insn)
+{
+ struct reg_state *regs = env->cur_state.regs;
+ struct reg_state *dst_reg = ®s[insn->dst_reg];
+ struct reg_state *src_reg = ®s[insn->src_reg];
+ s32 imm;
+
+ if (BPF_SRC(insn->code) == BPF_K) {
+ /* pkt_ptr += imm */
+ imm = insn->imm;
+
+add_imm:
+ if (imm <= 0) {
+ verbose("addition of negative constant to packet pointer is not allowed\n");
+ return -EACCES;
+ }
+ if (imm >= MAX_PACKET_OFF ||
+ imm + dst_reg->off >= MAX_PACKET_OFF) {
+ verbose("constant %d is too large to add to packet pointer\n",
+ imm);
+ return -EACCES;
+ }
+ /* a constant was added to pkt_ptr.
+ * Remember it while keeping the same 'id'
+ */
+ dst_reg->off += imm;
+ } else {
+ if (src_reg->type == CONST_IMM) {
+ /* pkt_ptr += reg where reg is known constant */
+ imm = src_reg->imm;
+ goto add_imm;
+ }
+ /* disallow pkt_ptr += reg
+ * if reg is not uknown_value with guaranteed zero upper bits
+ * otherwise pkt_ptr may overflow and addition will become
+ * subtraction which is not allowed
+ */
+ if (src_reg->type != UNKNOWN_VALUE) {
+ verbose("cannot add '%s' to ptr_to_packet\n",
+ reg_type_str[src_reg->type]);
+ return -EACCES;
+ }
+ if (src_reg->imm < 48) {
+ verbose("cannot add integer value with %lld upper zero bits to ptr_to_packet\n",
+ src_reg->imm);
+ return -EACCES;
+ }
+ /* dst_reg stays as pkt_ptr type and since some positive
+ * integer value was added to the pointer, increment its 'id'
+ */
+ dst_reg->id++;
+
+ /* something was added to pkt_ptr, set range and off to zero */
+ dst_reg->off = 0;
+ dst_reg->range = 0;
+ }
+ return 0;
+}
+
+static int evaluate_reg_alu(struct verifier_env *env, struct bpf_insn *insn)
+{
+ struct reg_state *regs = env->cur_state.regs;
+ struct reg_state *dst_reg = ®s[insn->dst_reg];
+ u8 opcode = BPF_OP(insn->code);
+ s64 imm_log2;
+
+ /* for type == UNKNOWN_VALUE:
+ * imm > 0 -> number of zero upper bits
+ * imm == 0 -> don't track which is the same as all bits can be non-zero
+ */
+
+ if (BPF_SRC(insn->code) == BPF_X) {
+ struct reg_state *src_reg = ®s[insn->src_reg];
+
+ if (src_reg->type == UNKNOWN_VALUE && src_reg->imm > 0 &&
+ dst_reg->imm && opcode == BPF_ADD) {
+ /* dreg += sreg
+ * where both have zero upper bits. Adding them
+ * can only result making one more bit non-zero
+ * in the larger value.
+ * Ex. 0xffff (imm=48) + 1 (imm=63) = 0x10000 (imm=47)
+ * 0xffff (imm=48) + 0xffff = 0x1fffe (imm=47)
+ */
+ dst_reg->imm = min(dst_reg->imm, src_reg->imm);
+ dst_reg->imm--;
+ return 0;
+ }
+ if (src_reg->type == CONST_IMM && src_reg->imm > 0 &&
+ dst_reg->imm && opcode == BPF_ADD) {
+ /* dreg += sreg
+ * where dreg has zero upper bits and sreg is const.
+ * Adding them can only result making one more bit
+ * non-zero in the larger value.
+ */
+ imm_log2 = __ilog2_u64((long long)src_reg->imm);
+ dst_reg->imm = min(dst_reg->imm, 63 - imm_log2);
+ dst_reg->imm--;
+ return 0;
+ }
+ /* all other cases non supported yet, just mark dst_reg */
+ dst_reg->imm = 0;
+ return 0;
+ }
+
+ /* sign extend 32-bit imm into 64-bit to make sure that
+ * negative values occupy bit 63. Note ilog2() would have
+ * been incorrect, since sizeof(insn->imm) == 4
+ */
+ imm_log2 = __ilog2_u64((long long)insn->imm);
+
+ if (dst_reg->imm && opcode == BPF_LSH) {
+ /* reg <<= imm
+ * if reg was a result of 2 byte load, then its imm == 48
+ * which means that upper 48 bits are zero and shifting this reg
+ * left by 4 would mean that upper 44 bits are still zero
+ */
+ dst_reg->imm -= insn->imm;
+ } else if (dst_reg->imm && opcode == BPF_MUL) {
+ /* reg *= imm
+ * if multiplying by 14 subtract 4
+ * This is conservative calculation of upper zero bits.
+ * It's not trying to special case insn->imm == 1 or 0 cases
+ */
+ dst_reg->imm -= imm_log2 + 1;
+ } else if (opcode == BPF_AND) {
+ /* reg &= imm */
+ dst_reg->imm = 63 - imm_log2;
+ } else if (dst_reg->imm && opcode == BPF_ADD) {
+ /* reg += imm */
+ dst_reg->imm = min(dst_reg->imm, 63 - imm_log2);
+ dst_reg->imm--;
+ } else if (opcode == BPF_RSH) {
+ /* reg >>= imm
+ * which means that after right shift, upper bits will be zero
+ * note that verifier already checked that
+ * 0 <= imm < 64 for shift insn
+ */
+ dst_reg->imm += insn->imm;
+ if (unlikely(dst_reg->imm > 64))
+ /* some dumb code did:
+ * r2 = *(u32 *)mem;
+ * r2 >>= 32;
+ * and all bits are zero now */
+ dst_reg->imm = 64;
+ } else {
+ /* all other alu ops, means that we don't know what will
+ * happen to the value, mark it with unknown number of zero bits
+ */
+ dst_reg->imm = 0;
+ }
+
+ if (dst_reg->imm < 0) {
+ /* all 64 bits of the register can contain non-zero bits
+ * and such value cannot be added to ptr_to_packet, since it
+ * may overflow, mark it as unknown to avoid further eval
+ */
+ dst_reg->imm = 0;
+ }
+ return 0;
+}
+
+static int evaluate_reg_imm_alu(struct verifier_env *env, struct bpf_insn *insn)
+{
+ struct reg_state *regs = env->cur_state.regs;
+ struct reg_state *dst_reg = ®s[insn->dst_reg];
+ struct reg_state *src_reg = ®s[insn->src_reg];
+ u8 opcode = BPF_OP(insn->code);
+
+ /* dst_reg->type == CONST_IMM here, simulate execution of 'add' insn.
+ * Don't care about overflow or negative values, just add them
+ */
+ if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_K)
+ dst_reg->imm += insn->imm;
+ else if (opcode == BPF_ADD && BPF_SRC(insn->code) == BPF_X &&
+ src_reg->type == CONST_IMM)
+ dst_reg->imm += src_reg->imm;
+ else
+ mark_reg_unknown_value(regs, insn->dst_reg);
return 0;
}
@@ -1245,6 +1567,21 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
dst_reg->type = PTR_TO_STACK;
dst_reg->imm = insn->imm;
return 0;
+ } else if (opcode == BPF_ADD &&
+ BPF_CLASS(insn->code) == BPF_ALU64 &&
+ dst_reg->type == PTR_TO_PACKET) {
+ /* ptr_to_packet += K|X */
+ return check_packet_ptr_add(env, insn);
+ } else if (BPF_CLASS(insn->code) == BPF_ALU64 &&
+ dst_reg->type == UNKNOWN_VALUE &&
+ env->allow_ptr_leaks) {
+ /* unknown += K|X */
+ return evaluate_reg_alu(env, insn);
+ } else if (BPF_CLASS(insn->code) == BPF_ALU64 &&
+ dst_reg->type == CONST_IMM &&
+ env->allow_ptr_leaks) {
+ /* reg_imm += K|X */
+ return evaluate_reg_imm_alu(env, insn);
} else if (is_pointer_value(env, insn->dst_reg)) {
verbose("R%d pointer arithmetic prohibited\n",
insn->dst_reg);
@@ -1263,6 +1600,34 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
return 0;
}
+static void find_good_pkt_pointers(struct verifier_env *env,
+ struct reg_state *dst_reg)
+{
+ struct verifier_state *state = &env->cur_state;
+ struct reg_state *regs = state->regs, *reg;
+ int i;
+ /* r2 = r3;
+ * r2 += 8
+ * if (r2 > pkt_end) goto somewhere
+ * r2 == dst_reg, pkt_end == src_reg,
+ * r2=pkt(id=n,off=8,r=0)
+ * r3=pkt(id=n,off=0,r=0)
+ * find register r3 and mark its range as r3=pkt(id=n,off=0,r=8)
+ * so that range of bytes [r3, r3 + 8) is safe to access
+ */
+ for (i = 0; i < MAX_BPF_REG; i++)
+ if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id)
+ regs[i].range = dst_reg->off;
+
+ for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) {
+ if (state->stack_slot_type[i] != STACK_SPILL)
+ continue;
+ reg = &state->spilled_regs[i / BPF_REG_SIZE];
+ if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id)
+ reg->range = dst_reg->off;
+ }
+}
+
static int check_cond_jmp_op(struct verifier_env *env,
struct bpf_insn *insn, int *insn_idx)
{
@@ -1346,6 +1711,10 @@ static int check_cond_jmp_op(struct verifier_env *env,
regs[insn->dst_reg].type = CONST_IMM;
regs[insn->dst_reg].imm = 0;
}
+ } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
+ dst_reg->type == PTR_TO_PACKET &&
+ regs[insn->src_reg].type == PTR_TO_PACKET_END) {
+ find_good_pkt_pointers(env, dst_reg);
} else if (is_pointer_value(env, insn->dst_reg)) {
verbose("R%d pointer comparison prohibited\n", insn->dst_reg);
return -EACCES;
@@ -1685,6 +2054,58 @@ err_free:
return ret;
}
+/* the following conditions reduce the number of explored insns
+ * from ~140k to ~80k for ultra large programs that use a lot of ptr_to_packet
+ */
+static bool compare_ptrs_to_packet(struct reg_state *old, struct reg_state *cur)
+{
+ if (old->id != cur->id)
+ return false;
+
+ /* old ptr_to_packet is more conservative, since it allows smaller
+ * range. Ex:
+ * old(off=0,r=10) is equal to cur(off=0,r=20), because
+ * old(off=0,r=10) means that with range=10 the verifier proceeded
+ * further and found no issues with the program. Now we're in the same
+ * spot with cur(off=0,r=20), so we're safe too, since anything further
+ * will only be looking at most 10 bytes after this pointer.
+ */
+ if (old->off == cur->off && old->range < cur->range)
+ return true;
+
+ /* old(off=20,r=10) is equal to cur(off=22,re=22 or 5 or 0)
+ * since both cannot be used for packet access and safe(old)
+ * pointer has smaller off that could be used for further
+ * 'if (ptr > data_end)' check
+ * Ex:
+ * old(off=20,r=10) and cur(off=22,r=22) and cur(off=22,r=0) mean
+ * that we cannot access the packet.
+ * The safe range is:
+ * [ptr, ptr + range - off)
+ * so whenever off >=range, it means no safe bytes from this pointer.
+ * When comparing old->off <= cur->off, it means that older code
+ * went with smaller offset and that offset was later
+ * used to figure out the safe range after 'if (ptr > data_end)' check
+ * Say, 'old' state was explored like:
+ * ... R3(off=0, r=0)
+ * R4 = R3 + 20
+ * ... now R4(off=20,r=0) <-- here
+ * if (R4 > data_end)
+ * ... R4(off=20,r=20), R3(off=0,r=20) and R3 can be used to access.
+ * ... the code further went all the way to bpf_exit.
+ * Now the 'cur' state at the mark 'here' has R4(off=30,r=0).
+ * old_R4(off=20,r=0) equal to cur_R4(off=30,r=0), since if the verifier
+ * goes further, such cur_R4 will give larger safe packet range after
+ * 'if (R4 > data_end)' and all further insn were already good with r=20,
+ * so they will be good with r=30 and we can prune the search.
+ */
+ if (old->off <= cur->off &&
+ old->off >= old->range && cur->off >= cur->range)
+ return true;
+
+ return false;
+}
+
/* compare two verifier states
*
* all states stored in state_list are known to be valid, since
@@ -1727,6 +2148,10 @@ static bool states_equal(struct verifier_state *old, struct verifier_state *cur)
(rold->type == UNKNOWN_VALUE && rcur->type != NOT_INIT))
continue;
+ if (rold->type == PTR_TO_PACKET && rcur->type == PTR_TO_PACKET &&
+ compare_ptrs_to_packet(rold, rcur))
+ continue;
+
return false;
}
--
2.8.0
^ permalink raw reply related
* [PATCH net-next 6/7] samples/bpf: add 'pointer to packet' tests
From: Alexei Starovoitov @ 2016-05-06 2:49 UTC (permalink / raw)
To: David S . Miller; +Cc: Daniel Borkmann, netdev, kernel-team
In-Reply-To: <1462502955-1731797-1-git-send-email-ast@fb.com>
parse_simple.c - packet parser exapmle with single length check that
filters out udp packets for port 9
parse_varlen.c - variable length parser that understand multiple vlan headers,
ipip, ipip6 and ip options to filter out udp or tcp packets on port 9.
The packet is parsed layer by layer with multitple length checks.
parse_ldabs.c - classic style of packet parsing using LD_ABS instruction.
Same functionality as parse_simple.
simple = 24.1Mpps per core
varlen = 22.7Mpps
ldabs = 21.4Mpps
Parser with LD_ABS instructions is slower than full direct access parser
which does more packet accesses and checks.
These examples demonstrate the choice bpf program authors can make between
flexibility of the parser vs speed.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
samples/bpf/Makefile | 2 +
samples/bpf/parse_ldabs.c | 41 ++++++++++++
samples/bpf/parse_simple.c | 48 ++++++++++++++
samples/bpf/parse_varlen.c | 153 ++++++++++++++++++++++++++++++++++++++++++++
samples/bpf/test_cls_bpf.sh | 37 +++++++++++
5 files changed, 281 insertions(+)
create mode 100644 samples/bpf/parse_ldabs.c
create mode 100644 samples/bpf/parse_simple.c
create mode 100644 samples/bpf/parse_varlen.c
create mode 100755 samples/bpf/test_cls_bpf.sh
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 66897e61232c..0bf2478cb7df 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -60,6 +60,7 @@ always += spintest_kern.o
always += map_perf_test_kern.o
always += test_overhead_tp_kern.o
always += test_overhead_kprobe_kern.o
+always += parse_varlen.o parse_simple.o parse_ldabs.o
HOSTCFLAGS += -I$(objtree)/usr/include
@@ -120,4 +121,5 @@ $(src)/*.c: verify_target_bpf
$(obj)/%.o: $(src)/%.c
$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \
-D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
+ -Wno-compare-distinct-pointer-types \
-O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@
diff --git a/samples/bpf/parse_ldabs.c b/samples/bpf/parse_ldabs.c
new file mode 100644
index 000000000000..d17550198d06
--- /dev/null
+++ b/samples/bpf/parse_ldabs.c
@@ -0,0 +1,41 @@
+/* Copyright (c) 2016 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <uapi/linux/bpf.h>
+#include "bpf_helpers.h"
+
+#define DEFAULT_PKTGEN_UDP_PORT 9
+#define IP_MF 0x2000
+#define IP_OFFSET 0x1FFF
+
+static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)
+{
+ return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
+ & (IP_MF | IP_OFFSET);
+}
+
+SEC("ldabs")
+int handle_ingress(struct __sk_buff *skb)
+{
+ __u64 troff = ETH_HLEN + sizeof(struct iphdr);
+
+ if (load_half(skb, offsetof(struct ethhdr, h_proto)) != ETH_P_IP)
+ return 0;
+ if (load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)) != IPPROTO_UDP ||
+ load_byte(skb, ETH_HLEN) != 0x45)
+ return 0;
+ if (ip_is_fragment(skb, ETH_HLEN))
+ return 0;
+ if (load_half(skb, troff + offsetof(struct udphdr, dest)) == DEFAULT_PKTGEN_UDP_PORT)
+ return TC_ACT_SHOT;
+ return 0;
+}
+char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/parse_simple.c b/samples/bpf/parse_simple.c
new file mode 100644
index 000000000000..cf2511c33905
--- /dev/null
+++ b/samples/bpf/parse_simple.c
@@ -0,0 +1,48 @@
+/* Copyright (c) 2016 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <uapi/linux/bpf.h>
+#include <net/ip.h>
+#include "bpf_helpers.h"
+
+#define DEFAULT_PKTGEN_UDP_PORT 9
+
+/* copy of 'struct ethhdr' without __packed */
+struct eth_hdr {
+ unsigned char h_dest[ETH_ALEN];
+ unsigned char h_source[ETH_ALEN];
+ unsigned short h_proto;
+};
+
+SEC("simple")
+int handle_ingress(struct __sk_buff *skb)
+{
+ void *data = (void *)(long)skb->data;
+ struct eth_hdr *eth = data;
+ struct iphdr *iph = data + sizeof(*eth);
+ struct udphdr *udp = data + sizeof(*eth) + sizeof(*iph);
+ void *data_end = (void *)(long)skb->data_end;
+
+ /* single length check */
+ if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*udp) > data_end)
+ return 0;
+
+ if (eth->h_proto != htons(ETH_P_IP))
+ return 0;
+ if (iph->protocol != IPPROTO_UDP || iph->ihl != 5)
+ return 0;
+ if (ip_is_fragment(iph))
+ return 0;
+ if (udp->dest == htons(DEFAULT_PKTGEN_UDP_PORT))
+ return TC_ACT_SHOT;
+ return 0;
+}
+char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/parse_varlen.c b/samples/bpf/parse_varlen.c
new file mode 100644
index 000000000000..edab34dce79b
--- /dev/null
+++ b/samples/bpf/parse_varlen.c
@@ -0,0 +1,153 @@
+/* Copyright (c) 2016 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <uapi/linux/bpf.h>
+#include <net/ip.h>
+#include "bpf_helpers.h"
+
+#define DEFAULT_PKTGEN_UDP_PORT 9
+#define DEBUG 0
+
+static int tcp(void *data, uint64_t tp_off, void *data_end)
+{
+ struct tcphdr *tcp = data + tp_off;
+
+ if (tcp + 1 > data_end)
+ return 0;
+ if (tcp->dest == htons(80) || tcp->source == htons(80))
+ return TC_ACT_SHOT;
+ return 0;
+}
+
+static int udp(void *data, uint64_t tp_off, void *data_end)
+{
+ struct udphdr *udp = data + tp_off;
+
+ if (udp + 1 > data_end)
+ return 0;
+ if (udp->dest == htons(DEFAULT_PKTGEN_UDP_PORT) ||
+ udp->source == htons(DEFAULT_PKTGEN_UDP_PORT)) {
+ if (DEBUG) {
+ char fmt[] = "udp port 9 indeed\n";
+
+ bpf_trace_printk(fmt, sizeof(fmt));
+ }
+ return TC_ACT_SHOT;
+ }
+ return 0;
+}
+
+static int parse_ipv4(void *data, uint64_t nh_off, void *data_end)
+{
+ struct iphdr *iph;
+ uint64_t ihl_len;
+
+ iph = data + nh_off;
+ if (iph + 1 > data_end)
+ return 0;
+
+ if (ip_is_fragment(iph))
+ return 0;
+ ihl_len = iph->ihl * 4;
+
+ if (iph->protocol == IPPROTO_IPIP) {
+ iph = data + nh_off + ihl_len;
+ if (iph + 1 > data_end)
+ return 0;
+ ihl_len += iph->ihl * 4;
+ }
+
+ if (iph->protocol == IPPROTO_TCP)
+ return tcp(data, nh_off + ihl_len, data_end);
+ else if (iph->protocol == IPPROTO_UDP)
+ return udp(data, nh_off + ihl_len, data_end);
+ return 0;
+}
+
+static int parse_ipv6(void *data, uint64_t nh_off, void *data_end)
+{
+ struct ipv6hdr *ip6h;
+ struct iphdr *iph;
+ uint64_t ihl_len = sizeof(struct ipv6hdr);
+ uint64_t nexthdr;
+
+ ip6h = data + nh_off;
+ if (ip6h + 1 > data_end)
+ return 0;
+
+ nexthdr = ip6h->nexthdr;
+
+ if (nexthdr == IPPROTO_IPIP) {
+ iph = data + nh_off + ihl_len;
+ if (iph + 1 > data_end)
+ return 0;
+ ihl_len += iph->ihl * 4;
+ nexthdr = iph->protocol;
+ } else if (nexthdr == IPPROTO_IPV6) {
+ ip6h = data + nh_off + ihl_len;
+ if (ip6h + 1 > data_end)
+ return 0;
+ ihl_len += sizeof(struct ipv6hdr);
+ nexthdr = ip6h->nexthdr;
+ }
+
+ if (nexthdr == IPPROTO_TCP)
+ return tcp(data, nh_off + ihl_len, data_end);
+ else if (nexthdr == IPPROTO_UDP)
+ return udp(data, nh_off + ihl_len, data_end);
+ return 0;
+}
+
+struct vlan_hdr {
+ uint16_t h_vlan_TCI;
+ uint16_t h_vlan_encapsulated_proto;
+};
+
+SEC("varlen")
+int handle_ingress(struct __sk_buff *skb)
+{
+ void *data = (void *)(long)skb->data;
+ struct ethhdr *eth = data;
+ void *data_end = (void *)(long)skb->data_end;
+ uint64_t h_proto, nh_off;
+
+ nh_off = sizeof(*eth);
+ if (data + nh_off > data_end)
+ return 0;
+
+ h_proto = eth->h_proto;
+
+ if (h_proto == ETH_P_8021Q || h_proto == ETH_P_8021AD) {
+ struct vlan_hdr *vhdr;
+
+ vhdr = data + nh_off;
+ nh_off += sizeof(struct vlan_hdr);
+ if (data + nh_off > data_end)
+ return 0;
+ h_proto = vhdr->h_vlan_encapsulated_proto;
+ }
+ if (h_proto == ETH_P_8021Q || h_proto == ETH_P_8021AD) {
+ struct vlan_hdr *vhdr;
+
+ vhdr = data + nh_off;
+ nh_off += sizeof(struct vlan_hdr);
+ if (data + nh_off > data_end)
+ return 0;
+ h_proto = vhdr->h_vlan_encapsulated_proto;
+ }
+ if (h_proto == htons(ETH_P_IP))
+ return parse_ipv4(data, nh_off, data_end);
+ else if (h_proto == htons(ETH_P_IPV6))
+ return parse_ipv6(data, nh_off, data_end);
+ return 0;
+}
+char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/test_cls_bpf.sh b/samples/bpf/test_cls_bpf.sh
new file mode 100755
index 000000000000..0365d5ee512c
--- /dev/null
+++ b/samples/bpf/test_cls_bpf.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+function pktgen {
+ ../pktgen/pktgen_bench_xmit_mode_netif_receive.sh -i $IFC -s 64 \
+ -m 90:e2:ba:ff:ff:ff -d 192.168.0.1 -t 4
+ local dropped=`tc -s qdisc show dev $IFC | tail -3 | awk '/drop/{print $7}'`
+ if [ "$dropped" == "0," ]; then
+ echo "FAIL"
+ else
+ echo "Successfully filtered " $dropped " packets"
+ fi
+}
+
+function test {
+ echo -n "Loading bpf program '$2'... "
+ tc qdisc add dev $IFC clsact
+ tc filter add dev $IFC ingress bpf da obj $1 sec $2
+ local status=$?
+ if [ $status -ne 0 ]; then
+ echo "FAIL"
+ else
+ echo "ok"
+ pktgen
+ fi
+ tc qdisc del dev $IFC clsact
+}
+
+IFC=test_veth
+
+ip link add name $IFC type veth peer name pair_$IFC
+ip link set $IFC up
+ip link set pair_$IFC up
+
+test ./parse_simple.o simple
+test ./parse_varlen.o varlen
+test ./parse_ldabs.o ldabs
+ip link del dev $IFC
--
2.8.0
^ permalink raw reply related
* [PATCH net-next 3/7] bpf: improve verifier state equivalence
From: Alexei Starovoitov @ 2016-05-06 2:49 UTC (permalink / raw)
To: David S . Miller; +Cc: Daniel Borkmann, netdev, kernel-team
In-Reply-To: <1462502955-1731797-1-git-send-email-ast@fb.com>
since UNKNOWN_VALUE type is weaker than CONST_IMM we can un-teach
verifier its recognition of constants in conditional branches
without affecting safety.
Ex:
if (reg == 123) {
.. here verifier was marking reg->type as CONST_IMM
instead keep reg as UNKNOWN_VALUE
}
Two verifier states with UNKNOWN_VALUE are equivalent, whereas
CONST_IMM_X != CONST_IMM_Y, since CONST_IMM is used for stack range
verification and other cases.
So help search pruning by marking registers as UNKNOWN_VALUE
where possible instead of CONST_IMM.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
---
kernel/bpf/verifier.c | 23 +++--------------------
1 file changed, 3 insertions(+), 20 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6338c61fc2a1..84bff68cf80e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1704,12 +1704,11 @@ static int check_cond_jmp_op(struct verifier_env *env,
*/
regs[insn->dst_reg].type = PTR_TO_MAP_VALUE;
/* branch targer cannot access it, since reg == 0 */
- other_branch->regs[insn->dst_reg].type = CONST_IMM;
- other_branch->regs[insn->dst_reg].imm = 0;
+ mark_reg_unknown_value(other_branch->regs,
+ insn->dst_reg);
} else {
other_branch->regs[insn->dst_reg].type = PTR_TO_MAP_VALUE;
- regs[insn->dst_reg].type = CONST_IMM;
- regs[insn->dst_reg].imm = 0;
+ mark_reg_unknown_value(regs, insn->dst_reg);
}
} else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
dst_reg->type == PTR_TO_PACKET &&
@@ -1718,22 +1717,6 @@ static int check_cond_jmp_op(struct verifier_env *env,
} else if (is_pointer_value(env, insn->dst_reg)) {
verbose("R%d pointer comparison prohibited\n", insn->dst_reg);
return -EACCES;
- } else if (BPF_SRC(insn->code) == BPF_K &&
- (opcode == BPF_JEQ || opcode == BPF_JNE)) {
-
- if (opcode == BPF_JEQ) {
- /* detect if (R == imm) goto
- * and in the target state recognize that R = imm
- */
- other_branch->regs[insn->dst_reg].type = CONST_IMM;
- other_branch->regs[insn->dst_reg].imm = insn->imm;
- } else {
- /* detect if (R != imm) goto
- * and in the fall-through state recognize that R = imm
- */
- regs[insn->dst_reg].type = CONST_IMM;
- regs[insn->dst_reg].imm = insn->imm;
- }
}
if (log_level)
print_verifier_state(&env->cur_state);
--
2.8.0
^ permalink raw reply related
* [PATCH net-next 1/7] bpf: cleanup verifier code
From: Alexei Starovoitov @ 2016-05-06 2:49 UTC (permalink / raw)
To: David S . Miller; +Cc: Daniel Borkmann, netdev, kernel-team
In-Reply-To: <1462502955-1731797-1-git-send-email-ast@fb.com>
cleanup verifier code and prepare it for addition of "pointer to packet" logic
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
---
kernel/bpf/verifier.c | 100 ++++++++++++++++++++++++++------------------------
1 file changed, 53 insertions(+), 47 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 63554b6d4e25..afeb62808902 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -249,28 +249,30 @@ static const char * const reg_type_str[] = {
[CONST_IMM] = "imm",
};
-static void print_verifier_state(struct verifier_env *env)
+static void print_verifier_state(struct verifier_state *state)
{
+ struct reg_state *reg;
enum bpf_reg_type t;
int i;
for (i = 0; i < MAX_BPF_REG; i++) {
- t = env->cur_state.regs[i].type;
+ reg = &state->regs[i];
+ t = reg->type;
if (t == NOT_INIT)
continue;
verbose(" R%d=%s", i, reg_type_str[t]);
if (t == CONST_IMM || t == PTR_TO_STACK)
- verbose("%ld", env->cur_state.regs[i].imm);
+ verbose("%ld", reg->imm);
else if (t == CONST_PTR_TO_MAP || t == PTR_TO_MAP_VALUE ||
t == PTR_TO_MAP_VALUE_OR_NULL)
verbose("(ks=%d,vs=%d)",
- env->cur_state.regs[i].map_ptr->key_size,
- env->cur_state.regs[i].map_ptr->value_size);
+ reg->map_ptr->key_size,
+ reg->map_ptr->value_size);
}
for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) {
- if (env->cur_state.stack_slot_type[i] == STACK_SPILL)
+ if (state->stack_slot_type[i] == STACK_SPILL)
verbose(" fp%d=%s", -MAX_BPF_STACK + i,
- reg_type_str[env->cur_state.spilled_regs[i / BPF_REG_SIZE].type]);
+ reg_type_str[state->spilled_regs[i / BPF_REG_SIZE].type]);
}
verbose("\n");
}
@@ -686,10 +688,11 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
int value_regno)
{
struct verifier_state *state = &env->cur_state;
+ struct reg_state *reg = &state->regs[regno];
int size, err = 0;
- if (state->regs[regno].type == PTR_TO_STACK)
- off += state->regs[regno].imm;
+ if (reg->type == PTR_TO_STACK)
+ off += reg->imm;
size = bpf_size_to_bytes(bpf_size);
if (size < 0)
@@ -700,7 +703,7 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
return -EACCES;
}
- if (state->regs[regno].type == PTR_TO_MAP_VALUE) {
+ if (reg->type == PTR_TO_MAP_VALUE) {
if (t == BPF_WRITE && value_regno >= 0 &&
is_pointer_value(env, value_regno)) {
verbose("R%d leaks addr into map\n", value_regno);
@@ -710,7 +713,7 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
if (!err && t == BPF_READ && value_regno >= 0)
mark_reg_unknown_value(state->regs, value_regno);
- } else if (state->regs[regno].type == PTR_TO_CTX) {
+ } else if (reg->type == PTR_TO_CTX) {
if (t == BPF_WRITE && value_regno >= 0 &&
is_pointer_value(env, value_regno)) {
verbose("R%d leaks addr into ctx\n", value_regno);
@@ -720,8 +723,7 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
if (!err && t == BPF_READ && value_regno >= 0)
mark_reg_unknown_value(state->regs, value_regno);
- } else if (state->regs[regno].type == FRAME_PTR ||
- state->regs[regno].type == PTR_TO_STACK) {
+ } else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) {
if (off >= 0 || off < -MAX_BPF_STACK) {
verbose("invalid stack off=%d size=%d\n", off, size);
return -EACCES;
@@ -739,7 +741,7 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
}
} else {
verbose("R%d invalid mem access '%s'\n",
- regno, reg_type_str[state->regs[regno].type]);
+ regno, reg_type_str[reg->type]);
return -EACCES;
}
return err;
@@ -1104,7 +1106,7 @@ static int check_call(struct verifier_env *env, int func_id)
/* check validity of 32-bit and 64-bit arithmetic operations */
static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
{
- struct reg_state *regs = env->cur_state.regs;
+ struct reg_state *regs = env->cur_state.regs, *dst_reg;
u8 opcode = BPF_OP(insn->code);
int err;
@@ -1193,8 +1195,6 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
} else { /* all other ALU ops: and, sub, xor, add, ... */
- bool stack_relative = false;
-
if (BPF_SRC(insn->code) == BPF_X) {
if (insn->imm != 0 || insn->off != 0) {
verbose("BPF_ALU uses reserved fields\n");
@@ -1232,11 +1232,19 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
}
}
+ /* check dest operand */
+ err = check_reg_arg(regs, insn->dst_reg, DST_OP_NO_MARK);
+ if (err)
+ return err;
+
+ dst_reg = ®s[insn->dst_reg];
+
/* pattern match 'bpf_add Rx, imm' instruction */
if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 &&
- regs[insn->dst_reg].type == FRAME_PTR &&
- BPF_SRC(insn->code) == BPF_K) {
- stack_relative = true;
+ dst_reg->type == FRAME_PTR && BPF_SRC(insn->code) == BPF_K) {
+ dst_reg->type = PTR_TO_STACK;
+ dst_reg->imm = insn->imm;
+ return 0;
} else if (is_pointer_value(env, insn->dst_reg)) {
verbose("R%d pointer arithmetic prohibited\n",
insn->dst_reg);
@@ -1248,15 +1256,8 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
return -EACCES;
}
- /* check dest operand */
- err = check_reg_arg(regs, insn->dst_reg, DST_OP);
- if (err)
- return err;
-
- if (stack_relative) {
- regs[insn->dst_reg].type = PTR_TO_STACK;
- regs[insn->dst_reg].imm = insn->imm;
- }
+ /* mark dest operand */
+ mark_reg_unknown_value(regs, insn->dst_reg);
}
return 0;
@@ -1265,7 +1266,7 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
static int check_cond_jmp_op(struct verifier_env *env,
struct bpf_insn *insn, int *insn_idx)
{
- struct reg_state *regs = env->cur_state.regs;
+ struct reg_state *regs = env->cur_state.regs, *dst_reg;
struct verifier_state *other_branch;
u8 opcode = BPF_OP(insn->code);
int err;
@@ -1303,11 +1304,12 @@ static int check_cond_jmp_op(struct verifier_env *env,
if (err)
return err;
+ dst_reg = ®s[insn->dst_reg];
+
/* detect if R == 0 where R was initialized to zero earlier */
if (BPF_SRC(insn->code) == BPF_K &&
(opcode == BPF_JEQ || opcode == BPF_JNE) &&
- regs[insn->dst_reg].type == CONST_IMM &&
- regs[insn->dst_reg].imm == insn->imm) {
+ dst_reg->type == CONST_IMM && dst_reg->imm == insn->imm) {
if (opcode == BPF_JEQ) {
/* if (imm == imm) goto pc+off;
* only follow the goto, ignore fall-through
@@ -1329,9 +1331,8 @@ static int check_cond_jmp_op(struct verifier_env *env,
/* detect if R == 0 where R is returned value from bpf_map_lookup_elem() */
if (BPF_SRC(insn->code) == BPF_K &&
- insn->imm == 0 && (opcode == BPF_JEQ ||
- opcode == BPF_JNE) &&
- regs[insn->dst_reg].type == PTR_TO_MAP_VALUE_OR_NULL) {
+ insn->imm == 0 && (opcode == BPF_JEQ || opcode == BPF_JNE) &&
+ dst_reg->type == PTR_TO_MAP_VALUE_OR_NULL) {
if (opcode == BPF_JEQ) {
/* next fallthrough insn can access memory via
* this register
@@ -1366,7 +1367,7 @@ static int check_cond_jmp_op(struct verifier_env *env,
}
}
if (log_level)
- print_verifier_state(env);
+ print_verifier_state(&env->cur_state);
return 0;
}
@@ -1444,14 +1445,14 @@ static int check_ld_abs(struct verifier_env *env, struct bpf_insn *insn)
int i, err;
if (!may_access_skb(env->prog->type)) {
- verbose("BPF_LD_ABS|IND instructions not allowed for this program type\n");
+ verbose("BPF_LD_[ABS|IND] instructions not allowed for this program type\n");
return -EINVAL;
}
if (insn->dst_reg != BPF_REG_0 || insn->off != 0 ||
BPF_SIZE(insn->code) == BPF_DW ||
(mode == BPF_ABS && insn->src_reg != BPF_REG_0)) {
- verbose("BPF_LD_ABS uses reserved fields\n");
+ verbose("BPF_LD_[ABS|IND] uses reserved fields\n");
return -EINVAL;
}
@@ -1712,17 +1713,21 @@ err_free:
*/
static bool states_equal(struct verifier_state *old, struct verifier_state *cur)
{
+ struct reg_state *rold, *rcur;
int i;
for (i = 0; i < MAX_BPF_REG; i++) {
- if (memcmp(&old->regs[i], &cur->regs[i],
- sizeof(old->regs[0])) != 0) {
- if (old->regs[i].type == NOT_INIT ||
- (old->regs[i].type == UNKNOWN_VALUE &&
- cur->regs[i].type != NOT_INIT))
- continue;
- return false;
- }
+ rold = &old->regs[i];
+ rcur = &cur->regs[i];
+
+ if (memcmp(rold, rcur, sizeof(*rold)) == 0)
+ continue;
+
+ if (rold->type == NOT_INIT ||
+ (rold->type == UNKNOWN_VALUE && rcur->type != NOT_INIT))
+ continue;
+
+ return false;
}
for (i = 0; i < MAX_BPF_STACK; i++) {
@@ -1844,7 +1849,7 @@ static int do_check(struct verifier_env *env)
if (log_level && do_print_state) {
verbose("\nfrom %d to %d:", prev_insn_idx, insn_idx);
- print_verifier_state(env);
+ print_verifier_state(&env->cur_state);
do_print_state = false;
}
@@ -2056,6 +2061,7 @@ process_bpf_exit:
insn_idx++;
}
+ verbose("processed %d insns\n", insn_processed);
return 0;
}
--
2.8.0
^ permalink raw reply related
* [PATCH net-next 4/7] bpf: wire in data and data_end for cls_act_bpf
From: Alexei Starovoitov @ 2016-05-06 2:49 UTC (permalink / raw)
To: David S . Miller; +Cc: Daniel Borkmann, netdev, kernel-team
In-Reply-To: <1462502955-1731797-1-git-send-email-ast@fb.com>
allow cls_bpf and act_bpf programs access skb->data and skb->data_end pointers.
The bpf helpers that change skb->data need to update data_end pointer as well.
The verifier checks that programs always reload data, data_end pointers
after calls to such bpf helpers.
We cannot add 'data_end' pointer to struct qdisc_skb_cb directly,
since it's embedded as-is by infiniband ipoib, so wrapper struct is needed.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
---
include/linux/filter.h | 16 ++++++++++++++++
net/core/filter.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------
net/sched/act_bpf.c | 2 ++
net/sched/cls_bpf.c | 2 ++
4 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 43aa1f8855c7..ec1411c89105 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -352,6 +352,22 @@ struct sk_filter {
#define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN
+struct bpf_skb_data_end {
+ struct qdisc_skb_cb qdisc_cb;
+ void *data_end;
+};
+
+/* compute the linear packet data range [data, data_end) which
+ * will be accessed by cls_bpf and act_bpf programs
+ */
+static inline void bpf_compute_data_end(struct sk_buff *skb)
+{
+ struct bpf_skb_data_end *cb = (struct bpf_skb_data_end *)skb->cb;
+
+ BUILD_BUG_ON(sizeof(*cb) > FIELD_SIZEOF(struct sk_buff, cb));
+ cb->data_end = skb->data + skb_headlen(skb);
+}
+
static inline u8 *bpf_skb_cb(struct sk_buff *skb)
{
/* eBPF programs may read/write skb->cb[] area to transfer meta
diff --git a/net/core/filter.c b/net/core/filter.c
index 218e5de8c402..71c2a1f473ad 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1344,6 +1344,21 @@ struct bpf_scratchpad {
static DEFINE_PER_CPU(struct bpf_scratchpad, bpf_sp);
+static inline int bpf_try_make_writable(struct sk_buff *skb,
+ unsigned int write_len)
+{
+ int err;
+
+ if (!skb_cloned(skb))
+ return 0;
+ if (skb_clone_writable(skb, write_len))
+ return 0;
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (!err)
+ bpf_compute_data_end(skb);
+ return err;
+}
+
static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
{
struct bpf_scratchpad *sp = this_cpu_ptr(&bpf_sp);
@@ -1366,7 +1381,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
*/
if (unlikely((u32) offset > 0xffff || len > sizeof(sp->buff)))
return -EFAULT;
- if (unlikely(skb_try_make_writable(skb, offset + len)))
+ if (unlikely(bpf_try_make_writable(skb, offset + len)))
return -EFAULT;
ptr = skb_header_pointer(skb, offset, len, sp->buff);
@@ -1444,7 +1459,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
return -EINVAL;
if (unlikely((u32) offset > 0xffff))
return -EFAULT;
- if (unlikely(skb_try_make_writable(skb, offset + sizeof(sum))))
+ if (unlikely(bpf_try_make_writable(skb, offset + sizeof(sum))))
return -EFAULT;
ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
@@ -1499,7 +1514,7 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
return -EINVAL;
if (unlikely((u32) offset > 0xffff))
return -EFAULT;
- if (unlikely(skb_try_make_writable(skb, offset + sizeof(sum))))
+ if (unlikely(bpf_try_make_writable(skb, offset + sizeof(sum))))
return -EFAULT;
ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
@@ -1699,12 +1714,15 @@ static u64 bpf_skb_vlan_push(u64 r1, u64 r2, u64 vlan_tci, u64 r4, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
__be16 vlan_proto = (__force __be16) r2;
+ int ret;
if (unlikely(vlan_proto != htons(ETH_P_8021Q) &&
vlan_proto != htons(ETH_P_8021AD)))
vlan_proto = htons(ETH_P_8021Q);
- return skb_vlan_push(skb, vlan_proto, vlan_tci);
+ ret = skb_vlan_push(skb, vlan_proto, vlan_tci);
+ bpf_compute_data_end(skb);
+ return ret;
}
const struct bpf_func_proto bpf_skb_vlan_push_proto = {
@@ -1720,8 +1738,11 @@ EXPORT_SYMBOL_GPL(bpf_skb_vlan_push_proto);
static u64 bpf_skb_vlan_pop(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
+ int ret;
- return skb_vlan_pop(skb);
+ ret = skb_vlan_pop(skb);
+ bpf_compute_data_end(skb);
+ return ret;
}
const struct bpf_func_proto bpf_skb_vlan_pop_proto = {
@@ -2066,8 +2087,12 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type)
static bool sk_filter_is_valid_access(int off, int size,
enum bpf_access_type type)
{
- if (off == offsetof(struct __sk_buff, tc_classid))
+ switch (off) {
+ case offsetof(struct __sk_buff, tc_classid):
+ case offsetof(struct __sk_buff, data):
+ case offsetof(struct __sk_buff, data_end):
return false;
+ }
if (type == BPF_WRITE) {
switch (off) {
@@ -2215,6 +2240,20 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
*insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, ctx_off);
break;
+ case offsetof(struct __sk_buff, data):
+ *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct sk_buff, data)),
+ dst_reg, src_reg,
+ offsetof(struct sk_buff, data));
+ break;
+
+ case offsetof(struct __sk_buff, data_end):
+ ctx_off -= offsetof(struct __sk_buff, data_end);
+ ctx_off += offsetof(struct sk_buff, cb);
+ ctx_off += offsetof(struct bpf_skb_data_end, data_end);
+ *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(sizeof(void *)),
+ dst_reg, src_reg, ctx_off);
+ break;
+
case offsetof(struct __sk_buff, tc_index):
#ifdef CONFIG_NET_SCHED
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tc_index) != 2);
diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
index 4fd703362563..c7123e01c2ca 100644
--- a/net/sched/act_bpf.c
+++ b/net/sched/act_bpf.c
@@ -53,9 +53,11 @@ static int tcf_bpf(struct sk_buff *skb, const struct tc_action *act,
filter = rcu_dereference(prog->filter);
if (at_ingress) {
__skb_push(skb, skb->mac_len);
+ bpf_compute_data_end(skb);
filter_res = BPF_PROG_RUN(filter, skb);
__skb_pull(skb, skb->mac_len);
} else {
+ bpf_compute_data_end(skb);
filter_res = BPF_PROG_RUN(filter, skb);
}
rcu_read_unlock();
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 425fe6a0eda3..7b342c779da7 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -96,9 +96,11 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
if (at_ingress) {
/* It is safe to push/pull even if skb_shared() */
__skb_push(skb, skb->mac_len);
+ bpf_compute_data_end(skb);
filter_res = BPF_PROG_RUN(prog->filter, skb);
__skb_pull(skb, skb->mac_len);
} else {
+ bpf_compute_data_end(skb);
filter_res = BPF_PROG_RUN(prog->filter, skb);
}
--
2.8.0
^ permalink raw reply related
* [PATCH net-next 5/7] bpf: add documentation for 'direct packet access'
From: Alexei Starovoitov @ 2016-05-06 2:49 UTC (permalink / raw)
To: David S . Miller; +Cc: Daniel Borkmann, netdev, kernel-team
In-Reply-To: <1462502955-1731797-1-git-send-email-ast@fb.com>
explain how verifier checks safety of packet access
and update email addresses.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
---
Documentation/networking/filter.txt | 85 ++++++++++++++++++++++++++++++++++++-
1 file changed, 83 insertions(+), 2 deletions(-)
diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt
index 96da119a47e7..6aef0b5f3bc7 100644
--- a/Documentation/networking/filter.txt
+++ b/Documentation/networking/filter.txt
@@ -1095,6 +1095,87 @@ all use cases.
See details of eBPF verifier in kernel/bpf/verifier.c
+Direct packet access
+--------------------
+In cls_bpf and act_bpf programs the verifier allows direct access to the packet
+data via skb->data and skb->data_end pointers.
+Ex:
+1: r4 = *(u32 *)(r1 +80) /* load skb->data_end */
+2: r3 = *(u32 *)(r1 +76) /* load skb->data */
+3: r5 = r3
+4: r5 += 14
+5: if r5 > r4 goto pc+16
+R1=ctx R3=pkt(id=0,off=0,r=14) R4=pkt_end R5=pkt(id=0,off=14,r=14) R10=fp
+6: r0 = *(u16 *)(r3 +12) /* access 12 and 13 bytes of the packet */
+
+this 2byte load from the packet is safe to do, since the program author
+did check 'if (skb->data + 14 > skb->data_end) goto err' at insn #5 which
+means that in the fall-through case the register R3 (which points to skb->data)
+has at least 14 directly accessible bytes. The verifier marks it
+as R3=pkt(id=0,off=0,r=14).
+id=0 means that no additional variables were added to the register.
+off=0 means that no additional constants were added.
+r=14 is the range of safe access which means that bytes [R3, R3 + 14) are ok.
+Note that R5 is marked as R5=pkt(id=0,off=14,r=14). It also points
+to the packet data, but constant 14 was added to the register, so
+it now points to 'skb->data + 14' and accessible range is [R5, R5 + 14 - 14)
+which is zero bytes.
+
+More complex packet access may look like:
+ R0=imm1 R1=ctx R3=pkt(id=0,off=0,r=14) R4=pkt_end R5=pkt(id=0,off=14,r=14) R10=fp
+ 6: r0 = *(u8 *)(r3 +7) /* load 7th byte from the packet */
+ 7: r4 = *(u8 *)(r3 +12)
+ 8: r4 *= 14
+ 9: r3 = *(u32 *)(r1 +76) /* load skb->data */
+10: r3 += r4
+11: r2 = r1
+12: r2 <<= 48
+13: r2 >>= 48
+14: r3 += r2
+15: r2 = r3
+16: r2 += 8
+17: r1 = *(u32 *)(r1 +80) /* load skb->data_end */
+18: if r2 > r1 goto pc+2
+ R0=inv56 R1=pkt_end R2=pkt(id=2,off=8,r=8) R3=pkt(id=2,off=0,r=8) R4=inv52 R5=pkt(id=0,off=14,r=14) R10=fp
+19: r1 = *(u8 *)(r3 +4)
+The state of the register R3 is R3=pkt(id=2,off=0,r=8)
+id=2 means that two 'r3 += rX' instructions were seen, so r3 points to some
+offset within a packet and since the program author did
+'if (r3 + 8 > r1) goto err' at insn #18, the safe range is [R3, R3 + 8).
+The verifier only allows 'add' operation on packet registers. Any other
+operation will set the register state to 'unknown_value' and it won't be
+available for direct packet access.
+Operation 'r3 += rX' may overflow and become less than original skb->data,
+therefore the verifier has to prevent that. So it tracks the number of
+upper zero bits in all 'uknown_value' registers, so when it sees
+'r3 += rX' instruction and rX is more than 16-bit value, it will error as:
+"cannot add integer value with N upper zero bits to ptr_to_packet"
+Ex. after insn 'r4 = *(u8 *)(r3 +12)' (insn #7 above) the state of r4 is
+R4=inv56 which means that upper 56 bits on the register are guaranteed
+to be zero. After insn 'r4 *= 14' the state becomes R4=inv52, since
+multiplying 8-bit value by constant 14 will keep upper 52 bits as zero.
+Similarly 'r2 >>= 48' will make R2=inv48, since the shift is not sign
+extending. This logic is implemented in evaluate_reg_alu() function.
+
+The end result is that bpf program author can access packet directly
+using normal C code as:
+ void *data = (void *)(long)skb->data;
+ void *data_end = (void *)(long)skb->data_end;
+ struct eth_hdr *eth = data;
+ struct iphdr *iph = data + sizeof(*eth);
+ struct udphdr *udp = data + sizeof(*eth) + sizeof(*iph);
+
+ if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*udp) > data_end)
+ return 0;
+ if (eth->h_proto != htons(ETH_P_IP))
+ return 0;
+ if (iph->protocol != IPPROTO_UDP || iph->ihl != 5)
+ return 0;
+ if (udp->dest == 53 || udp->source == 9)
+ ...;
+which makes such programs easier to write comparing to LD_ABS insn
+and significantly faster.
+
eBPF maps
---------
'maps' is a generic storage of different types for sharing data between kernel
@@ -1293,5 +1374,5 @@ to give potential BPF hackers or security auditors a better overview of
the underlying architecture.
Jay Schulist <jschlst@samba.org>
-Daniel Borkmann <dborkman@redhat.com>
-Alexei Starovoitov <ast@plumgrid.com>
+Daniel Borkmann <daniel@iogearbox.net>
+Alexei Starovoitov <ast@kernel.org>
--
2.8.0
^ permalink raw reply related
* Re: [PATCH v2 net-next] net/mlx4: Avoid wrong virtual mappings
From: David Miller @ 2016-05-06 3:22 UTC (permalink / raw)
To: hagaya
Cc: dledford, linux-rdma, netdev, okaya, timur, eli, ogerlitz, eranbe,
yishaih, talal, saeedm
In-Reply-To: <1462362615-20238-1-git-send-email-hagaya@mellanox.com>
From: Haggai Abramovsky <hagaya@mellanox.com>
Date: Wed, 4 May 2016 14:50:15 +0300
> The dma_alloc_coherent() function returns a virtual address which can
> be used for coherent access to the underlying memory. On some
> architectures, like arm64, undefined behavior results if this memory is
> also accessed via virtual mappings that are not coherent. Because of
> their undefined nature, operations like virt_to_page() return garbage
> when passed virtual addresses obtained from dma_alloc_coherent(). Any
> subsequent mappings via vmap() of the garbage page values are unusable
> and result in bad things like bus errors (synchronous aborts in ARM64
> speak).
>
> The mlx4 driver contains code that does the equivalent of:
> vmap(virt_to_page(dma_alloc_coherent)), this results in an OOPs when the
> device is opened.
>
> Prevent Ethernet driver to run this problematic code by forcing it to
> allocate contiguous memory. As for the Infiniband driver, at first we
> are trying to allocate contiguous memory, but in case of failure roll
> back to work with fragmented memory.
>
> Signed-off-by: Haggai Abramovsky <hagaya@mellanox.com>
> Signed-off-by: Yishai Hadas <yishaih@mellanox.com>
> Reported-by: David Daney <david.daney@cavium.com>
> Tested-by: Sinan Kaya <okaya@codeaurora.org>
Applied, thanks.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox