Commit 398d1e37 authored by Shannon Nelson's avatar Shannon Nelson Committed by David S. Miller
Browse files

ionic: add FW_STOPPING state



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: c672412f ("ionic: remove lifs on fw reset")
Signed-off-by: default avatarShannon Nelson <snelson@pensando.io>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b8fd0271
Loading
Loading
Loading
Loading
+14 −11
Original line number Diff line number Diff line
@@ -197,21 +197,24 @@ int ionic_heartbeat_check(struct ionic *ionic)
		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))
		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 {
			dev_info(ionic->dev, "FW running %u\n", fw_status);
			if (lif && test_bit(IONIC_LIF_F_FW_RESET, lif->state))

		} 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 @@ int ionic_heartbeat_check(struct ionic *ionic)
		}
	}

	if (!fw_status_ready)
	if (!idev->fw_status_ready)
		return -ENXIO;

	/* wait at least one watchdog period since the last heartbeat */
+1 −0
Original line number Diff line number Diff line
@@ -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");
}

+1 −0
Original line number Diff line number Diff line
@@ -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,
+3 −1
Original line number Diff line number Diff line
@@ -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);