Commit b8221b0f authored by Johannes Berg's avatar Johannes Berg Committed by Luca Coelho
Browse files

iwlwifi: prepare for synchronous error dumps



In some cases it may be necessary to synchronously create
a firmware error report, add the necessary infrastructure
for this.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20210802170640.481b6642f0fc.I7c9c958408a285e3d19aceed2a5a3341cfc08382@changeid


Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 6ac57200
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
 *
 * Copyright(c) 2003 - 2014, 2018 - 2020  Intel Corporation. All rights reserved.
 * Copyright(c) 2003 - 2014, 2018 - 2021  Intel Corporation. All rights reserved.
 * Copyright(c) 2015 Intel Deutschland GmbH
 *
 * Portions of this file are derived from the ipw3945 project, as well
@@ -1950,7 +1950,7 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
	}
}

static void iwl_nic_error(struct iwl_op_mode *op_mode)
static void iwl_nic_error(struct iwl_op_mode *op_mode, bool sync)
{
	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);

+52 −45
Original line number Diff line number Diff line
@@ -2530,51 +2530,6 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
}
IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect);

int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,
			   struct iwl_fwrt_dump_data *dump_data)
{
	struct iwl_fw_ini_trigger_tlv *trig = dump_data->trig;
	enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trig->time_point);
	u32 occur, delay;
	unsigned long idx;

	if (!iwl_fw_ini_trigger_on(fwrt, trig)) {
		IWL_WARN(fwrt, "WRT: Trigger %d is not active, aborting dump\n",
			 tp_id);
		return -EINVAL;
	}

	delay = le32_to_cpu(trig->dump_delay);
	occur = le32_to_cpu(trig->occurrences);
	if (!occur)
		return 0;

	trig->occurrences = cpu_to_le32(--occur);

	/* Check there is an available worker.
	 * ffz return value is undefined if no zero exists,
	 * so check against ~0UL first.
	 */
	if (fwrt->dump.active_wks == ~0UL)
		return -EBUSY;

	idx = ffz(fwrt->dump.active_wks);

	if (idx >= IWL_FW_RUNTIME_DUMP_WK_NUM ||
	    test_and_set_bit(fwrt->dump.wks[idx].idx, &fwrt->dump.active_wks))
		return -EBUSY;

	fwrt->dump.wks[idx].dump_data = *dump_data;

	IWL_WARN(fwrt,
		 "WRT: Collecting data: ini trigger %d fired (delay=%dms).\n",
		 tp_id, (u32)(delay / USEC_PER_MSEC));

	schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay));

	return 0;
}

int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt,
			    struct iwl_fw_dbg_trigger_tlv *trigger,
			    const char *fmt, ...)
@@ -2703,6 +2658,58 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)
	clear_bit(wk_idx, &fwrt->dump.active_wks);
}

int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,
			   struct iwl_fwrt_dump_data *dump_data,
			   bool sync)
{
	struct iwl_fw_ini_trigger_tlv *trig = dump_data->trig;
	enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trig->time_point);
	u32 occur, delay;
	unsigned long idx;

	if (!iwl_fw_ini_trigger_on(fwrt, trig)) {
		IWL_WARN(fwrt, "WRT: Trigger %d is not active, aborting dump\n",
			 tp_id);
		return -EINVAL;
	}

	delay = le32_to_cpu(trig->dump_delay);
	occur = le32_to_cpu(trig->occurrences);
	if (!occur)
		return 0;

	trig->occurrences = cpu_to_le32(--occur);

	/* Check there is an available worker.
	 * ffz return value is undefined if no zero exists,
	 * so check against ~0UL first.
	 */
	if (fwrt->dump.active_wks == ~0UL)
		return -EBUSY;

	idx = ffz(fwrt->dump.active_wks);

	if (idx >= IWL_FW_RUNTIME_DUMP_WK_NUM ||
	    test_and_set_bit(fwrt->dump.wks[idx].idx, &fwrt->dump.active_wks))
		return -EBUSY;

	fwrt->dump.wks[idx].dump_data = *dump_data;

	if (sync)
		delay = 0;

	IWL_WARN(fwrt,
		 "WRT: Collecting data: ini trigger %d fired (delay=%dms).\n",
		 tp_id, (u32)(delay / USEC_PER_MSEC));

	schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay));

	if (sync)
		iwl_fw_dbg_collect_sync(fwrt, idx);

	return 0;
}

void iwl_fw_error_dump_wk(struct work_struct *work)
{
	struct iwl_fwrt_wk_data *wks =
+4 −3
Original line number Diff line number Diff line
@@ -46,7 +46,8 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt,
			     enum iwl_fw_dbg_trigger trig_type);
int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,
			   struct iwl_fwrt_dump_data *dump_data);
			   struct iwl_fwrt_dump_data *dump_data,
			   bool sync);
int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
		       enum iwl_fw_dbg_trigger trig, const char *str,
		       size_t len, struct iwl_fw_dbg_trigger_tlv *trigger);
