Commit d5705afb authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge tag 'wireless-2025-06-12' of...

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: default avatarJakub Kicinski <kuba@kernel.org>
parents 613fd52e f8758659
Loading
Loading
Loading
Loading
+25 −8
Original line number Diff line number Diff line
@@ -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,14 +5960,11 @@ 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");
	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
	 * until after associated vdev is deleted.
+3 −1
Original line number Diff line number Diff line
@@ -937,6 +937,8 @@ static int ath10k_snoc_hif_start(struct ath10k *ar)

	dev_set_threaded(ar->napi_dev, true);
	ath10k_core_napi_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);

+15 −14
Original line number Diff line number Diff line
@@ -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);

+3 −1
Original line number Diff line number Diff line
@@ -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;
+13 −135
Original line number Diff line number Diff line
// 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 */
	bool is_end = true;

	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
	/* WMI_REQUEST_PDEV_STAT, WMI_REQUEST_RSSI_PER_CHAIN_STAT and
	 * WMI_REQUEST_VDEV_STAT requests have been already processed.
	 */
		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;
	}

	if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
		if (list_empty(&stats->bcn)) {
			ath11k_warn(ab, "empty bcn stats");
@@ -152,95 +109,16 @@ 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)
@@ -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;
Loading