Commit b8a0d83f authored by Aditya Kumar Singh's avatar Aditya Kumar Singh Committed by Jeff Johnson
Browse files

wifi: ath12k: move firmware stats out of debugfs



Currently, firmware stats, comprising pdev, vdev and beacon stats are
part of debugfs. In firmware pdev stats, firmware reports the final
Tx power used to transmit each packet. If driver wants to know the
final Tx power being used at firmware level, it can leverage from
firmware pdev stats.

Move firmware stats out of debugfs context in order to leverage
the final Tx power reported in it even when debugfs is disabled.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3

Signed-off-by: default avatarAditya Kumar Singh <aditya.kumar.singh@oss.qualcomm.com>
Signed-off-by: default avatarRameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
Reviewed-by: default avatarMahendran P <quic_mahep@quicinc.com>
Link: https://patch.msgid.link/20250306102215.1300522-2-rameshkumar.sundaram@oss.qualcomm.com


Signed-off-by: default avatarJeff Johnson <jeff.johnson@oss.qualcomm.com>
parent 5e44b181
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -1084,6 +1084,59 @@ bool ath12k_core_hw_group_start_ready(struct ath12k_hw_group *ag)
	return (ag->num_started == ag->num_devices);
}

static void ath12k_fw_stats_pdevs_free(struct list_head *head)
{
	struct ath12k_fw_stats_pdev *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

void ath12k_fw_stats_bcn_free(struct list_head *head)
{
	struct ath12k_fw_stats_bcn *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

static void ath12k_fw_stats_vdevs_free(struct list_head *head)
{
	struct ath12k_fw_stats_vdev *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

void ath12k_fw_stats_init(struct ath12k *ar)
{
	INIT_LIST_HEAD(&ar->fw_stats.vdevs);
	INIT_LIST_HEAD(&ar->fw_stats.pdevs);
	INIT_LIST_HEAD(&ar->fw_stats.bcn);
	init_completion(&ar->fw_stats_complete);
}

void ath12k_fw_stats_free(struct ath12k_fw_stats *stats)
{
	ath12k_fw_stats_pdevs_free(&stats->pdevs);
	ath12k_fw_stats_vdevs_free(&stats->vdevs);
	ath12k_fw_stats_bcn_free(&stats->bcn);
}

void ath12k_fw_stats_reset(struct ath12k *ar)
{
	spin_lock_bh(&ar->data_lock);
	ar->fw_stats.fw_stats_done = false;
	ath12k_fw_stats_free(&ar->fw_stats);
	spin_unlock_bh(&ar->data_lock);
}

static void ath12k_core_trigger_partner(struct ath12k_base *ab)
{
	struct ath12k_hw_group *ag = ab->ag;
+4 −0
Original line number Diff line number Diff line
@@ -1215,6 +1215,10 @@ u32 ath12k_core_get_max_peers_per_radio(struct ath12k_base *ab);
u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab);

void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag);
void ath12k_fw_stats_init(struct ath12k *ar);
void ath12k_fw_stats_bcn_free(struct list_head *head);
void ath12k_fw_stats_free(struct ath12k_fw_stats *stats);
void ath12k_fw_stats_reset(struct ath12k *ar);

static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state)
{
+4 −108
Original line number Diff line number Diff line
@@ -870,102 +870,6 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
	 */
}

static void ath12k_fw_stats_pdevs_free(struct list_head *head)
{
	struct ath12k_fw_stats_pdev *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

static void ath12k_fw_stats_bcn_free(struct list_head *head)
{
	struct ath12k_fw_stats_bcn *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

static void ath12k_fw_stats_vdevs_free(struct list_head *head)
{
	struct ath12k_fw_stats_vdev *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

void ath12k_debugfs_fw_stats_reset(struct ath12k *ar)
{
	spin_lock_bh(&ar->data_lock);
	ar->fw_stats.fw_stats_done = false;
	ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
	ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn);
	ath12k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
	spin_unlock_bh(&ar->data_lock);
}

static int ath12k_debugfs_fw_stats_request(struct ath12k *ar,
					   struct ath12k_fw_stats_req_params *param)
{
	struct ath12k_base *ab = ar->ab;
	unsigned long timeout, time_left;
	int ret;

	lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);

	/* 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 + msecs_to_jiffies(3 * 1000);

	ath12k_debugfs_fw_stats_reset(ar);

	reinit_completion(&ar->fw_stats_complete);

	ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
						param->vdev_id, param->pdev_id);

	if (ret) {
		ath12k_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 the wait timed out, return -ETIMEDOUT */
	if (!time_left)
		return -ETIMEDOUT;

	/* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
	 * when stats data buffer limit is reached. fw_stats_complete
	 * is completed once host receives first event from firmware, but
	 * still end might not be marked in the TLV.
	 * Below loop is to confirm that firmware completed sending all the event
	 * and fw_stats_done is marked true when end is marked in the TLV
	 */
	for (;;) {
		if (time_after(jiffies, timeout))
			break;

		spin_lock_bh(&ar->data_lock);
		if (ar->fw_stats.fw_stats_done) {
			spin_unlock_bh(&ar->data_lock);
			break;
		}
		spin_unlock_bh(&ar->data_lock);
	}
	return 0;
}

void
ath12k_debugfs_fw_stats_process(struct ath12k *ar,
				struct ath12k_fw_stats *stats)
@@ -1022,10 +926,6 @@ ath12k_debugfs_fw_stats_process(struct ath12k *ar,
			num_bcn = 0;
		}
	}
	if (stats->stats_id == WMI_REQUEST_PDEV_STAT) {
		list_splice_tail_init(&stats->pdevs, &ar->fw_stats.pdevs);
		ar->fw_stats.fw_stats_done = true;
	}
}

