qcacld-3.0: add support for transmit latency stats

Add support for per-link transmit latency statistics

Change-Id: If5f2f7785e45379491f085690b7fc9ea109e1148
CRs-Fixed: 3597015
This commit is contained in:
Yu Wang 2023-07-28 17:45:56 +08:00 committed by Rahul Choudhary
parent b8002c6a8f
commit 33b2ada4cb
11 changed files with 994 additions and 2 deletions

6
Kbuild
View File

@ -4130,6 +4130,12 @@ ccflags-y += -DWLAN_FEATURE_TSF_UPLINK_DELAY
CONFIG_WLAN_TSF_AUTO_REPORT := y
endif
# Enable tx latency stats feature
ifeq ($(CONFIG_WLAN_TX_LATENCY_STATS), y)
ccflags-y += -DWLAN_FEATURE_TX_LATENCY_STATS
CONFIG_WLAN_TSF_AUTO_REPORT := y
endif
# Enable TSF auto report feature
ccflags-$(CONFIG_WLAN_TSF_AUTO_REPORT) += -DWLAN_FEATURE_TSF_AUTO_REPORT

View File

@ -1638,6 +1638,11 @@ config WLAN_TSF_UPLINK_DELAY
depends on WLAN_TSF_AUTO_REPORT
default n
config WLAN_TX_LATENCY_STATS
bool "Enable WLAN_TX_LATENCY_STATS"
depends on WLAN_TSF_AUTO_REPORT
default n
config WLAN_TWT_CONVERGED
bool "Enable WLAN_TWT_CONVERGED"
default n

View File

@ -1336,6 +1336,10 @@
#define WLAN_FEATURE_TSF_AUTO_REPORT (1)
#endif
#ifdef CONFIG_WLAN_TX_LATENCY_STATS
#define WLAN_FEATURE_TX_LATENCY_STATS (1)
#endif
#ifdef CONFIG_WLAN_TSF_UPLINK_DELAY
#define WLAN_FEATURE_TSF_UPLINK_DELAY (1)
#endif

View File

@ -1284,6 +1284,7 @@ struct wlan_hdd_tx_power {
* @deflink: Default link pointing to the 0th index of the linkinfo array
* @link_info: Data structure to hold link specific information
* @tx_power: Structure to hold connection tx Power info
* @tx_latency_cfg: configuration for per-link transmit latency statistics
*/
struct hdd_adapter {
uint32_t magic;
@ -1474,6 +1475,9 @@ struct hdd_adapter {
struct wlan_hdd_link_info *deflink;
struct wlan_hdd_link_info link_info[WLAN_MAX_ML_BSS_LINKS];
struct wlan_hdd_tx_power tx_power;
#ifdef WLAN_FEATURE_TX_LATENCY_STATS
struct cdp_tx_latency_config tx_latency_cfg;
#endif
};
#define WLAN_HDD_GET_STATION_CTX_PTR(link_info) (&(link_info)->session.station)

View File

@ -2171,6 +2171,7 @@ static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
.subcmd = QCA_NL80211_VENDOR_SUBCMD_AUDIO_TRANSPORT_SWITCH,
},
FEATURE_TX_LATENCY_STATS_EVENTS
};
/**
@ -20575,6 +20576,7 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
FEATURE_ML_LINK_STATE_COMMANDS
FEATURE_AFC_VENDOR_COMMANDS
FEATURE_LL_LT_SAP_VENDOR_COMMANDS
FEATURE_TX_LATENCY_STATS_COMMANDS
};
struct hdd_context *hdd_cfg80211_wiphy_alloc(void)

View File

@ -5786,6 +5786,7 @@ status_ret:
/* Update FW WoW pattern with new MAC address */
ucfg_pmo_del_wow_pattern(vdev);
ucfg_pmo_register_wow_default_patterns(vdev);
hdd_tx_latency_restore_config(link_info);
allow_suspend:
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DYN_MAC_ADDR_UPDATE);
@ -15389,6 +15390,8 @@ static int hdd_pre_enable_configure(struct hdd_context *hdd_ctx)
uint8_t index = 0;
cdp_register_pause_cb(soc, wlan_hdd_txrx_pause_cb);
hdd_tx_latency_register_cb(soc);
/* Register HL netdev flow control callback */
cdp_hl_fc_register(soc, OL_TXRX_PDEV_ID, wlan_hdd_txrx_pause_cb);
/* Register rx mic error indication handler */

View File

