ath10k: support dev_coredump for crash dump
authorArun Khandavalli <akhandav@qti.qualcomm.com>
Wed, 21 Dec 2016 12:19:21 +0000 (14:19 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Fri, 30 Dec 2016 09:14:07 +0000 (11:14 +0200)
Whenever firmware crashes, and both CONFIG_ATH10K_DEBUGFS and
CONFIG_ALLOW_DEV_COREDUMP are enabled, dump information about the crash via a
devcoredump device. Dump can be read from userspace for further analysis from:

/sys/class/devcoredump/devcd*/data

As until now we have provided the firmware crash dump file via fw_crash_dump
debugfs keep it still available but deprecate and a warning print that the user
should switch to using dev_coredump.

Future improvement would be not to depend on CONFIG_ATH10K_DEBUGFS, as there
might be systems which want to get the firmware crash dump but not enable
debugfs. How to handle memory consumption is also something which needs to be
taken into account.

Signed-off-by: Arun Khandavalli <akhandav@qti.qualcomm.com>
[kvalo@qca.qualcomm.com: rebase, fixes, improve commit log]
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/debug.h

index 749e381edd380e945bcd7d26c4cab35eb51b3272..3319db178c2eeb1d3204606d636344f4a6fc5b67 100644 (file)
@@ -1510,6 +1510,7 @@ static int ath10k_init_hw_params(struct ath10k *ar)
 static void ath10k_core_restart(struct work_struct *work)
 {
        struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+       int ret;
 
        set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
 
@@ -1561,6 +1562,11 @@ static void ath10k_core_restart(struct work_struct *work)
        }
 
        mutex_unlock(&ar->conf_mutex);
+
+       ret = ath10k_debug_fw_devcoredump(ar);
+       if (ret)
+               ath10k_warn(ar, "failed to send firmware crash dump via devcoredump: %d",
+                           ret);
 }
 
 static void ath10k_core_set_coverage_class_work(struct work_struct *work)
index 82a4c67f3672ba8e7f05951ee3dd6916ac1ca356..e1a70dffc52a80a351eb45a04c0e576333a3769e 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/utsname.h>
 #include <linux/crc32.h>
 #include <linux/firmware.h>
+#include <linux/devcoredump.h>
 
 #include "core.h"
 #include "debug.h"
@@ -721,7 +722,8 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_debug_get_new_fw_crash_data);
 
-static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar)
+static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar,
+                                                           bool mark_read)
 {
        struct ath10k_fw_crash_data *crash_data = ar->debug.fw_crash_data;
        struct ath10k_dump_file_data *dump_data;
@@ -790,19 +792,54 @@ static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar)
               sizeof(crash_data->registers));
        sofar += sizeof(*dump_tlv) + sizeof(crash_data->registers);
 
-       ar->debug.fw_crash_data->crashed_since_read = false;
+       ar->debug.fw_crash_data->crashed_since_read = !mark_read;
 
        spin_unlock_bh(&ar->data_lock);
 
        return dump_data;
 }
 
+int ath10k_debug_fw_devcoredump(struct ath10k *ar)
+{
+       struct ath10k_dump_file_data *dump;
+       void *dump_ptr;
+       u32 dump_len;
+
+       /* To keep the dump file available also for debugfs don't mark the
+        * file read, only debugfs should do that.
+        */
+       dump = ath10k_build_dump_file(ar, false);
+       if (!dump) {
+               ath10k_warn(ar, "no crash dump data found for devcoredump");
+               return -ENODATA;
+       }
+
+       /* Make a copy of the dump file for dev_coredumpv() as during the
+        * transition period we need to own the original file. Once
+        * fw_crash_dump debugfs file is removed no need to have a copy
+        * anymore.
+        */
+       dump_len = le32_to_cpu(dump->len);
+       dump_ptr = vzalloc(dump_len);
+
+       if (!dump_ptr)
+               return -ENOMEM;
+
+       memcpy(dump_ptr, dump, dump_len);
+
+       dev_coredumpv(ar->dev, dump_ptr, dump_len, GFP_KERNEL);
+
+       return 0;
+}
+
 static int ath10k_fw_crash_dump_open(struct inode *inode, struct file *file)
 {
        struct ath10k *ar = inode->i_private;
        struct ath10k_dump_file_data *dump;
 
-       dump = ath10k_build_dump_file(ar);
+       ath10k_warn(ar, "fw_crash_dump debugfs file is deprecated, please use /sys/class/devcoredump instead.");
+
+       dump = ath10k_build_dump_file(ar, true);
        if (!dump)
                return -ENODATA;
 
index 335512b11ca2907ddc22dc7061a7730dd35c32af..2368f47314ae81994d337f948a685c55477c2b85 100644 (file)
@@ -84,6 +84,9 @@ struct ath10k_fw_crash_data *
 ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
 
 void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len);
+
+int ath10k_debug_fw_devcoredump(struct ath10k *ar);
+
 #define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
 
 void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
@@ -166,6 +169,11 @@ static inline u32 ath10k_debug_get_fw_dbglog_level(struct ath10k *ar)
        return 0;
 }
 
+static inline int ath10k_debug_fw_devcoredump(struct ath10k *ar)
+{
+       return 0;
+}
+
 #define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
 
 #define ath10k_debug_get_et_strings NULL