Commit 6324c173 authored by Mordechay Goodstein's avatar Mordechay Goodstein Committed by Luca Coelho

iwlwifi: mvm: add support for statistics update version 15

The main changes are remove the respond from STATISTICS_CMD and sending
it with STATISTICS_NOTIFICATION, and updating for all mac id's and phy
id's in one notification.
Signed-off-by: default avatarMordechay Goodstein <mordechay.goodstein@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20211204130722.832c7b599202.If192dce8f51ec13005999c3ff96fe09a73cd8f91@changeidSigned-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent ba16c04f
......@@ -357,7 +357,7 @@ enum iwl_legacy_cmds {
* &struct iwl_notif_statistics_v11,
* &struct iwl_notif_statistics_v10,
* &struct iwl_notif_statistics,
* &struct iwl_statistics_operational_ntfy
* &struct iwl_statistics_operational_ntfy_ver_14
*/
STATISTICS_CMD = 0x9c,
......@@ -366,6 +366,7 @@ enum iwl_legacy_cmds {
* one of &struct iwl_notif_statistics_v10,
* &struct iwl_notif_statistics_v11,
* &struct iwl_notif_statistic,
* &struct iwl_statistics_operational_ntfy_ver_14
* &struct iwl_statistics_operational_ntfy
*/
STATISTICS_NOTIFICATION = 0x9d,
......
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2012-2014, 2018, 2020 Intel Corporation
* Copyright (C) 2012-2014, 2018, 2020 - 2021 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
......@@ -432,6 +432,7 @@ enum iwl_fw_statistics_type {
FW_STATISTICS_HE,
}; /* FW_STATISTICS_TYPE_API_E_VER_1 */
#define IWL_STATISTICS_TYPE_MSK 0x7f
/**
* struct iwl_statistics_ntfy_hdr
*
......@@ -445,11 +446,98 @@ struct iwl_statistics_ntfy_hdr {
__le16 size;
}; /* STATISTICS_NTFY_HDR_API_S_VER_1 */
/**
* struct iwl_statistics_ntfy_per_mac
*
* @beacon_filter_average_energy: Average energy [-dBm] of the 2
* antennas.
* @air_time: air time
* @beacon_counter: all beacons (both filtered and not filtered)
* @beacon_average_energy: all beacons (both filtered and not
* filtered)
* @beacon_rssi_a: beacon RSSI on antenna A
* @beacon_rssi_b: beacon RSSI on antenna B
* @rx_bytes: RX byte count
*/
struct iwl_statistics_ntfy_per_mac {
__le32 beacon_filter_average_energy;
__le32 air_time;
__le32 beacon_counter;
__le32 beacon_average_energy;
__le32 beacon_rssi_a;
__le32 beacon_rssi_b;
__le32 rx_bytes;
} __packed; /* STATISTICS_NTFY_PER_MAC_API_S_VER_1 */
#define IWL_STATS_MAX_BW_INDEX 5
/** struct iwl_statistics_ntfy_per_phy
* @channel_load: channel load
* @channel_load_by_us: device contribution to MCLM
* @channel_load_not_by_us: other devices' contribution to MCLM
* @clt: CLT HW timer (TIM_CH_LOAD2)
* @act: active accumulator SW
* @elp: elapsed time accumulator SW
* @rx_detected_per_ch_width: number of deferred TX per channel width,
* 0 - 20, 1/2/3 - 40/80/160
* @success_per_ch_width: number of frames that got ACK/BACK/CTS
* per channel BW. note, BACK counted as 1
* @fail_per_ch_width: number of frames that didn't get ACK/BACK/CTS
* per channel BW. note BACK counted as 1
* @last_tx_ch_width_indx: last txed frame channel width index
*/
struct iwl_statistics_ntfy_per_phy {
__le32 channel_load;
__le32 channel_load_by_us;
__le32 channel_load_not_by_us;
__le32 clt;
__le32 act;
__le32 elp;
__le32 rx_detected_per_ch_width[IWL_STATS_MAX_BW_INDEX];
__le32 success_per_ch_width[IWL_STATS_MAX_BW_INDEX];
__le32 fail_per_ch_width[IWL_STATS_MAX_BW_INDEX];
__le32 last_tx_ch_width_indx;
} __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_1 */
/**
* struct iwl_statistics_ntfy_per_sta
*
* @average_energy: in fact it is minus the energy..
*/
struct iwl_statistics_ntfy_per_sta {
__le32 average_energy;
} __packed; /* STATISTICS_NTFY_PER_STA_API_S_VER_1 */
#define IWL_STATS_MAX_PHY_OPERTINAL 3
/**
* struct iwl_statistics_operational_ntfy
*
* @hdr: general statistics header
* @flags: bitmap of possible notification structures
* @per_mac_stats: per mac statistics, &struct iwl_statistics_ntfy_per_mac
* @per_phy_stats: per phy statistics, &struct iwl_statistics_ntfy_per_phy
* @per_sta_stats: per sta statistics, &struct iwl_statistics_ntfy_per_sta
* @rx_time: rx time
* @tx_time: usec the radio is transmitting.
* @on_time_rf: The total time in usec the RF is awake.
* @on_time_scan: usec the radio is awake due to scan.
*/
struct iwl_statistics_operational_ntfy {
struct iwl_statistics_ntfy_hdr hdr;
__le32 flags;
struct iwl_statistics_ntfy_per_mac per_mac_stats[MAC_INDEX_AUX];
struct iwl_statistics_ntfy_per_phy per_phy_stats[IWL_STATS_MAX_PHY_OPERTINAL];
struct iwl_statistics_ntfy_per_sta per_sta_stats[IWL_MVM_STATION_COUNT_MAX];
__le64 rx_time;
__le64 tx_time;
__le64 on_time_rf;
__le64 on_time_scan;
} __packed; /* STATISTICS_OPERATIONAL_NTFY_API_S_VER_15 */
/**
* struct iwl_statistics_operational_ntfy_ver_14
*
* @hdr: general statistics header
* @flags: bitmap of possible notification structures
* @mac_id: mac on which the beacon was received
* @beacon_filter_average_energy: Average energy [-dBm] of the 2
* antennas.
......@@ -469,7 +557,7 @@ struct iwl_statistics_ntfy_hdr {
* @average_energy: in fact it is minus the energy..
* @reserved: reserved
*/
struct iwl_statistics_operational_ntfy {
struct iwl_statistics_operational_ntfy_ver_14 {
struct iwl_statistics_ntfy_hdr hdr;
__le32 flags;
__le32 mac_id;
......
......@@ -527,40 +527,19 @@ struct iwl_mvm_stat_data {
u8 *beacon_average_energy;
};
static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
struct iwl_mvm_stat_data_all_macs {
struct iwl_mvm *mvm;
__le32 flags;
struct iwl_statistics_ntfy_per_mac *per_mac_stats;
};
static void iwl_mvm_update_vif_sig(struct ieee80211_vif *vif, int sig)
{
struct iwl_mvm_stat_data *data = _data;
struct iwl_mvm *mvm = data->mvm;
int sig = -data->beacon_filter_average_energy;
int last_event;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = mvmvif->mvm;
int thold = vif->bss_conf.cqm_rssi_thold;
int hyst = vif->bss_conf.cqm_rssi_hyst;
u16 id = le32_to_cpu(data->mac_id);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u16 vif_id = mvmvif->id;
/* This doesn't need the MAC ID check since it's not taking the
* data copied into the "data" struct, but rather the data from
* the notification directly.
*/
mvmvif->beacon_stats.num_beacons =
le32_to_cpu(data->beacon_counter[vif_id]);
mvmvif->beacon_stats.avg_signal =
-data->beacon_average_energy[vif_id];
/* make sure that beacon statistics don't go backwards with TCM
* request to clear statistics
*/
if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
mvmvif->beacon_stats.accu_num_beacons +=
mvmvif->beacon_stats.num_beacons;
if (mvmvif->id != id)
return;
if (vif->type != NL80211_IFTYPE_STATION)
return;
int last_event;
if (sig == 0) {
IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n");
......@@ -618,6 +597,73 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
}
}
static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_stat_data *data = _data;
int sig = -data->beacon_filter_average_energy;
u16 id = le32_to_cpu(data->mac_id);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u16 vif_id = mvmvif->id;
/* This doesn't need the MAC ID check since it's not taking the
* data copied into the "data" struct, but rather the data from
* the notification directly.
*/
mvmvif->beacon_stats.num_beacons =
le32_to_cpu(data->beacon_counter[vif_id]);
mvmvif->beacon_stats.avg_signal =
-data->beacon_average_energy[vif_id];
if (mvmvif->id != id)
return;
if (vif->type != NL80211_IFTYPE_STATION)
return;
/* make sure that beacon statistics don't go backwards with TCM
* request to clear statistics
*/
if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
mvmvif->beacon_stats.accu_num_beacons +=
mvmvif->beacon_stats.num_beacons;
iwl_mvm_update_vif_sig(vif, sig);
}
static void iwl_mvm_stat_iterator_all_macs(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
struct iwl_mvm_stat_data_all_macs *data = _data;
struct iwl_statistics_ntfy_per_mac *mac_stats;
int sig;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u16 vif_id = mvmvif->id;
if (WARN_ONCE(vif_id > MAC_INDEX_AUX, "invalid vif id: %d", vif_id))
return;
if (vif->type != NL80211_IFTYPE_STATION)
return;
mac_stats = &data->per_mac_stats[vif_id];
mvmvif->beacon_stats.num_beacons =
le32_to_cpu(mac_stats->beacon_counter);
mvmvif->beacon_stats.avg_signal =
-le32_to_cpu(mac_stats->beacon_average_energy);
/* make sure that beacon statistics don't go backwards with TCM
* request to clear statistics
*/
if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
mvmvif->beacon_stats.accu_num_beacons +=
mvmvif->beacon_stats.num_beacons;
sig = -le32_to_cpu(mac_stats->beacon_filter_average_energy);
iwl_mvm_update_vif_sig(vif, sig);
}
static inline void
iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
{
......@@ -684,47 +730,41 @@ iwl_mvm_update_tcm_from_stats(struct iwl_mvm *mvm, __le32 *air_time_le,
}
static void
iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
iwl_mvm_stats_ver_15(struct iwl_mvm *mvm,
struct iwl_statistics_operational_ntfy *stats)
{
struct iwl_mvm_stat_data_all_macs data = {
.mvm = mvm,
.flags = stats->flags,
.per_mac_stats = stats->per_mac_stats,
};
ieee80211_iterate_active_interfaces(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_stat_iterator_all_macs,
&data);
}
static void
iwl_mvm_stats_ver_14(struct iwl_mvm *mvm,
struct iwl_statistics_operational_ntfy_ver_14 *stats)
{
struct iwl_mvm_stat_data data = {
.mvm = mvm,
};
u8 beacon_average_energy[MAC_INDEX_AUX];
u8 average_energy[IWL_MVM_STATION_COUNT_MAX];
struct iwl_statistics_operational_ntfy *stats;
int expected_size;
__le32 flags;
int i;
expected_size = sizeof(*stats);
if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) < expected_size,
"received invalid statistics size (%d)!, expected_size: %d\n",
iwl_rx_packet_payload_len(pkt), expected_size))
return;
stats = (void *)&pkt->data;
if (WARN_ONCE(stats->hdr.type != FW_STATISTICS_OPERATIONAL ||
stats->hdr.version !=
iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, STATISTICS_CMD, 0),
"received unsupported hdr type %d, version %d\n",
stats->hdr.type, stats->hdr.version))
return;
flags = stats->flags;
mvm->radio_stats.rx_time = le64_to_cpu(stats->rx_time);
mvm->radio_stats.tx_time = le64_to_cpu(stats->tx_time);
mvm->radio_stats.on_time_rf = le64_to_cpu(stats->on_time_rf);
mvm->radio_stats.on_time_scan = le64_to_cpu(stats->on_time_scan);
iwl_mvm_rx_stats_check_trigger(mvm, pkt);
data.mac_id = stats->mac_id;
data.beacon_filter_average_energy =
le32_to_cpu(stats->beacon_filter_average_energy);
data.flags = flags;
data.beacon_counter = stats->beacon_counter;
for (i = 0; i < ARRAY_SIZE(beacon_average_energy); i++)
beacon_average_energy[i] =
le32_to_cpu(stats->beacon_average_energy[i]);
......@@ -735,9 +775,105 @@ iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_stat_iterator,
&data);
}
static bool iwl_mvm_verify_stats_len(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt,
u32 expected_size)
{
struct iwl_statistics_ntfy_hdr *hdr;
if (WARN_ONCE(iwl_rx_packet_payload_len(pkt) < expected_size,
"received invalid statistics size (%d)!, expected_size: %d\n",
iwl_rx_packet_payload_len(pkt), expected_size))
return false;
hdr = (void *)&pkt->data;
if (WARN_ONCE((hdr->type & IWL_STATISTICS_TYPE_MSK) != FW_STATISTICS_OPERATIONAL ||
hdr->version !=
iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, STATISTICS_NOTIFICATION, 0),
"received unsupported hdr type %d, version %d\n",
hdr->type, hdr->version))
return false;
if (WARN_ONCE(le16_to_cpu(hdr->size) != expected_size,
"received invalid statistics size in header (%d)!, expected_size: %d\n",
le16_to_cpu(hdr->size), expected_size))
return false;
return true;
}
static void
iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{
u8 average_energy[IWL_MVM_STATION_COUNT_MAX];
__le32 air_time[MAC_INDEX_AUX];
__le32 rx_bytes[MAC_INDEX_AUX];
__le32 flags = 0;
int i;
u32 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
STATISTICS_NOTIFICATION, 0);
if (WARN_ONCE(notif_ver > 15,
"invalid statistics version id: %d\n", notif_ver))
return;
if (notif_ver == 14) {
struct iwl_statistics_operational_ntfy_ver_14 *stats =
(void *)pkt->data;
if (!iwl_mvm_verify_stats_len(mvm, pkt, sizeof(*stats)))
return;
iwl_mvm_stats_ver_14(mvm, stats);
flags = stats->flags;
mvm->radio_stats.rx_time = le64_to_cpu(stats->rx_time);
mvm->radio_stats.tx_time = le64_to_cpu(stats->tx_time);
mvm->radio_stats.on_time_rf = le64_to_cpu(stats->on_time_rf);
mvm->radio_stats.on_time_scan =
le64_to_cpu(stats->on_time_scan);
for (i = 0; i < ARRAY_SIZE(average_energy); i++)
average_energy[i] = le32_to_cpu(stats->average_energy[i]);
for (i = 0; i < ARRAY_SIZE(air_time); i++) {
air_time[i] = stats->air_time[i];
rx_bytes[i] = stats->rx_bytes[i];
}
}
if (notif_ver == 15) {
struct iwl_statistics_operational_ntfy *stats =
(void *)pkt->data;
if (!iwl_mvm_verify_stats_len(mvm, pkt, sizeof(*stats)))
return;
iwl_mvm_stats_ver_15(mvm, stats);
flags = stats->flags;
mvm->radio_stats.rx_time = le64_to_cpu(stats->rx_time);
mvm->radio_stats.tx_time = le64_to_cpu(stats->tx_time);
mvm->radio_stats.on_time_rf = le64_to_cpu(stats->on_time_rf);
mvm->radio_stats.on_time_scan =
le64_to_cpu(stats->on_time_scan);
for (i = 0; i < ARRAY_SIZE(average_energy); i++)
average_energy[i] =
le32_to_cpu(stats->per_sta_stats[i].average_energy);
for (i = 0; i < ARRAY_SIZE(air_time); i++) {
air_time[i] = stats->per_mac_stats[i].air_time;
rx_bytes[i] = stats->per_mac_stats[i].rx_bytes;
}
}
iwl_mvm_rx_stats_check_trigger(mvm, pkt);
for (i = 0; i < ARRAY_SIZE(average_energy); i++)
average_energy[i] = le32_to_cpu(stats->average_energy[i]);
ieee80211_iterate_stations_atomic(mvm->hw, iwl_mvm_stats_energy_iter,
average_energy);
/*
......@@ -746,8 +882,7 @@ iwl_mvm_handle_rx_statistics_tlv(struct iwl_mvm *mvm,
* request and once in statistics notification.
*/
if (le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
iwl_mvm_update_tcm_from_stats(mvm, stats->air_time,
stats->rx_bytes);
iwl_mvm_update_tcm_from_stats(mvm, air_time, rx_bytes);
}
void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
......@@ -761,8 +896,8 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
u8 *energy;
/* From ver 14 and up we use TLV statistics format */
if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP,
STATISTICS_CMD, 0) >= 14)
if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
STATISTICS_NOTIFICATION, 0) >= 14)
return iwl_mvm_handle_rx_statistics_tlv(mvm, pkt);
if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
......
......@@ -340,25 +340,64 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
ieee80211_request_smps(vif, smps_mode);
}
static bool iwl_wait_stats_complete(struct iwl_notif_wait_data *notif_wait,
struct iwl_rx_packet *pkt, void *data)
{
WARN_ON(pkt->hdr.cmd != STATISTICS_NOTIFICATION);
return true;
}
int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear)
{
struct iwl_statistics_cmd scmd = {
.flags = clear ? cpu_to_le32(IWL_STATISTICS_FLG_CLEAR) : 0,
};
struct iwl_host_cmd cmd = {
.id = STATISTICS_CMD,
.len[0] = sizeof(scmd),
.data[0] = &scmd,
.flags = CMD_WANT_SKB,
};
int ret;
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret)
return ret;
/* From version 15 - STATISTICS_NOTIFICATION, the reply for
* STATISTICS_CMD is empty, and the response is with
* STATISTICS_NOTIFICATION notification
*/
if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
STATISTICS_NOTIFICATION, 0) < 15) {
cmd.flags = CMD_WANT_SKB;
iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt);
iwl_free_resp(&cmd);
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret)
return ret;
iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt);
iwl_free_resp(&cmd);
} else {
struct iwl_notification_wait stats_wait;
static const u16 stats_complete[] = {
STATISTICS_NOTIFICATION,
};
iwl_init_notification_wait(&mvm->notif_wait, &stats_wait,
stats_complete, ARRAY_SIZE(stats_complete),
iwl_wait_stats_complete, NULL);
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret) {
iwl_remove_notification(&mvm->notif_wait, &stats_wait);
return ret;
}
/* 200ms should be enough for FW to collect data from all
* LMACs and send STATISTICS_NOTIFICATION to host
*/
ret = iwl_wait_notification(&mvm->notif_wait, &stats_wait, HZ / 5);
if (ret)
return ret;
}
if (clear)
iwl_mvm_accu_radio_stats(mvm);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment