From: Viktor Barna <viktor.barna@xxxxxxxxxx> (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx> --- drivers/net/wireless/celeno/cl8k/stats.c | 438 +++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/stats.c diff --git a/drivers/net/wireless/celeno/cl8k/stats.c b/drivers/net/wireless/celeno/cl8k/stats.c new file mode 100644 index 000000000000..c526199513f4 --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/stats.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Copyright(c) 2019-2022, Celeno Communications Ltd. */ + +#include <linux/string.h> + +#include "reg/reg_access.h" +#include "sta.h" +#include "utils.h" +#include "reg/reg_defs.h" +#include "rates.h" +#include "debug.h" +#include "tx.h" +#include "vif.h" +#include "stats.h" + +static void cll_stats_config_ps(struct cl_sta *cl_sta) +{ + struct sta_info *stainfo = IEEE80211_STA_TO_STAINFO(cl_sta->sta); + + cl_sta->stats->ps.timestamp_sleep = jiffies; + cl_sta->stats->ps.is_ps = test_sta_flag(stainfo, WLAN_STA_PS_STA); +} + +static void cl_stats_free(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + kfree(cl_sta->stats); + cl_sta->stats = NULL; +} + +static void cl_stats_disable(struct cl_hw *cl_hw) +{ + pr_debug("Statistics disabled\n"); + cl_hw->conf->ci_stats_en = false; + cl_sta_loop(cl_hw, cl_stats_free); + + if (cl_hw_is_prod_or_listener(cl_hw)) { + kfree(cl_hw->rx_stats); + cl_hw->rx_stats = NULL; + } +} + +static void _cl_stats_update_tx(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + struct cl_agg_tx_report *agg_report) +{ + struct cl_stats *stats = cl_sta->stats; + struct cl_tx_cntrs *cntrs; + union cl_rate_ctrl_info rate_ctrl_info = { + .word = le32_to_cpu(agg_report->rate_cntrl_info)}; + u8 bw, nss, mcs, gi, bf; + + switch (rate_ctrl_info.field.format_mod) { + case WRS_MODE_HE: + nss = (rate_ctrl_info.field.mcs_index >> 4); + mcs = (rate_ctrl_info.field.mcs_index & 0xF); + gi = rate_ctrl_info.field.gi; + + { + bw = rate_ctrl_info.field.bw; + bf = agg_report->bf; + + cntrs = &stats->tx.he[bw][nss][mcs][gi][bf]; + } + break; + case WRS_MODE_VHT: + bw = rate_ctrl_info.field.bw; + nss = (rate_ctrl_info.field.mcs_index >> 4); + mcs = (rate_ctrl_info.field.mcs_index & 0xF); + gi = rate_ctrl_info.field.gi; + bf = agg_report->bf; + + cntrs = &stats->tx.vht[bw][nss][mcs][gi][bf]; + break; + case WRS_MODE_HT: + bw = rate_ctrl_info.field.bw; + nss = (rate_ctrl_info.field.mcs_index >> 3); + mcs = (rate_ctrl_info.field.mcs_index & 0x7); + gi = rate_ctrl_info.field.gi; + cntrs = &stats->tx.ht[bw][nss][mcs][gi]; + break; + case WRS_MODE_OFDM: + mcs = rate_ctrl_info.field.mcs_index - RATE_CTRL_OFFSET_OFDM; + cntrs = &stats->tx.ofdm[mcs]; + break; + case WRS_MODE_CCK: + mcs = rate_ctrl_info.field.mcs_index; + cntrs = &stats->tx.cck[mcs]; + break; + default: + return; + } + + cntrs->success += agg_report->success; + cntrs->fail += agg_report->fail; + stats->tx.packet_success += agg_report->success; + stats->tx.packet_fail += agg_report->fail; +} + +static void _cl_stats_update_rx_rate(struct cl_hw *cl_hw, struct cl_rx_stats *rx_stats, + struct hw_rxhdr *rxhdr) +{ + u8 bw, nss, mcs, gi; + + switch (rxhdr->format_mod) { + case FORMATMOD_HE_TRIG: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE); + gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE); + rx_stats->he_trig[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HE_TRIG; + break; + case FORMATMOD_HE_EXT: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE); + gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE); + rx_stats->he_ext[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HE_EXT; + break; + case FORMATMOD_HE_MU: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE); + gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE); + rx_stats->he_mu[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HE_MU; + break; + case FORMATMOD_HE_SU: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_HE); + gi = min_t(u8, rxhdr->gi_type, WRS_GI_MAX_HE); + rx_stats->he_su[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HE_SU; + break; + case FORMATMOD_VHT: + nss = rxhdr->n_sts & 0x3; + mcs = min_t(u8, rxhdr->mcs, WRS_MCS_MAX_VHT); + gi = rxhdr->gi_type & 0x1; + rx_stats->vht[rxhdr->ch_bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_VHT; + break; + case FORMATMOD_HT_MF: + case FORMATMOD_HT_GF: + bw = rxhdr->ch_bw & 0x1; + nss = (rxhdr->mcs >> 3) & 0x3; + mcs = rxhdr->mcs & 0x7; + gi = rxhdr->gi_type & 0x1; + rx_stats->ht[bw][nss][mcs][gi] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_HT; + break; + case FORMATMOD_NON_HT: + if (rxhdr->mcs >= RATE_CTRL_OFFSET_OFDM) { + mcs = (rxhdr->mcs - RATE_CTRL_OFFSET_OFDM) & 0x7; + rx_stats->ofdm[mcs] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_OFDM; + } else if (cl_band_is_24g(cl_hw)) { + mcs = rxhdr->mcs & 0x3; + rx_stats->cck[mcs] += rxhdr->frm_successful_rx; + rx_stats->flag |= RX_STATS_CCK; + } + break; + } + + rx_stats->packet_success += rxhdr->frm_successful_rx; +} + +void cl_stats_init(struct cl_hw *cl_hw) +{ + spin_lock_init(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en && cl_hw_is_prod_or_listener(cl_hw)) { + cl_hw->rx_stats = kzalloc(sizeof(*cl_hw->rx_stats), GFP_ATOMIC); + + if (!cl_hw->rx_stats) + cl_hw->conf->ci_stats_en = false; + } +} + +void cl_stats_deinit(struct cl_hw *cl_hw) +{ + spin_lock_bh(&cl_hw->lock_stats); + + if (cl_hw->conf->ci_stats_en && (cl_hw_is_prod_or_listener(cl_hw))) { + cl_hw->conf->ci_stats_en = false; + + kfree(cl_hw->rx_stats); + cl_hw->rx_stats = NULL; + } + + spin_unlock_bh(&cl_hw->lock_stats); +} + +void cl_stats_sta_add(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + /* + * If allocation failed disable ci_stats_en + * and free the memory of all other stations + */ + bool disable = false; + + if (cl_hw->conf->ci_stats_en) { + /* + * Take regular lock and not BH, + * because cl_sta_add_to_lut() already disables BH + */ + spin_lock(&cl_hw->lock_stats); + + cl_sta->stats = kzalloc(sizeof(*cl_sta->stats), GFP_ATOMIC); + + if (cl_sta->stats) + cll_stats_config_ps(cl_sta); + else + disable = true; + + spin_unlock(&cl_hw->lock_stats); + } + + if (disable && cl_hw->conf->ci_stats_en) { + spin_lock_bh(&cl_hw->lock_stats); + cl_stats_disable(cl_hw); + spin_unlock_bh(&cl_hw->lock_stats); + } +} + +void cl_stats_sta_remove(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + if (!cl_hw->conf->ci_stats_en) + return; + + spin_lock_bh(&cl_hw->lock_stats); + + cl_stats_free(cl_hw, cl_sta); + + spin_unlock_bh(&cl_hw->lock_stats); +} + +void cl_stats_update_tx_agg(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + struct cl_agg_tx_report *agg_report) +{ + struct cl_stats *stats = cl_sta->stats; + + if (!cl_hw->conf->ci_stats_en) + return; + + spin_lock(&cl_hw->lock_stats); + + stats->tx.agg_cntr++; + stats->tx.fail_cntr += agg_report->fail; + _cl_stats_update_tx(cl_hw, cl_sta, agg_report); + + spin_unlock(&cl_hw->lock_stats); +} + +void cl_stats_update_tx_single(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + struct cl_agg_tx_report *agg_report) +{ + if (!cl_hw->conf->ci_stats_en) + return; + + spin_lock(&cl_hw->lock_stats); + + cl_sta->stats->tx.fail_cntr += agg_report->fail; + _cl_stats_update_tx(cl_hw, cl_sta, agg_report); + + spin_unlock(&cl_hw->lock_stats); +} + +void cl_stats_update_rx_rssi(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + s8 rssi[MAX_ANTENNAS]) +{ + int i; + s8 rx_rssi; + + if (!cl_hw->conf->ci_stats_en) + return; + + spin_lock_bh(&cl_hw->lock_stats); + + for (i = 0; i < cl_hw->num_antennas; i++) { + rx_rssi = rssi[i] * -1; + + if (rx_rssi >= 0 && rx_rssi < RSSI_ARR_SIZE) + cl_sta->stats->rssi[rx_rssi][i]++; + } + + spin_unlock_bh(&cl_hw->lock_stats); +} + +void cl_stats_update_rx_rate(struct cl_hw *cl_hw, struct cl_sta *cl_sta, struct hw_rxhdr *rxhdr) +{ + if (!cl_hw->conf->ci_stats_en) + return; + + spin_lock(&cl_hw->lock_stats); + + _cl_stats_update_rx_rate(cl_hw, &cl_sta->stats->rx, rxhdr); + cl_sta->stats->fec_coding[rxhdr->fec_coding]++; + + spin_unlock(&cl_hw->lock_stats); +} + +void cl_stats_update_rx_rate_production(struct cl_hw *cl_hw, struct hw_rxhdr *rxhdr) +{ + if (!cl_hw->conf->ci_stats_en) + return; + + spin_lock(&cl_hw->lock_stats); + + _cl_stats_update_rx_rate(cl_hw, cl_hw->rx_stats, rxhdr); + + spin_unlock(&cl_hw->lock_stats); +} + +void cl_stats_update_ps(struct cl_hw *cl_hw, struct cl_sta *cl_sta, bool is_ps) +{ + struct cl_ps_stats *ps; + + if (!cl_hw->conf->ci_stats_en) + return; + + spin_lock_bh(&cl_hw->lock_stats); + + ps = &cl_sta->stats->ps; + + if (ps->is_ps == is_ps) + goto out; + + ps->is_ps = is_ps; + + if (is_ps) { + ps->timestamp_sleep = jiffies; + } else { + unsigned long sleep_time = jiffies_to_msecs(jiffies - ps->timestamp_sleep); + + if (sleep_time <= 50) + ps->period[PS_PERIOD_50MS]++; + else if (sleep_time <= 100) + ps->period[PS_PERIOD_100MS]++; + else if (sleep_time <= 250) + ps->period[PS_PERIOD_250MS]++; + else if (sleep_time <= 500) + ps->period[PS_PERIOD_500MS]++; + else if (sleep_time <= 750) + ps->period[PS_PERIOD_750MS]++; + else if (sleep_time <= 1000) + ps->period[PS_PERIOD_1000MS]++; + else if (sleep_time <= 2000) + ps->period[PS_PERIOD_2000MS]++; + else if (sleep_time <= 5000) + ps->period[PS_PERIOD_5000MS]++; + else if (sleep_time <= 10000) + ps->period[PS_PERIOD_10000MS]++; + else + ps->period[PS_PERIOD_ABOVE]++; + } + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +int cl_stats_get_rssi(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + struct cl_stats *stats = NULL; + u32 i = 0, j = 0; + u64 total_rssi = 0; + s8 avg_signal = 0; + + if (!cl_hw->conf->ci_stats_en) + return 0; + + u64 avg_rssi[MAX_ANTENNAS] = {0}; + u64 sum_rssi[MAX_ANTENNAS] = {0}; + + spin_lock_bh(&cl_hw->lock_stats); + + stats = cl_sta->stats; + + if (!stats) + goto out; + + for (i = 0; i < RSSI_ARR_SIZE; i++) { + total_rssi = 0; + + for (j = 0; j < cl_hw->num_antennas; j++) { + sum_rssi[j] += stats->rssi[i][j]; + avg_rssi[j] += i * stats->rssi[i][j]; + } + } + + for (j = 0; j < cl_hw->num_antennas; j++) + if (sum_rssi[j]) + avg_rssi[j] = div64_u64(avg_rssi[j], sum_rssi[j]); + + for (j = 0; j < cl_hw->num_antennas; j++) + total_rssi += avg_rssi[j]; + + avg_signal = -div64_u64(total_rssi, cl_hw->num_antennas); +out: + spin_unlock_bh(&cl_hw->lock_stats); + + return avg_signal; +} + +void cl_stats_get_tx(struct cl_hw *cl_hw, struct cl_sta *cl_sta, + u64 *total_tx_success, u64 *total_tx_fail) +{ + if (!cl_hw->conf->ci_stats_en) + return; + + spin_lock_bh(&cl_hw->lock_stats); + + if (!cl_sta->stats) + goto out; + + *total_tx_success = cl_sta->stats->tx.packet_success; + *total_tx_fail = cl_sta->stats->tx.packet_fail; + +out: + spin_unlock_bh(&cl_hw->lock_stats); +} + +u64 cl_stats_get_rx(struct cl_hw *cl_hw, struct cl_sta *cl_sta) +{ + u64 total_rx_packets = 0; + + if (!cl_hw->conf->ci_stats_en) + return 0; + + spin_lock_bh(&cl_hw->lock_stats); + + if (!cl_sta->stats) + goto out; + + total_rx_packets = cl_sta->stats->rx.packet_success; + +out: + spin_unlock_bh(&cl_hw->lock_stats); + + return total_rx_packets; +} + -- 2.36.1