iwlwifi: Fix pre operational dumping flows
There are several dumping flows in the driver in case of a fail prior to operational. In some cases we get 2 dumps while in others we get none. Fix this by uniting the different flows. Add a different dump type to driver triggered dumps in case we want a dump but did not got assert, and make all dumping go through iwl_fw_dbg_collect_desc to avoid multiple dumps. Signed-off-by: Shahar S Matityahu <shahar.s.matityahu@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
This commit is contained in:

committed by
Luca Coelho

parent
6032c06272
commit
700b3799b3
@@ -1365,44 +1365,6 @@ const struct iwl_fw_dump_desc iwl_dump_desc_assert = {
|
|||||||
};
|
};
|
||||||
IWL_EXPORT_SYMBOL(iwl_dump_desc_assert);
|
IWL_EXPORT_SYMBOL(iwl_dump_desc_assert);
|
||||||
|
|
||||||
void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt)
|
|
||||||
{
|
|
||||||
IWL_INFO(fwrt, "error dump due to fw assert\n");
|
|
||||||
fwrt->dump.desc = &iwl_dump_desc_assert;
|
|
||||||
iwl_fw_error_dump(fwrt);
|
|
||||||
}
|
|
||||||
IWL_EXPORT_SYMBOL(iwl_fw_assert_error_dump);
|
|
||||||
|
|
||||||
void iwl_fw_alive_timeout_dump(struct iwl_fw_runtime *fwrt)
|
|
||||||
{
|
|
||||||
struct iwl_fw_dump_desc *iwl_dump_desc_alive_timeout;
|
|
||||||
|
|
||||||
if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status))
|
|
||||||
return;
|
|
||||||
|
|
||||||
iwl_dump_desc_alive_timeout =
|
|
||||||
kmalloc(sizeof(*iwl_dump_desc_alive_timeout), GFP_KERNEL);
|
|
||||||
if (!iwl_dump_desc_alive_timeout)
|
|
||||||
return;
|
|
||||||
|
|
||||||
iwl_dump_desc_alive_timeout->trig_desc.type =
|
|
||||||
cpu_to_le32(FW_DBG_TRIGGER_ALIVE_TIMEOUT);
|
|
||||||
iwl_dump_desc_alive_timeout->len = 0;
|
|
||||||
|
|
||||||
if (WARN_ON(fwrt->dump.desc))
|
|
||||||
iwl_fw_free_dump_desc(fwrt);
|
|
||||||
|
|
||||||
IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n",
|
|
||||||
FW_DBG_TRIGGER_ALIVE_TIMEOUT);
|
|
||||||
|
|
||||||
/* set STATUS_FW_ERROR to collect all memory regions. */
|
|
||||||
set_bit(STATUS_FW_ERROR, &fwrt->trans->status);
|
|
||||||
|
|
||||||
fwrt->dump.desc = iwl_dump_desc_alive_timeout;
|
|
||||||
iwl_fw_error_dump(fwrt);
|
|
||||||
}
|
|
||||||
IWL_EXPORT_SYMBOL(iwl_fw_alive_timeout_dump);
|
|
||||||
|
|
||||||
int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
|
int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
|
||||||
const struct iwl_fw_dump_desc *desc,
|
const struct iwl_fw_dump_desc *desc,
|
||||||
bool monitor_only,
|
bool monitor_only,
|
||||||
@@ -1442,6 +1404,33 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
|
|||||||
}
|
}
|
||||||
IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_desc);
|
IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_desc);
|
||||||
|
|
||||||
|
int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt,
|
||||||
|
enum iwl_fw_dbg_trigger trig_type)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct iwl_fw_dump_desc *iwl_dump_error_desc =
|
||||||
|
kmalloc(sizeof(*iwl_dump_error_desc), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (!iwl_dump_error_desc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
iwl_dump_error_desc->trig_desc.type = cpu_to_le32(trig_type);
|
||||||
|
iwl_dump_error_desc->len = 0;
|
||||||
|
|
||||||
|
ret = iwl_fw_dbg_collect_desc(fwrt, iwl_dump_error_desc, false, 0);
|
||||||
|
if (ret) {
|
||||||
|
kfree(iwl_dump_error_desc);
|
||||||
|
} else {
|
||||||
|
set_bit(STATUS_FW_WAIT_DUMP, &fwrt->trans->status);
|
||||||
|
|
||||||
|
/* trigger nmi to halt the fw */
|
||||||
|
iwl_force_nmi(fwrt->trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
IWL_EXPORT_SYMBOL(iwl_fw_dbg_error_collect);
|
||||||
|
|
||||||
int _iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
|
int _iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
|
||||||
enum iwl_fw_dbg_trigger trig,
|
enum iwl_fw_dbg_trigger trig,
|
||||||
const char *str, size_t len,
|
const char *str, size_t len,
|
||||||
@@ -1893,3 +1882,27 @@ void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
|
|||||||
_iwl_fw_dbg_apply_point(fwrt, data, apply_point, true);
|
_iwl_fw_dbg_apply_point(fwrt, data, apply_point, true);
|
||||||
}
|
}
|
||||||
IWL_EXPORT_SYMBOL(iwl_fw_dbg_apply_point);
|
IWL_EXPORT_SYMBOL(iwl_fw_dbg_apply_point);
|
||||||
|
|
||||||
|
void iwl_fwrt_stop_device(struct iwl_fw_runtime *fwrt)
|
||||||
|
{
|
||||||
|
/* if the wait event timeout elapses instead of wake up then
|
||||||
|
* the driver did not receive NMI interrupt and can not assume the FW
|
||||||
|
* is halted
|
||||||
|
*/
|
||||||
|
int ret = wait_event_timeout(fwrt->trans->fw_halt_waitq,
|
||||||
|
!test_bit(STATUS_FW_WAIT_DUMP,
|
||||||
|
&fwrt->trans->status),
|
||||||
|
msecs_to_jiffies(2000));
|
||||||
|
if (!ret) {
|
||||||
|
/* failed to receive NMI interrupt, assuming the FW is stuck */
|
||||||
|
set_bit(STATUS_FW_ERROR, &fwrt->trans->status);
|
||||||
|
|
||||||
|
clear_bit(STATUS_FW_WAIT_DUMP, &fwrt->trans->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assuming the op mode mutex is held at this point */
|
||||||
|
iwl_fw_dbg_collect_sync(fwrt);
|
||||||
|
|
||||||
|
iwl_trans_stop_device(fwrt->trans);
|
||||||
|
}
|
||||||
|
IWL_EXPORT_SYMBOL(iwl_fwrt_stop_device);
|
||||||
|
@@ -112,6 +112,8 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt);
|
|||||||
int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
|
int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
|
||||||
const struct iwl_fw_dump_desc *desc,
|
const struct iwl_fw_dump_desc *desc,
|
||||||
bool monitor_only, unsigned int delay);
|
bool monitor_only, unsigned int delay);
|
||||||
|
int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt,
|
||||||
|
enum iwl_fw_dbg_trigger trig_type);
|
||||||
int _iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
|
int _iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
|
||||||
enum iwl_fw_dbg_trigger trig,
|
enum iwl_fw_dbg_trigger trig,
|
||||||
const char *str, size_t len,
|
const char *str, size_t len,
|
||||||
@@ -434,10 +436,9 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {}
|
|||||||
|
|
||||||
#endif /* CONFIG_IWLWIFI_DEBUGFS */
|
#endif /* CONFIG_IWLWIFI_DEBUGFS */
|
||||||
|
|
||||||
void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt);
|
|
||||||
void iwl_fw_alive_timeout_dump(struct iwl_fw_runtime *fwrt);
|
|
||||||
void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt);
|
void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt);
|
||||||
void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
|
void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
|
||||||
enum iwl_fw_ini_apply_point apply_point);
|
enum iwl_fw_ini_apply_point apply_point);
|
||||||
|
|
||||||
|
void iwl_fwrt_stop_device(struct iwl_fw_runtime *fwrt);
|
||||||
#endif /* __iwl_fw_dbg_h__ */
|
#endif /* __iwl_fw_dbg_h__ */
|
||||||
|
@@ -356,6 +356,8 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
|
|||||||
* @FW_DBG_TRIGGER_TX_STATUS: trigger log collection upon tx status when
|
* @FW_DBG_TRIGGER_TX_STATUS: trigger log collection upon tx status when
|
||||||
* the firmware sends a tx reply.
|
* the firmware sends a tx reply.
|
||||||
* @FW_DBG_TRIGGER_ALIVE_TIMEOUT: trigger log collection if alive flow timeouts
|
* @FW_DBG_TRIGGER_ALIVE_TIMEOUT: trigger log collection if alive flow timeouts
|
||||||
|
* @FW_DBG_TRIGGER_DRIVER: trigger log collection upon a flow failure
|
||||||
|
* in the driver.
|
||||||
*/
|
*/
|
||||||
enum iwl_fw_dbg_trigger {
|
enum iwl_fw_dbg_trigger {
|
||||||
FW_DBG_TRIGGER_INVALID = 0,
|
FW_DBG_TRIGGER_INVALID = 0,
|
||||||
@@ -374,6 +376,7 @@ enum iwl_fw_dbg_trigger {
|
|||||||
FW_DBG_TRIGGER_TDLS,
|
FW_DBG_TRIGGER_TDLS,
|
||||||
FW_DBG_TRIGGER_TX_STATUS,
|
FW_DBG_TRIGGER_TX_STATUS,
|
||||||
FW_DBG_TRIGGER_ALIVE_TIMEOUT,
|
FW_DBG_TRIGGER_ALIVE_TIMEOUT,
|
||||||
|
FW_DBG_TRIGGER_DRIVER,
|
||||||
|
|
||||||
/* must be last */
|
/* must be last */
|
||||||
FW_DBG_TRIGGER_MAX,
|
FW_DBG_TRIGGER_MAX,
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
* GPL LICENSE SUMMARY
|
* GPL LICENSE SUMMARY
|
||||||
*
|
*
|
||||||
* Copyright(c) 2017 Intel Deutschland GmbH
|
* Copyright(c) 2017 Intel Deutschland GmbH
|
||||||
|
* Copyright(c) 2019 Intel Corporation
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of version 2 of the GNU General Public License as
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
* BSD LICENSE
|
* BSD LICENSE
|
||||||
*
|
*
|
||||||
* Copyright(c) 2017 Intel Deutschland GmbH
|
* Copyright(c) 2017 Intel Deutschland GmbH
|
||||||
|
* Copyright(c) 2019 Intel Corporation
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -74,6 +76,7 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans,
|
|||||||
fwrt->ops_ctx = ops_ctx;
|
fwrt->ops_ctx = ops_ctx;
|
||||||
INIT_DELAYED_WORK(&fwrt->dump.wk, iwl_fw_error_dump_wk);
|
INIT_DELAYED_WORK(&fwrt->dump.wk, iwl_fw_error_dump_wk);
|
||||||
iwl_fwrt_dbgfs_register(fwrt, dbgfs_dir);
|
iwl_fwrt_dbgfs_register(fwrt, dbgfs_dir);
|
||||||
|
init_waitqueue_head(&fwrt->trans->fw_halt_waitq);
|
||||||
}
|
}
|
||||||
IWL_EXPORT_SYMBOL(iwl_fw_runtime_init);
|
IWL_EXPORT_SYMBOL(iwl_fw_runtime_init);
|
||||||
|
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
|
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
|
||||||
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
|
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
|
||||||
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
|
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
|
||||||
|
* Copyright(c) 2018 - 2019 Intel Corporation
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of version 2 of the GNU General Public License as
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
|
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
|
||||||
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
|
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
|
||||||
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
|
* Copyright(c) 2016 - 2017 Intel Deutschland GmbH
|
||||||
|
* Copyright(c) 2018 - 2019 Intel Corporation
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -330,6 +332,7 @@ enum iwl_d3_status {
|
|||||||
* are sent
|
* are sent
|
||||||
* @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
|
* @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent
|
||||||
* @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation
|
* @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation
|
||||||
|
* @STATUS_FW_WAIT_DUMP: if set, wait until cleared before collecting dump
|
||||||
*/
|
*/
|
||||||
enum iwl_trans_status {
|
enum iwl_trans_status {
|
||||||
STATUS_SYNC_HCMD_ACTIVE,
|
STATUS_SYNC_HCMD_ACTIVE,
|
||||||
@@ -342,6 +345,7 @@ enum iwl_trans_status {
|
|||||||
STATUS_TRANS_GOING_IDLE,
|
STATUS_TRANS_GOING_IDLE,
|
||||||
STATUS_TRANS_IDLE,
|
STATUS_TRANS_IDLE,
|
||||||
STATUS_TRANS_DEAD,
|
STATUS_TRANS_DEAD,
|
||||||
|
STATUS_FW_WAIT_DUMP,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
@@ -796,6 +800,11 @@ struct iwl_trans {
|
|||||||
bool suspending;
|
bool suspending;
|
||||||
bool dbg_rec_on;
|
bool dbg_rec_on;
|
||||||
|
|
||||||
|
u32 lmac_error_event_table[2];
|
||||||
|
u32 umac_error_event_table;
|
||||||
|
unsigned int error_event_table_tlv_status;
|
||||||
|
wait_queue_head_t fw_halt_waitq;
|
||||||
|
|
||||||
/* pointer to trans specific struct */
|
/* pointer to trans specific struct */
|
||||||
/*Ensure that this pointer will always be aligned to sizeof pointer */
|
/*Ensure that this pointer will always be aligned to sizeof pointer */
|
||||||
char trans_specific[0] __aligned(sizeof(void *));
|
char trans_specific[0] __aligned(sizeof(void *));
|
||||||
@@ -1202,6 +1211,10 @@ static inline void iwl_trans_fw_error(struct iwl_trans *trans)
|
|||||||
/* prevent double restarts due to the same erroneous FW */
|
/* prevent double restarts due to the same erroneous FW */
|
||||||
if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status))
|
if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status))
|
||||||
iwl_op_mode_nic_error(trans->op_mode);
|
iwl_op_mode_nic_error(trans->op_mode);
|
||||||
|
|
||||||
|
if (test_and_clear_bit(STATUS_FW_WAIT_DUMP, &trans->status))
|
||||||
|
wake_up(&trans->fw_halt_waitq);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************
|
/*****************************************************
|
||||||
|
@@ -332,7 +332,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
|
|||||||
struct iwl_trans *trans = mvm->trans;
|
struct iwl_trans *trans = mvm->trans;
|
||||||
|
|
||||||
if (ret == -ETIMEDOUT)
|
if (ret == -ETIMEDOUT)
|
||||||
iwl_fw_alive_timeout_dump(&mvm->fwrt);
|
iwl_fw_dbg_error_collect(&mvm->fwrt,
|
||||||
|
FW_DBG_TRIGGER_ALIVE_TIMEOUT);
|
||||||
|
|
||||||
if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000)
|
if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000)
|
||||||
IWL_ERR(mvm,
|
IWL_ERR(mvm,
|
||||||
@@ -408,7 +409,6 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
|
|||||||
ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
|
ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
|
IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
|
||||||
iwl_fw_assert_error_dump(&mvm->fwrt);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1053,7 +1053,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
|
|||||||
ret = iwl_mvm_load_rt_fw(mvm);
|
ret = iwl_mvm_load_rt_fw(mvm);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
|
IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
|
||||||
iwl_fw_assert_error_dump(&mvm->fwrt);
|
iwl_fw_dbg_error_collect(&mvm->fwrt, FW_DBG_TRIGGER_DRIVER);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2013,15 +2013,12 @@ static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm)
|
|||||||
&mvm->status))
|
&mvm->status))
|
||||||
iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
|
iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
|
||||||
false, 0);
|
false, 0);
|
||||||
/* calling this function without using dump_start/end since at this
|
|
||||||
* point we already hold the op mode mutex
|
|
||||||
*/
|
|
||||||
iwl_fw_dbg_collect_sync(&mvm->fwrt);
|
|
||||||
iwl_fw_cancel_timestamp(&mvm->fwrt);
|
iwl_fw_cancel_timestamp(&mvm->fwrt);
|
||||||
iwl_free_fw_paging(&mvm->fwrt);
|
|
||||||
clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
|
clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
|
||||||
|
iwl_fwrt_stop_device(&mvm->fwrt);
|
||||||
|
iwl_free_fw_paging(&mvm->fwrt);
|
||||||
iwl_fw_dump_conf_clear(&mvm->fwrt);
|
iwl_fw_dump_conf_clear(&mvm->fwrt);
|
||||||
iwl_trans_stop_device(mvm->trans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Re-configure the SCD for a queue that has already been configured */
|
/* Re-configure the SCD for a queue that has already been configured */
|
||||||
|
@@ -817,6 +817,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||||||
mutex_lock(&mvm->mutex);
|
mutex_lock(&mvm->mutex);
|
||||||
iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE);
|
iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE);
|
||||||
err = iwl_run_init_mvm_ucode(mvm, true);
|
err = iwl_run_init_mvm_ucode(mvm, true);
|
||||||
|
if (err)
|
||||||
|
iwl_fw_dbg_error_collect(&mvm->fwrt, FW_DBG_TRIGGER_DRIVER);
|
||||||
if (!iwlmvm_mod_params.init_dbg || !err)
|
if (!iwlmvm_mod_params.init_dbg || !err)
|
||||||
iwl_mvm_stop_device(mvm);
|
iwl_mvm_stop_device(mvm);
|
||||||
iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE);
|
iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE);
|
||||||
|
Reference in New Issue
Block a user