Commit 710a95f9 authored by Venkateswara Naralasetty's avatar Venkateswara Naralasetty Committed by Kalle Valo
Browse files

wifi: ath11k: Add support to get power save duration for each client



Add support to get the following power save information through debugfs interface,

 * Current ps state of the peer
 * Time duration since the peer is in power save
 * Total duration of the peer spent in power save

Above information is helpful in debugging the issues with power save clients.

This patch also add trace log support for PS timekeeper to track the PS state
change of the peers alongs with the peer MAC address and timestamp.

Use the below commands to get the above power save information,

To know the time_since_station_in_power_save:
cat /sys/kernel/debug/ieee80211/phyX/netdev:wlanX/stations/
XX:XX:XX:XX:XX:XX/current_ps_duration

To know power_save_duration:
cat /sys/kernel/debug/ieee80211/phyX/netdev:wlanX/stations/
XX:XX:XX:XX:XX:XX/total_ps_duration

To reset the power_save_duration of all stations connected to AP:
echo 1 > /sys/kernel/debug/ieee80211/phyX/ath11k/reset_ps_duration

To enable/disable the ps_timekeeper:
echo Y > /sys/kernel/debug/ieee80211/phyX/ath11k/ps_timekeeper_enable
Y = 1 to enable and Y = 0 to disable.

To record PS timekeeer logs after enabling ps_timekeeper:
trace-cmd record -e ath11k_ps_timekeeper

Tested-on: Tested-on: IPQ8074 WLAN.HK.2.5.0.1-00991-QCAHKSWPL_SILICONZ-1

Signed-off-by: default avatarVenkateswara Naralasetty <quic_vnaralas@quicinc.com>
Signed-off-by: default avatarTamizh Chelvam Raja <quic_tamizhr@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220725054601.14719-1-quic_tamizhr@quicinc.com
parent d673cb6f
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -498,6 +498,13 @@ struct ath11k_sta {

	bool use_4addr_set;
	u16 tcl_metadata;

	/* Protected with ar->data_lock */
	enum ath11k_wmi_peer_ps_state peer_ps_state;
	u64 ps_start_time;
	u64 ps_start_jiffies;
	u64 ps_total_duration;
	bool peer_current_ps_valid;
};

#define ATH11K_MIN_5G_FREQ 4150
@@ -710,6 +717,10 @@ struct ath11k {
	struct ath11k_fw_stats fw_stats;
	struct completion fw_stats_complete;
	bool fw_stats_done;

	/* protected by conf_mutex */
	bool ps_state_enable;
	bool ps_timekeeper_enable;
};

