ionic: add FW_STOPPING state
authorShannon Nelson <snelson@pensando.io>
Mon, 24 Jan 2022 18:53:01 +0000 (10:53 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 25 Jan 2022 11:15:08 +0000 (11:15 +0000)
Between fw running and fw actually stopped into reset, we need
a fw_stopping concept to catch and block some actions while
we're transitioning to FW_RESET state.  This will help to be
sure the fw_up task is not scheduled until after the fw_down
task has completed.

On some rare occasion timing, it is possible for the fw_up task
to try to run before the fw_down task, then not get run after
the fw_down task has run, leaving the device in a down state.
This is possible if the watchdog goes off in between finding the
down transition and starting the fw_down task, where the later
watchdog sees the FW is back up and schedules a fw_up task.

Fixes: c672412f6172 ("ionic: remove lifs on fw reset")
Signed-off-by: Shannon Nelson <snelson@pensando.io>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/pensando/ionic/ionic_dev.c
drivers/net/ethernet/pensando/ionic/ionic_lif.c
drivers/net/ethernet/pensando/ionic/ionic_lif.h
drivers/net/ethernet/pensando/ionic/ionic_main.c

index 86791e0..8edbd7c 100644 (file)
@@ -197,21 +197,24 @@ do_check_time:
                struct ionic_lif *lif = ionic->lif;
                bool trigger = false;
 
-               idev->fw_status_ready = fw_status_ready;
-
-               if (!fw_status_ready) {
-                       dev_info(ionic->dev, "FW stopped %u\n", fw_status);
-                       if (lif && !test_bit(IONIC_LIF_F_FW_RESET, lif->state))
-                               trigger = true;
-               } else {
-                       dev_info(ionic->dev, "FW running %u\n", fw_status);
-                       if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state))
-                               trigger = true;
+               if (!fw_status_ready && lif &&
+                   !test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
+                   !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
+                       dev_info(ionic->dev, "FW stopped 0x%02x\n", fw_status);
+                       trigger = true;
+
+               } else if (fw_status_ready && lif &&
+                          test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
+                          !test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
+                       dev_info(ionic->dev, "FW running 0x%02x\n", fw_status);
+                       trigger = true;
                }
 
                if (trigger) {
                        struct ionic_deferred_work *work;
 
+                       idev->fw_status_ready = fw_status_ready;
+
                        work = kzalloc(sizeof(*work), GFP_ATOMIC);
                        if (work) {
                                work->type = IONIC_DW_TYPE_LIF_RESET;
@@ -221,7 +224,7 @@ do_check_time:
                }
        }
 
-       if (!fw_status_ready)
+       if (!idev->fw_status_ready)
                return -ENXIO;
 
        /* wait at least one watchdog period since the last heartbeat */
index 2ff7be1..08c8589 100644 (file)
@@ -2835,6 +2835,7 @@ static void ionic_lif_handle_fw_down(struct ionic_lif *lif)
 
        mutex_unlock(&lif->queue_lock);
 
+       clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state);
        dev_info(ionic->dev, "FW Down: LIFs stopped\n");
 }
 
index 9f7ab2f..2db708d 100644 (file)
@@ -135,6 +135,7 @@ enum ionic_lif_state_flags {
        IONIC_LIF_F_LINK_CHECK_REQUESTED,
        IONIC_LIF_F_FILTER_SYNC_NEEDED,
        IONIC_LIF_F_FW_RESET,
+       IONIC_LIF_F_FW_STOPPING,
        IONIC_LIF_F_SPLIT_INTR,
        IONIC_LIF_F_BROKEN,
        IONIC_LIF_F_TX_DIM_INTR,
index a548f2a..449e9ee 100644 (file)
@@ -332,7 +332,9 @@ int ionic_adminq_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx,
                        break;
 
                /* interrupt the wait if FW stopped */
-               if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
+               if ((test_bit(IONIC_LIF_F_FW_RESET, lif->state) &&
+                    !lif->ionic->idev.fw_status_ready) ||
+                   test_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) {
                        if (do_msg)
                                netdev_err(netdev, "%s (%d) interrupted, FW in reset\n",
                                           name, ctx->cmd.cmd.opcode);