diff options
60 files changed, 2328 insertions, 779 deletions
diff --git a/Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml b/Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml index 5592f58fa6f0..228683773151 100644 --- a/Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml +++ b/Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml @@ -48,7 +48,7 @@ properties: "^led@[01]$": type: object description: Hellcreek leds - $ref: ../../leds/common.yaml# + $ref: /schemas/leds/common.yaml# properties: reg: diff --git a/Documentation/networking/devlink/mlxsw.rst b/Documentation/networking/devlink/mlxsw.rst index cf857cb4ba8f..433962225bd4 100644 --- a/Documentation/networking/devlink/mlxsw.rst +++ b/Documentation/networking/devlink/mlxsw.rst @@ -58,6 +58,30 @@ The ``mlxsw`` driver reports the following versions - running - Three digit firmware version +Line card auxiliary device info versions +======================================== + +The ``mlxsw`` driver reports the following versions for line card auxiliary device + +.. list-table:: devlink info versions implemented + :widths: 5 5 90 + + * - Name + - Type + - Description + * - ``hw.revision`` + - fixed + - The hardware revision for this line card + * - ``ini.version`` + - running + - Version of line card INI loaded + * - ``fw.psid`` + - fixed + - Line card device PSID + * - ``fw.version`` + - running + - Three digit firmware version of line card device + Driver-specific Traps ===================== diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 911aace42284..c79a5128235f 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -26,11 +26,6 @@ #include "ksz8795_reg.h" #include "ksz8.h" -static bool ksz_is_ksz88x3(struct ksz_device *dev) -{ - return dev->chip_id == 0x8830; -} - static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); @@ -1116,7 +1111,6 @@ void ksz8_port_mirror_del(struct ksz_device *dev, int port, static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port) { struct ksz_port *p = &dev->ports[port]; - u8 data8; if (!p->interface && dev->compat_interface) { dev_warn(dev->dev, @@ -1125,40 +1119,6 @@ static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port) port); p->interface = dev->compat_interface; } - - /* Configure MII interface for proper network communication. */ - ksz_read8(dev, REG_PORT_5_CTRL_6, &data8); - data8 &= ~PORT_INTERFACE_TYPE; - data8 &= ~PORT_GMII_1GPS_MODE; - switch (p->interface) { - case PHY_INTERFACE_MODE_MII: - p->phydev.speed = SPEED_100; - break; - case PHY_INTERFACE_MODE_RMII: - data8 |= PORT_INTERFACE_RMII; - p->phydev.speed = SPEED_100; - break; - case PHY_INTERFACE_MODE_GMII: - data8 |= PORT_GMII_1GPS_MODE; - data8 |= PORT_INTERFACE_GMII; - p->phydev.speed = SPEED_1000; - break; - default: - data8 &= ~PORT_RGMII_ID_IN_ENABLE; - data8 &= ~PORT_RGMII_ID_OUT_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_RXID) - data8 |= PORT_RGMII_ID_IN_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_TXID) - data8 |= PORT_RGMII_ID_OUT_ENABLE; - data8 |= PORT_GMII_1GPS_MODE; - data8 |= PORT_INTERFACE_RGMII; - p->phydev.speed = SPEED_1000; - break; - } - ksz_write8(dev, REG_PORT_5_CTRL_6, data8); - p->phydev.duplex = 1; } void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index a848eb4c54cb..77487d611824 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -170,15 +170,7 @@ #define REG_PORT_5_CTRL_6 0x56 #define PORT_MII_INTERNAL_CLOCK BIT(7) -#define PORT_GMII_1GPS_MODE BIT(6) -#define PORT_RGMII_ID_IN_ENABLE BIT(4) -#define PORT_RGMII_ID_OUT_ENABLE BIT(3) #define PORT_GMII_MAC_MODE BIT(2) -#define PORT_INTERFACE_TYPE 0x3 -#define PORT_INTERFACE_MII 0 -#define PORT_INTERFACE_RMII 1 -#define PORT_INTERFACE_GMII 2 -#define PORT_INTERFACE_RGMII 3 #define REG_PORT_1_CTRL_7 0x17 #define REG_PORT_2_CTRL_7 0x27 diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 6453642fa14c..4b14d80d27ed 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -19,11 +19,6 @@ #include "ksz_common.h" #include "ksz9477.h" -/* Used with variable features to indicate capabilities. */ -#define GBIT_SUPPORT BIT(0) -#define NEW_XMII BIT(1) -#define IS_9893 BIT(2) - static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); @@ -866,142 +861,18 @@ void ksz9477_port_mirror_del(struct ksz_device *dev, int port, PORT_MIRROR_SNIFFER, false); } -static bool ksz9477_get_gbit(struct ksz_device *dev, u8 data) -{ - bool gbit; - - if (dev->features & NEW_XMII) - gbit = !(data & PORT_MII_NOT_1GBIT); - else - gbit = !!(data & PORT_MII_1000MBIT_S1); - return gbit; -} - -static void ksz9477_set_gbit(struct ksz_device *dev, bool gbit, u8 *data) -{ - if (dev->features & NEW_XMII) { - if (gbit) - *data &= ~PORT_MII_NOT_1GBIT; - else - *data |= PORT_MII_NOT_1GBIT; - } else { - if (gbit) - *data |= PORT_MII_1000MBIT_S1; - else - *data &= ~PORT_MII_1000MBIT_S1; - } -} - -static int ksz9477_get_xmii(struct ksz_device *dev, u8 data) -{ - int mode; - - if (dev->features & NEW_XMII) { - switch (data & PORT_MII_SEL_M) { - case PORT_MII_SEL: - mode = 0; - break; - case PORT_RMII_SEL: - mode = 1; - break; - case PORT_GMII_SEL: - mode = 2; - break; - default: - mode = 3; - } - } else { - switch (data & PORT_MII_SEL_M) { - case PORT_MII_SEL_S1: - mode = 0; - break; - case PORT_RMII_SEL_S1: - mode = 1; - break; - case PORT_GMII_SEL_S1: - mode = 2; - break; - default: - mode = 3; - } - } - return mode; -} - -static void ksz9477_set_xmii(struct ksz_device *dev, int mode, u8 *data) -{ - u8 xmii; - - if (dev->features & NEW_XMII) { - switch (mode) { - case 0: - xmii = PORT_MII_SEL; - break; - case 1: - xmii = PORT_RMII_SEL; - break; - case 2: - xmii = PORT_GMII_SEL; - break; - default: - xmii = PORT_RGMII_SEL; - break; - } - } else { - switch (mode) { - case 0: - xmii = PORT_MII_SEL_S1; - break; - case 1: - xmii = PORT_RMII_SEL_S1; - break; - case 2: - xmii = PORT_GMII_SEL_S1; - break; - default: - xmii = PORT_RGMII_SEL_S1; - break; - } - } - *data &= ~PORT_MII_SEL_M; - *data |= xmii; -} - static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port) { phy_interface_t interface; bool gbit; - int mode; - u8 data8; if (port < dev->phy_port_cnt) return PHY_INTERFACE_MODE_NA; - ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); - gbit = ksz9477_get_gbit(dev, data8); - mode = ksz9477_get_xmii(dev, data8); - switch (mode) { - case 2: - interface = PHY_INTERFACE_MODE_GMII; - if (gbit) - break; - fallthrough; - case 0: - interface = PHY_INTERFACE_MODE_MII; - break; - case 1: - interface = PHY_INTERFACE_MODE_RMII; - break; - default: - interface = PHY_INTERFACE_MODE_RGMII; - if (data8 & PORT_RGMII_ID_EG_ENABLE) - interface = PHY_INTERFACE_MODE_RGMII_TXID; - if (data8 & PORT_RGMII_ID_IG_ENABLE) { - interface = PHY_INTERFACE_MODE_RGMII_RXID; - if (data8 & PORT_RGMII_ID_EG_ENABLE) - interface = PHY_INTERFACE_MODE_RGMII_ID; - } - break; - } + + gbit = ksz_get_gbit(dev, port); + + interface = ksz_get_xmii(dev, port, gbit); + return interface; } @@ -1073,10 +944,9 @@ void ksz9477_get_caps(struct ksz_device *dev, int port, void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) { - struct ksz_port *p = &dev->ports[port]; struct dsa_switch *ds = dev->ds; - u8 data8, member; u16 data16; + u8 member; /* enable tag tail for host port */ if (cpu_port) @@ -1116,44 +986,6 @@ void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, true); - - /* configure MAC to 1G & RGMII mode */ - ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); - switch (p->interface) { - case PHY_INTERFACE_MODE_MII: - ksz9477_set_xmii(dev, 0, &data8); - ksz9477_set_gbit(dev, false, &data8); - p->phydev.speed = SPEED_100; - break; - case PHY_INTERFACE_MODE_RMII: - ksz9477_set_xmii(dev, 1, &data8); - ksz9477_set_gbit(dev, false, &data8); - p->phydev.speed = SPEED_100; - break; - case PHY_INTERFACE_MODE_GMII: - ksz9477_set_xmii(dev, 2, &data8); - ksz9477_set_gbit(dev, true, &data8); - p->phydev.speed = SPEED_1000; - break; - default: - ksz9477_set_xmii(dev, 3, &data8); - ksz9477_set_gbit(dev, true, &data8); - data8 &= ~PORT_RGMII_ID_IG_ENABLE; - data8 &= ~PORT_RGMII_ID_EG_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_RXID) - data8 |= PORT_RGMII_ID_IG_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_TXID) - data8 |= PORT_RGMII_ID_EG_ENABLE; - /* On KSZ9893, disable RGMII in-band status support */ - if (dev->features & IS_9893) - data8 &= ~PORT_MII_MAC_MODE; - p->phydev.speed = SPEED_1000; - break; - } - ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8); - p->phydev.duplex = 1; } if (cpu_port) @@ -1341,9 +1173,6 @@ int ksz9477_switch_init(struct ksz_device *dev) dev->features &= ~GBIT_SUPPORT; dev->phy_port_cnt = 2; } else { - /* Chip uses new XMII register definitions. */ - dev->features |= NEW_XMII; - /* Chip does not support gigabit. */ if (!(data8 & SW_GIGABIT_ABLE)) dev->features &= ~GBIT_SUPPORT; diff --git a/drivers/net/dsa/microchip/ksz9477_reg.h b/drivers/net/dsa/microchip/ksz9477_reg.h index d0cce4ca3cf9..ddf99d1e4bbd 100644 --- a/drivers/net/dsa/microchip/ksz9477_reg.h +++ b/drivers/net/dsa/microchip/ksz9477_reg.h @@ -1175,35 +1175,11 @@ #define PORT_LINK_STATUS_FAIL BIT(0) /* 3 - xMII */ -#define REG_PORT_XMII_CTRL_0 0x0300 - #define PORT_SGMII_SEL BIT(7) -#define PORT_MII_FULL_DUPLEX BIT(6) -#define PORT_MII_100MBIT BIT(4) #define PORT_GRXC_ENABLE BIT(0) -#define REG_PORT_XMII_CTRL_1 0x0301 - #define PORT_RMII_CLK_SEL BIT(7) -/* S1 */ -#define PORT_MII_1000MBIT_S1 BIT(6) -/* S2 */ -#define PORT_MII_NOT_1GBIT BIT(6) #define PORT_MII_SEL_EDGE BIT(5) -#define PORT_RGMII_ID_IG_ENABLE BIT(4) -#define PORT_RGMII_ID_EG_ENABLE BIT(3) -#define PORT_MII_MAC_MODE BIT(2) -#define PORT_MII_SEL_M 0x3 -/* S1 */ -#define PORT_MII_SEL_S1 0x0 -#define PORT_RMII_SEL_S1 0x1 -#define PORT_GMII_SEL_S1 0x2 -#define PORT_RGMII_SEL_S1 0x3 -/* S2 */ -#define PORT_RGMII_SEL 0x0 -#define PORT_RMII_SEL 0x1 -#define PORT_GMII_SEL 0x2 -#define PORT_MII_SEL 0x3 /* 4 - MAC */ #define REG_PORT_MAC_CTRL_0 0x0400 diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index fd12a68c1dcd..ed7d137cba99 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -222,8 +222,7 @@ static const struct ksz_dev_ops lan937x_dev_ops = { .mirror_add = ksz9477_port_mirror_add, .mirror_del = ksz9477_port_mirror_del, .get_caps = lan937x_phylink_get_caps, - .phylink_mac_config = lan937x_phylink_mac_config, - .phylink_mac_link_up = lan937x_phylink_mac_link_up, + .setup_rgmii_delay = lan937x_setup_rgmii_delay, .fdb_dump = ksz9477_fdb_dump, .fdb_add = ksz9477_fdb_add, .fdb_del = ksz9477_fdb_del, @@ -257,6 +256,8 @@ static const u16 ksz8795_regs[] = { [S_START_CTRL] = 0x01, [S_BROADCAST_CTRL] = 0x06, [S_MULTICAST_CTRL] = 0x04, + [P_XMII_CTRL_0] = 0x06, + [P_XMII_CTRL_1] = 0x56, }; static const u32 ksz8795_masks[] = { @@ -279,6 +280,24 @@ static const u32 ksz8795_masks[] = { [DYNAMIC_MAC_TABLE_FID] = GENMASK(26, 20), [DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(26, 24), [DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(28, 27), + [P_MII_TX_FLOW_CTRL] = BIT(5), + [P_MII_RX_FLOW_CTRL] = BIT(5), +}; + +static const u8 ksz8795_xmii_ctrl0[] = { + [P_MII_100MBIT] = 0, + [P_MII_10MBIT] = 1, + [P_MII_FULL_DUPLEX] = 0, + [P_MII_HALF_DUPLEX] = 1, +}; + +static const u8 ksz8795_xmii_ctrl1[] = { + [P_RGMII_SEL] = 3, + [P_GMII_SEL] = 2, + [P_RMII_SEL] = 1, + [P_MII_SEL] = 0, + [P_GMII_1GBIT] = 1, + [P_GMII_NOT_1GBIT] = 0, }; static const u8 ksz8795_shifts[] = { @@ -351,20 +370,42 @@ static const u16 ksz9477_regs[] = { [S_START_CTRL] = 0x0300, [S_BROADCAST_CTRL] = 0x0332, [S_MULTICAST_CTRL] = 0x0331, + [P_XMII_CTRL_0] = 0x0300, + [P_XMII_CTRL_1] = 0x0301, }; static const u32 ksz9477_masks[] = { [ALU_STAT_WRITE] = 0, [ALU_STAT_READ] = 1, + [P_MII_TX_FLOW_CTRL] = BIT(5), + [P_MII_RX_FLOW_CTRL] = BIT(3), }; static const u8 ksz9477_shifts[] = { [ALU_STAT_INDEX] = 16, }; +static const u8 ksz9477_xmii_ctrl0[] = { + [P_MII_100MBIT] = 1, + [P_MII_10MBIT] = 0, + [P_MII_FULL_DUPLEX] = 1, + [P_MII_HALF_DUPLEX] = 0, +}; + +static const u8 ksz9477_xmii_ctrl1[] = { + [P_RGMII_SEL] = 0, + [P_RMII_SEL] = 1, + [P_GMII_SEL] = 2, + [P_MII_SEL] = 3, + [P_GMII_1GBIT] = 0, + [P_GMII_NOT_1GBIT] = 1, +}; + static const u32 lan937x_masks[] = { [ALU_STAT_WRITE] = 1, [ALU_STAT_READ] = 2, + [P_MII_TX_FLOW_CTRL] = BIT(5), + [P_MII_RX_FLOW_CTRL] = BIT(3), }; static const u8 lan937x_shifts[] = { @@ -388,6 +429,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz8795_regs, .masks = ksz8795_masks, .shifts = ksz8795_shifts, + .xmii_ctrl0 = ksz8795_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, .supports_mii = {false, false, false, false, true}, .supports_rmii = {false, false, false, false, true}, .supports_rgmii = {false, false, false, false, true}, @@ -424,6 +467,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz8795_regs, .masks = ksz8795_masks, .shifts = ksz8795_shifts, + .xmii_ctrl0 = ksz8795_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, .supports_mii = {false, false, false, false, true}, .supports_rmii = {false, false, false, false, true}, .supports_rgmii = {false, false, false, false, true}, @@ -446,6 +491,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz8795_regs, .masks = ksz8795_masks, .shifts = ksz8795_shifts, + .xmii_ctrl0 = ksz8795_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, .supports_mii = {false, false, false, false, true}, .supports_rmii = {false, false, false, false, true}, .supports_rgmii = {false, false, false, false, true}, @@ -488,6 +535,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = ksz9477_masks, .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, .supports_mii = {false, false, false, false, false, true, false}, .supports_rmii = {false, false, false, false, @@ -514,6 +563,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = ksz9477_masks, .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, .supports_mii = {false, false, false, false, false, true, true}, .supports_rmii = {false, false, false, false, @@ -539,6 +590,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = ksz9477_masks, .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, /* Same as ksz8795 */ .supports_mii = {false, false, true}, .supports_rmii = {false, false, true}, .supports_rgmii = {false, false, true}, @@ -561,6 +614,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = ksz9477_masks, .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, .supports_mii = {false, false, false, false, false, true, true}, .supports_rmii = {false, false, false, false, @@ -586,6 +641,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = lan937x_masks, .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, .supports_mii = {false, false, false, false, true}, .supports_rmii = {false, false, false, false, true}, .supports_rgmii = {false, false, false, false, true}, @@ -607,6 +664,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = lan937x_masks, .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, .supports_mii = {false, false, false, false, true, true}, .supports_rmii = {false, false, false, false, true, true}, .supports_rgmii = {false, false, false, false, true, true}, @@ -628,6 +687,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = lan937x_masks, .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, .supports_mii = {false, false, false, false, true, true, false, false}, .supports_rmii = {false, false, false, false, @@ -653,6 +714,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = lan937x_masks, .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, .supports_mii = {false, false, false, false, true, true, false, false}, .supports_rmii = {false, false, false, false, @@ -678,6 +741,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .regs = ksz9477_regs, .masks = lan937x_masks, .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, .supports_mii = {false, false, false, false, true, true, false, false}, .supports_rmii = {false, false, false, false, @@ -1343,14 +1408,205 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port) return dev->dev_ops->max_mtu(dev, port); } +static void ksz_set_xmii(struct ksz_device *dev, int port, + phy_interface_t interface) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + struct ksz_port *p = &dev->ports[port]; + const u16 *regs = dev->info->regs; + u8 data8; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + data8 &= ~(P_MII_SEL_M | P_RGMII_ID_IG_ENABLE | + P_RGMII_ID_EG_ENABLE); + + switch (interface) { + case PHY_INTERFACE_MODE_MII: + data8 |= bitval[P_MII_SEL]; + break; + case PHY_INTERFACE_MODE_RMII: + data8 |= bitval[P_RMII_SEL]; + break; + case PHY_INTERFACE_MODE_GMII: + data8 |= bitval[P_GMII_SEL]; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + data8 |= bitval[P_RGMII_SEL]; + /* On KSZ9893, disable RGMII in-band status support */ + if (dev->features & IS_9893) + data8 &= ~P_MII_MAC_MODE; + break; + default: + dev_err(dev->dev, "Unsupported interface '%s' for port %d\n", + phy_modes(interface), port); + return; + } + + if (p->rgmii_tx_val) + data8 |= P_RGMII_ID_EG_ENABLE; + + if (p->rgmii_rx_val) + data8 |= P_RGMII_ID_IG_ENABLE; + + /* Write the updated value */ + ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8); +} + +phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + phy_interface_t interface; + u8 data8; + u8 val; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + val = FIELD_GET(P_MII_SEL_M, data8); + + if (val == bitval[P_MII_SEL]) { + if (gbit) + interface = PHY_INTERFACE_MODE_GMII; + else + interface = PHY_INTERFACE_MODE_MII; + } else if (val == bitval[P_RMII_SEL]) { + interface = PHY_INTERFACE_MODE_RGMII; + } else { + interface = PHY_INTERFACE_MODE_RGMII; + if (data8 & P_RGMII_ID_EG_ENABLE) + interface = PHY_INTERFACE_MODE_RGMII_TXID; + if (data8 & P_RGMII_ID_IG_ENABLE) { + interface = PHY_INTERFACE_MODE_RGMII_RXID; + if (data8 & P_RGMII_ID_EG_ENABLE) + interface = PHY_INTERFACE_MODE_RGMII_ID; + } + } + + return interface; +} + static void ksz_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) { struct ksz_device *dev = ds->priv; + if (ksz_is_ksz88x3(dev)) + return; + + /* Internal PHYs */ + if (dev->info->internal_phy[port]) + return; + + if (phylink_autoneg_inband(mode)) { + dev_err(dev->dev, "In-band AN not supported!\n"); + return; + } + + ksz_set_xmii(dev, port, state->interface); + if (dev->dev_ops->phylink_mac_config) dev->dev_ops->phylink_mac_config(dev, port, mode, state); + + if (dev->dev_ops->setup_rgmii_delay) + dev->dev_ops->setup_rgmii_delay(dev, port); +} + +bool ksz_get_gbit(struct ksz_device *dev, int port) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + bool gbit = false; + u8 data8; + bool val; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + val = FIELD_GET(P_GMII_1GBIT_M, data8); + + if (val == bitval[P_GMII_1GBIT]) + gbit = true; + + return gbit; +} + +static void ksz_set_gbit(struct ksz_device *dev, int port, bool gbit) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + u8 data8; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + data8 &= ~P_GMII_1GBIT_M; + + if (gbit) + data8 |= FIELD_PREP(P_GMII_1GBIT_M, bitval[P_GMII_1GBIT]); + else + data8 |= FIELD_PREP(P_GMII_1GBIT_M, bitval[P_GMII_NOT_1GBIT]); + + /* Write the updated value */ + ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8); +} + +static void ksz_set_100_10mbit(struct ksz_device *dev, int port, int speed) +{ + const u8 *bitval = dev->info->xmii_ctrl0; + const u16 *regs = dev->info->regs; + u8 data8; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_0], &data8); + + data8 &= ~P_MII_100MBIT_M; + + if (speed == SPEED_100) + data8 |= FIELD_PREP(P_MII_100MBIT_M, bitval[P_MII_100MBIT]); + else + data8 |= FIELD_PREP(P_MII_100MBIT_M, bitval[P_MII_10MBIT]); + + /* Write the updated value */ + ksz_pwrite8(dev, port, regs[P_XMII_CTRL_0], data8); +} + +static void ksz_port_set_xmii_speed(struct ksz_device *dev, int port, int speed) +{ + if (speed == SPEED_1000) + ksz_set_gbit(dev, port, true); + else + ksz_set_gbit(dev, port, false); + + if (speed == SPEED_100 || speed == SPEED_10) + ksz_set_100_10mbit(dev, port, speed); +} + +static void ksz_duplex_flowctrl(struct ksz_device *dev, int port, int duplex, + bool tx_pause, bool rx_pause) +{ + const u8 *bitval = dev->info->xmii_ctrl0; + const u32 *masks = dev->info->masks; + const u16 *regs = dev->info->regs; + u8 mask; + u8 val; + + mask = P_MII_DUPLEX_M | masks[P_MII_TX_FLOW_CTRL] | + masks[P_MII_RX_FLOW_CTRL]; + + if (duplex == DUPLEX_FULL) + val = FIELD_PREP(P_MII_DUPLEX_M, bitval[P_MII_FULL_DUPLEX]); + else + val = FIELD_PREP(P_MII_DUPLEX_M, bitval[P_MII_HALF_DUPLEX]); + + if (tx_pause) + val |= masks[P_MII_TX_FLOW_CTRL]; + + if (rx_pause) + val |= masks[P_MII_RX_FLOW_CTRL]; + + ksz_prmw8(dev, port, regs[P_XMII_CTRL_0], mask, val); } static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port, @@ -1360,6 +1616,19 @@ static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port, int duplex, bool tx_pause, bool rx_pause) { struct ksz_device *dev = ds->priv; + struct ksz_port *p; + + p = &dev->ports[port]; + + /* Internal PHYs */ + if (dev->info->internal_phy[port]) + return; + + p->phydev.speed = speed; + + ksz_port_set_xmii_speed(dev, port, speed); + + ksz_duplex_flowctrl(dev, port, duplex, tx_pause, rx_pause); if (dev->dev_ops->phylink_mac_link_up) dev->dev_ops->phylink_mac_link_up(dev, port, mode, interface, @@ -1494,6 +1763,43 @@ struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) } EXPORT_SYMBOL(ksz_switch_alloc); +static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num, + struct device_node *port_dn) +{ + phy_interface_t phy_mode = dev->ports[port_num].interface; + int rx_delay = -1, tx_delay = -1; + + if (!phy_interface_mode_is_rgmii(phy_mode)) + return; + + of_property_read_u32(port_dn, "rx-internal-delay-ps", &rx_delay); + of_property_read_u32(port_dn, "tx-internal-delay-ps", &tx_delay); + + if (rx_delay == -1 && tx_delay == -1) { + dev_warn(dev->dev, + "Port %d interpreting RGMII delay settings based on \"phy-mode\" property, " + "please update device tree to specify \"rx-internal-delay-ps\" and " + "\"tx-internal-delay-ps\"", + port_num); + + if (phy_mode == PHY_INTERFACE_MODE_RGMII_RXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + rx_delay = 2000; + + if (phy_mode == PHY_INTERFACE_MODE_RGMII_TXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + tx_delay = 2000; + } + + if (rx_delay < 0) + rx_delay = 0; + if (tx_delay < 0) + tx_delay = 0; + + dev->ports[port_num].rgmii_rx_val = rx_delay; + dev->ports[port_num].rgmii_tx_val = tx_delay; +} + int ksz_switch_register(struct ksz_device *dev) { const struct ksz_chip_data *info; @@ -1591,6 +1897,8 @@ int ksz_switch_register(struct ksz_device *dev) } of_get_phy_mode(port, &dev->ports[port_num].interface); + + ksz_parse_rgmii_delay(dev, port_num, port); } of_node_put(ports); } diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index d5dddb7ec045..764ada3a0f42 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -51,6 +51,8 @@ struct ksz_chip_data { const u16 *regs; const u32 *masks; const u8 *shifts; + const u8 *xmii_ctrl0; + const u8 *xmii_ctrl1; int stp_ctrl_reg; int broadcast_ctrl_reg; int multicast_ctrl_reg; @@ -77,6 +79,8 @@ struct ksz_port { struct ksz_port_mib mib; phy_interface_t interface; u16 max_frame; + u32 rgmii_tx_val; + u32 rgmii_rx_val; }; struct ksz_device { @@ -169,6 +173,8 @@ enum ksz_regs { S_START_CTRL, S_BROADCAST_CTRL, S_MULTICAST_CTRL, + P_XMII_CTRL_0, + P_XMII_CTRL_1, }; enum ksz_masks { @@ -193,6 +199,8 @@ enum ksz_masks { DYNAMIC_MAC_TABLE_TIMESTAMP, ALU_STAT_WRITE, ALU_STAT_READ, + P_MII_TX_FLOW_CTRL, + P_MII_RX_FLOW_CTRL, }; enum ksz_shifts { @@ -208,6 +216,22 @@ enum ksz_shifts { ALU_STAT_INDEX, }; +enum ksz_xmii_ctrl0 { + P_MII_100MBIT, + P_MII_10MBIT, + P_MII_FULL_DUPLEX, + P_MII_HALF_DUPLEX, +}; + +enum ksz_xmii_ctrl1 { + P_RGMII_SEL, + P_RMII_SEL, + P_GMII_SEL, + P_MII_SEL, + P_GMII_1GBIT, + P_GMII_NOT_1GBIT, +}; + struct alu_struct { /* entry 1 */ u8 is_static:1; @@ -279,6 +303,7 @@ struct ksz_dev_ops { phy_interface_t interface, struct phy_device *phydev, int speed, int duplex, bool tx_pause, bool rx_pause); + void (*setup_rgmii_delay)(struct ksz_device *dev, int port); void (*config_cpu_port)(struct dsa_switch *ds); int (*enable_stp_addr)(struct ksz_device *dev); int (*reset)(struct ksz_device *dev); @@ -293,6 +318,8 @@ void ksz_switch_remove(struct ksz_device *dev); void ksz_init_mib_timer(struct ksz_device *dev); void ksz_r_mib_stats64(struct ksz_device *dev, int port); void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); +bool ksz_get_gbit(struct ksz_device *dev, int port); +phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit); extern const struct ksz_chip_data ksz_switch_chips[]; /* Common register access functions */ @@ -399,6 +426,14 @@ static inline void ksz_pwrite32(struct ksz_device *dev, int port, int offset, ksz_write32(dev, dev->dev_ops->get_port_addr(port, offset), data); } +static inline void ksz_prmw8(struct ksz_device *dev, int port, int offset, + u8 mask, u8 val) +{ + regmap_update_bits(dev->regmap[0], + dev->dev_ops->get_port_addr(port, offset), + mask, val); +} + static inline void ksz_regmap_lock(void *__mtx) { struct mutex *mtx = __mtx; @@ -411,6 +446,11 @@ static inline void ksz_regmap_unlock(void *__mtx) mutex_unlock(mtx); } +static inline bool ksz_is_ksz88x3(struct ksz_device *dev) +{ + return dev->chip_id == KSZ8830_CHIP_ID; +} + static inline int is_lan937x(struct ksz_device *dev) { return dev->chip_id == LAN9370_CHIP_ID || @@ -456,6 +496,20 @@ static inline int is_lan937x(struct ksz_device *dev) #define SW_START 0x01 +/* Used with variable features to indicate capabilities. */ +#define GBIT_SUPPORT BIT(0) +#define IS_9893 BIT(2) + +/* xMII configuration */ +#define P_MII_DUPLEX_M BIT(6) +#define P_MII_100MBIT_M BIT(4) + +#define P_GMII_1GBIT_M BIT(6) +#define P_RGMII_ID_IG_ENABLE BIT(4) +#define P_RGMII_ID_EG_ENABLE BIT(3) +#define P_MII_MAC_MODE BIT(2) +#define P_MII_SEL_M 0x3 + /* Regmap tables generation */ #define KSZ_SPI_OP_RD 3 #define KSZ_SPI_OP_WR 2 diff --git a/drivers/net/dsa/microchip/lan937x.h b/drivers/net/dsa/microchip/lan937x.h index 72ba9cb2fbc6..4e0b1dccec27 100644 --- a/drivers/net/dsa/microchip/lan937x.h +++ b/drivers/net/dsa/microchip/lan937x.h @@ -17,11 +17,5 @@ void lan937x_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val); int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu); void lan937x_phylink_get_caps(struct ksz_device *dev, int port, struct phylink_config *config); -void lan937x_phylink_mac_link_up(struct ksz_device *dev, int port, - unsigned int mode, phy_interface_t interface, - struct phy_device *phydev, int speed, - int duplex, bool tx_pause, bool rx_pause); -void lan937x_phylink_mac_config(struct ksz_device *dev, int port, - unsigned int mode, - const struct phylink_link_state *state); +void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port); #endif diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index c29d175ca6f7..daedd2bf20c1 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -234,6 +234,8 @@ int lan937x_reset_switch(struct ksz_device *dev) void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port) { + const u32 *masks = dev->info->masks; + const u16 *regs = dev->info->regs; struct dsa_switch *ds = dev->ds; u8 member; @@ -254,8 +256,9 @@ void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port) lan937x_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true); if (!dev->info->internal_phy[port]) - lan937x_port_cfg(dev, port, REG_PORT_XMII_CTRL_0, - PORT_MII_TX_FLOW_CTRL | PORT_MII_RX_FLOW_CTRL, + lan937x_port_cfg(dev, port, regs[P_XMII_CTRL_0], + masks[P_MII_TX_FLOW_CTRL] | + masks[P_MII_RX_FLOW_CTRL], true); if (cpu_port) @@ -312,75 +315,43 @@ int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu) return 0; } -static void lan937x_config_gbit(struct ksz_device *dev, bool gbit, u8 *data) +static void lan937x_set_tune_adj(struct ksz_device *dev, int port, + u16 reg, u8 val) { - if (gbit) - *data &= ~PORT_MII_NOT_1GBIT; - else - *data |= PORT_MII_NOT_1GBIT; -} + u16 data16; -static void lan937x_mac_config(struct ksz_device *dev, int port, - phy_interface_t interface) -{ - u8 data8; - - ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); - - /* clear MII selection & set it based on interface later */ - data8 &= ~PORT_MII_SEL_M; - - /* configure MAC based on interface */ - switch (interface) { - case PHY_INTERFACE_MODE_MII: - lan937x_config_gbit(dev, false, &data8); - data8 |= PORT_MII_SEL; - break; - case PHY_INTERFACE_MODE_RMII: - lan937x_config_gbit(dev, false, &data8); - data8 |= PORT_RMII_SEL; - break; - default: - dev_err(dev->dev, "Unsupported interface '%s' for port %d\n", - phy_modes(interface), port); - return; - } + ksz_pread16(dev, port, reg, &data16); - /* Write the updated value */ - ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8); + /* Update tune Adjust */ + data16 |= FIELD_PREP(PORT_TUNE_ADJ, val); + ksz_pwrite16(dev, port, reg, data16); + + /* write DLL reset to take effect */ + data16 |= PORT_DLL_RESET; + ksz_pwrite16(dev, port, reg, data16); } -static void lan937x_config_interface(struct ksz_device *dev, int port, - int speed, int duplex, - bool tx_pause, bool rx_pause) +static void lan937x_set_rgmii_tx_delay(struct ksz_device *dev, int port) { - u8 xmii_ctrl0, xmii_ctrl1; - - ksz_pread8(dev, port, REG_PORT_XMII_CTRL_0, &xmii_ctrl0); - ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &xmii_ctrl1); - - xmii_ctrl0 &= ~(PORT_MII_100MBIT | PORT_MII_FULL_DUPLEX | - PORT_MII_TX_FLOW_CTRL | PORT_MII_RX_FLOW_CTRL); - - if (speed == SPEED_1000) - lan937x_config_gbit(dev, true, &xmii_ctrl1); - else - lan937x_config_gbit(dev, false, &xmii_ctrl1); + u8 val; - if (speed == SPEED_100) - xmii_ctrl0 |= PORT_MII_100MBIT; + /* Apply different codes based on the ports as per characterization + * results + */ + val = (port == LAN937X_RGMII_1_PORT) ? RGMII_1_TX_DELAY_2NS : + RGMII_2_TX_DELAY_2NS; - if (duplex) - xmii_ctrl0 |= PORT_MII_FULL_DUPLEX; + lan937x_set_tune_adj(dev, port, REG_PORT_XMII_CTRL_5, val); +} - if (tx_pause) - xmii_ctrl0 |= PORT_MII_TX_FLOW_CTRL; +static void lan937x_set_rgmii_rx_delay(struct ksz_device *dev, int port) +{ + u8 val; - if (rx_pause) - xmii_ctrl0 |= PORT_MII_RX_FLOW_CTRL; + val = (port == LAN937X_RGMII_1_PORT) ? RGMII_1_RX_DELAY_2NS : + RGMII_2_RX_DELAY_2NS; - ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_0, xmii_ctrl0); - ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, xmii_ctrl1); + lan937x_set_tune_adj(dev, port, REG_PORT_XMII_CTRL_4, val); } void lan937x_phylink_get_caps(struct ksz_device *dev, int port, @@ -395,33 +366,21 @@ void lan937x_phylink_get_caps(struct ksz_device *dev, int port, } } -void lan937x_phylink_mac_link_up(struct ksz_device *dev, int port, - unsigned int mode, phy_interface_t interface, - struct phy_device *phydev, int speed, - int duplex, bool tx_pause, bool rx_pause) +void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port) { - /* Internal PHYs */ - if (dev->info->internal_phy[port]) - return; + struct ksz_port *p = &dev->ports[port]; - lan937x_config_interface(dev, port, speed, duplex, - tx_pause, rx_pause); -} - -void lan937x_phylink_mac_config(struct ksz_device *dev, int port, - unsigned int mode, - const struct phylink_link_state *state) -{ - /* Internal PHYs */ - if (dev->info->internal_phy[port]) - return; - - if (phylink_autoneg_inband(mode)) { - dev_err(dev->dev, "In-band AN not supported!\n"); - return; + if (p->rgmii_tx_val) { + lan937x_set_rgmii_tx_delay(dev, port); + dev_info(dev->dev, "Applied rgmii tx delay for the port %d\n", + port); } - lan937x_mac_config(dev, port, state->interface); + if (p->rgmii_rx_val) { + lan937x_set_rgmii_rx_delay(dev, port); + dev_info(dev->dev, "Applied rgmii rx delay for the port %d\n", + port); + } } int lan937x_setup(struct dsa_switch *ds) diff --git a/drivers/net/dsa/microchip/lan937x_reg.h b/drivers/net/dsa/microchip/lan937x_reg.h index c187d0a3e7fa..ba4adaddb3ec 100644 --- a/drivers/net/dsa/microchip/lan937x_reg.h +++ b/drivers/net/dsa/microchip/lan937x_reg.h @@ -131,24 +131,16 @@ #define REG_PORT_T1_PHY_CTRL_BASE 0x0100 /* 3 - xMII */ -#define REG_PORT_XMII_CTRL_0 0x0300 #define PORT_SGMII_SEL BIT(7) -#define PORT_MII_FULL_DUPLEX BIT(6) -#define PORT_MII_TX_FLOW_CTRL BIT(5) -#define PORT_MII_100MBIT BIT(4) -#define PORT_MII_RX_FLOW_CTRL BIT(3) #define PORT_GRXC_ENABLE BIT(0) -#define REG_PORT_XMII_CTRL_1 0x0301 -#define PORT_MII_NOT_1GBIT BIT(6) #define PORT_MII_SEL_EDGE BIT(5) -#define PORT_RGMII_ID_IG_ENABLE BIT(4) -#define PORT_RGMII_ID_EG_ENABLE BIT(3) -#define PORT_MII_MAC_MODE BIT(2) -#define PORT_MII_SEL_M 0x3 -#define PORT_RGMII_SEL 0x0 -#define PORT_RMII_SEL 0x1 -#define PORT_MII_SEL 0x2 + +#define REG_PORT_XMII_CTRL_4 0x0304 +#define REG_PORT_XMII_CTRL_5 0x0306 + +#define PORT_DLL_RESET BIT(15) +#define PORT_TUNE_ADJ GENMASK(13, 7) /* 4 - MAC */ #define REG_PORT_MAC_CTRL_0 0x0400 @@ -175,6 +167,18 @@ #define P_PRIO_CTRL REG_PORT_MRI_PRIO_CTRL +/* The port number as per the datasheet */ +#define RGMII_2_PORT_NUM 5 +#define RGMII_1_PORT_NUM 6 + +#define LAN937X_RGMII_2_PORT (RGMII_2_PORT_NUM - 1) +#define LAN937X_RGMII_1_PORT (RGMII_1_PORT_NUM - 1) + +#define RGMII_1_TX_DELAY_2NS 2 +#define RGMII_2_TX_DELAY_2NS 0 +#define RGMII_1_RX_DELAY_2NS 0x1B +#define RGMII_2_RX_DELAY_2NS 0x14 + #define LAN937X_TAG_LEN 2 #endif diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 37b649501500..07e9a4da924c 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3293,7 +3293,12 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) * port and all DSA ports to their maximum bandwidth and full duplex. */ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { - unsigned long caps = dp->pl_config.mac_capabilities; + struct phylink_config pl_config = {}; + unsigned long caps; + + mv88e6xxx_get_caps(ds, port, &pl_config); + + caps = pl_config.mac_capabilities; if (chip->info->ops->port_max_speed_mode) mode = chip->info->ops->port_max_speed_mode(port); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index e64318c110fd..28b19945d716 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -28,9 +28,6 @@ #define MAX_RATE_EXPONENT 0x0FULL #define MAX_RATE_MANTISSA 0xFFULL -#define CN10K_MAX_BURST_MANTISSA 0x7FFFULL -#define CN10K_MAX_BURST_SIZE 8453888ULL - /* Bitfields in NIX_TLX_PIR register */ #define TLX_RATE_MANTISSA GENMASK_ULL(8, 1) #define TLX_RATE_EXPONENT GENMASK_ULL(12, 9) @@ -38,9 +35,6 @@ #define TLX_BURST_MANTISSA GENMASK_ULL(36, 29) #define TLX_BURST_EXPONENT GENMASK_ULL(40, 37) -#define CN10K_TLX_BURST_MANTISSA GENMASK_ULL(43, 29) -#define CN10K_TLX_BURST_EXPONENT GENMASK_ULL(47, 44) - struct otx2_tc_flow_stats { u64 bytes; u64 pkts; @@ -83,42 +77,33 @@ int otx2_tc_alloc_ent_bitmap(struct otx2_nic *nic) } EXPORT_SYMBOL(otx2_tc_alloc_ent_bitmap); -static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, - u32 *burst_exp, u32 *burst_mantissa) +static void otx2_get_egress_burst_cfg(u32 burst, u32 *burst_exp, + u32 *burst_mantissa) { - int max_burst, max_mantissa; unsigned int tmp; - if (is_dev_otx2(nic->pdev)) { - max_burst = MAX_BURST_SIZE; - max_mantissa = MAX_BURST_MANTISSA; - } else { - max_burst = CN10K_MAX_BURST_SIZE; - max_mantissa = CN10K_MAX_BURST_MANTISSA; - } - /* Burst is calculated as * ((256 + BURST_MANTISSA) << (1 + BURST_EXPONENT)) / 256 * Max supported burst size is 130,816 bytes. */ - burst = min_t(u32, burst, max_burst); + burst = min_t(u32, burst, MAX_BURST_SIZE); if (burst) { *burst_exp = ilog2(burst) ? ilog2(burst) - 1 : 0; tmp = burst - rounddown_pow_of_two(burst); - if (burst < max_mantissa) + if (burst < MAX_BURST_MANTISSA) *burst_mantissa = tmp * 2; else *burst_mantissa = tmp / (1ULL << (*burst_exp - 7)); } else { *burst_exp = MAX_BURST_EXPONENT; - *burst_mantissa = max_mantissa; + *burst_mantissa = MAX_BURST_MANTISSA; } } -static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp, +static void otx2_get_egress_rate_cfg(u32 maxrate, u32 *exp, u32 *mantissa, u32 *div_exp) { - u64 tmp; + unsigned int tmp; /* Rate calculation by hardware * @@ -147,44 +132,21 @@ static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp, } } -static u64 otx2_get_txschq_rate_regval(struct otx2_nic *nic, - u64 maxrate, u32 burst) -{ - u32 burst_exp, burst_mantissa; - u32 exp, mantissa, div_exp; - u64 regval = 0; - - /* Get exponent and mantissa values from the desired rate */ - otx2_get_egress_burst_cfg(nic, burst, &burst_exp, &burst_mantissa); - otx2_get_egress_rate_cfg(maxrate, &exp, &mantissa, &div_exp); - - if (is_dev_otx2(nic->pdev)) { - regval = FIELD_PREP(TLX_BURST_EXPONENT, (u64)burst_exp) | - FIELD_PREP(TLX_BURST_MANTISSA, (u64)burst_mantissa) | - FIELD_PREP(TLX_RATE_DIVIDER_EXPONENT, div_exp) | - FIELD_PREP(TLX_RATE_EXPONENT, exp) | - FIELD_PREP(TLX_RATE_MANTISSA, mantissa) | BIT_ULL(0); - } else { - regval = FIELD_PREP(CN10K_TLX_BURST_EXPONENT, (u64)burst_exp) | - FIELD_PREP(CN10K_TLX_BURST_MANTISSA, (u64)burst_mantissa) | - FIELD_PREP(TLX_RATE_DIVIDER_EXPONENT, div_exp) | - FIELD_PREP(TLX_RATE_EXPONENT, exp) | - FIELD_PREP(TLX_RATE_MANTISSA, mantissa) | BIT_ULL(0); - } - - return regval; -} - -static int otx2_set_matchall_egress_rate(struct otx2_nic *nic, - u32 burst, u64 maxrate) +static int otx2_set_matchall_egress_rate(struct otx2_nic *nic, u32 burst, u32 maxrate) { struct otx2_hw *hw = &nic->hw; struct nix_txschq_config *req; + u32 burst_exp, burst_mantissa; + u32 exp, mantissa, div_exp; int txschq, err; /* All SQs share the same TL4, so pick the first scheduler */ txschq = hw->txschq_list[NIX_TXSCH_LVL_TL4][0]; + /* Get exponent and mantissa values from the desired rate */ + otx2_get_egress_burst_cfg(burst, &burst_exp, &burst_mantissa); + otx2_get_egress_rate_cfg(maxrate, &exp, &mantissa, &div_exp); + mutex_lock(&nic->mbox.lock); req = otx2_mbox_alloc_msg_nix_txschq_cfg(&nic->mbox); if (!req) { @@ -195,7 +157,11 @@ static int otx2_set_matchall_egress_rate(struct otx2_nic *nic, req->lvl = NIX_TXSCH_LVL_TL4; req->num_regs = 1; req->reg[0] = NIX_AF_TL4X_PIR(txschq); - req->regval[0] = otx2_get_txschq_rate_regval(nic, maxrate, burst); + req->regval[0] = FIELD_PREP(TLX_BURST_EXPONENT, burst_exp) | + FIELD_PREP(TLX_BURST_MANTISSA, burst_mantissa) | + FIELD_PREP(TLX_RATE_DIVIDER_EXPONENT, div_exp) | + FIELD_PREP(TLX_RATE_EXPONENT, exp) | + FIELD_PREP(TLX_RATE_MANTISSA, mantissa) | BIT_ULL(0); err = otx2_sync_mbox_msg(&nic->mbox); mutex_unlock(&nic->mbox.lock); @@ -264,7 +230,7 @@ static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, struct netlink_ext_ack *extack = cls->common.extack; struct flow_action *actions = &cls->rule->action; struct flow_action_entry *entry; - u64 rate; + u32 rate; int err; err = otx2_tc_validate_flow(nic, actions, extack); @@ -290,7 +256,7 @@ static int otx2_tc_egress_matchall_install(struct otx2_nic *nic, } /* Convert bytes per second to Mbps */ rate = entry->police.rate_bytes_ps * 8; - rate = max_t(u64, rate / 1000000, 1); + rate = max_t(u32, rate / 1000000, 1); err = otx2_set_matchall_egress_rate(nic, entry->police.burst, rate); if (err) return err; @@ -648,27 +614,21 @@ static int otx2_tc_prepare_flow(struct otx2_nic *nic, struct otx2_tc_flow *node, flow_spec->dport = match.key->dst; flow_mask->dport = match.mask->dst; - - if (flow_mask->dport) { - if (ip_proto == IPPROTO_UDP) - req->features |= BIT_ULL(NPC_DPORT_UDP); - else if (ip_proto == IPPROTO_TCP) - req->features |= BIT_ULL(NPC_DPORT_TCP); - else if (ip_proto == IPPROTO_SCTP) - req->features |= BIT_ULL(NPC_DPORT_SCTP); - } + if (ip_proto == IPPROTO_UDP) + req->features |= BIT_ULL(NPC_DPORT_UDP); + else if (ip_proto == IPPROTO_TCP) + req->features |= BIT_ULL(NPC_DPORT_TCP); + else if (ip_proto == IPPROTO_SCTP) + req->features |= BIT_ULL(NPC_DPORT_SCTP); flow_spec->sport = match.key->src; flow_mask->sport = match.mask->src; - - if (flow_mask->sport) { - if (ip_proto == IPPROTO_UDP) - req->features |= BIT_ULL(NPC_SPORT_UDP); - else if (ip_proto == IPPROTO_TCP) - req->features |= BIT_ULL(NPC_SPORT_TCP); - else if (ip_proto == IPPROTO_SCTP) - req->features |= BIT_ULL(NPC_SPORT_SCTP); - } + if (ip_proto == IPPROTO_UDP) + req->features |= BIT_ULL(NPC_SPORT_UDP); + else if (ip_proto == IPPROTO_TCP) + req->features |= BIT_ULL(NPC_SPORT_TCP); + else if (ip_proto == IPPROTO_SCTP) + req->features |= BIT_ULL(NPC_SPORT_SCTP); } return otx2_tc_parse_actions(nic, &rule->action, req, f, node); diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index 4683312861ac..a510bf2cff2f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -7,6 +7,7 @@ config MLXSW_CORE tristate "Mellanox Technologies Switch ASICs support" select NET_DEVLINK select MLXFW + select AUXILIARY_BUS help This driver supports Mellanox Technologies Switch ASICs family. diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index c2d6d64ffe4b..3ca9fce759ea 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o mlxsw_core-objs := core.o core_acl_flex_keys.o \ core_acl_flex_actions.o core_env.o \ - core_linecards.o + core_linecards.o core_linecard_dev.o mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 1b61bc8f59a2..a48f893cf7b0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -951,6 +951,20 @@ static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind) return mlxsw_driver; } +int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, + struct mlxfw_dev *mlxfw_dev, + const struct firmware *firmware, + struct netlink_ext_ack *extack) +{ + int err; + + mlxsw_core->fw_flash_in_progress = true; + err = mlxfw_firmware_flash(mlxfw_dev, firmware, extack); + mlxsw_core->fw_flash_in_progress = false; + + return err; +} + struct mlxsw_core_fw_info { struct mlxfw_dev mlxfw_dev; struct mlxsw_core *mlxsw_core; @@ -1105,8 +1119,9 @@ static const struct mlxfw_dev_ops mlxsw_core_fw_mlxsw_dev_ops = { .fsm_release = mlxsw_core_fw_fsm_release, }; -static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmware *firmware, - struct netlink_ext_ack *extack) +static int mlxsw_core_dev_fw_flash(struct mlxsw_core *mlxsw_core, + const struct firmware *firmware, + struct netlink_ext_ack *extack) { struct mlxsw_core_fw_info mlxsw_core_fw_info = { .mlxfw_dev = { @@ -1117,13 +1132,9 @@ static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmw }, .mlxsw_core = mlxsw_core }; - int err; - - mlxsw_core->fw_flash_in_progress = true; - err = mlxfw_firmware_flash(&mlxsw_core_fw_info.mlxfw_dev, firmware, extack); - mlxsw_core->fw_flash_in_progress = false; - return err; + return mlxsw_core_fw_flash(mlxsw_core, &mlxsw_core_fw_info.mlxfw_dev, + firmware, extack); } static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core, @@ -1169,7 +1180,7 @@ static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core, return err; } - err = mlxsw_core_fw_flash(mlxsw_core, firmware, NULL); + err = mlxsw_core_dev_fw_flash(mlxsw_core, firmware, NULL); release_firmware(firmware); if (err) dev_err(mlxsw_bus_info->dev, "Could not upgrade firmware\n"); @@ -1187,7 +1198,7 @@ static int mlxsw_core_fw_flash_update(struct mlxsw_core *mlxsw_core, struct devlink_flash_update_params *params, struct netlink_ext_ack *extack) { - return mlxsw_core_fw_flash(mlxsw_core, params->fw, extack); + return mlxsw_core_dev_fw_flash(mlxsw_core, params->fw, extack); } static int mlxsw_core_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id, @@ -3334,9 +3345,15 @@ static int __init mlxsw_core_module_init(void) { int err; + err = mlxsw_linecard_driver_register(); + if (err) + return err; + mlxsw_wq = alloc_workqueue(mlxsw_core_driver_name, 0, 0); - if (!mlxsw_wq) - return -ENOMEM; + if (!mlxsw_wq) { + err = -ENOMEM; + goto err_alloc_workqueue; + } mlxsw_owq = alloc_ordered_workqueue("%s_ordered", 0, mlxsw_core_driver_name); if (!mlxsw_owq) { @@ -3347,6 +3364,8 @@ static int __init mlxsw_core_module_init(void) err_alloc_ordered_workqueue: destroy_workqueue(mlxsw_wq); +err_alloc_workqueue: + mlxsw_linecard_driver_unregister(); return err; } @@ -3354,6 +3373,7 @@ static void __exit mlxsw_core_module_exit(void) { destroy_workqueue(mlxsw_owq); destroy_workqueue(mlxsw_wq); + mlxsw_linecard_driver_unregister(); } module_init(mlxsw_core_module_init); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 9d2e8a8d3a75..7213e4528298 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -12,12 +12,14 @@ #include <linux/skbuff.h> #include <linux/workqueue.h> #include <linux/net_namespace.h> +#include <linux/auxiliary_bus.h> #include <net/devlink.h> #include "trap.h" #include "reg.h" #include "cmd.h" #include "resources.h" +#include "../mlxfw/mlxfw.h" enum mlxsw_core_resource_id { MLXSW_CORE_RESOURCE_PORTS = 1, @@ -47,6 +49,11 @@ mlxsw_core_fw_rev_minor_subminor_validate(const struct mlxsw_fw_rev *rev, int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver); void mlxsw_core_driver_unregister(struct mlxsw_driver *mlxsw_driver); +int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, + struct mlxfw_dev *mlxfw_dev, + const struct firmware *firmware, + struct netlink_ext_ack *extack); + int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, const struct mlxsw_bus *mlxsw_bus, void *bus_priv, bool reload, @@ -563,6 +570,15 @@ enum mlxsw_linecard_status_event_type { MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION, }; +struct mlxsw_linecard_bdev; + +struct mlxsw_linecard_device_info { + u16 fw_major; + u16 fw_minor; + u16 fw_sub_minor; + char psid[MLXSW_REG_MGIR_FW_INFO_PSID_SIZE]; +}; + struct mlxsw_linecard { u8 slot_index; struct mlxsw_linecards *linecards; @@ -577,6 +593,11 @@ struct mlxsw_linecard { active:1; u16 hw_revision; u16 ini_version; + struct mlxsw_linecard_bdev *bdev; + struct { + struct mlxsw_linecard_device_info info; + u8 index; + } device; }; struct mlxsw_linecard_types_info; @@ -597,6 +618,14 @@ mlxsw_linecard_get(struct mlxsw_linecards *linecards, u8 slot_index) return &linecards->linecards[slot_index - 1]; } +int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard, + struct devlink_info_req *req, + struct netlink_ext_ack *extack); +int mlxsw_linecard_flash_update(struct devlink *linecard_devlink, + struct mlxsw_linecard *linecard, + const struct firmware *firmware, + struct netlink_ext_ack *extack); + int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *bus_info); void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core); @@ -616,4 +645,10 @@ void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core, struct mlxsw_linecards_event_ops *ops, void *priv); +int mlxsw_linecard_bdev_add(struct mlxsw_linecard *linecard); +void mlxsw_linecard_bdev_del(struct mlxsw_linecard *linecard); + +int mlxsw_linecard_driver_register(void); +void mlxsw_linecard_driver_unregister(void); + #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c b/drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c new file mode 100644 index 000000000000..49fee038a99c --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/auxiliary_bus.h> +#include <linux/idr.h> +#include <linux/gfp.h> +#include <linux/slab.h> +#include <net/devlink.h> +#include "core.h" + +#define MLXSW_LINECARD_DEV_ID_NAME "lc" + +struct mlxsw_linecard_dev { + struct mlxsw_linecard *linecard; +}; + +struct mlxsw_linecard_bdev { + struct auxiliary_device adev; + struct mlxsw_linecard *linecard; + struct mlxsw_linecard_dev *linecard_dev; +}; + +static DEFINE_IDA(mlxsw_linecard_bdev_ida); + +static int mlxsw_linecard_bdev_id_alloc(void) +{ + return ida_alloc(&mlxsw_linecard_bdev_ida, GFP_KERNEL); +} + +static void mlxsw_linecard_bdev_id_free(int id) +{ + ida_free(&mlxsw_linecard_bdev_ida, id); +} + +static void mlxsw_linecard_bdev_release(struct device *device) +{ + struct auxiliary_device *adev = + container_of(device, struct auxiliary_device, dev); + struct mlxsw_linecard_bdev *linecard_bdev = + container_of(adev, struct mlxsw_linecard_bdev, adev); + + mlxsw_linecard_bdev_id_free(adev->id); + kfree(linecard_bdev); +} + +int mlxsw_linecard_bdev_add(struct mlxsw_linecard *linecard) +{ + struct mlxsw_linecard_bdev *linecard_bdev; + int err; + int id; + + id = mlxsw_linecard_bdev_id_alloc(); + if (id < 0) + return id; + + linecard_bdev = kzalloc(sizeof(*linecard_bdev), GFP_KERNEL); + if (!linecard_bdev) { + mlxsw_linecard_bdev_id_free(id); + return -ENOMEM; + } + linecard_bdev->adev.id = id; + linecard_bdev->adev.name = MLXSW_LINECARD_DEV_ID_NAME; + linecard_bdev->adev.dev.release = mlxsw_linecard_bdev_release; + linecard_bdev->adev.dev.parent = linecard->linecards->bus_info->dev; + linecard_bdev->linecard = linecard; + + err = auxiliary_device_init(&linecard_bdev->adev); + if (err) { + mlxsw_linecard_bdev_id_free(id); + kfree(linecard_bdev); + return err; + } + + err = auxiliary_device_add(&linecard_bdev->adev); + if (err) { + auxiliary_device_uninit(&linecard_bdev->adev); + return err; + } + + linecard->bdev = linecard_bdev; + return 0; +} + +void mlxsw_linecard_bdev_del(struct mlxsw_linecard *linecard) +{ + struct mlxsw_linecard_bdev *linecard_bdev = linecard->bdev; + + if (!linecard_bdev) + /* Unprovisioned line cards do not have an auxiliary device. */ + return; + auxiliary_device_delete(&linecard_bdev->adev); + auxiliary_device_uninit(&linecard_bdev->adev); + linecard->bdev = NULL; +} + +static int mlxsw_linecard_dev_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink); + struct mlxsw_linecard *linecard = linecard_dev->linecard; + + return mlxsw_linecard_devlink_info_get(linecard, req, extack); +} + +static int +mlxsw_linecard_dev_devlink_flash_update(struct devlink *devlink, + struct devlink_flash_update_params *params, + struct netlink_ext_ack *extack) +{ + struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink); + struct mlxsw_linecard *linecard = linecard_dev->linecard; + + return mlxsw_linecard_flash_update(devlink, linecard, + params->fw, extack); +} + +static const struct devlink_ops mlxsw_linecard_dev_devlink_ops = { + .info_get = mlxsw_linecard_dev_devlink_info_get, + .flash_update = mlxsw_linecard_dev_devlink_flash_update, +}; + +static int mlxsw_linecard_bdev_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct mlxsw_linecard_bdev *linecard_bdev = + container_of(adev, struct mlxsw_linecard_bdev, adev); + struct mlxsw_linecard *linecard = linecard_bdev->linecard; + struct mlxsw_linecard_dev *linecard_dev; + struct devlink *devlink; + + devlink = devlink_alloc(&mlxsw_linecard_dev_devlink_ops, + sizeof(*linecard_dev), &adev->dev); + if (!devlink) + return -ENOMEM; + linecard_dev = devlink_priv(devlink); + linecard_dev->linecard = linecard_bdev->linecard; + linecard_bdev->linecard_dev = linecard_dev; + + devlink_register(devlink); + devlink_linecard_nested_dl_set(linecard->devlink_linecard, devlink); + return 0; +} + +static void mlxsw_linecard_bdev_remove(struct auxiliary_device *adev) +{ + struct mlxsw_linecard_bdev *linecard_bdev = + container_of(adev, struct mlxsw_linecard_bdev, adev); + struct devlink *devlink = priv_to_devlink(linecard_bdev->linecard_dev); + struct mlxsw_linecard *linecard = linecard_bdev->linecard; + + devlink_linecard_nested_dl_set(linecard->devlink_linecard, NULL); + devlink_unregister(devlink); + devlink_free(devlink); +} + +static const struct auxiliary_device_id mlxsw_linecard_bdev_id_table[] = { + { .name = KBUILD_MODNAME "." MLXSW_LINECARD_DEV_ID_NAME }, + {}, +}; + +MODULE_DEVICE_TABLE(auxiliary, mlxsw_linecard_bdev_id_table); + +static struct auxiliary_driver mlxsw_linecard_driver = { + .name = MLXSW_LINECARD_DEV_ID_NAME, + .probe = mlxsw_linecard_bdev_probe, + .remove = mlxsw_linecard_bdev_remove, + .id_table = mlxsw_linecard_bdev_id_table, +}; + +int mlxsw_linecard_driver_register(void) +{ + return auxiliary_driver_register(&mlxsw_linecard_driver); +} + +void mlxsw_linecard_driver_unregister(void) +{ + auxiliary_driver_unregister(&mlxsw_linecard_driver); +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c index 5c9869dcf674..ca59f0b946da 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c @@ -13,6 +13,7 @@ #include <linux/vmalloc.h> #include "core.h" +#include "../mlxfw/mlxfw.h" struct mlxsw_linecard_ini_file { __le16 size; @@ -87,6 +88,351 @@ static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard) return linecard->name; } +struct mlxsw_linecard_device_fw_info { + struct mlxfw_dev mlxfw_dev; + struct mlxsw_core *mlxsw_core; + struct mlxsw_linecard *linecard; +}; + +static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev, + u16 component_index, + u32 *p_max_size, + u8 *p_align_bits, + u16 *p_max_write_size) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcqi_pl; + int err; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG(mcqi), &mcqi_pl); + + mlxsw_reg_mcqi_pack(mcqi_pl, component_index); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); + if (err) + return err; + mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits, + p_max_write_size); + + *p_align_bits = max_t(u8, *p_align_bits, 2); + *p_max_write_size = min_t(u16, *p_max_write_size, + MLXSW_REG_MCDA_MAX_DATA_LEN); + return 0; +} + +static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev, + u32 *fwhandle) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + u8 control_state; + char *mcc_pl; + int err; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); + if (err) + return err; + + mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state); + if (control_state != MLXFW_FSM_STATE_IDLE) + return -EBUSY; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE, + 0, *fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int +mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle, + u16 component_index, + u32 component_size) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT, + component_index, fwhandle, component_size); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int +mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle, u8 *data, + u16 size, u32 offset) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcda_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcda), &mcda_pl); + mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int +mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle, u16 component_index) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT, + component_index, fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE, + 0, fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static int +mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle, + enum mlxfw_fsm_state *fsm_state, + enum mlxfw_fsm_state_err *fsm_state_err) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + u8 control_state; + u8 error_code; + char *mcc_pl; + int err; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); + if (err) + return err; + + mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state); + *fsm_state = control_state; + *fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code, + MLXFW_FSM_STATE_ERR_MAX); + return 0; +} + +static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL, + 0, fwhandle, 0); + mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev, + u32 fwhandle) +{ + struct mlxsw_linecard_device_fw_info *info = + container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info, + mlxfw_dev); + struct mlxsw_linecard *linecard = info->linecard; + struct mlxsw_core *mlxsw_core = info->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mcc_pl; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, + linecard->device.index, + MLXSW_REG_MDDT_METHOD_WRITE, + MLXSW_REG(mcc), &mcc_pl); + mlxsw_reg_mcc_pack(mcc_pl, + MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE, + 0, fwhandle, 0); + mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl); +} + +static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = { + .component_query = mlxsw_linecard_device_fw_component_query, + .fsm_lock = mlxsw_linecard_device_fw_fsm_lock, + .fsm_component_update = mlxsw_linecard_device_fw_fsm_component_update, + .fsm_block_download = mlxsw_linecard_device_fw_fsm_block_download, + .fsm_component_verify = mlxsw_linecard_device_fw_fsm_component_verify, + .fsm_activate = mlxsw_linecard_device_fw_fsm_activate, + .fsm_query_state = mlxsw_linecard_device_fw_fsm_query_state, + .fsm_cancel = mlxsw_linecard_device_fw_fsm_cancel, + .fsm_release = mlxsw_linecard_device_fw_fsm_release, +}; + +int mlxsw_linecard_flash_update(struct devlink *linecard_devlink, + struct mlxsw_linecard *linecard, + const struct firmware *firmware, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; + struct mlxsw_linecard_device_fw_info info = { + .mlxfw_dev = { + .ops = &mlxsw_linecard_device_dev_ops, + .psid = linecard->device.info.psid, + .psid_size = strlen(linecard->device.info.psid), + .devlink = linecard_devlink, + }, + .mlxsw_core = mlxsw_core, + .linecard = linecard, + }; + int err; + + mutex_lock(&linecard->lock); + if (!linecard->active) { + NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed"); + err = -EINVAL; + goto unlock; + } + err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev, + firmware, extack); +unlock: + mutex_unlock(&linecard->lock); + return err; +} + +static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard, + u8 device_index, char *psid) +{ + struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; + char mddt_pl[MLXSW_REG_MDDT_LEN]; + char *mgir_pl; + int err; + + mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, device_index, + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG(mgir), &mgir_pl); + + mlxsw_reg_mgir_pack(mgir_pl); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl); + if (err) + return err; + + mlxsw_reg_mgir_fw_info_psid_memcpy_from(mgir_pl, psid); + return 0; +} + +static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard) +{ + struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; + bool flashable_found = false; + u8 msg_seq = 0; + + do { + struct mlxsw_linecard_device_info info; + char mddq_pl[MLXSW_REG_MDDQ_LEN]; + bool flash_owner; + bool data_valid; + u8 device_index; + int err; + + mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index, + msg_seq); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); + if (err) + return err; + mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq, + &data_valid, &flash_owner, + &device_index, + &info.fw_major, + &info.fw_minor, + &info.fw_sub_minor); + if (!data_valid) + break; + if (!flash_owner) /* We care only about flashable ones. */ + continue; + if (flashable_found) { + dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n", + linecard->slot_index); + return 0; + } + + err = mlxsw_linecard_device_psid_get(linecard, device_index, + info.psid); + if (err) + return err; + + linecard->device.info = info; + linecard->device.index = device_index; + flashable_found = true; + } while (msg_seq); + + return 0; +} + static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard) { linecard->provisioned = false; @@ -226,12 +572,57 @@ void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister); +int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + char buf[32]; + int err; + + mutex_lock(&linecard->lock); + if (WARN_ON(!linecard->provisioned)) { + err = -EOPNOTSUPP; + goto unlock; + } + + sprintf(buf, "%d", linecard->hw_revision); + err = devlink_info_version_fixed_put(req, "hw.revision", buf); + if (err) + goto unlock; + + sprintf(buf, "%d", linecard->ini_version); + err = devlink_info_version_running_put(req, "ini.version", buf); + if (err) + goto unlock; + + if (linecard->active) { + struct mlxsw_linecard_device_info *info = &linecard->device.info; + + err = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_PSID, + info->psid); + + sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor, + info->fw_sub_minor); + err = devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); + if (err) + goto unlock; + } + +unlock: + mutex_unlock(&linecard->lock); + return err; +} + static int mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type, u16 hw_revision, u16 ini_version) { struct mlxsw_linecards *linecards = linecard->linecards; const char *type; + int err; type = mlxsw_linecard_types_lookup(linecards, card_type); mlxsw_linecard_status_event_done(linecard, @@ -252,6 +643,14 @@ mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type, linecard->provisioned = true; linecard->hw_revision = hw_revision; linecard->ini_version = ini_version; + + err = mlxsw_linecard_bdev_add(linecard); + if (err) { + linecard->provisioned = false; + mlxsw_linecard_provision_fail(linecard); + return err; + } + devlink_linecard_provision_set(linecard->devlink_linecard, type); return 0; } @@ -260,6 +659,7 @@ static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard) { mlxsw_linecard_status_event_done(linecard, MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); + mlxsw_linecard_bdev_del(linecard); linecard->provisioned = false; devlink_linecard_provision_clear(linecard->devlink_linecard); } @@ -270,6 +670,10 @@ static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard) char mddc_pl[MLXSW_REG_MDDC_LEN]; int err; + err = mlxsw_linecard_device_info_update(linecard); + if (err) + return err; + mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true); err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); if (err) @@ -885,6 +1289,7 @@ static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, mlxsw_core_flush_owq(); if (linecard->active) mlxsw_linecard_active_clear(linecard); + mlxsw_linecard_bdev_del(linecard); devlink_linecard_destroy(linecard->devlink_linecard); mutex_destroy(&linecard->lock); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index ddab5476c8b2..f27bdecdf952 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -11364,6 +11364,95 @@ mlxsw_reg_mbct_unpack(const char *payload, u8 *p_slot_index, *p_fsm_state = mlxsw_reg_mbct_fsm_state_get(payload); } +/* MDDT - Management DownStream Device Tunneling Register + * ------------------------------------------------------ + * This register allows to deliver query and request messages (PRM registers, + * commands) to a DownStream device. + */ +#define MLXSW_REG_MDDT_ID 0x9160 +#define MLXSW_REG_MDDT_LEN 0x110 + +MLXSW_REG_DEFINE(mddt, MLXSW_REG_MDDT_ID, MLXSW_REG_MDDT_LEN); + +/* reg_mddt_slot_index + * Slot index. + * Access: Index + */ +MLXSW_ITEM32(reg, mddt, slot_index, 0x00, 8, 4); + +/* reg_mddt_device_index + * Device index. + * Access: Index + */ +MLXSW_ITEM32(reg, mddt, device_index, 0x00, 0, 8); + +/* reg_mddt_read_size + * Read size in D-Words. + * Access: OP + */ +MLXSW_ITEM32(reg, mddt, read_size, 0x04, 24, 8); + +/* reg_mddt_write_size + * Write size in D-Words. + * Access: OP + */ +MLXSW_ITEM32(reg, mddt, write_size, 0x04, 16, 8); + +enum mlxsw_reg_mddt_status { + MLXSW_REG_MDDT_STATUS_OK, +}; + +/* reg_mddt_status + * Return code of the Downstream Device to the register that was sent. + * Access: RO + */ +MLXSW_ITEM32(reg, mddt, status, 0x0C, 24, 8); + +enum mlxsw_reg_mddt_method { + MLXSW_REG_MDDT_METHOD_QUERY, + MLXSW_REG_MDDT_METHOD_WRITE, +}; + +/* reg_mddt_method + * Access: OP + */ +MLXSW_ITEM32(reg, mddt, method, 0x0C, 22, 2); + +/* reg_mddt_register_id + * Access: Index + */ +MLXSW_ITEM32(reg, mddt, register_id, 0x0C, 0, 16); + +#define MLXSW_REG_MDDT_PAYLOAD_OFFSET 0x0C +#define MLXSW_REG_MDDT_PRM_REGISTER_HEADER_LEN 4 + +static inline char *mlxsw_reg_mddt_inner_payload(char *payload) +{ + return payload + MLXSW_REG_MDDT_PAYLOAD_OFFSET + + MLXSW_REG_MDDT_PRM_REGISTER_HEADER_LEN; +} + +static inline void mlxsw_reg_mddt_pack(char *payload, u8 slot_index, + u8 device_index, + enum mlxsw_reg_mddt_method method, + const struct mlxsw_reg_info *reg, + char **inner_payload) +{ + int len = reg->len + MLXSW_REG_MDDT_PRM_REGISTER_HEADER_LEN; + + if (WARN_ON(len + MLXSW_REG_MDDT_PAYLOAD_OFFSET > MLXSW_REG_MDDT_LEN)) + len = MLXSW_REG_MDDT_LEN - MLXSW_REG_MDDT_PAYLOAD_OFFSET; + + MLXSW_REG_ZERO(mddt, payload); + mlxsw_reg_mddt_slot_index_set(payload, slot_index); + mlxsw_reg_mddt_device_index_set(payload, device_index); + mlxsw_reg_mddt_method_set(payload, method); + mlxsw_reg_mddt_register_id_set(payload, reg->id); + mlxsw_reg_mddt_read_size_set(payload, len / 4); + mlxsw_reg_mddt_write_size_set(payload, len / 4); + *inner_payload = mlxsw_reg_mddt_inner_payload(payload); +} + /* MDDQ - Management DownStream Device Query Register * -------------------------------------------------- * This register allows to query the DownStream device properties. The desired @@ -11385,7 +11474,11 @@ MLXSW_ITEM32(reg, mddq, sie, 0x00, 31, 1); enum mlxsw_reg_mddq_query_type { MLXSW_REG_MDDQ_QUERY_TYPE_SLOT_INFO = 1, - MLXSW_REG_MDDQ_QUERY_TYPE_SLOT_NAME = 3, + MLXSW_REG_MDDQ_QUERY_TYPE_DEVICE_INFO, /* If there are no devices + * on the slot, data_valid + * will be '0'. + */ + MLXSW_REG_MDDQ_QUERY_TYPE_SLOT_NAME, }; /* reg_mddq_query_type @@ -11399,6 +11492,28 @@ MLXSW_ITEM32(reg, mddq, query_type, 0x00, 16, 8); */ MLXSW_ITEM32(reg, mddq, slot_index, 0x00, 0, 4); +/* reg_mddq_response_msg_seq + * Response message sequential number. For a specific request, the response + * message sequential number is the following one. In addition, the last + * message should be 0. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, response_msg_seq, 0x04, 16, 8); + +/* reg_mddq_request_msg_seq + * Request message sequential number. + * The first message number should be 0. + * Access: Index + */ +MLXSW_ITEM32(reg, mddq, request_msg_seq, 0x04, 0, 8); + +/* reg_mddq_data_valid + * If set, the data in the data field is valid and contain the information + * for the queried index. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, data_valid, 0x08, 31, 1); + /* reg_mddq_slot_info_provisioned * If set, the INI file is applied and the card is provisioned. * Access: RO @@ -11485,6 +11600,61 @@ mlxsw_reg_mddq_slot_info_unpack(const char *payload, u8 *p_slot_index, *p_card_type = mlxsw_reg_mddq_slot_info_card_type_get(payload); } +/* reg_mddq_device_info_flash_owner + * If set, the device is the flash owner. Otherwise, a shared flash + * is used by this device (another device is the flash owner). + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_flash_owner, 0x10, 30, 1); + +/* reg_mddq_device_info_device_index + * Device index. The first device should number 0. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_device_index, 0x10, 0, 8); + +/* reg_mddq_device_info_fw_major + * Major FW version number. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_fw_major, 0x14, 16, 16); + +/* reg_mddq_device_info_fw_minor + * Minor FW version number. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_fw_minor, 0x18, 16, 16); + +/* reg_mddq_device_info_fw_sub_minor + * Sub-minor FW version number. + * Access: RO + */ +MLXSW_ITEM32(reg, mddq, device_info_fw_sub_minor, 0x18, 0, 16); + +static inline void +mlxsw_reg_mddq_device_info_pack(char *payload, u8 slot_index, + u8 request_msg_seq) +{ + __mlxsw_reg_mddq_pack(payload, slot_index, + MLXSW_REG_MDDQ_QUERY_TYPE_DEVICE_INFO); + mlxsw_reg_mddq_request_msg_seq_set(payload, request_msg_seq); +} + +static inline void +mlxsw_reg_mddq_device_info_unpack(const char *payload, u8 *p_response_msg_seq, + bool *p_data_valid, bool *p_flash_owner, + u8 *p_device_index, u16 *p_fw_major, + u16 *p_fw_minor, u16 *p_fw_sub_minor) +{ + *p_response_msg_seq = mlxsw_reg_mddq_response_msg_seq_get(payload); + *p_data_valid = mlxsw_reg_mddq_data_valid_get(payload); + *p_flash_owner = mlxsw_reg_mddq_device_info_flash_owner_get(payload); + *p_device_index = mlxsw_reg_mddq_device_info_device_index_get(payload); + *p_fw_major = mlxsw_reg_mddq_device_info_fw_major_get(payload); + *p_fw_minor = mlxsw_reg_mddq_device_info_fw_minor_get(payload); + *p_fw_sub_minor = mlxsw_reg_mddq_device_info_fw_sub_minor_get(payload); +} + #define MLXSW_REG_MDDQ_SLOT_ASCII_NAME_LEN 20 /* reg_mddq_slot_ascii_name @@ -12862,6 +13032,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(mfgd), MLXSW_REG(mgpir), MLXSW_REG(mbct), + MLXSW_REG(mddt), MLXSW_REG(mddq), MLXSW_REG(mddc), MLXSW_REG(mfde), diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index 5f7e28de8b15..d34bb6ec1490 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -409,20 +409,19 @@ static void ism_create_system_eid(void) memcpy(&SYSTEM_EID.type, tmp, 4); } -static void ism_get_system_eid(struct smcd_dev *smcd, u8 **eid) +static u8 *ism_get_system_eid(void) { - *eid = &SYSTEM_EID.seid_string[0]; + return SYSTEM_EID.seid_string; } static u16 ism_get_chid(struct smcd_dev *smcd) { - struct ism_dev *ismdev; + struct ism_dev *ism = (struct ism_dev *)smcd->priv; - ismdev = (struct ism_dev *)smcd->priv; - if (!ismdev || !ismdev->pdev) + if (!ism || !ism->pdev) return 0; - return to_zpci(ismdev->pdev)->pchid; + return to_zpci(ism->pdev)->pchid; } static void ism_handle_event(struct ism_dev *ism) @@ -444,6 +443,7 @@ static irqreturn_t ism_handle_irq(int irq, void *data) struct ism_dev *ism = data; unsigned long bit, end; unsigned long *bv; + u16 dmbemask; bv = (void *) &ism->sba->dmb_bits[ISM_DMB_WORD_OFFSET]; end = sizeof(ism->sba->dmb_bits) * BITS_PER_BYTE - ISM_DMB_BIT_OFFSET; @@ -457,9 +457,10 @@ static irqreturn_t ism_handle_irq(int irq, void *data) break; clear_bit_inv(bit, bv); + dmbemask = ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET]; ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0; barrier(); - smcd_handle_irq(ism->smcd, bit + ISM_DMB_BIT_OFFSET); + smcd_handle_irq(ism->smcd, bit + ISM_DMB_BIT_OFFSET, dmbemask); } if (ism->sba->e) { diff --git a/include/linux/atm_tcp.h b/include/linux/atm_tcp.h index c8ecf6f68fb5..2558439d849b 100644 --- a/include/linux/atm_tcp.h +++ b/include/linux/atm_tcp.h @@ -9,6 +9,8 @@ #include <uapi/linux/atm_tcp.h> +struct atm_vcc; +struct module; struct atm_tcp_ops { int (*attach)(struct atm_vcc *vcc,int itf); diff --git a/include/linux/dsa/tag_qca.h b/include/linux/dsa/tag_qca.h index 4359fb0221cf..50be7cbd93a5 100644 --- a/include/linux/dsa/tag_qca.h +++ b/include/linux/dsa/tag_qca.h @@ -3,6 +3,11 @@ #ifndef __TAG_QCA_H #define __TAG_QCA_H +#include <linux/types.h> + +struct dsa_switch; +struct sk_buff; + #define QCA_HDR_LEN 2 #define QCA_HDR_VERSION 0x2 diff --git a/include/linux/hippidevice.h b/include/linux/hippidevice.h index 9dc01f7ab5b4..07414c241e65 100644 --- a/include/linux/hippidevice.h +++ b/include/linux/hippidevice.h @@ -23,6 +23,10 @@ #ifdef __KERNEL__ +struct neigh_parms; +struct net_device; +struct sk_buff; + struct hippi_cb { __u32 ifield; }; diff --git a/include/linux/if_eql.h b/include/linux/if_eql.h index d75601d613cc..07f9b660b741 100644 --- a/include/linux/if_eql.h +++ b/include/linux/if_eql.h @@ -21,6 +21,7 @@ #include <linux/timer.h> #include <linux/spinlock.h> +#include <net/net_trackers.h> #include <uapi/linux/if_eql.h> typedef struct slave { diff --git a/include/linux/if_hsr.h b/include/linux/if_hsr.h index 408539d5ea5f..0404f5bf4f30 100644 --- a/include/linux/if_hsr.h +++ b/include/linux/if_hsr.h @@ -2,6 +2,10 @@ #ifndef _LINUX_IF_HSR_H_ #define _LINUX_IF_HSR_H_ +#include <linux/types.h> + +struct net_device; + /* used to differentiate various protocols */ enum hsr_version { HSR_V0 = 0, diff --git a/include/linux/if_rmnet.h b/include/linux/if_rmnet.h index 10e7521ecb6c..839d1e48b85e 100644 --- a/include/linux/if_rmnet.h +++ b/include/linux/if_rmnet.h @@ -5,6 +5,8 @@ #ifndef _LINUX_IF_RMNET_H_ #define _LINUX_IF_RMNET_H_ +#include <linux/types.h> + struct rmnet_map_header { u8 flags; /* MAP_CMD_FLAG, MAP_PAD_LEN_MASK */ u8 mux_id; diff --git a/include/linux/if_tap.h b/include/linux/if_tap.h index 915a187cfabd..553552fa635c 100644 --- a/include/linux/if_tap.h +++ b/include/linux/if_tap.h @@ -2,14 +2,18 @@ #ifndef _LINUX_IF_TAP_H_ #define _LINUX_IF_TAP_H_ +#include <net/sock.h> +#include <linux/skb_array.h> + +struct file; +struct socket; + #if IS_ENABLED(CONFIG_TAP) struct socket *tap_get_socket(struct file *); struct ptr_ring *tap_get_ptr_ring(struct file *file); #else #include <linux/err.h> #include <linux/errno.h> -struct file; -struct socket; static inline struct socket *tap_get_socket(struct file *f) { return ERR_PTR(-EINVAL); @@ -20,9 +24,6 @@ static inline struct ptr_ring *tap_get_ptr_ring(struct file *f) } #endif /* CONFIG_TAP */ -#include <net/sock.h> -#include <linux/skb_array.h> - /* * Maximum times a tap device can be opened. This can be used to * configure the number of receive queue, e.g. for multiqueue virtio. diff --git a/include/linux/mdio/mdio-xgene.h b/include/linux/mdio/mdio-xgene.h index 8af93ada8b64..9e588965dc83 100644 --- a/include/linux/mdio/mdio-xgene.h +++ b/include/linux/mdio/mdio-xgene.h @@ -8,6 +8,10 @@ #ifndef __MDIO_XGENE_H__ #define __MDIO_XGENE_H__ +#include <linux/bits.h> +#include <linux/spinlock.h> +#include <linux/types.h> + #define BLOCK_XG_MDIO_CSR_OFFSET 0x5000 #define BLOCK_DIAG_CSR_OFFSET 0xd000 #define XGENET_CONFIG_REG_ADDR 0x20 diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index b22782225f27..cbe5fd1dd2e7 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -8,6 +8,8 @@ #ifndef NL802154_H #define NL802154_H +#include <net/netlink.h> + #define IEEE802154_NL_NAME "802.15.4 MAC" #define IEEE802154_MCAST_COORD_NAME "coordinator" #define IEEE802154_MCAST_BEACON_NAME "beacon" diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h index 52bc8e487ef7..1acafd86ab13 100644 --- a/include/linux/phy_fixed.h +++ b/include/linux/phy_fixed.h @@ -2,6 +2,8 @@ #ifndef __PHY_FIXED_H #define __PHY_FIXED_H +#include <linux/types.h> + struct fixed_phy_status { int link; int speed; @@ -12,6 +14,7 @@ struct fixed_phy_status { struct device_node; struct gpio_desc; +struct net_device; #if IS_ENABLED(CONFIG_FIXED_PHY) extern int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier); diff --git a/include/linux/ppp-comp.h b/include/linux/ppp-comp.h index 9d3ffc8f5ea6..fb847e47f148 100644 --- a/include/linux/ppp-comp.h +++ b/include/linux/ppp-comp.h @@ -9,7 +9,7 @@ #include <uapi/linux/ppp-comp.h> - +struct compstat; struct module; /* diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h index 91f9a928344e..45e6e427ceb8 100644 --- a/include/linux/ppp_channel.h +++ b/include/linux/ppp_channel.h @@ -20,6 +20,8 @@ #include <linux/poll.h> #include <net/net_namespace.h> +struct net_device_path; +struct net_device_path_ctx; struct ppp_channel; struct ppp_channel_ops { diff --git a/include/linux/ptp_kvm.h b/include/linux/ptp_kvm.h index f960a719f0d5..c2e28deef33a 100644 --- a/include/linux/ptp_kvm.h +++ b/include/linux/ptp_kvm.h @@ -8,6 +8,8 @@ #ifndef _PTP_KVM_H_ #define _PTP_KVM_H_ +#include <linux/types.h> + struct timespec64; struct clocksource; diff --git a/include/linux/ptp_pch.h b/include/linux/ptp_pch.h index 51818198c292..7ba643b62c15 100644 --- a/include/linux/ptp_pch.h +++ b/include/linux/ptp_pch.h @@ -10,6 +10,10 @@ #ifndef _PTP_PCH_H_ #define _PTP_PCH_H_ +#include <linux/types.h> + +struct pci_dev; + void pch_ch_control_write(struct pci_dev *pdev, u32 val); u32 pch_ch_event_read(struct pci_dev *pdev); void pch_ch_event_write(struct pci_dev *pdev, u32 val); diff --git a/include/linux/seq_file_net.h b/include/linux/seq_file_net.h index b97912fdbae7..79638395bc32 100644 --- a/include/linux/seq_file_net.h +++ b/include/linux/seq_file_net.h @@ -3,6 +3,7 @@ #define __SEQ_FILE_NET_H__ #include <linux/seq_file.h> +#include <net/net_trackers.h> struct net; extern struct net init_net; diff --git a/include/linux/sungem_phy.h b/include/linux/sungem_phy.h index 3a11fa41a131..c505f30e8b68 100644 --- a/include/linux/sungem_phy.h +++ b/include/linux/sungem_phy.h @@ -2,6 +2,8 @@ #ifndef __SUNGEM_PHY_H__ #define __SUNGEM_PHY_H__ +#include <linux/types.h> + struct mii_phy; /* Operations supported by any kind of PHY */ diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 1b4d72d5e891..b42b72391a8d 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -23,6 +23,12 @@ #ifndef __LINUX_USB_USBNET_H #define __LINUX_USB_USBNET_H +#include <linux/mii.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/types.h> +#include <linux/usb.h> + /* interface from usbnet core to each USB networking link we handle */ struct usbnet { /* housekeeping */ diff --git a/include/net/devlink.h b/include/net/devlink.h index 780744b550b8..5bd3fac12e9e 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -1580,6 +1580,8 @@ void devlink_linecard_provision_clear(struct devlink_linecard *linecard); void devlink_linecard_provision_fail(struct devlink_linecard *linecard); void devlink_linecard_activate(struct devlink_linecard *linecard); void devlink_linecard_deactivate(struct devlink_linecard *linecard); +void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, + struct devlink *nested_devlink); int devl_sb_register(struct devlink *devlink, unsigned int sb_index, u32 size, u16 ingress_pools_count, u16 egress_pools_count, u16 ingress_tc_count, diff --git a/include/net/llc_s_st.h b/include/net/llc_s_st.h index c4359e203013..ed5b2fa40d32 100644 --- a/include/net/llc_s_st.h +++ b/include/net/llc_s_st.h @@ -12,6 +12,12 @@ * See the GNU General Public License for more details. */ +#include <linux/types.h> +#include <net/llc_s_ac.h> +#include <net/llc_s_ev.h> + +struct llc_sap_state_trans; + #define LLC_NR_SAP_STATES 2 /* size of state table */ /* structures and types */ diff --git a/include/net/smc.h b/include/net/smc.h index 37f829d9c6e5..c926d3313e05 100644 --- a/include/net/smc.h +++ b/include/net/smc.h @@ -72,7 +72,7 @@ struct smcd_ops { int (*move_data)(struct smcd_dev *dev, u64 dmb_tok, unsigned int idx, bool sf, unsigned int offset, void *data, unsigned int size); - void (*get_system_eid)(struct smcd_dev *dev, u8 **eid); + u8* (*get_system_eid)(void); u16 (*get_chid)(struct smcd_dev *dev); }; @@ -101,5 +101,5 @@ int smcd_register_dev(struct smcd_dev *smcd); void smcd_unregister_dev(struct smcd_dev *smcd); void smcd_free_dev(struct smcd_dev *smcd); void smcd_handle_event(struct smcd_dev *dev, struct smcd_event *event); -void smcd_handle_irq(struct smcd_dev *dev, unsigned int bit); +void smcd_handle_irq(struct smcd_dev *dev, unsigned int bit, u16 dmbemask); #endif /* _SMC_H */ diff --git a/include/net/tcp.h b/include/net/tcp.h index f9e7c85ea829..b8620be32f8f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -673,6 +673,8 @@ void tcp_get_info(struct sock *, struct tcp_info *); int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, sk_read_actor_t recv_actor); int tcp_read_skb(struct sock *sk, skb_read_actor_t recv_actor); +struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off); +void tcp_read_done(struct sock *sk, size_t len); void tcp_initialize_rcv_mss(struct sock *sk); diff --git a/include/net/tls.h b/include/net/tls.h index 181c496b01b8..abb050b0df83 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -108,18 +108,33 @@ struct tls_sw_context_tx { unsigned long tx_bitmask; }; +struct tls_strparser { + struct sock *sk; + + u32 mark : 8; + u32 stopped : 1; + u32 copy_mode : 1; + u32 msg_ready : 1; + + struct strp_msg stm; + + struct sk_buff *anchor; + struct work_struct work; +}; + struct tls_sw_context_rx { struct crypto_aead *aead_recv; struct crypto_wait async_wait; - struct strparser strp; struct sk_buff_head rx_list; /* list of decrypted 'data' records */ void (*saved_data_ready)(struct sock *sk); - struct sk_buff *recv_pkt; u8 reader_present; u8 async_capable:1; u8 zc_capable:1; u8 reader_contended:1; + + struct tls_strparser strp; + atomic_t decrypt_pending; /* protect crypto_wait with decrypt_pending*/ spinlock_t decrypt_compl_lock; diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index b3d40a5d72ff..541321695f52 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -576,6 +576,8 @@ enum devlink_attr { DEVLINK_ATTR_LINECARD_TYPE, /* string */ DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES, /* nested */ + DEVLINK_ATTR_NESTED_DEVLINK, /* nested */ + /* add new attributes above here, update the policy in devlink.c */ __DEVLINK_ATTR_MAX, diff --git a/net/core/devlink.c b/net/core/devlink.c index 98d79feeb3dc..ca4c9939d569 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -70,6 +70,7 @@ struct devlink { u8 reload_failed:1; refcount_t refcount; struct completion comp; + struct rcu_head rcu; char priv[] __aligned(NETDEV_ALIGN); }; @@ -88,6 +89,7 @@ struct devlink_linecard { const char *type; struct devlink_linecard_type *types; unsigned int types_count; + struct devlink *nested_devlink; }; /** @@ -221,8 +223,6 @@ static DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC); /* devlink_mutex * * An overall lock guarding every operation coming from userspace. - * It also guards devlink devices list and it is taken when - * driver registers/unregisters it. */ static DEFINE_MUTEX(devlink_mutex); @@ -232,10 +232,21 @@ struct net *devlink_net(const struct devlink *devlink) } EXPORT_SYMBOL_GPL(devlink_net); +static void __devlink_put_rcu(struct rcu_head *head) +{ + struct devlink *devlink = container_of(head, struct devlink, rcu); + + complete(&devlink->comp); +} + void devlink_put(struct devlink *devlink) { if (refcount_dec_and_test(&devlink->refcount)) - complete(&devlink->comp); + /* Make sure unregister operation that may await the completion + * is unblocked only after all users are after the end of + * RCU grace period. + */ + call_rcu(&devlink->rcu, __devlink_put_rcu); } struct devlink *__must_check devlink_try_get(struct devlink *devlink) @@ -278,12 +289,62 @@ void devl_unlock(struct devlink *devlink) } EXPORT_SYMBOL_GPL(devl_unlock); +static struct devlink * +devlinks_xa_find_get(struct net *net, unsigned long *indexp, xa_mark_t filter, + void * (*xa_find_fn)(struct xarray *, unsigned long *, + unsigned long, xa_mark_t)) +{ + struct devlink *devlink; + + rcu_read_lock(); +retry: + devlink = xa_find_fn(&devlinks, indexp, ULONG_MAX, DEVLINK_REGISTERED); + if (!devlink) + goto unlock; + /* For a possible retry, the xa_find_after() should be always used */ + xa_find_fn = xa_find_after; + if (!devlink_try_get(devlink)) + goto retry; + if (!net_eq(devlink_net(devlink), net)) { + devlink_put(devlink); + goto retry; + } +unlock: + rcu_read_unlock(); + return devlink; +} + +static struct devlink *devlinks_xa_find_get_first(struct net *net, + unsigned long *indexp, + xa_mark_t filter) +{ + return devlinks_xa_find_get(net, indexp, filter, xa_find); +} + +static struct devlink *devlinks_xa_find_get_next(struct net *net, + unsigned long *indexp, + xa_mark_t filter) +{ + return devlinks_xa_find_get(net, indexp, filter, xa_find_after); +} + +/* Iterate over devlink pointers which were possible to get reference to. + * devlink_put() needs to be called for each iterated devlink pointer + * in loop body in order to release the reference. + */ +#define devlinks_xa_for_each_get(net, index, devlink, filter) \ + for (index = 0, \ + devlink = devlinks_xa_find_get_first(net, &index, filter); \ + devlink; devlink = devlinks_xa_find_get_next(net, &index, filter)) + +#define devlinks_xa_for_each_registered_get(net, index, devlink) \ + devlinks_xa_for_each_get(net, index, devlink, DEVLINK_REGISTERED) + static struct devlink *devlink_get_from_attrs(struct net *net, struct nlattr **attrs) { struct devlink *devlink; unsigned long index; - bool found = false; char *busname; char *devname; @@ -293,21 +354,14 @@ static struct devlink *devlink_get_from_attrs(struct net *net, busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]); devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); - lockdep_assert_held(&devlink_mutex); - - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { + devlinks_xa_for_each_registered_get(net, index, devlink) { if (strcmp(devlink->dev->bus->name, busname) == 0 && - strcmp(dev_name(devlink->dev), devname) == 0 && - net_eq(devlink_net(devlink), net)) { - found = true; - break; - } + strcmp(dev_name(devlink->dev), devname) == 0) + return devlink; + devlink_put(devlink); } - if (!found || !devlink_try_get(devlink)) - devlink = ERR_PTR(-ENODEV); - - return devlink; + return ERR_PTR(-ENODEV); } static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink, @@ -803,6 +857,24 @@ static int devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink) return 0; } +static int devlink_nl_put_nested_handle(struct sk_buff *msg, struct devlink *devlink) +{ + struct nlattr *nested_attr; + + nested_attr = nla_nest_start(msg, DEVLINK_ATTR_NESTED_DEVLINK); + if (!nested_attr) + return -EMSGSIZE; + if (devlink_nl_put_handle(msg, devlink)) + goto nla_put_failure; + + nla_nest_end(msg, nested_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, nested_attr); + return -EMSGSIZE; +} + struct devlink_reload_combination { enum devlink_reload_action action; enum devlink_reload_limit limit; @@ -1329,13 +1401,7 @@ static int devlink_nl_cmd_rate_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(devlink_rate, &devlink->rate_list, list) { enum devlink_command cmd = DEVLINK_CMD_RATE_NEW; @@ -1356,7 +1422,6 @@ static int devlink_nl_cmd_rate_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -1432,15 +1497,7 @@ static int devlink_nl_cmd_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) { - devlink_put(devlink); - continue; - } - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { if (idx < start) { idx++; devlink_put(devlink); @@ -1495,13 +1552,7 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(devlink_port, &devlink->port_list, list) { if (idx < start) { @@ -1521,7 +1572,6 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -2104,6 +2154,10 @@ static int devlink_nl_linecard_fill(struct sk_buff *msg, nla_nest_end(msg, attr); } + if (linecard->nested_devlink && + devlink_nl_put_nested_handle(msg, linecard->nested_devlink)) + goto nla_put_failure; + genlmsg_end(msg, hdr); return 0; @@ -2177,13 +2231,7 @@ static int devlink_nl_cmd_linecard_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { mutex_lock(&devlink->linecards_lock); list_for_each_entry(linecard, &devlink->linecard_list, list) { if (idx < start) { @@ -2206,7 +2254,6 @@ static int devlink_nl_cmd_linecard_get_dumpit(struct sk_buff *msg, idx++; } mutex_unlock(&devlink->linecards_lock); -retry: devlink_put(devlink); } out: @@ -2449,13 +2496,7 @@ static int devlink_nl_cmd_sb_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(devlink_sb, &devlink->sb_list, list) { if (idx < start) { @@ -2475,7 +2516,6 @@ static int devlink_nl_cmd_sb_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -2601,12 +2641,8 @@ static int devlink_nl_cmd_sb_pool_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || - !devlink->ops->sb_pool_get) + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { + if (!devlink->ops->sb_pool_get) goto retry; devl_lock(devlink); @@ -2822,12 +2858,8 @@ static int devlink_nl_cmd_sb_port_pool_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || - !devlink->ops->sb_port_pool_get) + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { + if (!devlink->ops->sb_port_pool_get) goto retry; devl_lock(devlink); @@ -3071,12 +3103,8 @@ devlink_nl_cmd_sb_tc_pool_bind_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) || - !devlink->ops->sb_tc_pool_bind_get) + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { + if (!devlink->ops->sb_tc_pool_bind_get) goto retry; devl_lock(devlink); @@ -5158,13 +5186,7 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(param_item, &devlink->param_list, list) { if (idx < start) { @@ -5186,7 +5208,6 @@ static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -5393,13 +5414,7 @@ static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(devlink_port, &devlink->port_list, list) { list_for_each_entry(param_item, @@ -5426,7 +5441,6 @@ static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg, } } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -5977,16 +5991,9 @@ static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { err = devlink_nl_cmd_region_get_devlink_dumpit(msg, cb, devlink, &idx, start); -retry: devlink_put(devlink); if (err) goto out; @@ -6511,13 +6518,7 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg, int err = 0; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { if (idx < start || !devlink->ops->info_get) goto inc; @@ -6535,7 +6536,6 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg, } inc: idx++; -retry: devlink_put(devlink); } mutex_unlock(&devlink_mutex); @@ -7691,13 +7691,7 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry_rep; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { mutex_lock(&devlink->reporters_lock); list_for_each_entry(reporter, &devlink->reporter_list, list) { @@ -7717,17 +7711,10 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg, idx++; } mutex_unlock(&devlink->reporters_lock); -retry_rep: devlink_put(devlink); } - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry_port; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(port, &devlink->port_list, list) { mutex_lock(&port->reporters_lock); @@ -7752,7 +7739,6 @@ retry_rep: mutex_unlock(&port->reporters_lock); } devl_unlock(devlink); -retry_port: devlink_put(devlink); } out: @@ -8291,13 +8277,7 @@ static int devlink_nl_cmd_trap_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(trap_item, &devlink->trap_list, list) { if (idx < start) { @@ -8317,7 +8297,6 @@ static int devlink_nl_cmd_trap_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -8518,13 +8497,7 @@ static int devlink_nl_cmd_trap_group_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(group_item, &devlink->trap_group_list, list) { @@ -8545,7 +8518,6 @@ static int devlink_nl_cmd_trap_group_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -8832,13 +8804,7 @@ static int devlink_nl_cmd_trap_policer_get_dumpit(struct sk_buff *msg, int err; mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) - goto retry; - + devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) { devl_lock(devlink); list_for_each_entry(policer_item, &devlink->trap_policer_list, list) { @@ -8859,7 +8825,6 @@ static int devlink_nl_cmd_trap_policer_get_dumpit(struct sk_buff *msg, idx++; } devl_unlock(devlink); -retry: devlink_put(devlink); } out: @@ -9589,10 +9554,8 @@ void devlink_register(struct devlink *devlink) ASSERT_DEVLINK_NOT_REGISTERED(devlink); /* Make sure that we are in .probe() routine */ - mutex_lock(&devlink_mutex); xa_set_mark(&devlinks, devlink->index, DEVLINK_REGISTERED); devlink_notify_register(devlink); - mutex_unlock(&devlink_mutex); } EXPORT_SYMBOL_GPL(devlink_register); @@ -9609,10 +9572,8 @@ void devlink_unregister(struct devlink *devlink) devlink_put(devlink); wait_for_completion(&devlink->comp); - mutex_lock(&devlink_mutex); devlink_notify_unregister(devlink); xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED); - mutex_unlock(&devlink_mutex); } EXPORT_SYMBOL_GPL(devlink_unregister); @@ -10316,6 +10277,7 @@ EXPORT_SYMBOL_GPL(devlink_linecard_provision_set); void devlink_linecard_provision_clear(struct devlink_linecard *linecard) { mutex_lock(&linecard->state_lock); + WARN_ON(linecard->nested_devlink); linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; linecard->type = NULL; devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); @@ -10334,6 +10296,7 @@ EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear); void devlink_linecard_provision_fail(struct devlink_linecard *linecard) { mutex_lock(&linecard->state_lock); + WARN_ON(linecard->nested_devlink); linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED; devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); mutex_unlock(&linecard->state_lock); @@ -10381,6 +10344,23 @@ void devlink_linecard_deactivate(struct devlink_linecard *linecard) } EXPORT_SYMBOL_GPL(devlink_linecard_deactivate); +/** + * devlink_linecard_nested_dl_set - Attach/detach nested devlink + * instance to linecard. + * + * @linecard: devlink linecard + * @nested_devlink: devlink instance to attach or NULL to detach + */ +void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, + struct devlink *nested_devlink) +{ + mutex_lock(&linecard->state_lock); + linecard->nested_devlink = nested_devlink; + devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); + mutex_unlock(&linecard->state_lock); +} +EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set); + int devl_sb_register(struct devlink *devlink, unsigned int sb_index, u32 size, u16 ingress_pools_count, u16 egress_pools_count, u16 ingress_tc_count, @@ -12281,13 +12261,7 @@ static void __net_exit devlink_pernet_pre_exit(struct net *net) * all devlink instances from this namespace into init_net. */ mutex_lock(&devlink_mutex); - xa_for_each_marked(&devlinks, index, devlink, DEVLINK_REGISTERED) { - if (!devlink_try_get(devlink)) - continue; - - if (!net_eq(devlink_net(devlink), net)) - goto retry; - + devlinks_xa_for_each_registered_get(net, index, devlink) { WARN_ON(!(devlink->features & DEVLINK_F_RELOAD)); err = devlink_reload(devlink, &init_net, DEVLINK_RELOAD_ACTION_DRIVER_REINIT, @@ -12295,7 +12269,6 @@ static void __net_exit devlink_pernet_pre_exit(struct net *net) &actions_performed, NULL); if (err && err != -EOPNOTSUPP) pr_warn("Failed to reload devlink instance into init_net\n"); -retry: devlink_put(devlink); } mutex_unlock(&devlink_mutex); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ba2bdc811374..dc7cc3ce6a53 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1635,7 +1635,7 @@ static void tcp_eat_recv_skb(struct sock *sk, struct sk_buff *skb) __kfree_skb(skb); } -static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) +struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) { struct sk_buff *skb; u32 offset; @@ -1658,6 +1658,7 @@ static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) } return NULL; } +EXPORT_SYMBOL(tcp_recv_skb); /* * This routine provides an alternative to tcp_recvmsg() for routines @@ -1788,6 +1789,45 @@ int tcp_read_skb(struct sock *sk, skb_read_actor_t recv_actor) } EXPORT_SYMBOL(tcp_read_skb); +void tcp_read_done(struct sock *sk, size_t len) +{ + struct tcp_sock *tp = tcp_sk(sk); + u32 seq = tp->copied_seq; + struct sk_buff *skb; + size_t left; + u32 offset; + + if (sk->sk_state == TCP_LISTEN) + return; + + left = len; + while (left && (skb = tcp_recv_skb(sk, seq, &offset)) != NULL) { + int used; + + used = min_t(size_t, skb->len - offset, left); + seq += used; + left -= used; + + if (skb->len > offset + used) + break; + + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) { + tcp_eat_recv_skb(sk, skb); + ++seq; + break; + } + tcp_eat_recv_skb(sk, skb); + } + WRITE_ONCE(tp->copied_seq, seq); + + tcp_rcv_space_adjust(sk); + + /* Clean up data we have read: This will do ACK frames. */ + if (left != len) + tcp_cleanup_rbuf(sk, len - left); +} +EXPORT_SYMBOL(tcp_read_done); + int tcp_peek_len(struct socket *sock) { return tcp_inq(sock->sk); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index d546fc09d803..a9ba41648e36 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -2133,10 +2133,8 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt, */ cache_proxy = mr_mfc_find_any_parent(mrt, vif); if (cache_proxy && - cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255) { - rcu_read_unlock(); + cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255) goto forward; - } } /* diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 599e26fc2fa8..91a0dc463c48 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -979,7 +979,7 @@ cbq_reset(struct Qdisc *sch) } -static int cbq_set_lss(struct cbq_class *cl, struct tc_cbq_lssopt *lss) +static void cbq_set_lss(struct cbq_class *cl, struct tc_cbq_lssopt *lss) { if (lss->change & TCF_CBQ_LSS_FLAGS) { cl->share = (lss->flags & TCF_CBQ_LSS_ISOLATED) ? NULL : cl->tparent; @@ -997,7 +997,6 @@ static int cbq_set_lss(struct cbq_class *cl, struct tc_cbq_lssopt *lss) } if (lss->change & TCF_CBQ_LSS_OFFTIME) cl->offtime = lss->offtime; - return 0; } static void cbq_rmprio(struct cbq_sched_data *q, struct cbq_class *cl) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 6e70d9c10b78..79c1318af1fe 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -3515,3 +3515,4 @@ MODULE_DESCRIPTION("smc socket address family"); MODULE_LICENSE("GPL"); MODULE_ALIAS_NETPROTO(PF_SMC); MODULE_ALIAS_TCP_ULP("smc"); +MODULE_ALIAS_GENL_FAMILY(SMC_GENL_FAMILY_NAME); diff --git a/net/smc/smc_diag.c b/net/smc/smc_diag.c index 1fca2f90a9c7..80ea7d954ece 100644 --- a/net/smc/smc_diag.c +++ b/net/smc/smc_diag.c @@ -268,3 +268,4 @@ module_init(smc_diag_init); module_exit(smc_diag_exit); MODULE_LICENSE("GPL"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 43 /* AF_SMC */); +MODULE_ALIAS_GENL_FAMILY(SMCR_GENL_FAMILY_NAME); diff --git a/net/smc/smc_ism.c b/net/smc/smc_ism.c index a2084ecdb97e..911fe08bc54b 100644 --- a/net/smc/smc_ism.c +++ b/net/smc/smc_ism.c @@ -33,17 +33,6 @@ int smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *smcd) vlan_id); } -int smc_ism_write(struct smcd_dev *smcd, const struct smc_ism_position *pos, - void *data, size_t len) -{ - int rc; - - rc = smcd->ops->move_data(smcd, pos->token, pos->index, pos->signal, - pos->offset, data, len); - - return rc < 0 ? rc : 0; -} - void smc_ism_get_system_eid(u8 **eid) { if (!smc_ism_v2_capable) @@ -440,7 +429,7 @@ int smcd_register_dev(struct smcd_dev *smcd) if (list_empty(&smcd_dev_list.list)) { u8 *system_eid = NULL; - smcd->ops->get_system_eid(smcd, &system_eid); + system_eid = smcd->ops->get_system_eid(); if (system_eid[24] != '0' || system_eid[28] != '0') { smc_ism_v2_capable = true; memcpy(smc_ism_v2_system_eid, system_eid, @@ -519,13 +508,13 @@ void smcd_handle_event(struct smcd_dev *smcd, struct smcd_event *event) EXPORT_SYMBOL_GPL(smcd_handle_event); /* SMCD Device interrupt handler. Called from ISM device interrupt handler. - * Parameters are smcd device pointer and DMB number. Find the connection and - * schedule the tasklet for this connection. + * Parameters are smcd device pointer, DMB number, and the DMBE bitmask. + * Find the connection and schedule the tasklet for this connection. * * Context: * - Function called in IRQ context from ISM device driver IRQ handler. */ -void smcd_handle_irq(struct smcd_dev *smcd, unsigned int dmbno) +void smcd_handle_irq(struct smcd_dev *smcd, unsigned int dmbno, u16 dmbemask) { struct smc_connection *conn = NULL; unsigned long flags; diff --git a/net/smc/smc_ism.h b/net/smc/smc_ism.h index 004b22a13ffa..d6b2db604fe8 100644 --- a/net/smc/smc_ism.h +++ b/net/smc/smc_ism.h @@ -28,13 +28,6 @@ struct smc_ism_vlanid { /* VLAN id set on ISM device */ refcount_t refcnt; /* Reference count */ }; -struct smc_ism_position { /* ISM device position to write to */ - u64 token; /* Token of DMB */ - u32 offset; /* Offset into DMBE */ - u8 index; /* Index of DMBE */ - u8 signal; /* Generate interrupt on owner side */ -}; - struct smcd_dev; int smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *dev); @@ -45,12 +38,21 @@ int smc_ism_put_vlan(struct smcd_dev *dev, unsigned short vlan_id); int smc_ism_register_dmb(struct smc_link_group *lgr, int buf_size, struct smc_buf_desc *dmb_desc); int smc_ism_unregister_dmb(struct smcd_dev *dev, struct smc_buf_desc *dmb_desc); -int smc_ism_write(struct smcd_dev *dev, const struct smc_ism_position *pos, - void *data, size_t len); int smc_ism_signal_shutdown(struct smc_link_group *lgr); void smc_ism_get_system_eid(u8 **eid); u16 smc_ism_get_chid(struct smcd_dev *dev); bool smc_ism_is_v2_capable(void); void smc_ism_init(void); int smcd_nl_get_device(struct sk_buff *skb, struct netlink_callback *cb); + +static inline int smc_ism_write(struct smcd_dev *smcd, u64 dmb_tok, + unsigned int idx, bool sf, unsigned int offset, + void *data, size_t len) +{ + int rc; + + rc = smcd->ops->move_data(smcd, dmb_tok, idx, sf, offset, data, len); + return rc < 0 ? rc : 0; +} + #endif diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c index 4e8377657a62..64dedffe9d26 100644 --- a/net/smc/smc_tx.c +++ b/net/smc/smc_tx.c @@ -320,15 +320,11 @@ int smc_tx_sendpage(struct smc_sock *smc, struct page *page, int offset, int smcd_tx_ism_write(struct smc_connection *conn, void *data, size_t len, u32 offset, int signal) { - struct smc_ism_position pos; int rc; - memset(&pos, 0, sizeof(pos)); - pos.token = conn->peer_token; - pos.index = conn->peer_rmbe_idx; - pos.offset = conn->tx_off + offset; - pos.signal = signal; - rc = smc_ism_write(conn->lgr->smcd, &pos, data, len); + rc = smc_ism_write(conn->lgr->smcd, conn->peer_token, + conn->peer_rmbe_idx, signal, conn->tx_off + offset, + data, len); if (rc) conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; return rc; diff --git a/net/tls/tls.h b/net/tls/tls.h index 3740740504e3..0e840a0c3437 100644 --- a/net/tls/tls.h +++ b/net/tls/tls.h @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016 Tom Herbert <[email protected]> * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved. * Copyright (c) 2016-2017, Dave Watson <[email protected]>. All rights reserved. * @@ -127,8 +128,24 @@ int tls_sw_fallback_init(struct sock *sk, struct tls_offload_context_tx *offload_ctx, struct tls_crypto_info *crypto_info); -int tls_strp_msg_hold(struct sock *sk, struct sk_buff *skb, - struct sk_buff_head *dst); +int tls_strp_dev_init(void); +void tls_strp_dev_exit(void); + +void tls_strp_done(struct tls_strparser *strp); +void tls_strp_stop(struct tls_strparser *strp); +int tls_strp_init(struct tls_strparser *strp, struct sock *sk); +void tls_strp_data_ready(struct tls_strparser *strp); + +void tls_strp_check_rcv(struct tls_strparser *strp); +void tls_strp_msg_done(struct tls_strparser *strp); + +int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb); +void tls_rx_msg_ready(struct tls_strparser *strp); + +void tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh); +int tls_strp_msg_cow(struct tls_sw_context_rx *ctx); +struct sk_buff *tls_strp_msg_detach(struct tls_sw_context_rx *ctx); +int tls_strp_msg_hold(struct tls_strparser *strp, struct sk_buff_head *dst); static inline struct tls_msg *tls_msg(struct sk_buff *skb) { @@ -139,7 +156,13 @@ static inline struct tls_msg *tls_msg(struct sk_buff *skb) static inline struct sk_buff *tls_strp_msg(struct tls_sw_context_rx *ctx) { - return ctx->recv_pkt; + DEBUG_NET_WARN_ON_ONCE(!ctx->strp.msg_ready || !ctx->strp.anchor->len); + return ctx->strp.anchor; +} + +static inline bool tls_strp_msg_ready(struct tls_sw_context_rx *ctx) +{ + return ctx->strp.msg_ready; } #ifdef CONFIG_TLS_DEVICE diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index b1fcd61836d1..fc513c1806a0 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -894,27 +894,26 @@ static void tls_device_core_ctrl_rx_resync(struct tls_context *tls_ctx, static int tls_device_reencrypt(struct sock *sk, struct tls_sw_context_rx *sw_ctx) { - int err = 0, offset, copy, nsg, data_len, pos; - struct sk_buff *skb, *skb_iter, *unused; + int err, offset, copy, data_len, pos; + struct sk_buff *skb, *skb_iter; struct scatterlist sg[1]; struct strp_msg *rxm; char *orig_buf, *buf; - skb = tls_strp_msg(sw_ctx); - rxm = strp_msg(skb); - offset = rxm->offset; - + rxm = strp_msg(tls_strp_msg(sw_ctx)); orig_buf = kmalloc(rxm->full_len + TLS_HEADER_SIZE + TLS_CIPHER_AES_GCM_128_IV_SIZE, sk->sk_allocation); if (!orig_buf) return -ENOMEM; buf = orig_buf; - nsg = skb_cow_data(skb, 0, &unused); - if (unlikely(nsg < 0)) { - err = nsg; + err = tls_strp_msg_cow(sw_ctx); + if (unlikely(err)) goto free_buf; - } + + skb = tls_strp_msg(sw_ctx); + rxm = strp_msg(skb); + offset = rxm->offset; sg_init_table(sg, 1); sg_set_buf(&sg[0], buf, diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 9703636cfc60..08ddf9d837ae 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -725,6 +725,10 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, if (tx) { ctx->sk_write_space = sk->sk_write_space; sk->sk_write_space = tls_write_space; + } else { + struct tls_sw_context_rx *rx_ctx = tls_sw_ctx_rx(ctx); + + tls_strp_check_rcv(&rx_ctx->strp); } return 0; @@ -1141,20 +1145,28 @@ static int __init tls_register(void) if (err) return err; + err = tls_strp_dev_init(); + if (err) + goto err_pernet; + err = tls_device_init(); - if (err) { - unregister_pernet_subsys(&tls_proc_ops); - return err; - } + if (err) + goto err_strp; tcp_register_ulp(&tcp_tls_ulp_ops); return 0; +err_strp: + tls_strp_dev_exit(); +err_pernet: + unregister_pernet_subsys(&tls_proc_ops); + return err; } static void __exit tls_unregister(void) { tcp_unregister_ulp(&tcp_tls_ulp_ops); + tls_strp_dev_exit(); tls_device_cleanup(); unregister_pernet_subsys(&tls_proc_ops); } diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c index 9ccab79a6e1e..b945288c312e 100644 --- a/net/tls/tls_strp.c +++ b/net/tls/tls_strp.c @@ -1,17 +1,493 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2016 Tom Herbert <[email protected]> */ #include <linux/skbuff.h> +#include <linux/workqueue.h> +#include <net/strparser.h> +#include <net/tcp.h> +#include <net/sock.h> +#include <net/tls.h> #include "tls.h" -int tls_strp_msg_hold(struct sock *sk, struct sk_buff *skb, - struct sk_buff_head *dst) +static struct workqueue_struct *tls_strp_wq; + +static void tls_strp_abort_strp(struct tls_strparser *strp, int err) +{ + if (strp->stopped) + return; + + strp->stopped = 1; + + /* Report an error on the lower socket */ + strp->sk->sk_err = -err; + sk_error_report(strp->sk); +} + +static void tls_strp_anchor_free(struct tls_strparser *strp) +{ + struct skb_shared_info *shinfo = skb_shinfo(strp->anchor); + + DEBUG_NET_WARN_ON_ONCE(atomic_read(&shinfo->dataref) != 1); + shinfo->frag_list = NULL; + consume_skb(strp->anchor); + strp->anchor = NULL; +} + +/* Create a new skb with the contents of input copied to its page frags */ +static struct sk_buff *tls_strp_msg_make_copy(struct tls_strparser *strp) { - struct sk_buff *clone; + struct strp_msg *rxm; + struct sk_buff *skb; + int i, err, offset; + + skb = alloc_skb_with_frags(0, strp->anchor->len, TLS_PAGE_ORDER, + &err, strp->sk->sk_allocation); + if (!skb) + return NULL; + + offset = strp->stm.offset; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + WARN_ON_ONCE(skb_copy_bits(strp->anchor, offset, + skb_frag_address(frag), + skb_frag_size(frag))); + offset += skb_frag_size(frag); + } + + skb_copy_header(skb, strp->anchor); + rxm = strp_msg(skb); + rxm->offset = 0; + return skb; +} + +/* Steal the input skb, input msg is invalid after calling this function */ +struct sk_buff *tls_strp_msg_detach(struct tls_sw_context_rx *ctx) +{ + struct tls_strparser *strp = &ctx->strp; + +#ifdef CONFIG_TLS_DEVICE + DEBUG_NET_WARN_ON_ONCE(!strp->anchor->decrypted); +#else + /* This function turns an input into an output, + * that can only happen if we have offload. + */ + WARN_ON(1); +#endif + + if (strp->copy_mode) { + struct sk_buff *skb; + + /* Replace anchor with an empty skb, this is a little + * dangerous but __tls_cur_msg() warns on empty skbs + * so hopefully we'll catch abuses. + */ + skb = alloc_skb(0, strp->sk->sk_allocation); + if (!skb) + return NULL; - clone = skb_clone(skb, sk->sk_allocation); - if (!clone) + swap(strp->anchor, skb); + return skb; + } + + return tls_strp_msg_make_copy(strp); +} + +/* Force the input skb to be in copy mode. The data ownership remains + * with the input skb itself (meaning unpause will wipe it) but it can + * be modified. + */ +int tls_strp_msg_cow(struct tls_sw_context_rx *ctx) +{ + struct tls_strparser *strp = &ctx->strp; + struct sk_buff *skb; + + if (strp->copy_mode) + return 0; + + skb = tls_strp_msg_make_copy(strp); + if (!skb) return -ENOMEM; - __skb_queue_tail(dst, clone); + + tls_strp_anchor_free(strp); + strp->anchor = skb; + + tcp_read_done(strp->sk, strp->stm.full_len); + strp->copy_mode = 1; + return 0; } + +/* Make a clone (in the skb sense) of the input msg to keep a reference + * to the underlying data. The reference-holding skbs get placed on + * @dst. + */ +int tls_strp_msg_hold(struct tls_strparser *strp, struct sk_buff_head *dst) +{ + struct skb_shared_info *shinfo = skb_shinfo(strp->anchor); + + if (strp->copy_mode) { + struct sk_buff *skb; + + WARN_ON_ONCE(!shinfo->nr_frags); + + /* We can't skb_clone() the anchor, it gets wiped by unpause */ + skb = alloc_skb(0, strp->sk->sk_allocation); + if (!skb) + return -ENOMEM; + + __skb_queue_tail(dst, strp->anchor); + strp->anchor = skb; + } else { + struct sk_buff *iter, *clone; + int chunk, len, offset; + + offset = strp->stm.offset; + len = strp->stm.full_len; + iter = shinfo->frag_list; + + while (len > 0) { + if (iter->len <= offset) { + offset -= iter->len; + goto next; + } + + chunk = iter->len - offset; + offset = 0; + + clone = skb_clone(iter, strp->sk->sk_allocation); + if (!clone) + return -ENOMEM; + __skb_queue_tail(dst, clone); + + len -= chunk; +next: + iter = iter->next; + } + } + + return 0; +} + +static void tls_strp_flush_anchor_copy(struct tls_strparser *strp) +{ + struct skb_shared_info *shinfo = skb_shinfo(strp->anchor); + int i; + + DEBUG_NET_WARN_ON_ONCE(atomic_read(&shinfo->dataref) != 1); + + for (i = 0; i < shinfo->nr_frags; i++) + __skb_frag_unref(&shinfo->frags[i], false); + shinfo->nr_frags = 0; + strp->copy_mode = 0; +} + +static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb, + unsigned int offset, size_t in_len) +{ + struct tls_strparser *strp = (struct tls_strparser *)desc->arg.data; + size_t sz, len, chunk; + struct sk_buff *skb; + skb_frag_t *frag; + + if (strp->msg_ready) + return 0; + + skb = strp->anchor; + frag = &skb_shinfo(skb)->frags[skb->len / PAGE_SIZE]; + + len = in_len; + /* First make sure we got the header */ + if (!strp->stm.full_len) { + /* Assume one page is more than enough for headers */ + chunk = min_t(size_t, len, PAGE_SIZE - skb_frag_size(frag)); + WARN_ON_ONCE(skb_copy_bits(in_skb, offset, + skb_frag_address(frag) + + skb_frag_size(frag), + chunk)); + + sz = tls_rx_msg_size(strp, strp->anchor); + if (sz < 0) { + desc->error = sz; + return 0; + } + + /* We may have over-read, sz == 0 is guaranteed under-read */ + if (sz > 0) + chunk = min_t(size_t, chunk, sz - skb->len); + + skb->len += chunk; + skb->data_len += chunk; + skb_frag_size_add(frag, chunk); + frag++; + len -= chunk; + offset += chunk; + + strp->stm.full_len = sz; + if (!strp->stm.full_len) + goto read_done; + } + + /* Load up more data */ + while (len && strp->stm.full_len > skb->len) { + chunk = min_t(size_t, len, strp->stm.full_len - skb->len); + chunk = min_t(size_t, chunk, PAGE_SIZE - skb_frag_size(frag)); + WARN_ON_ONCE(skb_copy_bits(in_skb, offset, + skb_frag_address(frag) + + skb_frag_size(frag), + chunk)); + + skb->len += chunk; + skb->data_len += chunk; + skb_frag_size_add(frag, chunk); + frag++; + len -= chunk; + offset += chunk; + } + + if (strp->stm.full_len == skb->len) { + desc->count = 0; + + strp->msg_ready = 1; + tls_rx_msg_ready(strp); + } + +read_done: + return in_len - len; +} + +static int tls_strp_read_copyin(struct tls_strparser *strp) +{ + struct socket *sock = strp->sk->sk_socket; + read_descriptor_t desc; + + desc.arg.data = strp; + desc.error = 0; + desc.count = 1; /* give more than one skb per call */ + + /* sk should be locked here, so okay to do read_sock */ + sock->ops->read_sock(strp->sk, &desc, tls_strp_copyin); + + return desc.error; +} + +static int tls_strp_read_short(struct tls_strparser *strp) +{ + struct skb_shared_info *shinfo; + struct page *page; + int need_spc, len; + + /* If the rbuf is small or rcv window has collapsed to 0 we need + * to read the data out. Otherwise the connection will stall. + * Without pressure threshold of INT_MAX will never be ready. + */ + if (likely(!tcp_epollin_ready(strp->sk, INT_MAX))) + return 0; + + shinfo = skb_shinfo(strp->anchor); + shinfo->frag_list = NULL; + + /* If we don't know the length go max plus page for cipher overhead */ + need_spc = strp->stm.full_len ?: TLS_MAX_PAYLOAD_SIZE + PAGE_SIZE; + + for (len = need_spc; len > 0; len -= PAGE_SIZE) { + page = alloc_page(strp->sk->sk_allocation); + if (!page) { + tls_strp_flush_anchor_copy(strp); + return -ENOMEM; + } + + skb_fill_page_desc(strp->anchor, shinfo->nr_frags++, + page, 0, 0); + } + + strp->copy_mode = 1; + strp->stm.offset = 0; + + strp->anchor->len = 0; + strp->anchor->data_len = 0; + strp->anchor->truesize = round_up(need_spc, PAGE_SIZE); + + tls_strp_read_copyin(strp); + + return 0; +} + +static void tls_strp_load_anchor_with_queue(struct tls_strparser *strp, int len) +{ + struct tcp_sock *tp = tcp_sk(strp->sk); + struct sk_buff *first; + u32 offset; + + first = tcp_recv_skb(strp->sk, tp->copied_seq, &offset); + if (WARN_ON_ONCE(!first)) + return; + + /* Bestow the state onto the anchor */ + strp->anchor->len = offset + len; + strp->anchor->data_len = offset + len; + strp->anchor->truesize = offset + len; + + skb_shinfo(strp->anchor)->frag_list = first; + + skb_copy_header(strp->anchor, first); + strp->anchor->destructor = NULL; + + strp->stm.offset = offset; +} + +void tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh) +{ + struct strp_msg *rxm; + struct tls_msg *tlm; + + DEBUG_NET_WARN_ON_ONCE(!strp->msg_ready); + DEBUG_NET_WARN_ON_ONCE(!strp->stm.full_len); + + if (!strp->copy_mode && force_refresh) { + if (WARN_ON(tcp_inq(strp->sk) < strp->stm.full_len)) + return; + + tls_strp_load_anchor_with_queue(strp, strp->stm.full_len); + } + + rxm = strp_msg(strp->anchor); + rxm->full_len = strp->stm.full_len; + rxm->offset = strp->stm.offset; + tlm = tls_msg(strp->anchor); + tlm->control = strp->mark; +} + +/* Called with lock held on lower socket */ +static int tls_strp_read_sock(struct tls_strparser *strp) +{ + int sz, inq; + + inq = tcp_inq(strp->sk); + if (inq < 1) + return 0; + + if (unlikely(strp->copy_mode)) + return tls_strp_read_copyin(strp); + + if (inq < strp->stm.full_len) + return tls_strp_read_short(strp); + + if (!strp->stm.full_len) { + tls_strp_load_anchor_with_queue(strp, inq); + + sz = tls_rx_msg_size(strp, strp->anchor); + if (sz < 0) { + tls_strp_abort_strp(strp, sz); + return sz; + } + + strp->stm.full_len = sz; + + if (!strp->stm.full_len || inq < strp->stm.full_len) + return tls_strp_read_short(strp); + } + + strp->msg_ready = 1; + tls_rx_msg_ready(strp); + + return 0; +} + +void tls_strp_check_rcv(struct tls_strparser *strp) +{ + if (unlikely(strp->stopped) || strp->msg_ready) + return; + + if (tls_strp_read_sock(strp) == -ENOMEM) + queue_work(tls_strp_wq, &strp->work); +} + +/* Lower sock lock held */ +void tls_strp_data_ready(struct tls_strparser *strp) +{ + /* This check is needed to synchronize with do_tls_strp_work. + * do_tls_strp_work acquires a process lock (lock_sock) whereas + * the lock held here is bh_lock_sock. The two locks can be + * held by different threads at the same time, but bh_lock_sock + * allows a thread in BH context to safely check if the process + * lock is held. In this case, if the lock is held, queue work. + */ + if (sock_owned_by_user_nocheck(strp->sk)) { + queue_work(tls_strp_wq, &strp->work); + return; + } + + tls_strp_check_rcv(strp); +} + +static void tls_strp_work(struct work_struct *w) +{ + struct tls_strparser *strp = + container_of(w, struct tls_strparser, work); + + lock_sock(strp->sk); + tls_strp_check_rcv(strp); + release_sock(strp->sk); +} + +void tls_strp_msg_done(struct tls_strparser *strp) +{ + WARN_ON(!strp->stm.full_len); + + if (likely(!strp->copy_mode)) + tcp_read_done(strp->sk, strp->stm.full_len); + else + tls_strp_flush_anchor_copy(strp); + + strp->msg_ready = 0; + memset(&strp->stm, 0, sizeof(strp->stm)); + + tls_strp_check_rcv(strp); +} + +void tls_strp_stop(struct tls_strparser *strp) +{ + strp->stopped = 1; +} + +int tls_strp_init(struct tls_strparser *strp, struct sock *sk) +{ + memset(strp, 0, sizeof(*strp)); + + strp->sk = sk; + + strp->anchor = alloc_skb(0, GFP_KERNEL); + if (!strp->anchor) + return -ENOMEM; + + INIT_WORK(&strp->work, tls_strp_work); + + return 0; +} + +/* strp must already be stopped so that tls_strp_recv will no longer be called. + * Note that tls_strp_done is not called with the lower socket held. + */ +void tls_strp_done(struct tls_strparser *strp) +{ + WARN_ON(!strp->stopped); + + cancel_work_sync(&strp->work); + tls_strp_anchor_free(strp); +} + +int __init tls_strp_dev_init(void) +{ + tls_strp_wq = create_singlethread_workqueue("kstrp"); + if (unlikely(!tls_strp_wq)) + return -ENOMEM; + + return 0; +} + +void tls_strp_dev_exit(void) +{ + destroy_workqueue(tls_strp_wq); +} diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index ed5e6f1df9c7..0fc24a5ce208 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1283,13 +1283,13 @@ int tls_sw_sendpage(struct sock *sk, struct page *page, static int tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, - long timeo) + bool released, long timeo) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); DEFINE_WAIT_FUNC(wait, woken_wake_function); - while (!ctx->recv_pkt) { + while (!tls_strp_msg_ready(ctx)) { if (!sk_psock_queue_empty(psock)) return 0; @@ -1297,8 +1297,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, return sock_error(sk); if (!skb_queue_empty(&sk->sk_receive_queue)) { - __strp_unpause(&ctx->strp); - if (ctx->recv_pkt) + tls_strp_check_rcv(&ctx->strp); + if (tls_strp_msg_ready(ctx)) break; } @@ -1311,10 +1311,12 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, if (nonblock || !timeo) return -EAGAIN; + released = true; add_wait_queue(sk_sleep(sk), &wait); sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk); sk_wait_event(sk, &timeo, - ctx->recv_pkt || !sk_psock_queue_empty(psock), + tls_strp_msg_ready(ctx) || + !sk_psock_queue_empty(psock), &wait); sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk); remove_wait_queue(sk_sleep(sk), &wait); @@ -1324,6 +1326,8 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, return sock_intr_errno(timeo); } + tls_strp_msg_load(&ctx->strp, released); + return 1; } @@ -1408,13 +1412,15 @@ tls_alloc_clrtxt_skb(struct sock *sk, struct sk_buff *skb, /* Decrypt handlers * - * tls_decrypt_sg() and tls_decrypt_device() are decrypt handlers. + * tls_decrypt_sw() and tls_decrypt_device() are decrypt handlers. * They must transform the darg in/out argument are as follows: * | Input | Output * ------------------------------------------------------------------- * zc | Zero-copy decrypt allowed | Zero-copy performed * async | Async decrypt allowed | Async crypto used / in progress * skb | * | Output skb + * + * If ZC decryption was performed darg.skb will point to the input skb. */ /* This function decrypts the input skb into either out_iov or in out_sg @@ -1567,7 +1573,7 @@ static int tls_decrypt_sg(struct sock *sk, struct iov_iter *out_iov, clear_skb = NULL; if (unlikely(darg->async)) { - err = tls_strp_msg_hold(sk, skb, &ctx->async_hold); + err = tls_strp_msg_hold(&ctx->strp, &ctx->async_hold); if (err) __skb_queue_tail(&ctx->async_hold, darg->skb); return err; @@ -1588,49 +1594,22 @@ exit_free_skb: } static int -tls_decrypt_device(struct sock *sk, struct tls_context *tls_ctx, - struct tls_decrypt_arg *darg) -{ - struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); - int err; - - if (tls_ctx->rx_conf != TLS_HW) - return 0; - - err = tls_device_decrypted(sk, tls_ctx); - if (err <= 0) - return err; - - darg->zc = false; - darg->async = false; - darg->skb = tls_strp_msg(ctx); - ctx->recv_pkt = NULL; - return 1; -} - -static int tls_rx_one_record(struct sock *sk, struct iov_iter *dest, - struct tls_decrypt_arg *darg) +tls_decrypt_sw(struct sock *sk, struct tls_context *tls_ctx, + struct msghdr *msg, struct tls_decrypt_arg *darg) { - struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); struct tls_prot_info *prot = &tls_ctx->prot_info; struct strp_msg *rxm; int pad, err; - err = tls_decrypt_device(sk, tls_ctx, darg); - if (err < 0) - return err; - if (err) - goto decrypt_done; - - err = tls_decrypt_sg(sk, dest, NULL, darg); + err = tls_decrypt_sg(sk, &msg->msg_iter, NULL, darg); if (err < 0) { if (err == -EBADMSG) TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSDECRYPTERROR); return err; } - if (darg->async) - goto decrypt_done; + /* keep going even for ->async, the code below is TLS 1.3 */ + /* If opportunistic TLS 1.3 ZC failed retry without ZC */ if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION && darg->tail != TLS_RECORD_TYPE_DATA)) { @@ -1638,21 +1617,87 @@ static int tls_rx_one_record(struct sock *sk, struct iov_iter *dest, if (!darg->tail) TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXNOPADVIOL); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSDECRYPTRETRY); - return tls_rx_one_record(sk, dest, darg); + return tls_decrypt_sw(sk, tls_ctx, msg, darg); } -decrypt_done: - if (darg->skb == ctx->recv_pkt) - ctx->recv_pkt = NULL; - pad = tls_padding_length(prot, darg->skb, darg); if (pad < 0) { - consume_skb(darg->skb); + if (darg->skb != tls_strp_msg(ctx)) + consume_skb(darg->skb); return pad; } rxm = strp_msg(darg->skb); rxm->full_len -= pad; + + return 0; +} + +static int +tls_decrypt_device(struct sock *sk, struct msghdr *msg, + struct tls_context *tls_ctx, struct tls_decrypt_arg *darg) +{ + struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); + struct tls_prot_info *prot = &tls_ctx->prot_info; + struct strp_msg *rxm; + int pad, err; + + if (tls_ctx->rx_conf != TLS_HW) + return 0; + + err = tls_device_decrypted(sk, tls_ctx); + if (err <= 0) + return err; + + pad = tls_padding_length(prot, tls_strp_msg(ctx), darg); + if (pad < 0) + return pad; + + darg->async = false; + darg->skb = tls_strp_msg(ctx); + /* ->zc downgrade check, in case TLS 1.3 gets here */ + darg->zc &= !(prot->version == TLS_1_3_VERSION && + tls_msg(darg->skb)->control != TLS_RECORD_TYPE_DATA); + + rxm = strp_msg(darg->skb); + rxm->full_len -= pad; + + if (!darg->zc) { + /* Non-ZC case needs a real skb */ + darg->skb = tls_strp_msg_detach(ctx); + if (!darg->skb) + return -ENOMEM; + } else { + unsigned int off, len; + + /* In ZC case nobody cares about the output skb. + * Just copy the data here. Note the skb is not fully trimmed. + */ + off = rxm->offset + prot->prepend_size; + len = rxm->full_len - prot->overhead_size; + + err = skb_copy_datagram_msg(darg->skb, off, msg, len); + if (err) + return err; + } + return 1; +} + +static int tls_rx_one_record(struct sock *sk, struct msghdr *msg, + struct tls_decrypt_arg *darg) +{ + struct tls_context *tls_ctx = tls_get_ctx(sk); + struct tls_prot_info *prot = &tls_ctx->prot_info; + struct strp_msg *rxm; + int err; + + err = tls_decrypt_device(sk, msg, tls_ctx, darg); + if (!err) + err = tls_decrypt_sw(sk, tls_ctx, msg, darg); + if (err < 0) + return err; + + rxm = strp_msg(darg->skb); rxm->offset += prot->prepend_size; rxm->full_len -= prot->overhead_size; tls_advance_record_sn(sk, prot, &tls_ctx->rx); @@ -1692,9 +1737,7 @@ static int tls_record_content_type(struct msghdr *msg, struct tls_msg *tlm, static void tls_rx_rec_done(struct tls_sw_context_rx *ctx) { - consume_skb(ctx->recv_pkt); - ctx->recv_pkt = NULL; - __strp_unpause(&ctx->strp); + tls_strp_msg_done(&ctx->strp); } /* This function traverses the rx_list in tls receive context to copies the @@ -1781,7 +1824,7 @@ out: return copied ? : err; } -static void +static bool tls_read_flush_backlog(struct sock *sk, struct tls_prot_info *prot, size_t len_left, size_t decrypted, ssize_t done, size_t *flushed_at) @@ -1789,14 +1832,14 @@ tls_read_flush_backlog(struct sock *sk, struct tls_prot_info *prot, size_t max_rec; if (len_left <= decrypted) - return; + return false; max_rec = prot->overhead_size - prot->tail_size + TLS_MAX_PAYLOAD_SIZE; if (done - *flushed_at < SZ_128K && tcp_inq(sk) > max_rec) - return; + return false; *flushed_at = done; - sk_flush_backlog(sk); + return sk_flush_backlog(sk); } static long tls_rx_reader_lock(struct sock *sk, struct tls_sw_context_rx *ctx, @@ -1868,13 +1911,13 @@ int tls_sw_recvmsg(struct sock *sk, size_t flushed_at = 0; struct strp_msg *rxm; struct tls_msg *tlm; - struct sk_buff *skb; ssize_t copied = 0; bool async = false; int target, err = 0; long timeo; bool is_kvec = iov_iter_is_kvec(&msg->msg_iter); bool is_peek = flags & MSG_PEEK; + bool released = true; bool bpf_strp_enabled; bool zc_capable; @@ -1907,11 +1950,12 @@ int tls_sw_recvmsg(struct sock *sk, zc_capable = !bpf_strp_enabled && !is_kvec && !is_peek && ctx->zc_capable; decrypted = 0; - while (len && (decrypted + copied < target || ctx->recv_pkt)) { + while (len && (decrypted + copied < target || tls_strp_msg_ready(ctx))) { struct tls_decrypt_arg darg; int to_decrypt, chunk; - err = tls_rx_rec_wait(sk, psock, flags & MSG_DONTWAIT, timeo); + err = tls_rx_rec_wait(sk, psock, flags & MSG_DONTWAIT, released, + timeo); if (err <= 0) { if (psock) { chunk = sk_msg_recvmsg(sk, psock, msg, len, @@ -1927,8 +1971,8 @@ int tls_sw_recvmsg(struct sock *sk, memset(&darg.inargs, 0, sizeof(darg.inargs)); - rxm = strp_msg(ctx->recv_pkt); - tlm = tls_msg(ctx->recv_pkt); + rxm = strp_msg(tls_strp_msg(ctx)); + tlm = tls_msg(tls_strp_msg(ctx)); to_decrypt = rxm->full_len - prot->overhead_size; @@ -1942,16 +1986,12 @@ int tls_sw_recvmsg(struct sock *sk, else darg.async = false; - err = tls_rx_one_record(sk, &msg->msg_iter, &darg); + err = tls_rx_one_record(sk, msg, &darg); if (err < 0) { tls_err_abort(sk, -EBADMSG); goto recv_end; } - skb = darg.skb; - rxm = strp_msg(skb); - tlm = tls_msg(skb); - async |= darg.async; /* If the type of records being processed is not known yet, @@ -1961,24 +2001,30 @@ int tls_sw_recvmsg(struct sock *sk, * is known just after record is dequeued from stream parser. * For tls1.3, we disable async. */ - err = tls_record_content_type(msg, tlm, &control); + err = tls_record_content_type(msg, tls_msg(darg.skb), &control); if (err <= 0) { + DEBUG_NET_WARN_ON_ONCE(darg.zc); tls_rx_rec_done(ctx); put_on_rx_list_err: - __skb_queue_tail(&ctx->rx_list, skb); + __skb_queue_tail(&ctx->rx_list, darg.skb); goto recv_end; } /* periodically flush backlog, and feed strparser */ - tls_read_flush_backlog(sk, prot, len, to_decrypt, - decrypted + copied, &flushed_at); + released = tls_read_flush_backlog(sk, prot, len, to_decrypt, + decrypted + copied, + &flushed_at); /* TLS 1.3 may have updated the length by more than overhead */ + rxm = strp_msg(darg.skb); chunk = rxm->full_len; tls_rx_rec_done(ctx); if (!darg.zc) { bool partially_consumed = chunk > len; + struct sk_buff *skb = darg.skb; + + DEBUG_NET_WARN_ON_ONCE(darg.skb == tls_strp_msg(ctx)); if (async) { /* TLS 1.2-only, to_decrypt must be text len */ @@ -1992,6 +2038,7 @@ put_on_rx_list: } if (bpf_strp_enabled) { + released = true; err = sk_psock_tls_strp_read(psock, skb); if (err != __SK_PASS) { rxm->offset = rxm->offset + rxm->full_len; @@ -2018,13 +2065,13 @@ put_on_rx_list: rxm->full_len -= chunk; goto put_on_rx_list; } + + consume_skb(skb); } decrypted += chunk; len -= chunk; - consume_skb(skb); - /* Return full control message to userspace before trying * to parse another message type */ @@ -2098,7 +2145,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, struct tls_decrypt_arg darg; err = tls_rx_rec_wait(sk, NULL, flags & SPLICE_F_NONBLOCK, - timeo); + true, timeo); if (err <= 0) goto splice_read_end; @@ -2158,23 +2205,21 @@ bool tls_sw_sock_is_readable(struct sock *sk) ingress_empty = list_empty(&psock->ingress_msg); rcu_read_unlock(); - return !ingress_empty || ctx->recv_pkt || + return !ingress_empty || tls_strp_msg_ready(ctx) || !skb_queue_empty(&ctx->rx_list); } -static int tls_read_size(struct strparser *strp, struct sk_buff *skb) +int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb) { struct tls_context *tls_ctx = tls_get_ctx(strp->sk); struct tls_prot_info *prot = &tls_ctx->prot_info; char header[TLS_HEADER_SIZE + MAX_IV_SIZE]; - struct strp_msg *rxm = strp_msg(skb); - struct tls_msg *tlm = tls_msg(skb); size_t cipher_overhead; size_t data_len = 0; int ret; /* Verify that we have a full TLS header, or wait for more data */ - if (rxm->offset + prot->prepend_size > skb->len) + if (strp->stm.offset + prot->prepend_size > skb->len) return 0; /* Sanity-check size of on-stack buffer. */ @@ -2184,11 +2229,11 @@ static int tls_read_size(struct strparser *strp, struct sk_buff *skb) } /* Linearize header to local buffer */ - ret = skb_copy_bits(skb, rxm->offset, header, prot->prepend_size); + ret = skb_copy_bits(skb, strp->stm.offset, header, prot->prepend_size); if (ret < 0) goto read_failure; - tlm->control = header[0]; + strp->mark = header[0]; data_len = ((header[4] & 0xFF) | (header[3] << 8)); @@ -2215,7 +2260,7 @@ static int tls_read_size(struct strparser *strp, struct sk_buff *skb) } tls_device_rx_resync_new_rec(strp->sk, data_len + TLS_HEADER_SIZE, - TCP_SKB_CB(skb)->seq + rxm->offset); + TCP_SKB_CB(skb)->seq + strp->stm.offset); return data_len + TLS_HEADER_SIZE; read_failure: @@ -2224,14 +2269,11 @@ read_failure: return ret; } -static void tls_queue(struct strparser *strp, struct sk_buff *skb) +void tls_rx_msg_ready(struct tls_strparser *strp) { - struct tls_context *tls_ctx = tls_get_ctx(strp->sk); - struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); - - ctx->recv_pkt = skb; - strp_pause(strp); + struct tls_sw_context_rx *ctx; + ctx = container_of(strp, struct tls_sw_context_rx, strp); ctx->saved_data_ready(strp->sk); } @@ -2241,7 +2283,7 @@ static void tls_data_ready(struct sock *sk) struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); struct sk_psock *psock; - strp_data_ready(&ctx->strp); + tls_strp_data_ready(&ctx->strp); psock = sk_psock_get(sk); if (psock) { @@ -2317,13 +2359,11 @@ void tls_sw_release_resources_rx(struct sock *sk) kfree(tls_ctx->rx.iv); if (ctx->aead_recv) { - kfree_skb(ctx->recv_pkt); - ctx->recv_pkt = NULL; __skb_queue_purge(&ctx->rx_list); crypto_free_aead(ctx->aead_recv); - strp_stop(&ctx->strp); + tls_strp_stop(&ctx->strp); /* If tls_sw_strparser_arm() was not called (cleanup paths) - * we still want to strp_stop(), but sk->sk_data_ready was + * we still want to tls_strp_stop(), but sk->sk_data_ready was * never swapped. */ if (ctx->saved_data_ready) { @@ -2338,7 +2378,7 @@ void tls_sw_strparser_done(struct tls_context *tls_ctx) { struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); - strp_done(&ctx->strp); + tls_strp_done(&ctx->strp); } void tls_sw_free_ctx_rx(struct tls_context *tls_ctx) @@ -2411,8 +2451,6 @@ void tls_sw_strparser_arm(struct sock *sk, struct tls_context *tls_ctx) rx_ctx->saved_data_ready = sk->sk_data_ready; sk->sk_data_ready = tls_data_ready; write_unlock_bh(&sk->sk_callback_lock); - - strp_check_rcv(&rx_ctx->strp); } void tls_update_rx_zc_capable(struct tls_context *tls_ctx) @@ -2432,7 +2470,6 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) struct tls_sw_context_rx *sw_ctx_rx = NULL; struct cipher_context *cctx; struct crypto_aead **aead; - struct strp_callbacks cb; u16 nonce_size, tag_size, iv_size, rec_seq_size, salt_size; struct crypto_tfm *tfm; char *iv, *rec_seq, *key, *salt, *cipher_name; @@ -2666,12 +2703,7 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) crypto_info->version != TLS_1_3_VERSION && !!(tfm->__crt_alg->cra_flags & CRYPTO_ALG_ASYNC); - /* Set up strparser */ - memset(&cb, 0, sizeof(cb)); - cb.rcv_msg = tls_queue; - cb.parse_msg = tls_read_size; - - strp_init(&sw_ctx_rx->strp, sk, &cb); + tls_strp_init(&sw_ctx_rx->strp, sk); } goto out; diff --git a/tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh b/tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh index 08a922d8b86a..224ca3695c89 100755 --- a/tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh +++ b/tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh @@ -84,6 +84,13 @@ lc_wait_until_port_count_is() busywait "$timeout" until_lc_port_count_is "$port_count" lc_port_count_get "$lc" } +lc_nested_devlink_dev_get() +{ + local lc=$1 + + devlink lc show $DEVLINK_DEV lc $lc -j | jq -e -r ".[][][].nested_devlink" +} + PROV_UNPROV_TIMEOUT=8000 # ms POST_PROV_ACT_TIMEOUT=2000 # ms PROV_PORTS_INSTANTIATION_TIMEOUT=15000 # ms @@ -191,12 +198,30 @@ ports_check() check_err $? "Unexpected port count linecard $lc (got $port_count, expected $expected_port_count)" } +lc_dev_info_provisioned_check() +{ + local lc=$1 + local nested_devlink_dev=$2 + local fixed_hw_revision + local running_ini_version + + fixed_hw_revision=$(devlink dev info $nested_devlink_dev -j | \ + jq -e -r '.[][].versions.fixed."hw.revision"') + check_err $? "Failed to get linecard $lc fixed.hw.revision" + log_info "Linecard $lc fixed.hw.revision: \"$fixed_hw_revision\"" + running_ini_version=$(devlink dev info $nested_devlink_dev -j | \ + jq -e -r '.[][].versions.running."ini.version"') + check_err $? "Failed to get linecard $lc running.ini.version" + log_info "Linecard $lc running.ini.version: \"$running_ini_version\"" +} + provision_test() { RET=0 local lc local type local state + local nested_devlink_dev lc=$LC_SLOT supported_types_check $lc @@ -207,6 +232,11 @@ provision_test() fi provision_one $lc $LC_16X100G_TYPE ports_check $lc $LC_16X100G_PORT_COUNT + + nested_devlink_dev=$(lc_nested_devlink_dev_get $lc) + check_err $? "Failed to get nested devlink handle of linecard $lc" + lc_dev_info_provisioned_check $lc $nested_devlink_dev + log_test "Provision" } @@ -220,12 +250,32 @@ interface_check() setup_wait } +lc_dev_info_active_check() +{ + local lc=$1 + local nested_devlink_dev=$2 + local fixed_device_fw_psid + local running_device_fw + + fixed_device_fw_psid=$(devlink dev info $nested_devlink_dev -j | \ + jq -e -r ".[][].versions.fixed" | \ + jq -e -r '."fw.psid"') + check_err $? "Failed to get linecard $lc fixed fw PSID" + log_info "Linecard $lc fixed.fw.psid: \"$fixed_device_fw_psid\"" + + running_device_fw=$(devlink dev info $nested_devlink_dev -j | \ + jq -e -r ".[][].versions.running.fw") + check_err $? "Failed to get linecard $lc running.fw.version" + log_info "Linecard $lc running.fw: \"$running_device_fw\"" +} + activation_16x100G_test() { RET=0 local lc local type local state + local nested_devlink_dev lc=$LC_SLOT type=$LC_16X100G_TYPE @@ -238,6 +288,10 @@ activation_16x100G_test() interface_check + nested_devlink_dev=$(lc_nested_devlink_dev_get $lc) + check_err $? "Failed to get nested devlink handle of linecard $lc" + lc_dev_info_active_check $lc $nested_devlink_dev + log_test "Activation 16x100G" } diff --git a/tools/testing/selftests/net/forwarding/vxlan_asymmetric.sh b/tools/testing/selftests/net/forwarding/vxlan_asymmetric.sh index 0727e2012b68..43469c7de118 100755 --- a/tools/testing/selftests/net/forwarding/vxlan_asymmetric.sh +++ b/tools/testing/selftests/net/forwarding/vxlan_asymmetric.sh @@ -525,7 +525,7 @@ arp_suppression() log_test "neigh_suppress: on / neigh exists: yes" - # Delete the neighbour from the the SVI. A single ARP request should be + # Delete the neighbour from the SVI. A single ARP request should be # received by the remote VTEP RET=0 |