static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
@@ -1052,7 +952,7 @@ static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
	param.vdev_id = 0;
	param.stats_id = WMI_REQUEST_VDEV_STAT;

	ret = ath12k_debugfs_fw_stats_request(ar, &param);
	ret = ath12k_mac_get_fw_stats(ar, &param);
	if (ret) {
		ath12k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);
		return ret;
@@ -1117,7 +1017,7 @@ static int ath12k_open_bcn_stats(struct inode *inode, struct file *file)
			continue;

		param.vdev_id = arvif->vdev_id;
		ret = ath12k_debugfs_fw_stats_request(ar, &param);
		ret = ath12k_mac_get_fw_stats(ar, &param);
		if (ret) {
			ath12k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);
			return ret;
@@ -1184,7 +1084,7 @@ static int ath12k_open_pdev_stats(struct inode *inode, struct file *file)
	param.vdev_id = 0;
	param.stats_id = WMI_REQUEST_PDEV_STAT;

	ret = ath12k_debugfs_fw_stats_request(ar, &param);
	ret = ath12k_mac_get_fw_stats(ar, &param);
	if (ret) {
		ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
		return ret;
@@ -1239,11 +1139,7 @@ void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
	debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar,
			    &fops_pdev_stats);

	INIT_LIST_HEAD(&ar->fw_stats.vdevs);
	INIT_LIST_HEAD(&ar->fw_stats.bcn);
	INIT_LIST_HEAD(&ar->fw_stats.pdevs);

	init_completion(&ar->fw_stats_complete);
	ath12k_fw_stats_init(ar);
}

void ath12k_debugfs_register(struct ath12k *ar)
+0 −5
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@ void ath12k_debugfs_register(struct ath12k *ar);
void ath12k_debugfs_unregister(struct ath12k *ar);
void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
				     struct ath12k_fw_stats *stats);
void ath12k_debugfs_fw_stats_reset(struct ath12k *ar);

static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
{
@@ -129,10 +128,6 @@ static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
{
}

static inline void ath12k_debugfs_fw_stats_reset(struct ath12k *ar)
{
}

static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
{
	return false;
+41 −9
Original line number Diff line number Diff line
@@ -10267,12 +10267,12 @@ static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx,
	return 0;
}

static int ath12k_mac_get_fw_stats(struct ath12k *ar, u32 pdev_id,
				   u32 vdev_id, u32 stats_id)
int ath12k_mac_get_fw_stats(struct ath12k *ar,
			    struct ath12k_fw_stats_req_params *param)
{
	struct ath12k_base *ab = ar->ab;
	struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
	unsigned long time_left;
	unsigned long timeout, time_left;
	int ret;

	guard(mutex)(&ah->hw_mutex);
@@ -10280,9 +10280,18 @@ static int ath12k_mac_get_fw_stats(struct ath12k *ar, u32 pdev_id,
	if (ah->state != ATH12K_HW_STATE_ON)
		return -ENETDOWN;

	/* 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 + msecs_to_jiffies(3 * 1000);
	ath12k_fw_stats_reset(ar);

	reinit_completion(&ar->fw_stats_complete);

	ret = ath12k_wmi_send_stats_request_cmd(ar, stats_id, vdev_id, pdev_id);
	ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
						param->vdev_id, param->pdev_id);

	if (ret) {
		ath12k_warn(ab, "failed to request fw stats: %d\n", ret);
@@ -10291,14 +10300,33 @@ static int ath12k_mac_get_fw_stats(struct ath12k *ar, u32 pdev_id,

	ath12k_dbg(ab, ATH12K_DBG_WMI,
		   "get fw stat pdev id %d vdev id %d stats id 0x%x\n",
		   pdev_id, vdev_id, stats_id);
		   param->pdev_id, param->vdev_id, param->stats_id);

	time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);

	if (!time_left)
	if (!time_left) {
		ath12k_warn(ab, "time out while waiting for get fw stats\n");
		return -ETIMEDOUT;
	}

	return ret;
	/* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
	 * when stats data buffer limit is reached. fw_stats_complete
	 * is completed once host receives first event from firmware, but
	 * still end might not be marked in the TLV.
	 * Below loop is to confirm that firmware completed sending all the event
	 * and fw_stats_done is marked true when end is marked in the TLV.
	 */
	for (;;) {
		if (time_after(jiffies, timeout))
			break;
		spin_lock_bh(&ar->data_lock);
		if (ar->fw_stats.fw_stats_done) {
			spin_unlock_bh(&ar->data_lock);
			break;
		}
		spin_unlock_bh(&ar->data_lock);
	}
	return 0;
}

static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
@@ -10307,6 +10335,7 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
					 struct station_info *sinfo)
{
	struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta);
	struct ath12k_fw_stats_req_params params = {};
	struct ath12k_link_sta *arsta;
	struct ath12k *ar;
	s8 signal;
@@ -10348,10 +10377,13 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
	/* TODO: Use real NF instead of default one. */
	signal = arsta->rssi_comb;

	params.pdev_id = ar->pdev->pdev_id;
	params.vdev_id = 0;
	params.stats_id = WMI_REQUEST_VDEV_STAT;

	if (!signal &&
	    ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
	    !(ath12k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0,
				      WMI_REQUEST_VDEV_STAT)))
	    !(ath12k_mac_get_fw_stats(ar, &params)))
		signal = arsta->rssi_beacon;

	if (signal) {
Loading