mirror of
https://github.com/torvalds/linux.git
synced 2026-05-21 21:37:25 +02:00
Another quick round of updates:
- revert mwifiex HT40 that was causing issues - many ath10k/ath11k/ath12k fixes - re-add some iwlwifi code I lost in a merge - use kfree_sensitive() on an error path in cfg80211 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmhKjoYACgkQ10qiO8sP aAADgQ//fAOMAGzuuyxy3KwxtytpYWq/k0jb3HmHct135qxteoOSv/ah0/+nvYFD 4BNAkDa44hqAP5ynWYgGQIqssJ0WkkZFooCzMpb3mzsN5sONy7XfkqG0M8RIC3xC d28nt5zDufKt+0QtWUq9pUHamm6f+4kG+LQa9kGSlUNJ3wHUMSsONTgC7T8Rpb3u CxW5vyeIp0OJDKN65qsN1iGqzzA5hF7j4jX2BH+NF/8eoztY3t5C/o0mpRaHqY/d RWB9Sm5TmIXKnEHvy8CxIwm4+5goEdRi1ua/xJAC/SWmLm3NEEQPotJASnP3+xky 1Ft2EEGkYJHExYnGZaAHjykVY1JGNZos5gitp13325iFGLy9CyeCQ87Uml/k0uw9 k3xZKLzbCwIr1gPy6gTn0ai2V2P3CLmDuuvKiulIvOkfVsXbtB6zCxgansmVW8Xx WklAX7ZMUxSvI628FFAbGW2Gt5OSPQOXRIsk4LdMO6JQs85mMRux69rIM69F4aEV 8Kean7BjT/7RZGyd5VKjkDwYvOlh2z6+UUzShudqW7otsdA2P+W3+6yu2zPq8oC/ KUQTHtb9wrYux1CdSOovmmIVnc2NALegKLP6rf3VlVF+51TIAPMz8QhDA3N29ArZ /lhImuPhDpva08pgmFyT6WkHb3lj3KAQsp/O5gLHpLKhjkSdhi8= =wXKD -----END PGP SIGNATURE----- Merge tag 'wireless-2025-06-12' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless Johannes Berg says: ==================== Another quick round of updates: - revert mwifiex HT40 that was causing issues - many ath10k/ath11k/ath12k fixes - re-add some iwlwifi code I lost in a merge - use kfree_sensitive() on an error path in cfg80211 * tag 'wireless-2025-06-12' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless: wifi: cfg80211: use kfree_sensitive() for connkeys cleanup wifi: iwlwifi: fix merge damage related to iwl_pci_resume Revert "wifi: mwifiex: Fix HT40 bandwidth issue." wifi: ath12k: fix uaf in ath12k_core_init() wifi: ath12k: Fix hal_reo_cmd_status kernel-doc wifi: ath12k: fix GCC_GCC_PCIE_HOT_RST definition for WCN7850 wifi: ath11k: validate ath11k_crypto_mode on top of ath11k_core_qmi_firmware_ready wifi: ath11k: consistently use ath11k_mac_get_fw_stats() wifi: ath11k: move locking outside of ath11k_mac_get_fw_stats() wifi: ath11k: adjust unlock sequence in ath11k_update_stats_event() wifi: ath11k: move some firmware stats related functions outside of debugfs wifi: ath11k: don't wait when there is no vdev started wifi: ath11k: don't use static variables in ath11k_debugfs_fw_stats_process() wifi: ath11k: avoid burning CPU in ath11k_debugfs_fw_stats_request() wil6210: fix support for sparrow chipsets wifi: ath10k: Avoid vdev delete timeout when firmware is already down ath10k: snoc: fix unbalanced IRQ enable in crash recovery ==================== Link: https://patch.msgid.link/20250612082519.11447-3-johannes@sipsolutions.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
d5705afbac
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
|
||||
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include "mac.h"
|
||||
|
|
@ -1022,6 +1023,26 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
|
|||
return ar->last_wmi_vdev_start_status;
|
||||
}
|
||||
|
||||
static inline int ath10k_vdev_delete_sync(struct ath10k *ar)
|
||||
{
|
||||
unsigned long time_left;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
if (!test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map))
|
||||
return 0;
|
||||
|
||||
if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
time_left = wait_for_completion_timeout(&ar->vdev_delete_done,
|
||||
ATH10K_VDEV_DELETE_TIMEOUT_HZ);
|
||||
if (time_left == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
|
||||
{
|
||||
struct cfg80211_chan_def *chandef = NULL;
|
||||
|
|
@ -5900,7 +5921,6 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
|
|||
struct ath10k *ar = hw->priv;
|
||||
struct ath10k_vif *arvif = (void *)vif->drv_priv;
|
||||
struct ath10k_peer *peer;
|
||||
unsigned long time_left;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
|
|
@ -5940,13 +5960,10 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
|
|||
ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n",
|
||||
arvif->vdev_id, ret);
|
||||
|
||||
if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) {
|
||||
time_left = wait_for_completion_timeout(&ar->vdev_delete_done,
|
||||
ATH10K_VDEV_DELETE_TIMEOUT_HZ);
|
||||
if (time_left == 0) {
|
||||
ath10k_warn(ar, "Timeout in receiving vdev delete response\n");
|
||||
goto out;
|
||||
}
|
||||
ret = ath10k_vdev_delete_sync(ar);
|
||||
if (ret) {
|
||||
ath10k_warn(ar, "Error in receiving vdev delete response: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Some firmware revisions don't notify host about self-peer removal
|
||||
|
|
|
|||
|
|
@ -937,7 +937,9 @@ static int ath10k_snoc_hif_start(struct ath10k *ar)
|
|||
|
||||
dev_set_threaded(ar->napi_dev, true);
|
||||
ath10k_core_napi_enable(ar);
|
||||
ath10k_snoc_irq_enable(ar);
|
||||
/* IRQs are left enabled when we restart due to a firmware crash */
|
||||
if (!test_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags))
|
||||
ath10k_snoc_irq_enable(ar);
|
||||
ath10k_snoc_rx_post(ar);
|
||||
|
||||
clear_bit(ATH10K_SNOC_FLAG_RECOVERY, &ar_snoc->flags);
|
||||
|
|
|
|||
|
|
@ -990,6 +990,7 @@ void ath11k_fw_stats_init(struct ath11k *ar)
|
|||
INIT_LIST_HEAD(&ar->fw_stats.bcn);
|
||||
|
||||
init_completion(&ar->fw_stats_complete);
|
||||
init_completion(&ar->fw_stats_done);
|
||||
}
|
||||
|
||||
void ath11k_fw_stats_free(struct ath11k_fw_stats *stats)
|
||||
|
|
@ -2134,6 +2135,20 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab)
|
|||
{
|
||||
int ret;
|
||||
|
||||
switch (ath11k_crypto_mode) {
|
||||
case ATH11K_CRYPT_MODE_SW:
|
||||
set_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags);
|
||||
set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags);
|
||||
break;
|
||||
case ATH11K_CRYPT_MODE_HW:
|
||||
clear_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags);
|
||||
clear_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags);
|
||||
break;
|
||||
default:
|
||||
ath11k_info(ab, "invalid crypto_mode: %d\n", ath11k_crypto_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ath11k_core_start_firmware(ab, ab->fw_mode);
|
||||
if (ret) {
|
||||
ath11k_err(ab, "failed to start firmware: %d\n", ret);
|
||||
|
|
@ -2152,20 +2167,6 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab)
|
|||
goto err_firmware_stop;
|
||||
}
|
||||
|
||||
switch (ath11k_crypto_mode) {
|
||||
case ATH11K_CRYPT_MODE_SW:
|
||||
set_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags);
|
||||
set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags);
|
||||
break;
|
||||
case ATH11K_CRYPT_MODE_HW:
|
||||
clear_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags);
|
||||
clear_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags);
|
||||
break;
|
||||
default:
|
||||
ath11k_info(ab, "invalid crypto_mode: %d\n", ath11k_crypto_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ath11k_frame_mode == ATH11K_HW_TXRX_RAW)
|
||||
set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags);
|
||||
|
||||
|
|
|
|||
|
|
@ -600,6 +600,8 @@ struct ath11k_fw_stats {
|
|||
struct list_head pdevs;
|
||||
struct list_head vdevs;
|
||||
struct list_head bcn;
|
||||
u32 num_vdev_recvd;
|
||||
u32 num_bcn_recvd;
|
||||
};
|
||||
|
||||
struct ath11k_dbg_htt_stats {
|
||||
|
|
@ -784,7 +786,7 @@ struct ath11k {
|
|||
u8 alpha2[REG_ALPHA2_LEN + 1];
|
||||
struct ath11k_fw_stats fw_stats;
|
||||
struct completion fw_stats_complete;
|
||||
bool fw_stats_done;
|
||||
struct completion fw_stats_done;
|
||||
|
||||
/* protected by conf_mutex */
|
||||
bool ps_state_enable;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
/*
|
||||
* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/vmalloc.h>
|
||||
|
|
@ -93,57 +93,14 @@ void ath11k_debugfs_add_dbring_entry(struct ath11k *ar,
|
|||
spin_unlock_bh(&dbr_data->lock);
|
||||
}
|
||||
|
||||
static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar)
|
||||
{
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
ar->fw_stats_done = false;
|
||||
ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
|
||||
ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
}
|
||||
|
||||
void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *stats)
|
||||
{
|
||||
struct ath11k_base *ab = ar->ab;
|
||||
struct ath11k_pdev *pdev;
|
||||
bool is_end;
|
||||
static unsigned int num_vdev, num_bcn;
|
||||
size_t total_vdevs_started = 0;
|
||||
int i;
|
||||
|
||||
/* WMI_REQUEST_PDEV_STAT request has been already processed */
|
||||
|
||||
if (stats->stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {
|
||||
ar->fw_stats_done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {
|
||||
if (list_empty(&stats->vdevs)) {
|
||||
ath11k_warn(ab, "empty vdev stats");
|
||||
return;
|
||||
}
|
||||
/* FW sends all the active VDEV stats irrespective of PDEV,
|
||||
* hence limit until the count of all VDEVs started
|
||||
*/
|
||||
for (i = 0; i < ab->num_radios; i++) {
|
||||
pdev = rcu_dereference(ab->pdevs_active[i]);
|
||||
if (pdev && pdev->ar)
|
||||
total_vdevs_started += ar->num_started_vdevs;
|
||||
}
|
||||
|
||||
is_end = ((++num_vdev) == total_vdevs_started);
|
||||
|
||||
list_splice_tail_init(&stats->vdevs,
|
||||
&ar->fw_stats.vdevs);
|
||||
|
||||
if (is_end) {
|
||||
ar->fw_stats_done = true;
|
||||
num_vdev = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
bool is_end = true;
|
||||
|
||||
/* WMI_REQUEST_PDEV_STAT, WMI_REQUEST_RSSI_PER_CHAIN_STAT and
|
||||
* WMI_REQUEST_VDEV_STAT requests have been already processed.
|
||||
*/
|
||||
if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
|
||||
if (list_empty(&stats->bcn)) {
|
||||
ath11k_warn(ab, "empty bcn stats");
|
||||
|
|
@ -152,97 +109,18 @@ void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *
|
|||
/* Mark end until we reached the count of all started VDEVs
|
||||
* within the PDEV
|
||||
*/
|
||||
is_end = ((++num_bcn) == ar->num_started_vdevs);
|
||||
if (ar->num_started_vdevs)
|
||||
is_end = ((++ar->fw_stats.num_bcn_recvd) ==
|
||||
ar->num_started_vdevs);
|
||||
|
||||
list_splice_tail_init(&stats->bcn,
|
||||
&ar->fw_stats.bcn);
|
||||
|
||||
if (is_end) {
|
||||
ar->fw_stats_done = true;
|
||||
num_bcn = 0;
|
||||
}
|
||||
if (is_end)
|
||||
complete(&ar->fw_stats_done);
|
||||
}
|
||||
}
|
||||
|
||||
static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,
|
||||
struct stats_request_params *req_param)
|
||||
{
|
||||
struct ath11k_base *ab = ar->ab;
|
||||
unsigned long timeout, time_left;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
/* FW stats can get split when exceeding the stats data buffer limit.
|
||||
* In that case, since there is no end marking for the back-to-back
|
||||
* received 'update stats' event, we keep a 3 seconds timeout in case,
|
||||
* fw_stats_done is not marked yet
|
||||
*/
|
||||
timeout = jiffies + secs_to_jiffies(3);
|
||||
|
||||
ath11k_debugfs_fw_stats_reset(ar);
|
||||
|
||||
reinit_completion(&ar->fw_stats_complete);
|
||||
|
||||
ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
|
||||
|
||||
if (ret) {
|
||||
ath11k_warn(ab, "could not request fw stats (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
|
||||
|
||||
if (!time_left)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
for (;;) {
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
if (ar->fw_stats_done) {
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
break;
|
||||
}
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id,
|
||||
u32 vdev_id, u32 stats_id)
|
||||
{
|
||||
struct ath11k_base *ab = ar->ab;
|
||||
struct stats_request_params req_param;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
if (ar->state != ATH11K_STATE_ON) {
|
||||
ret = -ENETDOWN;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
req_param.pdev_id = pdev_id;
|
||||
req_param.vdev_id = vdev_id;
|
||||
req_param.stats_id = stats_id;
|
||||
|
||||
ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
|
||||
if (ret)
|
||||
ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
|
||||
|
||||
ath11k_dbg(ab, ATH11K_DBG_WMI,
|
||||
"debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
|
||||
pdev_id, vdev_id, stats_id);
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath11k_open_pdev_stats(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ath11k *ar = inode->i_private;
|
||||
|
|
@ -268,7 +146,7 @@ static int ath11k_open_pdev_stats(struct inode *inode, struct file *file)
|
|||
req_param.vdev_id = 0;
|
||||
req_param.stats_id = WMI_REQUEST_PDEV_STAT;
|
||||
|
||||
ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
|
||||
ret = ath11k_mac_fw_stats_request(ar, &req_param);
|
||||
if (ret) {
|
||||
ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
|
||||
goto err_free;
|
||||
|
|
@ -339,7 +217,7 @@ static int ath11k_open_vdev_stats(struct inode *inode, struct file *file)
|
|||
req_param.vdev_id = 0;
|
||||
req_param.stats_id = WMI_REQUEST_VDEV_STAT;
|
||||
|
||||
ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
|
||||
ret = ath11k_mac_fw_stats_request(ar, &req_param);
|
||||
if (ret) {
|
||||
ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);
|
||||
goto err_free;
|
||||
|
|
@ -415,7 +293,7 @@ static int ath11k_open_bcn_stats(struct inode *inode, struct file *file)
|
|||
continue;
|
||||
|
||||
req_param.vdev_id = arvif->vdev_id;
|
||||
ret = ath11k_debugfs_fw_stats_request(ar, &req_param);
|
||||
ret = ath11k_mac_fw_stats_request(ar, &req_param);
|
||||
if (ret) {
|
||||
ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);
|
||||
goto err_free;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
|
||||
/*
|
||||
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2021-2022, 2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ATH11K_DEBUGFS_H_
|
||||
|
|
@ -273,8 +273,6 @@ void ath11k_debugfs_unregister(struct ath11k *ar);
|
|||
void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *stats);
|
||||
|
||||
void ath11k_debugfs_fw_stats_init(struct ath11k *ar);
|
||||
int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id,
|
||||
u32 vdev_id, u32 stats_id);
|
||||
|
||||
static inline bool ath11k_debugfs_is_pktlog_lite_mode_enabled(struct ath11k *ar)
|
||||
{
|
||||
|
|
@ -381,12 +379,6 @@ static inline int ath11k_debugfs_rx_filter(struct ath11k *ar)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int ath11k_debugfs_get_fw_stats(struct ath11k *ar,
|
||||
u32 pdev_id, u32 vdev_id, u32 stats_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ath11k_debugfs_add_dbring_entry(struct ath11k *ar,
|
||||
enum wmi_direct_buffer_module id,
|
||||
|
|
|
|||
|
|
@ -8997,6 +8997,81 @@ static void ath11k_mac_put_chain_rssi(struct station_info *sinfo,
|
|||
}
|
||||
}
|
||||
|
||||
static void ath11k_mac_fw_stats_reset(struct ath11k *ar)
|
||||
{
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
|
||||
ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
|
||||
ar->fw_stats.num_vdev_recvd = 0;
|
||||
ar->fw_stats.num_bcn_recvd = 0;
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
}
|
||||
|
||||
int ath11k_mac_fw_stats_request(struct ath11k *ar,
|
||||
struct stats_request_params *req_param)
|
||||
{
|
||||
struct ath11k_base *ab = ar->ab;
|
||||
unsigned long time_left;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
ath11k_mac_fw_stats_reset(ar);
|
||||
|
||||
reinit_completion(&ar->fw_stats_complete);
|
||||
reinit_completion(&ar->fw_stats_done);
|
||||
|
||||
ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
|
||||
|
||||
if (ret) {
|
||||
ath11k_warn(ab, "could not request fw stats (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
|
||||
if (!time_left)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* FW stats can get split when exceeding the stats data buffer limit.
|
||||
* In that case, since there is no end marking for the back-to-back
|
||||
* received 'update stats' event, we keep a 3 seconds timeout in case,
|
||||
* fw_stats_done is not marked yet
|
||||
*/
|
||||
time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ);
|
||||
if (!time_left)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath11k_mac_get_fw_stats(struct ath11k *ar, u32 pdev_id,
|
||||
u32 vdev_id, u32 stats_id)
|
||||
{
|
||||
struct ath11k_base *ab = ar->ab;
|
||||
struct stats_request_params req_param;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
if (ar->state != ATH11K_STATE_ON)
|
||||
return -ENETDOWN;
|
||||
|
||||
req_param.pdev_id = pdev_id;
|
||||
req_param.vdev_id = vdev_id;
|
||||
req_param.stats_id = stats_id;
|
||||
|
||||
ret = ath11k_mac_fw_stats_request(ar, &req_param);
|
||||
if (ret)
|
||||
ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
|
||||
|
||||
ath11k_dbg(ab, ATH11K_DBG_WMI,
|
||||
"debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
|
||||
pdev_id, vdev_id, stats_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
|
|
@ -9031,11 +9106,12 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw,
|
|||
|
||||
ath11k_mac_put_chain_rssi(sinfo, arsta, "ppdu", false);
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) &&
|
||||
arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA &&
|
||||
ar->ab->hw_params.supports_rssi_stats &&
|
||||
!ath11k_debugfs_get_fw_stats(ar, ar->pdev->pdev_id, 0,
|
||||
WMI_REQUEST_RSSI_PER_CHAIN_STAT)) {
|
||||
!ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0,
|
||||
WMI_REQUEST_RSSI_PER_CHAIN_STAT)) {
|
||||
ath11k_mac_put_chain_rssi(sinfo, arsta, "fw stats", true);
|
||||
}
|
||||
|
||||
|
|
@ -9043,9 +9119,10 @@ static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw,
|
|||
if (!signal &&
|
||||
arsta->arvif->vdev_type == WMI_VDEV_TYPE_STA &&
|
||||
ar->ab->hw_params.supports_rssi_stats &&
|
||||
!(ath11k_debugfs_get_fw_stats(ar, ar->pdev->pdev_id, 0,
|
||||
WMI_REQUEST_VDEV_STAT)))
|
||||
!(ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0,
|
||||
WMI_REQUEST_VDEV_STAT)))
|
||||
signal = arsta->rssi_beacon;
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
|
||||
ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
|
||||
"sta statistics db2dbm %u rssi comb %d rssi beacon %d\n",
|
||||
|
|
@ -9380,38 +9457,6 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ath11k_fw_stats_request(struct ath11k *ar,
|
||||
struct stats_request_params *req_param)
|
||||
{
|
||||
struct ath11k_base *ab = ar->ab;
|
||||
unsigned long time_left;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
ar->fw_stats_done = false;
|
||||
ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
|
||||
reinit_completion(&ar->fw_stats_complete);
|
||||
|
||||
ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
|
||||
if (ret) {
|
||||
ath11k_warn(ab, "could not request fw stats (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_left = wait_for_completion_timeout(&ar->fw_stats_complete,
|
||||
1 * HZ);
|
||||
|
||||
if (!time_left)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
unsigned int link_id,
|
||||
|
|
@ -9419,7 +9464,6 @@ static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
|
|||
{
|
||||
struct ath11k *ar = hw->priv;
|
||||
struct ath11k_base *ab = ar->ab;
|
||||
struct stats_request_params req_param = {0};
|
||||
struct ath11k_fw_stats_pdev *pdev;
|
||||
int ret;
|
||||
|
||||
|
|
@ -9431,9 +9475,6 @@ static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
|
|||
*/
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
if (ar->state != ATH11K_STATE_ON)
|
||||
goto err_fallback;
|
||||
|
||||
/* Firmware doesn't provide Tx power during CAC hence no need to fetch
|
||||
* the stats.
|
||||
*/
|
||||
|
|
@ -9442,10 +9483,8 @@ static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
req_param.pdev_id = ar->pdev->pdev_id;
|
||||
req_param.stats_id = WMI_REQUEST_PDEV_STAT;
|
||||
|
||||
ret = ath11k_fw_stats_request(ar, &req_param);
|
||||
ret = ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0,
|
||||
WMI_REQUEST_PDEV_STAT);
|
||||
if (ret) {
|
||||
ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
|
||||
goto err_fallback;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
|
||||
/*
|
||||
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef ATH11K_MAC_H
|
||||
|
|
@ -179,4 +179,6 @@ int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
|
|||
void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_chanctx_conf *ctx);
|
||||
int ath11k_mac_fw_stats_request(struct ath11k *ar,
|
||||
struct stats_request_params *req_param);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8158,6 +8158,11 @@ static void ath11k_peer_assoc_conf_event(struct ath11k_base *ab, struct sk_buff
|
|||
static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *skb)
|
||||
{
|
||||
struct ath11k_fw_stats stats = {};
|
||||
size_t total_vdevs_started = 0;
|
||||
struct ath11k_pdev *pdev;
|
||||
bool is_end = true;
|
||||
int i;
|
||||
|
||||
struct ath11k *ar;
|
||||
int ret;
|
||||
|
||||
|
|
@ -8184,25 +8189,57 @@ static void ath11k_update_stats_event(struct ath11k_base *ab, struct sk_buff *sk
|
|||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
|
||||
/* WMI_REQUEST_PDEV_STAT can be requested via .get_txpower mac ops or via
|
||||
/* WMI_REQUEST_PDEV_STAT, WMI_REQUEST_VDEV_STAT and
|
||||
* WMI_REQUEST_RSSI_PER_CHAIN_STAT can be requested via mac ops or via
|
||||
* debugfs fw stats. Therefore, processing it separately.
|
||||
*/
|
||||
if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
|
||||
list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
|
||||
ar->fw_stats_done = true;
|
||||
complete(&ar->fw_stats_done);
|
||||
goto complete;
|
||||
}
|
||||
|
||||
/* WMI_REQUEST_VDEV_STAT, WMI_REQUEST_BCN_STAT and WMI_REQUEST_RSSI_PER_CHAIN_STAT
|
||||
* are currently requested only via debugfs fw stats. Hence, processing these
|
||||
* in debugfs context
|
||||
if (stats.stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {
|
||||
complete(&ar->fw_stats_done);
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (stats.stats_id == WMI_REQUEST_VDEV_STAT) {
|
||||
if (list_empty(&stats.vdevs)) {
|
||||
ath11k_warn(ab, "empty vdev stats");
|
||||
goto complete;
|
||||
}
|
||||
/* FW sends all the active VDEV stats irrespective of PDEV,
|
||||
* hence limit until the count of all VDEVs started
|
||||
*/
|
||||
for (i = 0; i < ab->num_radios; i++) {
|
||||
pdev = rcu_dereference(ab->pdevs_active[i]);
|
||||
if (pdev && pdev->ar)
|
||||
total_vdevs_started += ar->num_started_vdevs;
|
||||
}
|
||||
|
||||
if (total_vdevs_started)
|
||||
is_end = ((++ar->fw_stats.num_vdev_recvd) ==
|
||||
total_vdevs_started);
|
||||
|
||||
list_splice_tail_init(&stats.vdevs,
|
||||
&ar->fw_stats.vdevs);
|
||||
|
||||
if (is_end)
|
||||
complete(&ar->fw_stats_done);
|
||||
|
||||
goto complete;
|
||||
}
|
||||
|
||||
/* WMI_REQUEST_BCN_STAT is currently requested only via debugfs fw stats.
|
||||
* Hence, processing it in debugfs context
|
||||
*/
|
||||
ath11k_debugfs_fw_stats_process(ar, &stats);
|
||||
|
||||
complete:
|
||||
complete(&ar->fw_stats_complete);
|
||||
rcu_read_unlock();
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
/* Since the stats's pdev, vdev and beacon list are spliced and reinitialised
|
||||
* at this point, no need to free the individual list.
|
||||
|
|
|
|||
|
|
@ -2129,7 +2129,8 @@ int ath12k_core_init(struct ath12k_base *ab)
|
|||
if (!ag) {
|
||||
mutex_unlock(&ath12k_hw_group_mutex);
|
||||
ath12k_warn(ab, "unable to get hw group\n");
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto err_unregister_notifier;
|
||||
}
|
||||
|
||||
mutex_unlock(&ath12k_hw_group_mutex);
|
||||
|
|
@ -2144,7 +2145,7 @@ int ath12k_core_init(struct ath12k_base *ab)
|
|||
if (ret) {
|
||||
mutex_unlock(&ag->mutex);
|
||||
ath12k_warn(ab, "unable to create hw group\n");
|
||||
goto err;
|
||||
goto err_destroy_hw_group;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2152,9 +2153,12 @@ int ath12k_core_init(struct ath12k_base *ab)
|
|||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
err_destroy_hw_group:
|
||||
ath12k_core_hw_group_destroy(ab->ag);
|
||||
ath12k_core_hw_group_unassign(ab);
|
||||
err_unregister_notifier:
|
||||
ath12k_core_panic_notifier_unregister(ab);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -585,7 +585,8 @@ enum hal_reo_cmd_type {
|
|||
* or cache was blocked
|
||||
* @HAL_REO_CMD_FAILED: Command execution failed, could be due to
|
||||
* invalid queue desc
|
||||
* @HAL_REO_CMD_RESOURCE_BLOCKED:
|
||||
* @HAL_REO_CMD_RESOURCE_BLOCKED: Command could not be executed because
|
||||
* one or more descriptors were blocked
|
||||
* @HAL_REO_CMD_DRAIN:
|
||||
*/
|
||||
enum hal_reo_cmd_status {
|
||||
|
|
|
|||
|
|
@ -951,6 +951,8 @@ static const struct ath12k_hw_regs qcn9274_v1_regs = {
|
|||
.hal_umac_ce0_dest_reg_base = 0x01b81000,
|
||||
.hal_umac_ce1_src_reg_base = 0x01b82000,
|
||||
.hal_umac_ce1_dest_reg_base = 0x01b83000,
|
||||
|
||||
.gcc_gcc_pcie_hot_rst = 0x1e38338,
|
||||
};
|
||||
|
||||
static const struct ath12k_hw_regs qcn9274_v2_regs = {
|
||||
|
|
@ -1042,6 +1044,8 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = {
|
|||
.hal_umac_ce0_dest_reg_base = 0x01b81000,
|
||||
.hal_umac_ce1_src_reg_base = 0x01b82000,
|
||||
.hal_umac_ce1_dest_reg_base = 0x01b83000,
|
||||
|
||||
.gcc_gcc_pcie_hot_rst = 0x1e38338,
|
||||
};
|
||||
|
||||
static const struct ath12k_hw_regs ipq5332_regs = {
|
||||
|
|
@ -1215,6 +1219,8 @@ static const struct ath12k_hw_regs wcn7850_regs = {
|
|||
.hal_umac_ce0_dest_reg_base = 0x01b81000,
|
||||
.hal_umac_ce1_src_reg_base = 0x01b82000,
|
||||
.hal_umac_ce1_dest_reg_base = 0x01b83000,
|
||||
|
||||
.gcc_gcc_pcie_hot_rst = 0x1e40304,
|
||||
};
|
||||
|
||||
static const struct ath12k_hw_hal_params ath12k_hw_hal_params_qcn9274 = {
|
||||
|
|
|
|||
|
|
@ -375,6 +375,8 @@ struct ath12k_hw_regs {
|
|||
u32 hal_reo_cmd_ring_base;
|
||||
|
||||
u32 hal_reo_status_ring_base;
|
||||
|
||||
u32 gcc_gcc_pcie_hot_rst;
|
||||
};
|
||||
|
||||
static inline const char *ath12k_bd_ie_type_str(enum ath12k_bd_ie_type type)
|
||||
|
|
|
|||
|
|
@ -292,10 +292,10 @@ static void ath12k_pci_enable_ltssm(struct ath12k_base *ab)
|
|||
|
||||
ath12k_dbg(ab, ATH12K_DBG_PCI, "pci ltssm 0x%x\n", val);
|
||||
|
||||
val = ath12k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST);
|
||||
val = ath12k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST(ab));
|
||||
val |= GCC_GCC_PCIE_HOT_RST_VAL;
|
||||
ath12k_pci_write32(ab, GCC_GCC_PCIE_HOT_RST, val);
|
||||
val = ath12k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST);
|
||||
ath12k_pci_write32(ab, GCC_GCC_PCIE_HOT_RST(ab), val);
|
||||
val = ath12k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST(ab));
|
||||
|
||||
ath12k_dbg(ab, ATH12K_DBG_PCI, "pci pcie_hot_rst 0x%x\n", val);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@
|
|||
#define PCIE_PCIE_PARF_LTSSM 0x1e081b0
|
||||
#define PARM_LTSSM_VALUE 0x111
|
||||
|
||||
#define GCC_GCC_PCIE_HOT_RST 0x1e38338
|
||||
#define GCC_GCC_PCIE_HOT_RST(ab) \
|
||||
((ab)->hw_params->regs->gcc_gcc_pcie_hot_rst)
|
||||
|
||||
#define GCC_GCC_PCIE_HOT_RST_VAL 0x10
|
||||
|
||||
#define PCIE_PCIE_INT_ALL_CLEAR 0x1e08228
|
||||
|
|
|
|||
|
|
@ -179,9 +179,11 @@ void wil_mask_irq(struct wil6210_priv *wil)
|
|||
wil_dbg_irq(wil, "mask_irq\n");
|
||||
|
||||
wil6210_mask_irq_tx(wil);
|
||||
wil6210_mask_irq_tx_edma(wil);
|
||||
if (wil->use_enhanced_dma_hw)
|
||||
wil6210_mask_irq_tx_edma(wil);
|
||||
wil6210_mask_irq_rx(wil);
|
||||
wil6210_mask_irq_rx_edma(wil);
|
||||
if (wil->use_enhanced_dma_hw)
|
||||
wil6210_mask_irq_rx_edma(wil);
|
||||
wil6210_mask_irq_misc(wil, true);
|
||||
wil6210_mask_irq_pseudo(wil);
|
||||
}
|
||||
|
|
@ -190,10 +192,12 @@ void wil_unmask_irq(struct wil6210_priv *wil)
|
|||
{
|
||||
wil_dbg_irq(wil, "unmask_irq\n");
|
||||
|
||||
wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, ICC),
|
||||
WIL_ICR_ICC_VALUE);
|
||||
wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, ICC),
|
||||
WIL_ICR_ICC_VALUE);
|
||||
if (wil->use_enhanced_dma_hw) {
|
||||
wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, ICC),
|
||||
WIL_ICR_ICC_VALUE);
|
||||
wil_w(wil, RGF_DMA_EP_TX_ICR + offsetof(struct RGF_ICR, ICC),
|
||||
WIL_ICR_ICC_VALUE);
|
||||
}
|
||||
wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICC),
|
||||
WIL_ICR_ICC_MISC_VALUE);
|
||||
wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC),
|
||||
|
|
@ -845,10 +849,12 @@ void wil6210_clear_irq(struct wil6210_priv *wil)
|
|||
offsetof(struct RGF_ICR, ICR));
|
||||
wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) +
|
||||
offsetof(struct RGF_ICR, ICR));
|
||||
wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_RX_ICR) +
|
||||
offsetof(struct RGF_ICR, ICR));
|
||||
wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) +
|
||||
offsetof(struct RGF_ICR, ICR));
|
||||
if (wil->use_enhanced_dma_hw) {
|
||||
wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_RX_ICR) +
|
||||
offsetof(struct RGF_ICR, ICR));
|
||||
wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) +
|
||||
offsetof(struct RGF_ICR, ICR));
|
||||
}
|
||||
wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
|
||||
offsetof(struct RGF_ICR, ICR));
|
||||
wmb(); /* make sure write completed */
|
||||
|
|
|
|||
|
|
@ -1501,11 +1501,27 @@ static int _iwl_pci_resume(struct device *device, bool restore)
|
|||
* Scratch value was altered, this means the device was powered off, we
|
||||
* need to reset it completely.
|
||||
* Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan,
|
||||
* so assume that any bits there mean that the device is usable.
|
||||
* but not bits [15:8]. So if we have bits set in lower word, assume
|
||||
* the device is alive.
|
||||
* For older devices, just try silently to grab the NIC.
|
||||
*/
|
||||
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ &&
|
||||
!iwl_read32(trans, CSR_FUNC_SCRATCH))
|
||||
device_was_powered_off = true;
|
||||
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
|
||||
if (!(iwl_read32(trans, CSR_FUNC_SCRATCH) &
|
||||
CSR_FUNC_SCRATCH_POWER_OFF_MASK))
|
||||
device_was_powered_off = true;
|
||||
} else {
|
||||
/*
|
||||
* bh are re-enabled by iwl_trans_pcie_release_nic_access,
|
||||
* so re-enable them if _iwl_trans_pcie_grab_nic_access fails.
|
||||
*/
|
||||
local_bh_disable();
|
||||
if (_iwl_trans_pcie_grab_nic_access(trans, true)) {
|
||||
iwl_trans_pcie_release_nic_access(trans);
|
||||
} else {
|
||||
device_was_powered_off = true;
|
||||
local_bh_enable();
|
||||
}
|
||||
}
|
||||
|
||||
if (restore || device_was_powered_off) {
|
||||
trans->state = IWL_TRANS_NO_FW;
|
||||
|
|
|
|||
|
|
@ -403,14 +403,12 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
|
|||
|
||||
if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
|
||||
bss_desc->bcn_ht_oper->ht_param &
|
||||
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) {
|
||||
chan_list->chan_scan_param[0].radio_type |=
|
||||
CHAN_BW_40MHZ << 2;
|
||||
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)
|
||||
SET_SECONDARYCHAN(chan_list->chan_scan_param[0].
|
||||
radio_type,
|
||||
(bss_desc->bcn_ht_oper->ht_param &
|
||||
IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
|
||||
}
|
||||
|
||||
*buffer += struct_size(chan_list, chan_scan_param, 1);
|
||||
ret_len += struct_size(chan_list, chan_scan_param, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1583,7 +1583,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
|
|||
|
||||
return result;
|
||||
error:
|
||||
kfree(result);
|
||||
kfree_sensitive(result);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user