aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-03-18 15:15:06 -0400
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-03-18 15:15:06 -0400
commit1bd3cbc1a0e9ed977a6bd470c5bc7bd36fd87e26 (patch)
treee630bfe41212778b8358c86f6d603fb5a90546ec
parent8bd22e7bb0b02c24b3c9997670bbb65e0e0a7371 (diff)
iwlwifi: mvm: send udev event upon firmware error to dump logs
When the firmware asserts, the driver will dump the firmware state to an internal buffer. This buffer is kept aside until it is dumped through debugfs. Once an external application fetched the data, the buffer is freed and a new buffer can be allocated in case another assert occurs. A udev event is sent to trigger an external application. A simple rule like: DRIVER=="iwlwifi", ACTION=="change", RUN+="/sbin/dump_sram.sh" can fetch the data from debugfs. Here is my dump_sram.sh: phyname=$(basename ${DEVPATH}) date=$(date +%F_%H_%M) filename=/var/log/iwl-sram-${phyname}-${date}.bin cat /sys/kernel/debug/ieee80211/${phyname}/iwlwifi/iwlmvm/fw_error_dump > ${filename} The current SRAM size is 80KB so, currently: $ ls -lh iwl-sram-phy0-2014-03-16_13_14.bin -rw-r--r-- 1 emmanuel emmanuel 81K Mar 16 13:15 iwl-sram-phy0-2014-03-16_13_14.bin and after compression: $ ls -lh iwl-sram-phy0-2014-03-16_13_14.bin.xz -rw-r--r-- 1 emmanuel emmanuel 13K Mar 16 13:15 iwl-sram-phy0-2014-03-16_13_14.bin.xz Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c53
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h106
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c9
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c47
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c27
6 files changed, 230 insertions, 20 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index 21ef8daede05..cf1fa498e53e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -65,6 +65,7 @@
65#include "iwl-io.h" 65#include "iwl-io.h"
66#include "iwl-prph.h" 66#include "iwl-prph.h"
67#include "debugfs.h" 67#include "debugfs.h"
68#include "fw-error-dump.h"
68 69
69static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, 70static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
70 size_t count, loff_t *ppos) 71 size_t count, loff_t *ppos)
@@ -117,6 +118,51 @@ static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
117 return ret; 118 return ret;
118} 119}
119 120
121static int iwl_dbgfs_fw_error_dump_open(struct inode *inode, struct file *file)
122{
123 struct iwl_mvm *mvm = inode->i_private;
124 int ret;
125
126 if (!mvm)
127 return -EINVAL;
128
129 mutex_lock(&mvm->mutex);
130 if (!mvm->fw_error_dump) {
131 ret = -ENODATA;
132 goto out;
133 }
134
135 file->private_data = mvm->fw_error_dump;
136 mvm->fw_error_dump = NULL;
137 kfree(mvm->fw_error_sram);
138 mvm->fw_error_sram = NULL;
139 mvm->fw_error_sram_len = 0;
140 ret = 0;
141
142out:
143 mutex_unlock(&mvm->mutex);
144 return ret;
145}
146
147static ssize_t iwl_dbgfs_fw_error_dump_read(struct file *file,
148 char __user *user_buf,
149 size_t count, loff_t *ppos)
150{
151 struct iwl_fw_error_dump_file *dump_file = file->private_data;
152
153 return simple_read_from_buffer(user_buf, count, ppos,
154 dump_file,
155 le32_to_cpu(dump_file->file_len));
156}
157
158static int iwl_dbgfs_fw_error_dump_release(struct inode *inode,
159 struct file *file)
160{
161 vfree(file->private_data);
162
163 return 0;
164}
165
120static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, 166static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
121 size_t count, loff_t *ppos) 167 size_t count, loff_t *ppos)
122{ 168{
@@ -1042,6 +1088,12 @@ MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
1042MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); 1088MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
1043MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); 1089MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
1044 1090
1091static const struct file_operations iwl_dbgfs_fw_error_dump_ops = {
1092 .open = iwl_dbgfs_fw_error_dump_open,
1093 .read = iwl_dbgfs_fw_error_dump_read,
1094 .release = iwl_dbgfs_fw_error_dump_release,
1095};
1096
1045#ifdef CONFIG_IWLWIFI_BCAST_FILTERING 1097#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
1046MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); 1098MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
1047MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256); 1099MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
@@ -1064,6 +1116,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
1064 MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR); 1116 MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
1065 MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR); 1117 MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
1066 MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR); 1118 MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
1119 MVM_DEBUGFS_ADD_FILE(fw_error_dump, dbgfs_dir, S_IRUSR);
1067 MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); 1120 MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
1068 MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR); 1121 MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
1069 if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD) 1122 if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h b/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h
new file mode 100644
index 000000000000..58c8941c0d95
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h
@@ -0,0 +1,106 @@
1/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2014 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called COPYING.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2014 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *****************************************************************************/
62
63#ifndef __fw_error_dump_h__
64#define __fw_error_dump_h__
65
66#include <linux/types.h>
67
68#define IWL_FW_ERROR_DUMP_BARKER 0x14789632
69
70/**
71 * enum iwl_fw_error_dump_type - types of data in the dump file
72 * @IWL_FW_ERROR_DUMP_SRAM:
73 * @IWL_FW_ERROR_DUMP_REG:
74 */
75enum iwl_fw_error_dump_type {
76 IWL_FW_ERROR_DUMP_SRAM = 0,
77 IWL_FW_ERROR_DUMP_REG = 1,
78
79 IWL_FW_ERROR_DUMP_MAX,
80};
81
82/**
83 * struct iwl_fw_error_dump_data - data for one type
84 * @type: %enum iwl_fw_error_dump_type
85 * @len: the length starting from %data - must be a multiplier of 4.
86 * @data: the data itself padded to be a multiplier of 4.
87 */
88struct iwl_fw_error_dump_data {
89 __le32 type;
90 __le32 len;
91 __u8 data[];
92} __packed __aligned(4);
93
94/**
95 * struct iwl_fw_error_dump_file - the layout of the header of the file
96 * @barker: must be %IWL_FW_ERROR_DUMP_BARKER
97 * @file_len: the length of all the file starting from %barker
98 * @data: array of %struct iwl_fw_error_dump_data
99 */
100struct iwl_fw_error_dump_file {
101 __le32 barker;
102 __le32 file_len;
103 u8 data[0];
104} __packed __aligned(4);
105
106#endif /* __fw_error_dump_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 48a8e67992f8..8414c031e274 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -607,6 +607,15 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
607 607
608static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) 608static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
609{ 609{
610#ifdef CONFIG_IWLWIFI_DEBUGFS
611 static char *env[] = { "DRIVER=iwlwifi", "EVENT=error_dump", NULL };
612
613 iwl_mvm_fw_error_dump(mvm);
614
615 /* notify the userspace about the error we had */
616 kobject_uevent_env(&mvm->hw->wiphy->dev.kobj, KOBJ_CHANGE, env);
617#endif
618
610 iwl_trans_stop_device(mvm->trans); 619 iwl_trans_stop_device(mvm->trans);
611 620
612 mvm->scan_status = IWL_MVM_SCAN_NONE; 621 mvm->scan_status = IWL_MVM_SCAN_NONE;
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 46fe81702963..cb6dbb140820 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -575,6 +575,9 @@ struct iwl_mvm {
575 575
576 /* -1 for always, 0 for never, >0 for that many times */ 576 /* -1 for always, 0 for never, >0 for that many times */
577 s8 restart_fw; 577 s8 restart_fw;
578 void *fw_error_dump;
579 void *fw_error_sram;
580 u32 fw_error_sram_len;
578 581
579 struct led_classdev led; 582 struct led_classdev led;
580 583
@@ -698,7 +701,10 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
698 struct ieee80211_tx_rate *r); 701 struct ieee80211_tx_rate *r);
699u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); 702u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
700void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); 703void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
701void iwl_mvm_dump_sram(struct iwl_mvm *mvm); 704#ifdef CONFIG_IWLWIFI_DEBUGFS
705void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
706void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm);
707#endif
702u8 first_antenna(u8 mask); 708u8 first_antenna(u8 mask);
703u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); 709u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
704 710
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 10846b648d70..9545d7fdd4bf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -61,6 +61,7 @@
61 * 61 *
62 *****************************************************************************/ 62 *****************************************************************************/
63#include <linux/module.h> 63#include <linux/module.h>
64#include <linux/vmalloc.h>
64#include <net/mac80211.h> 65#include <net/mac80211.h>
65 66
66#include "iwl-notif-wait.h" 67#include "iwl-notif-wait.h"
@@ -78,6 +79,7 @@
78#include "iwl-prph.h" 79#include "iwl-prph.h"
79#include "rs.h" 80#include "rs.h"
80#include "fw-api-scan.h" 81#include "fw-api-scan.h"
82#include "fw-error-dump.h"
81#include "time-event.h" 83#include "time-event.h"
82 84
83/* 85/*
@@ -534,6 +536,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
534 ieee80211_unregister_hw(mvm->hw); 536 ieee80211_unregister_hw(mvm->hw);
535 537
536 kfree(mvm->scan_cmd); 538 kfree(mvm->scan_cmd);
539 vfree(mvm->fw_error_dump);
540 kfree(mvm->fw_error_sram);
537 kfree(mvm->mcast_filter_cmd); 541 kfree(mvm->mcast_filter_cmd);
538 mvm->mcast_filter_cmd = NULL; 542 mvm->mcast_filter_cmd = NULL;
539 543
@@ -804,13 +808,52 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
804 } 808 }
805} 809}
806 810
811#ifdef CONFIG_IWLWIFI_DEBUGFS
812void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
813{
814 struct iwl_fw_error_dump_file *dump_file;
815 struct iwl_fw_error_dump_data *dump_data;
816 u32 file_len;
817
818 lockdep_assert_held(&mvm->mutex);
819
820 if (mvm->fw_error_dump)
821 return;
822
823 file_len = mvm->fw_error_sram_len +
824 sizeof(*dump_file) +
825 sizeof(*dump_data);
826
827 dump_file = vmalloc(file_len);
828 if (!dump_file)
829 return;
830
831 mvm->fw_error_dump = dump_file;
832
833 dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
834 dump_file->file_len = cpu_to_le32(file_len);
835 dump_data = (void *)dump_file->data;
836 dump_data->type = IWL_FW_ERROR_DUMP_SRAM;
837 dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
838
839 /*
840 * No need for lock since at the stage the FW isn't loaded. So it
841 * can't assert - we are the only one who can possibly be accessing
842 * mvm->fw_error_sram right now.
843 */
844 memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len);
845}
846#endif
847
807static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) 848static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
808{ 849{
809 struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); 850 struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
810 851
811 iwl_mvm_dump_nic_error_log(mvm); 852 iwl_mvm_dump_nic_error_log(mvm);
812 if (!mvm->restart_fw) 853
813 iwl_mvm_dump_sram(mvm); 854#ifdef CONFIG_IWLWIFI_DEBUGFS
855 iwl_mvm_fw_error_sram_dump(mvm);
856#endif
814 857
815 iwl_mvm_nic_restart(mvm); 858 iwl_mvm_nic_restart(mvm);
816} 859}
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
index bbfe529d7b64..e9de033d6b9e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -516,33 +516,26 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
516 iwl_mvm_dump_umac_error_log(mvm); 516 iwl_mvm_dump_umac_error_log(mvm);
517} 517}
518 518
519void iwl_mvm_dump_sram(struct iwl_mvm *mvm) 519void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
520{ 520{
521 const struct fw_img *img; 521 const struct fw_img *img;
522 int ofs, len = 0; 522 u32 ofs, sram_len;
523 int i; 523 void *sram;
524 __le32 *buf;
525 524
526 if (!mvm->ucode_loaded) 525 if (!mvm->ucode_loaded || mvm->fw_error_sram)
527 return; 526 return;
528 527
529 img = &mvm->fw->img[mvm->cur_ucode]; 528 img = &mvm->fw->img[mvm->cur_ucode];
530 ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; 529 ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
531 len = img->sec[IWL_UCODE_SECTION_DATA].len; 530 sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
532 531
533 buf = kzalloc(len, GFP_ATOMIC); 532 sram = kzalloc(sram_len, GFP_ATOMIC);
534 if (!buf) 533 if (!sram)
535 return; 534 return;
536 535
537 iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len); 536 iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len);
538 len = len >> 2; 537 mvm->fw_error_sram = sram;
539 for (i = 0; i < len; i++) { 538 mvm->fw_error_sram_len = sram_len;
540 IWL_ERR(mvm, "0x%08X\n", le32_to_cpu(buf[i]));
541 /* Add a small delay to let syslog catch up */
542 udelay(10);
543 }
544
545 kfree(buf);
546} 539}
547 540
548/** 541/**