Commit b61a6610 authored by Shahar S Matityahu's avatar Shahar S Matityahu Committed by Luca Coelho

iwlwifi: dbg_ini: rewrite trigger flow and align to FW API changes

Trigger field ignore_default was changed to override_trig.
The first byte of the field indicates the driver to override existing
configuration or keep the previous one
The second byte of the field indicated the driver to replace the regions
of the previous trigger or to append new regions to it.

Change the way the active triggers are maintained to support trigger
override in different apply points.
Do this by making a trigger that updates at runtime by the
triggers that are being used in the different apply points.

In case of an assert, the driver does not reconfigure the triggers
and uses the old configuration which leads to undefined behavior.
Solve this by clearing the triggers in assert recovery flow.
Signed-off-by: default avatarShahar S Matityahu <shahar.s.matityahu@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 22463857
...@@ -204,8 +204,13 @@ struct iwl_fw_ini_region_tlv { ...@@ -204,8 +204,13 @@ struct iwl_fw_ini_region_tlv {
* struct iwl_fw_ini_trigger - (IWL_FW_INI_TLV_TYPE_DUMP_CFG) * struct iwl_fw_ini_trigger - (IWL_FW_INI_TLV_TYPE_DUMP_CFG)
* Region sections define IDs and triggers that use those IDs TLV * Region sections define IDs and triggers that use those IDs TLV
* *
* @trigger_id: enum &iwl_fw_ini_trigger_id * @trigger_id: enum &iwl_fw_ini_tigger_id
* @ignore_default: override FW TLV with binary TLV * @override_trig: determines how apply trigger in case a trigger with the
* same id is already in use. Using the first 2 bytes:
* Byte 0: if 0, override trigger configuration, otherwise use the
* existing configuration.
* Byte 1: if 0, override trigger regions, otherwise append regions to
* existing trigger.
* @dump_delay: delay from trigger fire to dump, in usec * @dump_delay: delay from trigger fire to dump, in usec
* @occurrences: max amount of times to be fired * @occurrences: max amount of times to be fired
* @ignore_consec: ignore consecutive triggers, in usec * @ignore_consec: ignore consecutive triggers, in usec
...@@ -217,7 +222,7 @@ struct iwl_fw_ini_region_tlv { ...@@ -217,7 +222,7 @@ struct iwl_fw_ini_region_tlv {
*/ */
struct iwl_fw_ini_trigger { struct iwl_fw_ini_trigger {
__le32 trigger_id; __le32 trigger_id;
__le32 ignore_default; __le32 override_trig;
__le32 dump_delay; __le32 dump_delay;
__le32 occurrences; __le32 occurrences;
__le32 ignore_consec; __le32 ignore_consec;
......
...@@ -1473,20 +1473,19 @@ _iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt, ...@@ -1473,20 +1473,19 @@ _iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt,
int size, id = le32_to_cpu(fwrt->dump.desc->trig_desc.type); int size, id = le32_to_cpu(fwrt->dump.desc->trig_desc.type);
struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_data *dump_data;
struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_file *dump_file;
struct iwl_fw_ini_trigger *trigger, *ext; struct iwl_fw_ini_trigger *trigger;
if (id == FW_DBG_TRIGGER_FW_ASSERT) if (id == FW_DBG_TRIGGER_FW_ASSERT)
id = IWL_FW_TRIGGER_ID_FW_ASSERT; id = IWL_FW_TRIGGER_ID_FW_ASSERT;
if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_trigs))) if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_trigs)) ||
!fwrt->dump.active_trigs[id].active)
return NULL; return NULL;
trigger = fwrt->dump.active_trigs[id].conf; trigger = fwrt->dump.active_trigs[id].trig;
ext = fwrt->dump.active_trigs[id].conf_ext;
size = sizeof(*dump_file); size = sizeof(*dump_file);
size += iwl_fw_ini_get_trigger_len(fwrt, trigger); size += iwl_fw_ini_get_trigger_len(fwrt, trigger);
size += iwl_fw_ini_get_trigger_len(fwrt, ext);
if (!size) if (!size)
return NULL; return NULL;
...@@ -1501,10 +1500,7 @@ _iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt, ...@@ -1501,10 +1500,7 @@ _iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt,
dump_data = (void *)dump_file->data; dump_data = (void *)dump_file->data;
dump_file->file_len = cpu_to_le32(size); dump_file->file_len = cpu_to_le32(size);
if (trigger) iwl_fw_ini_dump_trigger(fwrt, trigger, &dump_data);
iwl_fw_ini_dump_trigger(fwrt, trigger, &dump_data);
if (ext)
iwl_fw_ini_dump_trigger(fwrt, ext, &dump_data);
return dump_file; return dump_file;
} }
...@@ -1696,6 +1692,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, ...@@ -1696,6 +1692,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
u32 id, const char *str, size_t len) u32 id, const char *str, size_t len)
{ {
struct iwl_fw_dump_desc *desc; struct iwl_fw_dump_desc *desc;
struct iwl_fw_ini_active_triggers *active;
u32 occur, delay; u32 occur, delay;
if (!fwrt->trans->ini_valid) if (!fwrt->trans->ini_valid)
...@@ -1704,15 +1701,17 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, ...@@ -1704,15 +1701,17 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
if (id == FW_DBG_TRIGGER_USER) if (id == FW_DBG_TRIGGER_USER)
id = IWL_FW_TRIGGER_ID_USER_TRIGGER; id = IWL_FW_TRIGGER_ID_USER_TRIGGER;
if (WARN_ON(!fwrt->dump.active_trigs[id].active)) active = &fwrt->dump.active_trigs[id];
if (WARN_ON(!active->active))
return -EINVAL; return -EINVAL;
delay = le32_to_cpu(fwrt->dump.active_trigs[id].conf->dump_delay); delay = le32_to_cpu(active->trig->dump_delay);
occur = le32_to_cpu(fwrt->dump.active_trigs[id].conf->occurrences); occur = le32_to_cpu(active->trig->occurrences);
if (!occur) if (!occur)
return 0; return 0;
if (le32_to_cpu(fwrt->dump.active_trigs[id].conf->force_restart)) { if (le32_to_cpu(active->trig->force_restart)) {
IWL_WARN(fwrt, "Force restart: trigger %d fired.\n", id); IWL_WARN(fwrt, "Force restart: trigger %d fired.\n", id);
iwl_force_nmi(fwrt->trans); iwl_force_nmi(fwrt->trans);
return 0; return 0;
...@@ -1722,8 +1721,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, ...@@ -1722,8 +1721,7 @@ int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt,
if (!desc) if (!desc)
return -ENOMEM; return -ENOMEM;
occur--; active->trig->occurrences = cpu_to_le32(--occur);
fwrt->dump.active_trigs[id].conf->occurrences = cpu_to_le32(occur);
desc->len = len; desc->len = len;
desc->trig_desc.type = cpu_to_le32(id); desc->trig_desc.type = cpu_to_le32(id);
...@@ -2022,6 +2020,26 @@ static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt, ...@@ -2022,6 +2020,26 @@ static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt,
} }
} }
static int iwl_fw_dbg_trig_realloc(struct iwl_fw_runtime *fwrt,
struct iwl_fw_ini_active_triggers *active,
u32 id, int size)
{
void *ptr;
if (size <= active->size)
return 0;
ptr = krealloc(active->trig, size, GFP_KERNEL);
if (!ptr) {
IWL_ERR(fwrt, "Failed to allocate memory for trigger %d\n", id);
return -ENOMEM;
}
active->trig = ptr;
active->size = size;
return 0;
}
static void iwl_fw_dbg_update_triggers(struct iwl_fw_runtime *fwrt, static void iwl_fw_dbg_update_triggers(struct iwl_fw_runtime *fwrt,
struct iwl_fw_ini_trigger_tlv *tlv, struct iwl_fw_ini_trigger_tlv *tlv,
bool ext, bool ext,
...@@ -2034,43 +2052,63 @@ static void iwl_fw_dbg_update_triggers(struct iwl_fw_runtime *fwrt, ...@@ -2034,43 +2052,63 @@ static void iwl_fw_dbg_update_triggers(struct iwl_fw_runtime *fwrt,
struct iwl_fw_ini_trigger *trig = iter; struct iwl_fw_ini_trigger *trig = iter;
struct iwl_fw_ini_active_triggers *active; struct iwl_fw_ini_active_triggers *active;
int id = le32_to_cpu(trig->trigger_id); int id = le32_to_cpu(trig->trigger_id);
u32 num; u32 trig_regs_size = le32_to_cpu(trig->num_regions) *
sizeof(__le32);
if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_trigs))) if (WARN_ON(id >= ARRAY_SIZE(fwrt->dump.active_trigs)))
break; break;
active = &fwrt->dump.active_trigs[id]; active = &fwrt->dump.active_trigs[id];
if (active->apply_point != apply_point) { if (!active->active) {
active->conf = NULL; size_t trig_size = sizeof(*trig) + trig_regs_size;
active->conf_ext = NULL;
} if (iwl_fw_dbg_trig_realloc(fwrt, active, id,
trig_size))
goto next;
num = le32_to_cpu(trig->num_regions); memcpy(active->trig, trig, trig_size);
if (ext && active->apply_point == apply_point) { } else {
num += le32_to_cpu(active->conf->num_regions); u32 conf_override =
if (trig->ignore_default) { !(le32_to_cpu(trig->override_trig) & 0xff);
active->conf_ext = active->conf; u32 region_override =
active->conf = trig; !(le32_to_cpu(trig->override_trig) & 0xff00);
u32 offset = 0;
u32 active_regs =
le32_to_cpu(active->trig->num_regions);
u32 new_regs = le32_to_cpu(trig->num_regions);
int mem_to_add = trig_regs_size;
if (region_override) {
mem_to_add -= active_regs * sizeof(__le32);
} else { } else {
active->conf_ext = trig; offset += active_regs;
new_regs += active_regs;
} }
} else {
active->conf = trig; if (iwl_fw_dbg_trig_realloc(fwrt, active, id,
active->size + mem_to_add))
goto next;
if (conf_override)
memcpy(active->trig, trig, sizeof(*trig));
memcpy(active->trig->data + offset, trig->data,
trig_regs_size);
active->trig->num_regions = cpu_to_le32(new_regs);
} }
/* Since zero means infinity - just set to -1 */ /* Since zero means infinity - just set to -1 */
if (!le32_to_cpu(trig->occurrences)) if (!le32_to_cpu(active->trig->occurrences))
trig->occurrences = cpu_to_le32(-1); active->trig->occurrences = cpu_to_le32(-1);
if (!le32_to_cpu(trig->ignore_consec)) if (!le32_to_cpu(active->trig->ignore_consec))
trig->ignore_consec = cpu_to_le32(-1); active->trig->ignore_consec = cpu_to_le32(-1);
iter += sizeof(*trig) + active->active = true;
le32_to_cpu(trig->num_regions) * sizeof(__le32); next:
iter += sizeof(*trig) + trig_regs_size;
active->active = num;
active->apply_point = apply_point;
} }
} }
...@@ -2129,6 +2167,10 @@ void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, ...@@ -2129,6 +2167,10 @@ void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
if (apply_point == IWL_FW_INI_APPLY_EARLY) { if (apply_point == IWL_FW_INI_APPLY_EARLY) {
for (i = 0; i < IWL_FW_INI_MAX_REGION_ID; i++) for (i = 0; i < IWL_FW_INI_MAX_REGION_ID; i++)
fwrt->dump.active_regs[i] = NULL; fwrt->dump.active_regs[i] = NULL;
/* disable the triggers, used in recovery flow */
for (i = 0; i < IWL_FW_TRIGGER_ID_NUM; i++)
fwrt->dump.active_trigs[i].active = false;
} }
_iwl_fw_dbg_apply_point(fwrt, data, apply_point, false); _iwl_fw_dbg_apply_point(fwrt, data, apply_point, false);
......
...@@ -225,16 +225,17 @@ static inline bool ...@@ -225,16 +225,17 @@ static inline bool
_iwl_fw_ini_trigger_on(struct iwl_fw_runtime *fwrt, _iwl_fw_ini_trigger_on(struct iwl_fw_runtime *fwrt,
const enum iwl_fw_dbg_trigger id) const enum iwl_fw_dbg_trigger id)
{ {
struct iwl_fw_ini_active_triggers *trig = &fwrt->dump.active_trigs[id]; struct iwl_fw_ini_active_triggers *active =
&fwrt->dump.active_trigs[id];
u32 ms; u32 ms;
if (!fwrt->trans->ini_valid) if (!fwrt->trans->ini_valid)
return false; return false;
if (!trig || !trig->active) if (!active->active)
return false; return false;
ms = le32_to_cpu(trig->conf->ignore_consec); ms = le32_to_cpu(active->trig->ignore_consec);
if (ms) if (ms)
ms /= USEC_PER_MSEC; ms /= USEC_PER_MSEC;
......
...@@ -234,15 +234,13 @@ struct iwl_fw_ini_allocation_data { ...@@ -234,15 +234,13 @@ struct iwl_fw_ini_allocation_data {
/** /**
* struct iwl_fw_ini_active_triggers * struct iwl_fw_ini_active_triggers
* @active: is this trigger active * @active: is this trigger active
* @apply_point: last apply point that updated this trigger * @size: allocated memory size of the trigger
* @conf: active trigger * @trig: trigger
* @conf_ext: second trigger, contains extra regions to dump
*/ */
struct iwl_fw_ini_active_triggers { struct iwl_fw_ini_active_triggers {
bool active; bool active;
enum iwl_fw_ini_apply_point apply_point; size_t size;
struct iwl_fw_ini_trigger *conf; struct iwl_fw_ini_trigger *trig;
struct iwl_fw_ini_trigger *conf_ext;
}; };
/** /**
......
...@@ -160,8 +160,20 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, ...@@ -160,8 +160,20 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans,
static inline void iwl_fw_runtime_free(struct iwl_fw_runtime *fwrt) static inline void iwl_fw_runtime_free(struct iwl_fw_runtime *fwrt)
{ {
int i;
kfree(fwrt->dump.d3_debug_data); kfree(fwrt->dump.d3_debug_data);
fwrt->dump.d3_debug_data = NULL; fwrt->dump.d3_debug_data = NULL;
for (i = 0; i < IWL_FW_TRIGGER_ID_NUM; i++) {
struct iwl_fw_ini_active_triggers *active =
&fwrt->dump.active_trigs[i];
active->active = false;
active->size = 0;
kfree(active->trig);
active->trig = NULL;
}
} }
void iwl_fw_runtime_suspend(struct iwl_fw_runtime *fwrt); void iwl_fw_runtime_suspend(struct iwl_fw_runtime *fwrt);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment