iwlwifi: mvm: add the cause of the firmware dump in the dump
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Thu, 29 Jan 2015 12:58:20 +0000 (14:58 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Mon, 2 Mar 2015 06:20:28 +0000 (08:20 +0200)
Now that the firmware dump can be triggered by events in
the code and not only the user or an firmware ASSERT, we
need a way to know why the firmware dump was triggered.
Add a section in the dump file for that.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c

index fabddd8ecd890f699484c17a0ba018727512706e..b733c58a06b07cbb8e58b35185bf12195dab8f13 100644 (file)
@@ -82,6 +82,8 @@
  *     sections like this in a single file.
  * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers
  * @IWL_FW_ERROR_DUMP_MEM: chunk of memory
+ * @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump.
+ *     Structured as &struct iwl_fw_error_dump_trigger_desc.
  */
 enum iwl_fw_error_dump_type {
        /* 0 is deprecated */
@@ -94,6 +96,7 @@ enum iwl_fw_error_dump_type {
        IWL_FW_ERROR_DUMP_TXF = 7,
        IWL_FW_ERROR_DUMP_FH_REGS = 8,
        IWL_FW_ERROR_DUMP_MEM = 9,
+       IWL_FW_ERROR_DUMP_ERROR_INFO = 10,
 
        IWL_FW_ERROR_DUMP_MAX,
 };
@@ -248,4 +251,14 @@ enum iwl_fw_dbg_trigger {
        FW_DBG_TRIGGER_MAX,
 };
 
+/**
+ * struct iwl_fw_error_dump_trigger_desc - describes the trigger condition
+ * @type: %enum iwl_fw_dbg_trigger
+ * @data: raw data about what happened
+ */
+struct iwl_fw_error_dump_trigger_desc {
+       __le32 type;
+       u8 data[];
+};
+
 #endif /* __fw_error_dump_h__ */
index f890d5e4673f2adb766a18ac30b302dc8c0a5ee4..8cbe77dc1dbb991b6cad15ed10ae828590682a20 100644 (file)
@@ -985,7 +985,7 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
        if (ret)
                return ret;
 
-       iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, 0);
+       iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, 0);
 
        iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
 
index 8d684d5da9640839c20e615e4caeec115a6fe9fd..64e9039254b90dff457665f6fc4f4874be90f946 100644 (file)
@@ -479,13 +479,20 @@ exit:
        iwl_free_resp(&cmd);
 }
 
-int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
-                          unsigned int delay)
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+                               struct iwl_mvm_dump_desc *desc,
+                               unsigned int delay)
 {
        if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
                return -EBUSY;
 
-       IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", trig);
+       if (WARN_ON(mvm->fw_dump_desc))
+               iwl_mvm_free_fw_dump_desc(mvm);
+
+       IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
+                le32_to_cpu(desc->trig_desc.type));
+
+       mvm->fw_dump_desc = desc;
 
        /* stop recording */
        if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
@@ -501,8 +508,25 @@ int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
        return 0;
 }
 
+int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
+                          const char *str, size_t len, unsigned int delay)
+{
+       struct iwl_mvm_dump_desc *desc;
+
+       desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
+       if (!desc)
+               return -ENOMEM;
+
+       desc->len = len;
+       desc->trig_desc.type = cpu_to_le32(trig);
+       memcpy(desc->trig_desc.data, str, len);
+
+       return iwl_mvm_fw_dbg_collect_desc(mvm, desc, delay);
+}
+
 int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
-                               struct iwl_fw_dbg_trigger_tlv *trigger)
+                               struct iwl_fw_dbg_trigger_tlv *trigger,
+                               const char *str, size_t len)
 {
        unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
        u16 occurrences = le16_to_cpu(trigger->occurrences);
@@ -511,7 +535,8 @@ int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
        if (!occurrences)
                return 0;
 
-       ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), delay);
+       ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), str,
+                                    len, delay);
        if (ret)
                return ret;
 
index a5261a4e7e110cbc9de70a84f8f9501ef395f009..3b6b9f6031aeb3026df4bfab939089c4efafeae4 100644 (file)
@@ -886,12 +886,23 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
        iwl_trans_release_nic_access(mvm->trans, &flags);
 }
 
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
+{
+       if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert ||
+           !mvm->fw_dump_desc)
+               return;
+
+       kfree(mvm->fw_dump_desc);
+       mvm->fw_dump_desc = NULL;
+}
+
 void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 {
        struct iwl_fw_error_dump_file *dump_file;
        struct iwl_fw_error_dump_data *dump_data;
        struct iwl_fw_error_dump_info *dump_info;
        struct iwl_fw_error_dump_mem *dump_mem;
+       struct iwl_fw_error_dump_trigger_desc *dump_trig;
        struct iwl_mvm_dump_ptrs *fw_error_dump;
        u32 sram_len, sram_ofs;
        u32 file_len, fifo_data_len = 0;
@@ -961,6 +972,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                   fifo_data_len +
                   sizeof(*dump_info);
 
+       if (mvm->fw_dump_desc)
+               file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
+                           mvm->fw_dump_desc->len;
+
        /* Make room for the SMEM, if it exists */
        if (smem_len)
                file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
@@ -972,6 +987,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        dump_file = vzalloc(file_len);
        if (!dump_file) {
                kfree(fw_error_dump);
+               iwl_mvm_free_fw_dump_desc(mvm);
                return;
        }
 
@@ -1000,6 +1016,19 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        if (test_bit(STATUS_FW_ERROR, &mvm->trans->status))
                iwl_mvm_dump_fifos(mvm, &dump_data);
 
+       if (mvm->fw_dump_desc) {
+               dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
+               dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
+                                            mvm->fw_dump_desc->len);
+               dump_trig = (void *)dump_data->data;
+               memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
+                      sizeof(*dump_trig) + mvm->fw_dump_desc->len);
+
+               /* now we can free this copy */
+               iwl_mvm_free_fw_dump_desc(mvm);
+               dump_data = iwl_fw_error_next_data(dump_data);
+       }
+
        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
        dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
        dump_mem = (void *)dump_data->data;
@@ -1042,14 +1071,22 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
 }
 
+struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
+       .trig_desc = {
+               .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
+       },
+};
+
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
        /* clear the D3 reconfig, we only need it to avoid dumping a
         * firmware coredump on reconfiguration, we shouldn't do that
         * on D3->D0 transition
         */
-       if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
+       if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) {
+               mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert;
                iwl_mvm_fw_error_dump(mvm);
+       }
 
        /* cleanup all stale references (scan, roc), but keep the
         * ucode_down ref until reconfig is complete
@@ -1261,6 +1298,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
        flush_work(&mvm->d0i3_exit_work);
        flush_work(&mvm->async_handlers_wk);
        cancel_delayed_work_sync(&mvm->fw_dump_wk);
+       iwl_mvm_free_fw_dump_desc(mvm);
 
        mutex_lock(&mvm->mutex);
        __iwl_mvm_mac_stop(mvm);
index 4068139efb547d7d57465cf146051b515844814c..f4ecd1bde1cff3277f12dab08507931d2cacd7ed 100644 (file)
@@ -146,6 +146,19 @@ struct iwl_mvm_dump_ptrs {
        u32 op_mode_len;
 };
 
+/**
+ * struct iwl_mvm_dump_desc - describes the dump
+ * @len: length of trig_desc->data
+ * @trig_desc: the description of the dump
+ */
+struct iwl_mvm_dump_desc {
+       size_t len;
+       /* must be last */
+       struct iwl_fw_error_dump_trigger_desc trig_desc;
+};
+
+extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert;
+
 struct iwl_mvm_phy_ctxt {
        u16 id;
        u16 color;
@@ -706,6 +719,7 @@ struct iwl_mvm {
        s8 restart_fw;
        u8 fw_dbg_conf;
        struct delayed_work fw_dump_wk;
+       struct iwl_mvm_dump_desc *fw_dump_desc;
 
 #ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
@@ -1415,9 +1429,14 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
 
 int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id);
 int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
-                          unsigned int delay);
+                          const char *str, size_t len, unsigned int delay);
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+                               struct iwl_mvm_dump_desc *desc,
+                               unsigned int delay);
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
 int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
-                               struct iwl_fw_dbg_trigger_tlv *trigger);
+                               struct iwl_fw_dbg_trigger_tlv *trigger,
+                               const char *str, size_t len);
 
 static inline bool
 iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
@@ -1451,7 +1470,8 @@ iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
 static inline void
 iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
                               struct ieee80211_vif *vif,
-                              enum iwl_fw_dbg_trigger trig)
+                              enum iwl_fw_dbg_trigger trig,
+                              const char *str, size_t len)
 {
        struct iwl_fw_dbg_trigger_tlv *trigger;
 
@@ -1462,7 +1482,7 @@ iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
        if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
                return;
 
-       iwl_mvm_fw_dbg_collect_trig(mvm, trigger);
+       iwl_mvm_fw_dbg_collect_trig(mvm, trigger, str, len);
 }
 
 #endif /* __IWL_MVM_H__ */
index 96a4a154a42c7751568bcfba61c9d361ed25d11e..f1c5751934d50352814b9e015ce24c6a648b07a9 100644 (file)
@@ -879,7 +879,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
         * can't recover this since we're already half suspended.
         */
        if (!mvm->restart_fw && fw_error) {
-               iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_FW_ASSERT, 0);
+               iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert, 0);
        } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART,
                                    &mvm->status)) {
                struct iwl_mvm_reprobe *reprobe;