Commit d49437a6 authored by Johannes Berg's avatar Johannes Berg
Browse files

wifi: iwlwifi: back off on continuous errors



When errors occur repeatedly, the driver shouldn't go into a
tight loop trying to reset the device. Implement the backoff
I had already defined IWL_TRANS_RESET_DELAY for, but clearly
forgotten the implementation of.

Fixes: 9a2f13c4 ("wifi: iwlwifi: implement reset escalation")
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250420095642.8816e299efa2.I82cde34e2345a2b33b1f03dbb040f5ad3439a5aa@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent d1ee2c19
Loading
Loading
Loading
Loading
+21 −6
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ struct iwl_trans_dev_restart_data {
	struct list_head list;
	unsigned int restart_count;
	time64_t last_error;
	bool backoff;
	char name[];
};

@@ -125,13 +126,20 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
	if (!data)
		return at_least;

	if (ktime_get_boottime_seconds() - data->last_error >=
	if (!data->backoff &&
	    ktime_get_boottime_seconds() - data->last_error >=
			IWL_TRANS_RESET_OK_TIME)
		data->restart_count = 0;

	index = data->restart_count;
	if (index >= ARRAY_SIZE(escalation_list))
	if (index >= ARRAY_SIZE(escalation_list)) {
		index = ARRAY_SIZE(escalation_list) - 1;
		if (!data->backoff) {
			data->backoff = true;
			return IWL_RESET_MODE_BACKOFF;
		}
		data->backoff = false;
	}

	return max(at_least, escalation_list[index]);
}
@@ -140,7 +148,8 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)

static void iwl_trans_restart_wk(struct work_struct *wk)
{
	struct iwl_trans *trans = container_of(wk, typeof(*trans), restart.wk);
	struct iwl_trans *trans = container_of(wk, typeof(*trans),
					       restart.wk.work);
	struct iwl_trans_reprobe *reprobe;
	enum iwl_reset_mode mode;

@@ -168,6 +177,12 @@ static void iwl_trans_restart_wk(struct work_struct *wk)
		return;

	mode = iwl_trans_determine_restart_mode(trans);
	if (mode == IWL_RESET_MODE_BACKOFF) {
		IWL_ERR(trans, "Too many device errors - delay next reset\n");
		queue_delayed_work(system_unbound_wq, &trans->restart.wk,
				   IWL_TRANS_RESET_DELAY);
		return;
	}

	iwl_trans_inc_restart_count(trans->dev);

@@ -227,7 +242,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
	trans->dev = dev;
	trans->num_rx_queues = 1;

	INIT_WORK(&trans->restart.wk, iwl_trans_restart_wk);
	INIT_DELAYED_WORK(&trans->restart.wk, iwl_trans_restart_wk);

	return trans;
}
@@ -271,7 +286,7 @@ int iwl_trans_init(struct iwl_trans *trans)

void iwl_trans_free(struct iwl_trans *trans)
{
	cancel_work_sync(&trans->restart.wk);
	cancel_delayed_work_sync(&trans->restart.wk);
	kmem_cache_destroy(trans->dev_cmd_pool);
}

@@ -403,7 +418,7 @@ void iwl_trans_op_mode_leave(struct iwl_trans *trans)

	iwl_trans_pcie_op_mode_leave(trans);

	cancel_work_sync(&trans->restart.wk);
	cancel_delayed_work_sync(&trans->restart.wk);

	trans->op_mode = NULL;

+5 −2
Original line number Diff line number Diff line
@@ -961,7 +961,7 @@ struct iwl_trans {
	struct iwl_dma_ptr invalid_tx_cmd;

	struct {
		struct work_struct wk;
		struct delayed_work wk;
		struct iwl_fw_error_dump_mode mode;
		bool during_reset;
	} restart;
@@ -1162,7 +1162,7 @@ static inline void iwl_trans_schedule_reset(struct iwl_trans *trans,
	 */
	trans->restart.during_reset = test_bit(STATUS_IN_SW_RESET,
					       &trans->status);
	queue_work(system_unbound_wq, &trans->restart.wk);
	queue_delayed_work(system_unbound_wq, &trans->restart.wk, 0);
}

static inline void iwl_trans_fw_error(struct iwl_trans *trans,
@@ -1261,6 +1261,9 @@ enum iwl_reset_mode {
	IWL_RESET_MODE_RESCAN,
	IWL_RESET_MODE_FUNC_RESET,
	IWL_RESET_MODE_PROD_RESET,

	/* keep last - special backoff value */
	IWL_RESET_MODE_BACKOFF,
};

void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode);
+2 −1
Original line number Diff line number Diff line
@@ -2351,7 +2351,8 @@ void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode)
	struct iwl_trans_pcie_removal *removal;
	char _msg = 0, *msg = &_msg;

	if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY))
	if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY ||
		    mode == IWL_RESET_MODE_BACKOFF))
		return;

	if (test_bit(STATUS_TRANS_DEAD, &trans->status))