diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.h | 21 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/debugfs.c | 40 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 31 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mvm.h | 17 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/ops.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/trans.c | 19 |
6 files changed, 95 insertions, 39 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 34d49e171fb4..656371a668da 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -394,6 +394,11 @@ struct iwl_trans_config { const char *const *command_names; }; +struct iwl_trans_dump_data { + u32 len; + u8 data[]; +}; + struct iwl_trans; /** @@ -461,10 +466,8 @@ struct iwl_trans; * @unref: release a reference previously taken with @ref. Note that * initially the reference count is 1, making an initial @unref * necessary to allow low power states. - * @dump_data: fill a data dump with debug data, maybe containing last - * TX'ed commands and similar. When called with a NULL buffer and - * zero buffer length, provide only the (estimated) required buffer - * length. Return the used buffer length. + * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last + * TX'ed commands and similar. The buffer will be vfree'd by the caller. * Note that the transport must fill in the proper file headers. */ struct iwl_trans_ops { @@ -518,7 +521,7 @@ struct iwl_trans_ops { void (*unref)(struct iwl_trans *trans); #ifdef CONFIG_IWLWIFI_DEBUGFS - u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen); + struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans); #endif }; @@ -685,12 +688,12 @@ static inline void iwl_trans_unref(struct iwl_trans *trans) } #ifdef CONFIG_IWLWIFI_DEBUGFS -static inline u32 iwl_trans_dump_data(struct iwl_trans *trans, - void *buf, u32 buflen) +static inline struct iwl_trans_dump_data * +iwl_trans_dump_data(struct iwl_trans *trans) { if (!trans->ops->dump_data) - return 0; - return trans->ops->dump_data(trans, buf, buflen); + return NULL; + return trans->ops->dump_data(trans); } #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index b26825921f9b..7d18f466fbb3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -146,17 +146,47 @@ static ssize_t iwl_dbgfs_fw_error_dump_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_fw_error_dump_file *dump_file = file->private_data; + struct iwl_mvm_dump_ptrs *dump_ptrs = (void *)file->private_data; + ssize_t bytes_read = 0; + ssize_t bytes_read_trans = 0; + + if (*ppos < dump_ptrs->op_mode_len) + bytes_read += + simple_read_from_buffer(user_buf, count, ppos, + dump_ptrs->op_mode_ptr, + dump_ptrs->op_mode_len); + + if (bytes_read < 0 || *ppos < dump_ptrs->op_mode_len) + return bytes_read; + + if (dump_ptrs->trans_ptr) { + *ppos -= dump_ptrs->op_mode_len; + bytes_read_trans = + simple_read_from_buffer(user_buf + bytes_read, + count - bytes_read, ppos, + dump_ptrs->trans_ptr->data, + dump_ptrs->trans_ptr->len); + *ppos += dump_ptrs->op_mode_len; + + if (bytes_read_trans >= 0) + bytes_read += bytes_read_trans; + else if (!bytes_read) + /* propagate the failure */ + return bytes_read_trans; + } + + return bytes_read; - return simple_read_from_buffer(user_buf, count, ppos, - dump_file, - le32_to_cpu(dump_file->file_len)); } static int iwl_dbgfs_fw_error_dump_release(struct inode *inode, struct file *file) { - vfree(file->private_data); + struct iwl_mvm_dump_ptrs *dump_ptrs = (void *)file->private_data; + + vfree(dump_ptrs->op_mode_ptr); + vfree(dump_ptrs->trans_ptr); + kfree(dump_ptrs); return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 46ff7cd52d92..bd924a1f7804 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -676,11 +676,11 @@ static 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_mvm_dump_ptrs *fw_error_dump; const struct fw_img *img; u32 sram_len, sram_ofs; u32 file_len, rxf_len; unsigned long flags; - u32 trans_len; int reg_val; lockdep_assert_held(&mvm->mutex); @@ -688,6 +688,10 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) if (mvm->fw_error_dump) return; + fw_error_dump = kzalloc(sizeof(*mvm->fw_error_dump), GFP_KERNEL); + if (!fw_error_dump) + return; + img = &mvm->fw->img[mvm->cur_ucode]; sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; @@ -705,18 +709,15 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) rxf_len + sizeof(*dump_info); - trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); - if (trans_len) - file_len += trans_len; - dump_file = vzalloc(file_len); - if (!dump_file) + if (!dump_file) { + kfree(fw_error_dump); return; + } - mvm->fw_error_dump = dump_file; + fw_error_dump->op_mode_ptr = dump_file; dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); - dump_file->file_len = cpu_to_le32(file_len); dump_data = (void *)dump_file->data; dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); @@ -757,14 +758,12 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, sram_len); - if (trans_len) { - void *buf = iwl_fw_error_next_data(dump_data); - u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, - trans_len); - dump_data = (void *)((u8 *)buf + real_trans_len); - dump_file->file_len = - cpu_to_le32(file_len - trans_len + real_trans_len); - } + fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans); + fw_error_dump->op_mode_len = file_len; + if (fw_error_dump->trans_ptr) + file_len += fw_error_dump->trans_ptr->len; + dump_file->file_len = cpu_to_le32(file_len); + mvm->fw_error_dump = fw_error_dump; } #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 5b17fdfafbfa..2e73d3bd7757 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -128,6 +128,21 @@ struct iwl_mvm_mod_params { }; extern struct iwl_mvm_mod_params iwlmvm_mod_params; +/** + * struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump + * + * @op_mode_ptr: pointer to the buffer coming from the mvm op_mode + * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the + * transport's data. + * @trans_len: length of the valid data in trans_ptr + * @op_mode_len: length of the valid data in op_mode_ptr + */ +struct iwl_mvm_dump_ptrs { + struct iwl_trans_dump_data *trans_ptr; + void *op_mode_ptr; + u32 op_mode_len; +}; + struct iwl_mvm_phy_ctxt { u16 id; u16 color; @@ -626,7 +641,7 @@ struct iwl_mvm { /* -1 for always, 0 for never, >0 for that many times */ s8 restart_fw; - void *fw_error_dump; + struct iwl_mvm_dump_ptrs *fw_error_dump; #ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 904228aa64c4..610dbcb0dc27 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -573,7 +573,11 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) ieee80211_unregister_hw(mvm->hw); kfree(mvm->scan_cmd); - vfree(mvm->fw_error_dump); + if (mvm->fw_error_dump) { + vfree(mvm->fw_error_dump->op_mode_ptr); + vfree(mvm->fw_error_dump->trans_ptr); + kfree(mvm->fw_error_dump); + } kfree(mvm->mcast_filter_cmd); mvm->mcast_filter_cmd = NULL; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 5b5b0d8c6f60..a90292c79342 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -67,6 +67,7 @@ #include <linux/sched.h> #include <linux/bitops.h> #include <linux/gfp.h> +#include <linux/vmalloc.h> #include "iwl-drv.h" #include "iwl-trans.h" @@ -1773,28 +1774,30 @@ static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd) return cmdlen; } -static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, - void *buf, u32 buflen) +static +struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_fw_error_dump_data *data; struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_fw_error_dump_txcmd *txcmd; + struct iwl_trans_dump_data *dump_data; u32 len; int i, ptr; - len = sizeof(*data) + + len = sizeof(*dump_data) + sizeof(*data) + cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); if (trans_pcie->fw_mon_page) len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + trans_pcie->fw_mon_size; - if (!buf) - return len; + dump_data = vzalloc(len); + if (!dump_data) + return NULL; len = 0; - data = buf; + data = (void *)dump_data->data; data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); txcmd = (void *)data->data; spin_lock_bh(&cmdq->lock); @@ -1852,7 +1855,9 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, trans_pcie->fw_mon_size; } - return len; + dump_data->len = len; + + return dump_data; } #else static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, |