struct ath11k_band_cap {
+201 −0
Original line number Diff line number Diff line
@@ -1369,6 +1369,193 @@ static const struct file_operations fops_dbr_debug = {
	.llseek = default_llseek,
};

static ssize_t ath11k_write_ps_timekeeper_enable(struct file *file,
						 const char __user *user_buf,
						 size_t count, loff_t *ppos)
{
	struct ath11k *ar = file->private_data;
	ssize_t ret;
	u8 ps_timekeeper_enable;

	if (kstrtou8_from_user(user_buf, count, 0, &ps_timekeeper_enable))
		return -EINVAL;

	mutex_lock(&ar->conf_mutex);

	if (ar->state != ATH11K_STATE_ON) {
		ret = -ENETDOWN;
		goto exit;
	}

	if (!ar->ps_state_enable) {
		ret = -EINVAL;
		goto exit;
	}

	ar->ps_timekeeper_enable = !!ps_timekeeper_enable;
	ret = count;
exit:
	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static ssize_t ath11k_read_ps_timekeeper_enable(struct file *file,
						char __user *user_buf,
						size_t count, loff_t *ppos)
{
	struct ath11k *ar = file->private_data;
	char buf[32];
	int len;

	mutex_lock(&ar->conf_mutex);
	len = scnprintf(buf, sizeof(buf), "%d\n", ar->ps_timekeeper_enable);
	mutex_unlock(&ar->conf_mutex);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static const struct file_operations fops_ps_timekeeper_enable = {
	.read = ath11k_read_ps_timekeeper_enable,
	.write = ath11k_write_ps_timekeeper_enable,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

static void ath11k_reset_peer_ps_duration(void *data,
					  struct ieee80211_sta *sta)
{
	struct ath11k *ar = data;
	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;

	spin_lock_bh(&ar->data_lock);
	arsta->ps_total_duration = 0;
	spin_unlock_bh(&ar->data_lock);
}

static ssize_t ath11k_write_reset_ps_duration(struct file *file,
					      const  char __user *user_buf,
					      size_t count, loff_t *ppos)
{
	struct ath11k *ar = file->private_data;
	int ret;
	u8 reset_ps_duration;

	if (kstrtou8_from_user(user_buf, count, 0, &reset_ps_duration))
		return -EINVAL;

	mutex_lock(&ar->conf_mutex);

	if (ar->state != ATH11K_STATE_ON) {
		ret = -ENETDOWN;
		goto exit;
	}

	if (!ar->ps_state_enable) {
		ret = -EINVAL;
		goto exit;
	}

	ieee80211_iterate_stations_atomic(ar->hw,
					  ath11k_reset_peer_ps_duration,
					  ar);

	ret = count;
exit:
	mutex_unlock(&ar->conf_mutex);
	return ret;
}

static const struct file_operations fops_reset_ps_duration = {
	.write = ath11k_write_reset_ps_duration,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

static void ath11k_peer_ps_state_disable(void *data,
					 struct ieee80211_sta *sta)
{
	struct ath11k *ar = data;
	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;

	spin_lock_bh(&ar->data_lock);
	arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
	arsta->ps_start_time = 0;
	arsta->ps_total_duration = 0;
	spin_unlock_bh(&ar->data_lock);
}

static ssize_t ath11k_write_ps_state_enable(struct file *file,
					    const char __user *user_buf,
					    size_t count, loff_t *ppos)
{
	struct ath11k *ar = file->private_data;
	struct ath11k_pdev *pdev = ar->pdev;
	int ret;
	u32 param;
	u8 ps_state_enable;

	if (kstrtou8_from_user(user_buf, count, 0, &ps_state_enable))
		return -EINVAL;

	mutex_lock(&ar->conf_mutex);

	ps_state_enable = !!ps_state_enable;

	if (ar->ps_state_enable == ps_state_enable) {
		ret = count;
		goto exit;
	}

	param = WMI_PDEV_PEER_STA_PS_STATECHG_ENABLE;
	ret = ath11k_wmi_pdev_set_param(ar, param, ps_state_enable, pdev->pdev_id);
	if (ret) {
		ath11k_warn(ar->ab, "failed to enable ps_state_enable: %d\n",
			    ret);
		goto exit;
	}
	ar->ps_state_enable = ps_state_enable;

	if (!ar->ps_state_enable) {
		ar->ps_timekeeper_enable = false;
		ieee80211_iterate_stations_atomic(ar->hw,
						  ath11k_peer_ps_state_disable,
						  ar);
	}

	ret = count;

exit:
	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static ssize_t ath11k_read_ps_state_enable(struct file *file,
					   char __user *user_buf,
					   size_t count, loff_t *ppos)
{
	struct ath11k *ar = file->private_data;
	char buf[32];
	int len;

	mutex_lock(&ar->conf_mutex);
	len = scnprintf(buf, sizeof(buf), "%d\n", ar->ps_state_enable);
	mutex_unlock(&ar->conf_mutex);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static const struct file_operations fops_ps_state_enable = {
	.read = ath11k_read_ps_state_enable,
	.write = ath11k_write_ps_state_enable,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

int ath11k_debugfs_register(struct ath11k *ar)
{
	struct ath11k_base *ab = ar->ab;
@@ -1415,6 +1602,20 @@ int ath11k_debugfs_register(struct ath11k *ar)
		debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev,
				    ar, &fops_dbr_debug);

	debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_pdev, ar,
			    &fops_ps_state_enable);

	if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT,
		     ar->ab->wmi_ab.svc_map)) {
		debugfs_create_file("ps_timekeeper_enable", 0600,
				    ar->debug.debugfs_pdev, ar,
				    &fops_ps_timekeeper_enable);

		debugfs_create_file("reset_ps_duration", 0200,
				    ar->debug.debugfs_pdev, ar,
				    &fops_reset_ps_duration);
	}

	return 0;
}

+107 −0
Original line number Diff line number Diff line
@@ -751,6 +751,102 @@ static const struct file_operations fops_htt_peer_stats_reset = {
	.llseek = default_llseek,
};

static ssize_t ath11k_dbg_sta_read_peer_ps_state(struct file *file,
						 char __user *user_buf,
						 size_t count, loff_t *ppos)
{
	struct ieee80211_sta *sta = file->private_data;
	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
	struct ath11k *ar = arsta->arvif->ar;
	char buf[20];
	int len;

	spin_lock_bh(&ar->data_lock);

	len = scnprintf(buf, sizeof(buf), "%d\n", arsta->peer_ps_state);

	spin_unlock_bh(&ar->data_lock);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static const struct file_operations fops_peer_ps_state = {
	.open = simple_open,
	.read = ath11k_dbg_sta_read_peer_ps_state,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

static ssize_t ath11k_dbg_sta_read_current_ps_duration(struct file *file,
						       char __user *user_buf,
						       size_t count,
						       loff_t *ppos)
{
	struct ieee80211_sta *sta = file->private_data;
	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
	struct ath11k *ar = arsta->arvif->ar;
	u64 time_since_station_in_power_save;
	char buf[20];
	int len;

	spin_lock_bh(&ar->data_lock);

	if (arsta->peer_ps_state == WMI_PEER_PS_STATE_ON &&
	    arsta->peer_current_ps_valid)
		time_since_station_in_power_save = jiffies_to_msecs(jiffies
						- arsta->ps_start_jiffies);
	else
		time_since_station_in_power_save = 0;

	len = scnprintf(buf, sizeof(buf), "%llu\n",
			time_since_station_in_power_save);
	spin_unlock_bh(&ar->data_lock);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static const struct file_operations fops_current_ps_duration = {
	.open = simple_open,
	.read = ath11k_dbg_sta_read_current_ps_duration,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

static ssize_t ath11k_dbg_sta_read_total_ps_duration(struct file *file,
						     char __user *user_buf,
						     size_t count, loff_t *ppos)
{
	struct ieee80211_sta *sta = file->private_data;
	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
	struct ath11k *ar = arsta->arvif->ar;
	char buf[20];
	u64 power_save_duration;
	int len;

	spin_lock_bh(&ar->data_lock);

	if (arsta->peer_ps_state == WMI_PEER_PS_STATE_ON &&
	    arsta->peer_current_ps_valid)
		power_save_duration = jiffies_to_msecs(jiffies
						- arsta->ps_start_jiffies)
						+ arsta->ps_total_duration;
	else
		power_save_duration = arsta->ps_total_duration;

	len = scnprintf(buf, sizeof(buf), "%llu\n", power_save_duration);

	spin_unlock_bh(&ar->data_lock);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static const struct file_operations fops_total_ps_duration = {
	.open = simple_open,
	.read = ath11k_dbg_sta_read_total_ps_duration,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

void ath11k_debugfs_sta_op_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			       struct ieee80211_sta *sta, struct dentry *dir)
{
@@ -778,4 +874,15 @@ void ath11k_debugfs_sta_op_add(struct ieee80211_hw *hw, struct ieee80211_vif *vi
		     ar->ab->wmi_ab.svc_map))
		debugfs_create_file("htt_peer_stats_reset", 0600, dir, sta,
				    &fops_htt_peer_stats_reset);

	debugfs_create_file("peer_ps_state", 0400, dir, sta,
			    &fops_peer_ps_state);

	if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT,
		     ar->ab->wmi_ab.svc_map)) {
		debugfs_create_file("current_ps_duration", 0440, dir, sta,
				    &fops_current_ps_duration);
		debugfs_create_file("total_ps_duration", 0440, dir, sta,
				    &fops_total_ps_duration);
	}
}
+1 −0
Original line number Diff line number Diff line
@@ -4529,6 +4529,7 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
	    new_state == IEEE80211_STA_NONE) {
		memset(arsta, 0, sizeof(*arsta));
		arsta->arvif = arvif;
		arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
		INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk);
		INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk);

+28 −0
Original line number Diff line number Diff line
@@ -305,6 +305,34 @@ TRACE_EVENT(ath11k_wmi_diag,
	)
);

TRACE_EVENT(ath11k_ps_timekeeper,
	    TP_PROTO(struct ath11k *ar, const void *peer_addr,
		     u32 peer_ps_timestamp, u8 peer_ps_state),
	TP_ARGS(ar, peer_addr, peer_ps_timestamp, peer_ps_state),

	TP_STRUCT__entry(__string(device, dev_name(ar->ab->dev))
			 __string(driver, dev_driver_string(ar->ab->dev))
			 __dynamic_array(u8, peer_addr, ETH_ALEN)
			 __field(u8, peer_ps_state)
			 __field(u32, peer_ps_timestamp)
	),

	TP_fast_assign(__assign_str(device, dev_name(ar->ab->dev));
		       __assign_str(driver, dev_driver_string(ar->ab->dev));
		       memcpy(__get_dynamic_array(peer_addr), peer_addr,
			      ETH_ALEN);
		       __entry->peer_ps_state = peer_ps_state;
		       __entry->peer_ps_timestamp = peer_ps_timestamp;
	),

	TP_printk("%s %s %u %u",
		  __get_str(driver),
		  __get_str(device),
		  __entry->peer_ps_state,
		  __entry->peer_ps_timestamp
	)
);

#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/

/* we don't want to use include/trace/events */
Loading