Merge branch '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next...
authorPaolo Abeni <pabeni@redhat.com>
Thu, 28 Jul 2022 09:54:56 +0000 (11:54 +0200)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 28 Jul 2022 09:54:56 +0000 (11:54 +0200)
Tony Nguyen says:

====================
ice: PPPoE offload support

Marcin Szycik says:

Add support for dissecting PPPoE and PPP-specific fields in flow dissector:
PPPoE session id and PPP protocol type. Add support for those fields in
tc-flower and support offloading PPPoE. Finally, add support for hardware
offload of PPPoE packets in switchdev mode in ice driver.

Example filter:
tc filter add dev $PF1 ingress protocol ppp_ses prio 1 flower pppoe_sid \
    1234 ppp_proto ip skip_sw action mirred egress redirect dev $VF1_PR

Changes in iproute2 are required to use the new fields (will be submitted
soon).

ICE COMMS DDP package is required to create a filter in ice.

* '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue:
  ice: Add support for PPPoE hardware offload
  flow_offload: Introduce flow_match_pppoe
  net/sched: flower: Add PPPoE filter
  flow_dissector: Add PPPoE dissectors
====================

Link: https://lore.kernel.org/r/20220726203133.2171332-1-anthony.l.nguyen@intel.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
60 files changed:
Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml
Documentation/networking/devlink/mlxsw.rst
drivers/net/dsa/microchip/ksz8795.c
drivers/net/dsa/microchip/ksz8795_reg.h
drivers/net/dsa/microchip/ksz9477.c
drivers/net/dsa/microchip/ksz9477_reg.h
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/ksz_common.h
drivers/net/dsa/microchip/lan937x.h
drivers/net/dsa/microchip/lan937x_main.c
drivers/net/dsa/microchip/lan937x_reg.h
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
drivers/net/ethernet/mellanox/mlxsw/Kconfig
drivers/net/ethernet/mellanox/mlxsw/Makefile
drivers/net/ethernet/mellanox/mlxsw/core.c
drivers/net/ethernet/mellanox/mlxsw/core.h
drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/s390/net/ism_drv.c
include/linux/atm_tcp.h
include/linux/dsa/tag_qca.h
include/linux/hippidevice.h
include/linux/if_eql.h
include/linux/if_hsr.h
include/linux/if_rmnet.h
include/linux/if_tap.h
include/linux/mdio/mdio-xgene.h
include/linux/nl802154.h
include/linux/phy_fixed.h
include/linux/ppp-comp.h
include/linux/ppp_channel.h
include/linux/ptp_kvm.h
include/linux/ptp_pch.h
include/linux/seq_file_net.h
include/linux/sungem_phy.h
include/linux/usb/usbnet.h
include/net/devlink.h
include/net/llc_s_st.h
include/net/smc.h
include/net/tcp.h
include/net/tls.h
include/uapi/linux/devlink.h
net/core/devlink.c
net/ipv4/tcp.c
net/ipv6/ip6mr.c
net/sched/sch_cbq.c
net/smc/af_smc.c
net/smc/smc_diag.c
net/smc/smc_ism.c
net/smc/smc_ism.h
net/smc/smc_tx.c
net/tls/tls.h
net/tls/tls_device.c
net/tls/tls_main.c
net/tls/tls_strp.c
net/tls/tls_sw.c
tools/testing/selftests/drivers/net/mlxsw/devlink_linecard.sh
tools/testing/selftests/net/forwarding/vxlan_asymmetric.sh

index 5592f58..2286837 100644 (file)
@@ -48,7 +48,7 @@ properties:
       "^led@[01]$":
         type: object
         description: Hellcreek leds
-        $ref: ../../leds/common.yaml#
+        $ref: /schemas/leds/common.yaml#
 
         properties:
           reg:
index cf857cb..4339622 100644 (file)
@@ -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
 =====================
 
index 911aace..c79a512 100644 (file)
 #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)
index a848eb4..77487d6 100644 (file)
 #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
index 6453642..4b14d80 100644 (file)
 #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;
index d0cce4c..ddf99d1 100644 (file)
 #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
index fd12a68..ed7d137 100644 (file)
@@ -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);
                }
