nfp: prevent dropped counter increment during probe
authorZiyang Chen <ziyang.chen@corigine.com>
Wed, 12 Jul 2023 12:35:51 +0000 (14:35 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 14 Jul 2023 03:36:10 +0000 (20:36 -0700)
The dev_rx_discards counter will increment by one when an interface is
toggled up and down. The main reason is that the driver first sends a
`NFP_NET_CFG_CTRL_ENABLE` configuration packet to the NIC to perform port
initialisation when an interface is set up. But there is a race between
physical link up and free list queue initialization which may lead to the
configuration packet being discarded.

To address this problem a new bit NFP_NET_CFG_CTRL_FREELIST_EN is added to
perform free list initialisation on the NIC. The FREELIST_EN should be sent
in advance to initialize free list queue. When a port is set to down,
FREELIST_EN should be sent after CTRL_ENABLE to avoid packet discards.

Signed-off-by: Ziyang Chen <ziyang.chen@corigine.com>
Acked-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: Louis Peens <louis.peens@corigine.com>
Link: https://lore.kernel.org/r/20230712123551.13858-1-louis.peens@corigine.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h

index 6b1fb57..f18c791 100644 (file)
@@ -924,7 +924,7 @@ static void nfp_net_write_mac_addr(struct nfp_net *nn, const u8 *addr)
  */
 static void nfp_net_clear_config_and_disable(struct nfp_net *nn)
 {
-       u32 new_ctrl, update;
+       u32 new_ctrl, new_ctrl_w1, update;
        unsigned int r;
        int err;
 
@@ -937,14 +937,29 @@ static void nfp_net_clear_config_and_disable(struct nfp_net *nn)
        if (nn->cap & NFP_NET_CFG_CTRL_RINGCFG)
                new_ctrl &= ~NFP_NET_CFG_CTRL_RINGCFG;
 
-       nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0);
-       nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0);
+       if (!(nn->cap_w1 & NFP_NET_CFG_CTRL_FREELIST_EN)) {
+               nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0);
+               nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0);
+       }
 
        nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
        err = nfp_net_reconfig(nn, update);
        if (err)
                nn_err(nn, "Could not disable device: %d\n", err);
 
+       if (nn->cap_w1 & NFP_NET_CFG_CTRL_FREELIST_EN) {
+               new_ctrl_w1 = nn->dp.ctrl_w1;
+               new_ctrl_w1 &= ~NFP_NET_CFG_CTRL_FREELIST_EN;
+               nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0);
+               nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0);
+
+               nn_writel(nn, NFP_NET_CFG_CTRL_WORD1, new_ctrl_w1);
+               err = nfp_net_reconfig(nn, update);
+               if (err)
+                       nn_err(nn, "Could not disable FREELIST_EN: %d\n", err);
+               nn->dp.ctrl_w1 = new_ctrl_w1;
+       }
+
        for (r = 0; r < nn->dp.num_rx_rings; r++) {
                nfp_net_rx_ring_reset(&nn->dp.rx_rings[r]);
                if (nfp_net_has_xsk_pool_slow(&nn->dp, nn->dp.rx_rings[r].idx))
@@ -964,11 +979,12 @@ static void nfp_net_clear_config_and_disable(struct nfp_net *nn)
  */
 static int nfp_net_set_config_and_enable(struct nfp_net *nn)
 {
-       u32 bufsz, new_ctrl, update = 0;
+       u32 bufsz, new_ctrl, new_ctrl_w1, update = 0;
        unsigned int r;
        int err;
 
        new_ctrl = nn->dp.ctrl;
+       new_ctrl_w1 = nn->dp.ctrl_w1;
 
        if (nn->dp.ctrl & NFP_NET_CFG_CTRL_RSS_ANY) {
                nfp_net_rss_write_key(nn);
@@ -1001,16 +1017,25 @@ static int nfp_net_set_config_and_enable(struct nfp_net *nn)
        bufsz = nn->dp.fl_bufsz - nn->dp.rx_dma_off - NFP_NET_RX_BUF_NON_DATA;
        nn_writel(nn, NFP_NET_CFG_FLBUFSZ, bufsz);
 
-       /* Enable device */
-       new_ctrl |= NFP_NET_CFG_CTRL_ENABLE;
+       /* Enable device
+        * Step 1: Replace the CTRL_ENABLE by NFP_NET_CFG_CTRL_FREELIST_EN if
+        * FREELIST_EN exits.
+        */
+       if (nn->cap_w1 & NFP_NET_CFG_CTRL_FREELIST_EN)
+               new_ctrl_w1 |= NFP_NET_CFG_CTRL_FREELIST_EN;
+       else
+               new_ctrl |= NFP_NET_CFG_CTRL_ENABLE;
        update |= NFP_NET_CFG_UPDATE_GEN;
        update |= NFP_NET_CFG_UPDATE_MSIX;
        update |= NFP_NET_CFG_UPDATE_RING;
        if (nn->cap & NFP_NET_CFG_CTRL_RINGCFG)
                new_ctrl |= NFP_NET_CFG_CTRL_RINGCFG;
 
+       /* Step 2: Send the configuration and write the freelist.
+        * - The freelist only need to be written once.
+        */
        nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
-       nn_writel(nn, NFP_NET_CFG_CTRL_WORD1, nn->dp.ctrl_w1);
+       nn_writel(nn, NFP_NET_CFG_CTRL_WORD1, new_ctrl_w1);
        err = nfp_net_reconfig(nn, update);
        if (err) {
                nfp_net_clear_config_and_disable(nn);
@@ -1018,10 +1043,25 @@ static int nfp_net_set_config_and_enable(struct nfp_net *nn)
        }
 
        nn->dp.ctrl = new_ctrl;
+       nn->dp.ctrl_w1 = new_ctrl_w1;
 
        for (r = 0; r < nn->dp.num_rx_rings; r++)
                nfp_net_rx_ring_fill_freelist(&nn->dp, &nn->dp.rx_rings[r]);
 
+       /* Step 3: Do the NFP_NET_CFG_CTRL_ENABLE. Send the configuration.
+        */
+       if (nn->cap_w1 & NFP_NET_CFG_CTRL_FREELIST_EN) {
+               new_ctrl |= NFP_NET_CFG_CTRL_ENABLE;
+               nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+
+               err = nfp_net_reconfig(nn, update);
+               if (err) {
+                       nfp_net_clear_config_and_disable(nn);
+                       return err;
+               }
+               nn->dp.ctrl = new_ctrl;
+       }
+
        return 0;
 }
 
index 669b9dc..3e63f6d 100644 (file)
 #define   NFP_NET_CFG_CTRL_PKT_TYPE      (0x1 << 0) /* Pkttype offload */
 #define   NFP_NET_CFG_CTRL_IPSEC         (0x1 << 1) /* IPsec offload */
 #define   NFP_NET_CFG_CTRL_MCAST_FILTER          (0x1 << 2) /* Multicast Filter */
+#define   NFP_NET_CFG_CTRL_FREELIST_EN   (0x1 << 6) /* Freelist enable flag bit */
 
 #define NFP_NET_CFG_CAP_WORD1          0x00a4