@ -57,6 +57,7 @@
#include <os_if_dp.h>
#include <cfg_ucfg_api.h>
#include <wlan_twt_ucfg_ext_api.h>
#include "wlan_hdd_stats.h"
/* Preprocessor definitions and constants */
#undef QCA_HDD_SAP_DUMP_SK_BUFF
@ -210,6 +211,7 @@ static void __hdd_softap_hard_start_xmit(struct sk_buff *skb,
QDF_STATUS status;
osif_dp_mark_pkt_type(skb);
hdd_tx_latency_record_ingress_ts(adapter, skb);
/* Get TL AC corresponding to Qdisc queue index/AC. */
ac = hdd_qdisc_ac_to_tl_ac[skb->queue_mapping];

View File

@ -10537,3 +10537,875 @@ int wlan_hdd_cfg80211_get_roam_stats(struct wiphy *wiphy,
return errno;
}
#endif
#ifdef WLAN_FEATURE_TX_LATENCY_STATS
#define TX_LATENCY_BUCKET_DISTRIBUTION_LEN \
(sizeof(uint32_t) * CDP_TX_LATENCY_TYPE_MAX)
#define TX_LATENCY_ATTR(_name) QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ ## _name
static const struct nla_policy
tx_latency_bucket_policy[TX_LATENCY_ATTR(BUCKET_MAX) + 1] = {
[TX_LATENCY_ATTR(BUCKET_TYPE)] = {.type = NLA_U8},
[TX_LATENCY_ATTR(BUCKET_GRANULARITY)] = {.type = NLA_U32},
[TX_LATENCY_ATTR(BUCKET_AVERAGE)] = {.type = NLA_U32},
[TX_LATENCY_ATTR(BUCKET_DISTRIBUTION)] = {
.type = NLA_BINARY, .len = TX_LATENCY_BUCKET_DISTRIBUTION_LEN},
};
static const struct nla_policy
tx_latency_link_policy[TX_LATENCY_ATTR(LINK_MAX) + 1] = {
[TX_LATENCY_ATTR(LINK_MAC_REMOTE)] = {
.type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE},
[TX_LATENCY_ATTR(LINK_STAT_BUCKETS)] =
VENDOR_NLA_POLICY_NESTED_ARRAY(tx_latency_bucket_policy),
};
const struct nla_policy
tx_latency_policy[TX_LATENCY_ATTR(MAX) + 1] = {
[TX_LATENCY_ATTR(ACTION)] = {.type = NLA_U32},
[TX_LATENCY_ATTR(PERIODIC_REPORT)] = {.type = NLA_FLAG},
[TX_LATENCY_ATTR(PERIOD)] = {.type = NLA_U32 },
[TX_LATENCY_ATTR(BUCKETS)] =
VENDOR_NLA_POLICY_NESTED_ARRAY(tx_latency_bucket_policy),
[TX_LATENCY_ATTR(LINKS)] =
VENDOR_NLA_POLICY_NESTED_ARRAY(tx_latency_link_policy),
};
/**
* struct tx_latency_link_node - Link info of remote peer
* @node: list node for membership in the link list
* @vdev_id: Unique value to identify VDEV
* @mac_remote: link MAC address of the remote peer
*/
struct tx_latency_link_node {
qdf_list_node_t node;
uint8_t vdev_id;
struct qdf_mac_addr mac_remote;
};
/**
* hdd_tx_latency_set_for_link() - set tx latency stats config for a link
* @link_info: link specific information
* @config: pointer to tx latency stats config
*
* Return: QDF_STATUS
*/
static inline QDF_STATUS
hdd_tx_latency_set_for_link(struct wlan_hdd_link_info *link_info,
struct cdp_tx_latency_config *config)
{
QDF_STATUS status;
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
if (!soc)
return QDF_STATUS_E_INVAL;
if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
return QDF_STATUS_SUCCESS;
status = cdp_host_tx_latency_stats_config(soc,
link_info->vdev_id,
config);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err_rl("failed to %s for vdev id %d, status %d",
config->enable ? "enable" : "disable",
link_info->vdev_id, status);
return status;
}
return QDF_STATUS_SUCCESS;
}
/**
* hdd_tx_latency_restore_config() - restore tx latency stats config for a link
* @link_info: link specific information
*
* Return: QDF_STATUS
*/
QDF_STATUS
hdd_tx_latency_restore_config(struct wlan_hdd_link_info *link_info)
{
QDF_STATUS status;
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
struct cdp_tx_latency_config *config;
if (!soc)
return QDF_STATUS_E_INVAL;
if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
return QDF_STATUS_SUCCESS;
config = &link_info->adapter->tx_latency_cfg;
status = cdp_host_tx_latency_stats_config(soc,
link_info->vdev_id,
config);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err_rl("failed to %s for vdev id %d, status %d",
config->enable ? "enable" : "disable",
link_info->vdev_id, status);
return status;
}
return QDF_STATUS_SUCCESS;
}
/**
* hdd_tx_latency_set() - restore tx latency stats config for a link
* @adapter: pointer to hdd vdev/net_device context
* @config: pointer to tx latency stats config
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_set(struct hdd_adapter *adapter,
struct cdp_tx_latency_config *config)
{
int ret;
struct wlan_hdd_link_info *link_info;
QDF_STATUS status = QDF_STATUS_E_NOENT;
ret = hdd_set_tsf_auto_report(adapter, config->enable,
HDD_TSF_AUTO_RPT_SOURCE_TX_LATENCY);
if (ret) {
hdd_err_rl("failed to %s tsf auto report, ret %d",
config->enable ? "enable" : "disable", ret);
return ret;
}
hdd_adapter_for_each_link_info(adapter, link_info) {
status = hdd_tx_latency_set_for_link(link_info, config);
if (QDF_IS_STATUS_ERROR(status))
break;
}
/* restore TSF auto report config on failure */
if (QDF_IS_STATUS_ERROR(status))
hdd_set_tsf_auto_report(adapter, !config->enable,
HDD_TSF_AUTO_RPT_SOURCE_TX_LATENCY);
else
qdf_mem_copy(&adapter->tx_latency_cfg, config,
sizeof(*config));
hdd_debug("enable %d status %d", config->enable, status);
return qdf_status_to_os_return(status);
}
/**
* hdd_tx_latency_fill_link_stats() - fill tx latency statistics info skb
* @skb: skb to be filled
* @latency: per link tx latency statistics
* @idx: index of the nested attribute
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_fill_link_stats(struct sk_buff *skb,
struct cdp_tx_latency *latency, int idx)
{
struct nlattr *link, *link_stat_buckets, *link_stat_bucket;
uint32_t type;
int ret = 0;
link = nla_nest_start(skb, idx);
if (!link) {
ret = -ENOMEM;
goto err;
}
if (nla_put(skb, TX_LATENCY_ATTR(LINK_MAC_REMOTE),
QDF_MAC_ADDR_SIZE, latency->mac_remote.bytes)) {
ret = -ENOMEM;
goto err;
}
hdd_debug_rl("idx %d link mac " QDF_MAC_ADDR_FMT,
idx, QDF_MAC_ADDR_REF(latency->mac_remote.bytes));
link_stat_buckets =
nla_nest_start(skb, TX_LATENCY_ATTR(LINK_STAT_BUCKETS));
for (type = 0; type < CDP_TX_LATENCY_TYPE_MAX; type++) {
link_stat_bucket = nla_nest_start(skb, type);
if (!link_stat_bucket) {
ret = -ENOMEM;
goto err;
}
if (nla_put_u8(skb, TX_LATENCY_ATTR(BUCKET_TYPE), type)) {
ret = -ENOMEM;
goto err;
}
if (nla_put_u32(skb, TX_LATENCY_ATTR(BUCKET_GRANULARITY),
latency->stats[type].granularity)) {
ret = -ENOMEM;
goto err;
}
if (nla_put_u32(skb, TX_LATENCY_ATTR(BUCKET_AVERAGE),
latency->stats[type].average)) {
ret = -ENOMEM;
goto err;
}
if (nla_put(skb, TX_LATENCY_ATTR(BUCKET_DISTRIBUTION),
TX_LATENCY_BUCKET_DISTRIBUTION_LEN,
latency->stats[type].distribution)) {
ret = -ENOMEM;
goto err;
}
nla_nest_end(skb, link_stat_bucket);
hdd_debug_rl(" type %u granularity %u average %u",
type, latency->stats[type].granularity,
latency->stats[type].average);
}
nla_nest_end(skb, link_stat_buckets);
nla_nest_end(skb, link);
err:
if (ret)
hdd_err("failed for link " QDF_MAC_ADDR_FMT " ret: %d",
QDF_MAC_ADDR_REF(latency->mac_remote.bytes), ret);
return ret;
}
/**
* hdd_tx_latency_get_skb_len() - get required skb length for vendor command
* response/async event
* @num: required number of entries
*
* Return: the required skb length
*/
static uint32_t hdd_tx_latency_get_skb_len(uint32_t num)
{
int32_t peer_stat_sz = 0, per_bucket_len = 0, len;
if (!num)
return 0;
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE */
per_bucket_len += nla_total_size(sizeof(uint8_t));
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_GRANULARITY */
per_bucket_len += nla_total_size(sizeof(uint32_t));
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_DISTRIBUTION */
per_bucket_len += nla_total_size(TX_LATENCY_BUCKET_DISTRIBUTION_LEN);
/* Nested attr */
per_bucket_len = nla_total_size(per_bucket_len);
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAC_REMOTE */
peer_stat_sz += nla_total_size(QDF_MAC_ADDR_SIZE);
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_STAT_BUCKETS */
peer_stat_sz +=
nla_total_size(per_bucket_len * CDP_TX_LATENCY_TYPE_MAX);
/* Nested attr */
peer_stat_sz = nla_total_size(peer_stat_sz);
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS */
len = nla_total_size(peer_stat_sz * num);
len += NLMSG_HDRLEN;
return len;
}
/**
* hdd_tx_latency_link_list_free() - free all the nodes in the list
* @list: list of the nodes for link info
*
* Return: None
*/
static void hdd_tx_latency_link_list_free(qdf_list_t *list)
{
struct tx_latency_link_node *entry, *next;
qdf_list_for_each_del(list, entry, next, node) {
qdf_list_remove_node(list, &entry->node);
qdf_mem_free(entry);
}
}
/**
* hdd_tx_latency_link_list_add() - add a new node to the list for tx latency
* links
* @list: list of the nodes for link info
* @vdev_id: Unique value to identify VDEV
* @mac: link mac address of the remote peer
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_link_list_add(qdf_list_t *list, uint8_t vdev_id, uint8_t *mac)
{
struct tx_latency_link_node *link;
link = (struct tx_latency_link_node *)qdf_mem_malloc(sizeof(*link));
if (!link)
return -ENOMEM;
qdf_mem_copy(link->mac_remote.bytes, mac, QDF_MAC_ADDR_SIZE);
link->vdev_id = vdev_id;
qdf_list_insert_back(list, &link->node);
return 0;
}
/**
* hdd_tx_latency_get_links_from_attr() - parse information of the links from
* attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS
* @adapter: pointer to hdd vdev/net_device context
* @links_attr: pointer to attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS
* @list: list of the nodes for link info
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_get_links_from_attr(struct hdd_adapter *adapter,
struct nlattr *links_attr,
qdf_list_t *list)
{
struct nlattr *attr, *link_mac_remote_attr;
struct nlattr *tb[TX_LATENCY_ATTR(LINK_MAX) + 1];
int ret = 0, rem;
uint8_t vdev_id, *mac;
if (!links_attr || !list)
return -EINVAL;
/* links for MLO STA are attached to different vdevs */
vdev_id = (adapter->device_mode == QDF_STA_MODE ?
CDP_VDEV_ALL : adapter->deflink->vdev_id);
nla_for_each_nested(attr, links_attr, rem) {
ret = wlan_cfg80211_nla_parse(tb, TX_LATENCY_ATTR(LINK_MAX),
nla_data(attr), nla_len(attr),
tx_latency_link_policy);
if (ret) {
hdd_err("Attribute parse failed, ret %d", ret);
ret = -EINVAL;
goto out;
}
link_mac_remote_attr = tb[TX_LATENCY_ATTR(LINK_MAC_REMOTE)];
if (!link_mac_remote_attr) {
hdd_err("Missing link mac remote attribute");
ret = -EINVAL;
goto out;
}
if (nla_len(link_mac_remote_attr) < QDF_MAC_ADDR_SIZE) {
hdd_err("Attribute link mac remote is invalid");
ret = -EINVAL;
goto out;
}
mac = (uint8_t *)nla_data(link_mac_remote_attr);
ret = hdd_tx_latency_link_list_add(list, vdev_id, mac);
if (ret)
goto out;
}
out:
if (ret)
hdd_tx_latency_link_list_free(list);
return ret;
}
/**
* hdd_tx_latency_get_links_for_sap() - get all the active links for SAP mode
* @adapter: pointer to hdd vdev/net_device context
* @list: list of the nodes for link info
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_get_links_for_sap(struct hdd_adapter *adapter, qdf_list_t *list)
{
struct hdd_station_info *sta, *tmp = NULL;
int ret = 0;
hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta, tmp,
STA_INFO_SOFTAP_GET_STA_INFO) {
if (QDF_IS_ADDR_BROADCAST(sta->sta_mac.bytes)) {
hdd_put_sta_info_ref(&adapter->sta_info_list,
&sta, true,
STA_INFO_SOFTAP_GET_STA_INFO);
continue;
}
ret = hdd_tx_latency_link_list_add(list,
adapter->deflink->vdev_id,
sta->sta_mac.bytes);
hdd_put_sta_info_ref(&adapter->sta_info_list, &sta, true,
STA_INFO_SOFTAP_GET_STA_INFO);
if (ret)
goto out;
}
out:
if (ret)
hdd_tx_latency_link_list_free(list);
return ret;
}
/**
* hdd_tx_latency_get_links_for_sta() - get all the active links for station
* mode
* @adapter: pointer to hdd vdev/net_device context
* @list: list of the nodes for link info
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_get_links_for_sta(struct hdd_adapter *adapter, qdf_list_t *list)
{
struct wlan_hdd_link_info *link_info;
struct hdd_station_ctx *ctx;
int ret = 0;
hdd_adapter_for_each_active_link_info(adapter, link_info) {
if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
continue;
ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
if (!hdd_cm_is_vdev_associated(link_info))
continue;
ret = hdd_tx_latency_link_list_add(list, link_info->vdev_id,
ctx->conn_info.bssid.bytes);
if (ret)
goto out;
}
out:
if (ret)
hdd_tx_latency_link_list_free(list);
return ret;
}
/**
* hdd_tx_latency_get_links() - get all the active links
* @adapter: pointer to hdd vdev/net_device context
* @links_attr: pointer to attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS
* @list: list of the nodes for link info
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_get_links(struct hdd_adapter *adapter,
struct nlattr *links_attr, qdf_list_t *list)
{
if (!list)
return -EINVAL;
if (links_attr)
return hdd_tx_latency_get_links_from_attr(adapter,
links_attr, list);
if (adapter->device_mode == QDF_SAP_MODE ||
adapter->device_mode == QDF_P2P_GO_MODE)
return hdd_tx_latency_get_links_for_sap(adapter, list);
else if (adapter->device_mode == QDF_STA_MODE ||
adapter->device_mode == QDF_P2P_CLIENT_MODE)
return hdd_tx_latency_get_links_for_sta(adapter, list);
else
return -ENOTSUPP;
}
/**
* hdd_tx_latency_populate_links() - get per link tx latency stats and fill
* into skb
* @soc: pointer to soc context
* @skb: skb for vendor command response/async event
* @list: list of the nodes for link info
*
* Return: 0 on success; error number otherwise.
*/
static inline int
hdd_tx_latency_populate_links(void *soc, struct sk_buff *skb, qdf_list_t *list)
{
struct nlattr *links;
struct tx_latency_link_node *entry, *next;
struct cdp_tx_latency latency = {0};
int ret, idx = 0;
uint8_t *mac;
QDF_STATUS status;
links = nla_nest_start(skb, TX_LATENCY_ATTR(LINKS));
if (!links)
return -ENOMEM;
qdf_list_for_each_del(list, entry, next, node) {
qdf_list_remove_node(list, &entry->node);
mac = entry->mac_remote.bytes;
status = cdp_host_tx_latency_stats_fetch(soc, entry->vdev_id,
mac, &latency);
if (QDF_IS_STATUS_ERROR(status)) {
qdf_mem_free(entry);
return qdf_status_to_os_return(status);
}
ret = hdd_tx_latency_fill_link_stats(skb, &latency, idx);
qdf_mem_free(entry);
if (ret)
return ret;
idx++;
}
nla_nest_end(skb, links);
return 0;
}
/**
* hdd_tx_latency_get() - get per link tx latency stats
* @wiphy: pointer to wiphy
* @adapter: pointer to hdd vdev/net_device context
* @links_attr: pointer to attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_get(struct wiphy *wiphy,
struct hdd_adapter *adapter, struct nlattr *links_attr)
{
int ret;
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
struct sk_buff *reply_skb = NULL;
uint32_t skb_len, links_num = 0;
qdf_list_t links_list;
if (!soc)
return -EINVAL;
qdf_list_create(&links_list, 0);
ret = hdd_tx_latency_get_links(adapter, links_attr, &links_list);
if (ret)
goto out;
links_num = qdf_list_size(&links_list);
if (!links_num) {
hdd_err_rl("no valid peers");
ret = -EINVAL;
goto out;
}
skb_len = hdd_tx_latency_get_skb_len(links_num);
reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len);
if (!reply_skb) {
ret = -ENOMEM;
goto out;
}
ret = hdd_tx_latency_populate_links(soc, reply_skb, &links_list);
if (ret)
goto free_skb;
ret = wlan_cfg80211_vendor_cmd_reply(reply_skb);
/* skb has been consumed regardless of the return value */
goto out;
free_skb:
wlan_cfg80211_vendor_free_skb(reply_skb);
hdd_tx_latency_link_list_free(&links_list);
out:
qdf_list_destroy(&links_list);
hdd_debug_rl("get stats with ret %d", ret);
return ret;
}
/**
* hdd_tx_latency_enable() - enable per link tx latency stats
* @adapter: pointer to hdd vdev/net_device context
* @period: statistical period for transmit latency
* @periodic_report: whether driver needs to report transmit latency
* statistics at the end of each period
* @buckets_attr: pointer to attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKETS
*
* Return: 0 on success; error number otherwise.
*/
static int
hdd_tx_latency_enable(struct hdd_adapter *adapter, uint32_t period,
bool periodic_report, struct nlattr *buckets_attr)
{
struct nlattr *tb[TX_LATENCY_ATTR(BUCKET_MAX) + 1];
struct nlattr *attr, *bucket_type_attr, *bucket_granularity_attr;
int rem, ret;
uint8_t bucket_type;
struct cdp_tx_latency_config config = {0};
nla_for_each_nested(attr, buckets_attr, rem) {
ret = wlan_cfg80211_nla_parse(tb, TX_LATENCY_ATTR(BUCKET_MAX),
nla_data(attr), nla_len(attr),
tx_latency_bucket_policy);
if (ret) {
hdd_err_rl("Attribute parse failed, ret %d", ret);
return -EINVAL;
}
bucket_type_attr = tb[TX_LATENCY_ATTR(BUCKET_TYPE)];
if (!bucket_type_attr) {
hdd_err_rl("Missing bucket type attribute");
return -EINVAL;
}
bucket_granularity_attr =
tb[TX_LATENCY_ATTR(BUCKET_GRANULARITY)];
if (!bucket_granularity_attr) {
hdd_err_rl("Missing bucket granularity attribute");
return -EINVAL;
}
bucket_type = nla_get_u8(bucket_type_attr);
if (bucket_type >= CDP_TX_LATENCY_TYPE_MAX) {
hdd_err_rl("Invalid bucket type %u", bucket_type);
return -EINVAL;
}
config.granularity[bucket_type] =
nla_get_u32(bucket_granularity_attr);
if (!config.granularity[bucket_type]) {
hdd_err_rl("Invalid granularity for type %d",
bucket_type);
return -EINVAL;
}
}
for (rem = 0; rem < CDP_TX_LATENCY_TYPE_MAX; rem++) {
if (config.granularity[rem])
continue;
hdd_err_rl("Invalid granularity for type %d", rem);
return -EINVAL;
}
config.enable = true;
config.report = periodic_report;
config.period = period;
return hdd_tx_latency_set(adapter, &config);
}
/**
* hdd_tx_latency_disable() - disable per link tx latency stats
* @adapter: pointer to hdd vdev/net_device context
*
* Return: 0 on success; error number otherwise.
*/
static int hdd_tx_latency_disable(struct hdd_adapter *adapter)
{
struct cdp_tx_latency_config config = {0};
return hdd_tx_latency_set(adapter, &config);
}
/**
* __wlan_hdd_cfg80211_tx_latency - configure/retrieve per-link transmit
* latency statistics
* @wiphy: wiphy handle
* @wdev: wdev handle
* @data: user layer input
* @data_len: length of user layer input
*
* this function is called in ssr protected environment.
*
* return: 0 success, none zero for failure
*/
static int
__wlan_hdd_cfg80211_tx_latency(struct wiphy *wiphy, struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
uint32_t action, period;
struct nlattr *period_attr, *buckets_attr, *links_attr;
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[TX_LATENCY_ATTR(MAX) + 1];
bool periodic_report;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_warn("command not allowed in ftm mode");
return -EPERM;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return -EINVAL;
if (wlan_cfg80211_nla_parse(tb, TX_LATENCY_ATTR(MAX),
data, data_len,
tx_latency_policy)) {
hdd_err_rl("invalid attribute");
return -EINVAL;
}
if (!tb[TX_LATENCY_ATTR(ACTION)]) {
hdd_err_rl("no attr action");
return -EINVAL;
}
action = nla_get_u32(tb[TX_LATENCY_ATTR(ACTION)]);
switch (action) {
case QCA_WLAN_VENDOR_TX_LATENCY_ACTION_DISABLE:
if (!adapter->tx_latency_cfg.enable) {
ret = 0;
break;
}
ret = hdd_tx_latency_disable(adapter);
break;
case QCA_WLAN_VENDOR_TX_LATENCY_ACTION_ENABLE:
period_attr = tb[TX_LATENCY_ATTR(PERIOD)];
if (!period_attr) {
hdd_err_rl("no attr period");
return -EINVAL;
}
buckets_attr = tb[TX_LATENCY_ATTR(BUCKETS)];
if (!buckets_attr) {
hdd_err_rl("no attr buckets");
return -EINVAL;
}
period = nla_get_u32(period_attr);
if (!period) {
hdd_err_rl("invalid period");
return -EINVAL;
}
periodic_report =
nla_get_flag(tb[TX_LATENCY_ATTR(PERIODIC_REPORT)]);
ret = hdd_tx_latency_enable(adapter, period,
periodic_report, buckets_attr);
break;
case QCA_WLAN_VENDOR_TX_LATENCY_ACTION_GET:
if (!adapter->tx_latency_cfg.enable) {
hdd_err_rl("please enable the feature first");
ret = -EINVAL;
break;
}
links_attr = tb[TX_LATENCY_ATTR(LINKS)];
ret = hdd_tx_latency_get(wiphy, adapter, links_attr);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/**
* wlan_hdd_cfg80211_tx_latency - configure/retrieve per-link transmit latency
* statistics
* @wiphy: wiphy handle
* @wdev: wdev handle
* @data: user layer input
* @data_len: length of user layer input
*
* return: 0 success, einval failure
*/
int wlan_hdd_cfg80211_tx_latency(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_tx_latency(wiphy, wdev, data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* hdd_tx_latency_stats_cb() - callback function for transmit latency stats
* @vdev_id: Unique value to identify VDEV
* @stats_list: list of the nodes for per-link transmit latency statistics
*
* Return: QDF_STATUS
*/
static QDF_STATUS
hdd_tx_latency_stats_cb(uint8_t vdev_id, qdf_list_t *stats_list)
{
uint32_t len, stats_cnt;
struct sk_buff *vendor_event;
struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
struct wlan_hdd_link_info *link_info;
struct cdp_tx_latency *entry, *next;
struct nlattr *links;
int ret, idx = 0, flags = cds_get_gfp_flags();
int event_idx = QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY_INDEX;
if (!hdd_ctx) {
hdd_err("HDD context is NULL");
return QDF_STATUS_E_FAULT;
}
if (!stats_list || qdf_list_empty(stats_list)) {
hdd_err("invalid stats list");
return QDF_STATUS_E_INVAL;
}
link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
if (!link_info) {
hdd_err("adapter NULL for vdev id %d", vdev_id);
return QDF_STATUS_E_INVAL;
}
stats_cnt = qdf_list_size(stats_list);
len = hdd_tx_latency_get_skb_len(stats_cnt);
hdd_debug_rl("vdev id %d stats cnt %d", vdev_id, stats_cnt);
vendor_event =
wlan_cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
&link_info->adapter->wdev,
len, event_idx, flags);
if (!vendor_event) {
hdd_err("event alloc failed vdev id %d, len %d",
vdev_id, len);
return QDF_STATUS_E_NOMEM;
}
links = nla_nest_start(vendor_event, TX_LATENCY_ATTR(LINKS));
if (!links) {
wlan_cfg80211_vendor_free_skb(vendor_event);
hdd_err("failed to put peers");
return QDF_STATUS_E_NOMEM;
}
qdf_list_for_each_del(stats_list, entry, next, node) {
qdf_list_remove_node(stats_list, &entry->node);
ret = hdd_tx_latency_fill_link_stats(vendor_event, entry, idx);
qdf_mem_free(entry);
if (ret) {
hdd_err("failed to populate stats for idx %d", idx);
wlan_cfg80211_vendor_free_skb(vendor_event);
return QDF_STATUS_E_NOMEM;
}
idx++;
}
nla_nest_end(vendor_event, links);
wlan_cfg80211_vendor_event(vendor_event, flags);
return QDF_STATUS_SUCCESS;
}
/**
* hdd_tx_latency_register_cb() - register callback function for transmit
* latency stats
* @soc: pointer to soc context
*
* Return: QDF_STATUS
*/
QDF_STATUS hdd_tx_latency_register_cb(void *soc)
{
hdd_debug("Register tx latency callback");
return cdp_host_tx_latency_stats_register_cb(soc,
hdd_tx_latency_stats_cb);
}
#endif

View File

@ -704,4 +704,95 @@ int wlan_hdd_cfg80211_get_roam_stats(struct wiphy *wiphy,
#define FEATURE_ROAM_STATS_COMMANDS
#define FEATURE_ROAM_STATS_EVENTS
#endif
#ifdef WLAN_FEATURE_TX_LATENCY_STATS
/**
* hdd_tx_latency_register_cb() - register callback function for transmit
* latency stats
* @soc: pointer to soc context
*
* Return: QDF_STATUS
*/
QDF_STATUS hdd_tx_latency_register_cb(void *soc);
/**
* hdd_tx_latency_restore_config() - restore tx latency stats config for a link
* @link_info: link specific information
*
* Return: QDF_STATUS
*/
QDF_STATUS
hdd_tx_latency_restore_config(struct wlan_hdd_link_info *link_info);
/**
* wlan_hdd_cfg80211_tx_latency - configure/retrieve per-link transmit latency
* statistics
* @wiphy: wiphy handle
* @wdev: wdev handle
* @data: user layer input
* @data_len: length of user layer input
*
* return: 0 success, einval failure
*/
int wlan_hdd_cfg80211_tx_latency(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len);
/**
* hdd_tx_latency_record_ingress_ts() - Record driver ingress timestamp in CB
* @adapter: pointer to hdd vdev/net_device context
* @skb: sk buff
*
* Return: None
*/
static inline void
hdd_tx_latency_record_ingress_ts(struct hdd_adapter *adapter,
struct sk_buff *skb)
{
if (adapter->tx_latency_cfg.enable)
qdf_nbuf_set_tx_ts(skb);
}
extern const struct nla_policy
tx_latency_policy[QCA_WLAN_VENDOR_ATTR_TX_LATENCY_MAX + 1];
#define FEATURE_TX_LATENCY_STATS_COMMANDS \
{ \
.info.vendor_id = QCA_NL80211_VENDOR_ID, \
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY, \
.flags = WIPHY_VENDOR_CMD_NEED_WDEV | \
WIPHY_VENDOR_CMD_NEED_NETDEV | \
WIPHY_VENDOR_CMD_NEED_RUNNING, \
.doit = wlan_hdd_cfg80211_tx_latency, \
vendor_command_policy(tx_latency_policy, \
QCA_WLAN_VENDOR_ATTR_TX_LATENCY_MAX) \
}, \
#define FEATURE_TX_LATENCY_STATS_EVENTS \
[QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY_INDEX] = { \
.vendor_id = QCA_NL80211_VENDOR_ID, \
.subcmd = QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY, \
}, \
#else
static inline QDF_STATUS hdd_tx_latency_register_cb(void *soc)
{
return QDF_STATUS_SUCCESS;
}
static inline QDF_STATUS
hdd_tx_latency_restore_config(struct wlan_hdd_link_info *link_info)
{
return QDF_STATUS_SUCCESS;
}
static inline void
hdd_tx_latency_record_ingress_ts(struct hdd_adapter *adapter,
struct sk_buff *skb)
{
}
#define FEATURE_TX_LATENCY_STATS_COMMANDS
#define FEATURE_TX_LATENCY_STATS_EVENTS
#endif
#endif /* end #if !defined(WLAN_HDD_STATS_H) */

View File

@ -3015,7 +3015,8 @@ hdd_set_tsf_auto_report(struct hdd_adapter *adapter, bool ena,
enabled = !!adapter->tsf.auto_rpt_src;
if (enabled == ena) {
hdd_info_rl("current %d and no action is required", enabled);
hdd_debug_rl("source %d current %d and no action is required",
source, enabled);
goto set_src;
}
@ -3024,7 +3025,7 @@ hdd_set_tsf_auto_report(struct hdd_adapter *adapter, bool ena,
(int)GEN_PARAM_TSF_AUTO_REPORT_DISABLE,
ena, GEN_CMD);
if (ret) {
hdd_err_rl("tsf auto report %d failed: %d", ena, ret);
hdd_err_rl("source %d enable %d failed: %d", source, ena, ret);
ret = -EINPROGRESS;
goto out;
}

View File

@ -59,6 +59,7 @@
#include "wlan_dp_ucfg_api.h"
#include "os_if_dp.h"
#include "wlan_ipa_ucfg_api.h"
#include "wlan_hdd_stats.h"
#ifdef TX_MULTIQ_PER_AC
#if defined(QCA_LL_TX_FLOW_CONTROL_V2) || defined(QCA_LL_PDEV_TX_FLOW_CONTROL)
@ -518,6 +519,7 @@ static void __hdd_hard_start_xmit(struct sk_buff *skb,
return;
osif_dp_mark_pkt_type(skb);
hdd_tx_latency_record_ingress_ts(adapter, skb);
/* Get TL AC corresponding to Qdisc queue index/AC. */
ac = hdd_qdisc_ac_to_tl_ac[skb->queue_mapping];