index d5dddb7..764ada3 100644 (file)
@@ -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
index 72ba9cb..4e0b1dc 100644 (file)
@@ -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
index c29d175..daedd2b 100644 (file)
@@ -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)
index c187d0a..ba4adad 100644 (file)
 #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
 
 #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
index 37b6495..07e9a4d 100644 (file)
@@ -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);
index e64318c..28b1994 100644 (file)
@@ -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);
index 4683312..a510bf2 100644 (file)
@@ -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.
 
index c2d6d64..3ca9fce 100644 (file)
@@ -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
index 1b61bc8..a48f893 100644 (file)
@@ -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);
index 9d2e8a8..7213e45 100644 (file)
 #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 (file)
index 0000000..49fee03
--- /dev/null
@@ -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);
+}
index 5c9869d..ca59f0b 100644 (file)
@@ -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);
 }
index ddab547..f27bdec 100644 (file)
@@ -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),
index 5f7e28d..d34bb6e 100644 (file)
@@ -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) {
index c8ecf6f..2558439 100644 (file)
@@ -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);
index 4359fb0..50be7cb 100644 (file)
@@ -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
 
index 9dc01f7..07414c2 100644 (file)
 
 #ifdef __KERNEL__
 
+struct neigh_parms;
+struct net_device;
+struct sk_buff;
+
 struct hippi_cb {
        __u32   ifield;
 };
index d75601d..07f9b66 100644 (file)
@@ -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 {
index 408539d..0404f5b 100644 (file)
@@ -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,
index 10e7521..839d1e4 100644 (file)
@@ -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;
index 915a187..553552f 100644 (file)
@@ -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.
index 8af93ad..9e58896 100644 (file)
@@ -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
index b227822..cbe5fd1 100644 (file)
@@ -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"
index 52bc8e4..1acafd8 100644 (file)
@@ -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);
index 9d3ffc8..fb847e4 100644 (file)
@@ -9,7 +9,7 @@
 
 #include <uapi/linux/ppp-comp.h>
 
-
+struct compstat;
 struct module;
 
 /*
index 91f9a92..45e6e42 100644 (file)
@@ -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 {
index f960a71..c2e28de 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef _PTP_KVM_H_
 #define _PTP_KVM_H_
 
+#include <linux/types.h>
+
 struct timespec64;
 struct clocksource;
 
index 5181819..7ba643b 100644 (file)
 #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);
index b97912f..7963839 100644 (file)
@@ -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;
index 3a11fa4..c505f30 100644 (file)
@@ -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 */
index 1b4d72d..b42b723 100644 (file)
 #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 */
index 780744b..5bd3fac 100644 (file)
@@ -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,
index c4359e2..ed5b2fa 100644 (file)
  * 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 */
index 37f829d..c926d33 100644 (file)
@@ -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 */
index f9e7c85..b8620be 100644 (file)
@@ -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);
 
index 181c496..abb050b 100644 (file)
@@ -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;
index b3d40a5..5413216 100644 (file)
@@ -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,
index 98d79fe..ca4c993 100644 (file)
@@ -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);
index ba2bdc8..dc7cc3c 100644 (file)
@@ -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);
index d546fc0..a9ba416 100644 (file)
@@ -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;
-               }
        }
 
        /*
index 599e26f..91a0dc4 100644 (file)
@@ -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)
index 6e70d9c..79c1318 100644 (file)
@@ -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);
index 1fca2f9..80ea7d9 100644 (file)
@@ -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);
index a2084ec..911fe08 100644 (file)
@@ -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;
index 004b22a..d6b2db6 100644 (file)
@@ -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
index 4e83776..64dedff 100644 (file)
@@ -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;
index 3740740..0e840a0 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2016 Tom Herbert <tom@herbertland.com>
  * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
  * Copyright (c) 2016-2017, Dave Watson <davejwatson@fb.com>. 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
index b1fcd61..fc513c1 100644 (file)
@@ -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,
index 9703636..08ddf9d 100644 (file)
@@ -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);
 }
index 9ccab79..b945288 100644 (file)
 // SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2016 Tom Herbert <tom@herbertland.com> */
 
 #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);
+}
index ed5e6f1..0fc24a5 100644 (file)
@@ -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;
index 08a922d..224ca36 100755 (executable)
@@ -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"
 }
 
index 0727e20..43469c7 100755 (executable)
@@ -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