blob: f5a821c563f3778ffb9eb86edd2c5c27ef7d0162 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/iwlwifi/platform/stats.h"
#include <lib/async/dispatcher.h>
#include <lib/async/task.h>
#include <lib/async/time.h> // for async_now()
#include <zircon/time.h>
#include <mutex>
#include "third_party/driver-lib/wlan/ieee80211.h"
#include "third_party/iwlwifi/platform/compiler.h"
#include "third_party/iwlwifi/platform/debug.h"
#include "third_party/iwlwifi/platform/kernel.h"
#define IWL_STATS_INTERVAL ZX_SEC(20)
static const char* descs[] = {"ints", "fw_cmd", "be", "bc", "mc", "uni", "from_mlme",
"data->fw", "cmd->fw", "txq_drop", "buf", "drop", "to"};
struct iwl_stats_data {
async_dispatcher_t* dispatcher;
async_task_t task;
int8_t last_rssi_dbm;
uint32_t last_data_rate;
zx_duration_t max_isr_duration;
zx_duration_t total_isr_duration;
size_t counters[IWL_STATS_CNT_MAX];
};
static struct iwl_stats_data stats_data;
static std::mutex mutex_lock;
static void iwl_stats_schedule_next(zx_duration_t interval) {
async_cancel_task(stats_data.dispatcher, &stats_data.task);
stats_data.task.deadline = interval + async_now(stats_data.dispatcher);
async_post_task(stats_data.dispatcher, &stats_data.task);
}
void iwl_stats_report_wk(async_dispatcher_t* dispatcher, async_task_t* task, zx_status_t status) {
const std::lock_guard<std::mutex> lock(mutex_lock);
// TODO(fxb/101542): better debug info for bug triage.
//clang-format off
IWL_LOG(linfo,
"rssi:%d rate:%u [%s:%zu %s:%zu (%s:%zu,%s:%zu,%s:%zu,%s:%zu)] "
"[%s:%zu %s:%zu %s:%zu] [%s:%zu] reorder:[%s:%zu %s:%zu %s:%zu]",
stats_data.last_rssi_dbm, stats_data.last_data_rate, descs[IWL_STATS_CNT_INTS_FROM_FW],
stats_data.counters[IWL_STATS_CNT_INTS_FROM_FW], descs[IWL_STATS_CNT_CMD_FROM_FW],
stats_data.counters[IWL_STATS_CNT_CMD_FROM_FW], descs[IWL_STATS_CNT_BCAST_TO_MLME],
stats_data.counters[IWL_STATS_CNT_BCAST_TO_MLME], descs[IWL_STATS_CNT_MCAST_TO_MLME],
stats_data.counters[IWL_STATS_CNT_MCAST_TO_MLME], descs[IWL_STATS_CNT_UNICAST_TO_MLME],
stats_data.counters[IWL_STATS_CNT_UNICAST_TO_MLME], descs[IWL_STATS_CNT_BEACON_TO_MLME],
stats_data.counters[IWL_STATS_CNT_BEACON_TO_MLME], descs[IWL_STATS_CNT_DATA_FROM_MLME],
stats_data.counters[IWL_STATS_CNT_DATA_FROM_MLME], descs[IWL_STATS_CNT_DATA_TO_FW],
stats_data.counters[IWL_STATS_CNT_DATA_TO_FW], descs[IWL_STATS_CNT_CMD_TO_FW],
stats_data.counters[IWL_STATS_CNT_CMD_TO_FW], descs[IWL_STATS_CNT_TXQ_DROP],
stats_data.counters[IWL_STATS_CNT_TXQ_DROP], descs[IWL_STATS_CNT_FRAMES_BUFFERED],
stats_data.counters[IWL_STATS_CNT_FRAMES_BUFFERED], descs[IWL_STATS_CNT_REORDER_DROP],
stats_data.counters[IWL_STATS_CNT_REORDER_DROP], descs[IWL_STATS_CNT_REORDER_TIMEOUT],
stats_data.counters[IWL_STATS_CNT_REORDER_TIMEOUT]);
// clang - format on
uint64_t avg_isr_duration = 0;
if (stats_data.counters[IWL_STATS_CNT_INTS_FROM_FW]) {
avg_isr_duration =
stats_data.total_isr_duration / stats_data.counters[IWL_STATS_CNT_INTS_FROM_FW];
}
IWL_LOG(linfo, "rx isr: avg:%zuns, max:%zuns", avg_isr_duration, stats_data.max_isr_duration);
iwl_stats_schedule_next(IWL_STATS_INTERVAL);
}
void iwl_stats_init(async_dispatcher_t* dispatcher) {
const std::lock_guard<std::mutex> lock(mutex_lock);
memset(&stats_data, 0, sizeof(stats_data));
stats_data.dispatcher = dispatcher;
stats_data.task.handler = &iwl_stats_report_wk;
}
void iwl_stats_start_reporting(void) { iwl_stats_schedule_next(ZX_SEC(0)); }
void iwl_stats_update_last_rssi(int8_t rssi_dbm) {
const std::lock_guard<std::mutex> lock(mutex_lock);
stats_data.last_rssi_dbm = rssi_dbm;
}
void iwl_stats_update_date_rate(uint32_t data_rate) {
const std::lock_guard<std::mutex> lock(mutex_lock);
stats_data.last_data_rate = data_rate;
}
void iwl_stats_update_rx_isr_duration(zx_duration_t isr_duration) {
const std::lock_guard<std::mutex> lock(mutex_lock);
stats_data.max_isr_duration = std::max(stats_data.max_isr_duration, isr_duration);
stats_data.total_isr_duration += isr_duration;
}
size_t iwl_stats_read(enum iwl_stats_counter_index index) {
ZX_ASSERT(index < IWL_STATS_CNT_MAX);
const std::lock_guard<std::mutex> lock(mutex_lock);
return stats_data.counters[index];
}
void iwl_stats_inc(enum iwl_stats_counter_index index) {
ZX_ASSERT(index < IWL_STATS_CNT_MAX);
const std::lock_guard<std::mutex> lock(mutex_lock);
stats_data.counters[index]++;
}
void iwl_stats_analyze_rx(const wlan_rx_packet_t* pkt) {
struct ieee80211_frame_header* fh = (struct ieee80211_frame_header*)pkt->mac_frame_buffer;
uint16_t frame_ctrl = fh->frame_ctrl;
uint8_t* addr1 = fh->addr1;
if (is_broadcast_addr(addr1)) {
if (frame_ctrl == 0x0080) {
iwl_stats_inc(IWL_STATS_CNT_BEACON_TO_MLME);
} else {
iwl_stats_inc(IWL_STATS_CNT_BCAST_TO_MLME);
}
} else if (is_multicast_addr(addr1)) {
iwl_stats_inc(IWL_STATS_CNT_MCAST_TO_MLME);
} else {
iwl_stats_inc(IWL_STATS_CNT_UNICAST_TO_MLME);
}
}