@@ -284,7 +285,7 @@ static inline void iwl_fw_umac_set_alive_err_table(struct iwl_trans *trans,
		trans->dbg.umac_error_event_table = umac_error_event_table;
}

static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt)
static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt, bool sync)
{
	enum iwl_fw_ini_time_point tp_id;

@@ -300,7 +301,7 @@ static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt)
		tp_id = IWL_FW_INI_TIME_POINT_FW_ASSERT;
	}

	iwl_dbg_tlv_time_point(fwrt, tp_id, NULL);
	_iwl_dbg_tlv_time_point(fwrt, tp_id, NULL, sync);
}

void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt);
+13 −12
Original line number Diff line number Diff line
@@ -683,7 +683,7 @@ static void iwl_dbg_tlv_periodic_trig_handler(struct timer_list *t)
	};
	int ret;

	ret = iwl_fw_dbg_ini_collect(timer_node->fwrt, &dump_data);
	ret = iwl_fw_dbg_ini_collect(timer_node->fwrt, &dump_data, false);
	if (!ret || ret == -EBUSY) {
		u32 occur = le32_to_cpu(dump_data.trig->occurrences);
		u32 collect_interval = le32_to_cpu(dump_data.trig->data[0]);
@@ -927,7 +927,7 @@ static bool iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime *fwrt,
}

static int
iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt,
iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,
		       struct list_head *active_trig_list,
		       union iwl_dbg_tlv_tp_data *tp_data,
		       bool (*data_check)(struct iwl_fw_runtime *fwrt,
@@ -946,7 +946,7 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt,
		int ret, i;

		if (!num_data) {
			ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data);
			ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);
			if (ret)
				return ret;
		}
@@ -955,7 +955,7 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt,
			if (!data_check ||
			    data_check(fwrt, &dump_data, tp_data,
				       le32_to_cpu(dump_data.trig->data[i]))) {
				ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data);
				ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);
				if (ret)
					return ret;

@@ -1043,9 +1043,10 @@ static void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime *fwrt)
	}
}

void iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
			     enum iwl_fw_ini_time_point tp_id,
			    union iwl_dbg_tlv_tp_data *tp_data)
			     union iwl_dbg_tlv_tp_data *tp_data,
			     bool sync)
{
	struct list_head *hcmd_list, *trig_list;

@@ -1060,12 +1061,12 @@ void iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
	switch (tp_id) {
	case IWL_FW_INI_TIME_POINT_EARLY:
		iwl_dbg_tlv_init_cfg(fwrt);
		iwl_dbg_tlv_tp_trigger(fwrt, trig_list, tp_data, NULL);
		iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
		break;
	case IWL_FW_INI_TIME_POINT_AFTER_ALIVE:
		iwl_dbg_tlv_apply_buffers(fwrt);
		iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
		iwl_dbg_tlv_tp_trigger(fwrt, trig_list, tp_data, NULL);
		iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
		break;
	case IWL_FW_INI_TIME_POINT_PERIODIC:
		iwl_dbg_tlv_set_periodic_trigs(fwrt);
@@ -1075,13 +1076,13 @@ void iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
	case IWL_FW_INI_TIME_POINT_MISSED_BEACONS:
	case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION:
		iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
		iwl_dbg_tlv_tp_trigger(fwrt, trig_list, tp_data,
		iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data,
				       iwl_dbg_tlv_check_fw_pkt);
		break;
	default:
		iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
		iwl_dbg_tlv_tp_trigger(fwrt, trig_list, tp_data, NULL);
		iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
		break;
	}
}
IWL_EXPORT_SYMBOL(iwl_dbg_tlv_time_point);
IWL_EXPORT_SYMBOL(_iwl_dbg_tlv_time_point);
+20 −4
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
 * Copyright (C) 2018-2020 Intel Corporation
 * Copyright (C) 2018-2021 Intel Corporation
 */
#ifndef __iwl_dbg_tlv_h__
#define __iwl_dbg_tlv_h__
@@ -48,9 +48,25 @@ void iwl_dbg_tlv_free(struct iwl_trans *trans);
void iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv,
		       bool ext);
void iwl_dbg_tlv_init(struct iwl_trans *trans);
void iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
			     enum iwl_fw_ini_time_point tp_id,
			    union iwl_dbg_tlv_tp_data *tp_data);
			     union iwl_dbg_tlv_tp_data *tp_data,
			     bool sync);

static inline void iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
					  enum iwl_fw_ini_time_point tp_id,
					  union iwl_dbg_tlv_tp_data *tp_data)
{
	_iwl_dbg_tlv_time_point(fwrt, tp_id, tp_data, false);
}

static inline void iwl_dbg_tlv_time_point_sync(struct iwl_fw_runtime *fwrt,
					       enum iwl_fw_ini_time_point tp_id,
					       union iwl_dbg_tlv_tp_data *tp_data)
{
	_iwl_dbg_tlv_time_point(fwrt, tp_id, tp_data, true);
}

void iwl_dbg_tlv_del_timers(struct iwl_trans *trans);

#endif /* __iwl_dbg_tlv_h__*/
Loading