Commit 91a8bcde authored by Johannes Berg's avatar Johannes Berg Committed by Emmanuel Grumbach

iwlwifi: mvm: support radio statistics as global survey

Export the radio statistics from the statistics v10 API (if the
firmware also has the capability to fill these statistics) using
the global survey data facility.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 777c9b6b
...@@ -286,6 +286,7 @@ enum iwl_ucode_tlv_api { ...@@ -286,6 +286,7 @@ enum iwl_ucode_tlv_api {
* which also implies support for the scheduler configuration command * which also implies support for the scheduler configuration command
* @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching
* @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
* @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics
*/ */
enum iwl_ucode_tlv_capa { enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0), IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0),
...@@ -300,6 +301,7 @@ enum iwl_ucode_tlv_capa { ...@@ -300,6 +301,7 @@ enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12), IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12),
IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = BIT(13), IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = BIT(13),
IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18), IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18),
IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = BIT(22),
}; };
/* The default calibrate table size if not specified by firmware file */ /* The default calibrate table size if not specified by firmware file */
......
...@@ -290,15 +290,7 @@ struct mvm_statistics_rx { ...@@ -290,15 +290,7 @@ struct mvm_statistics_rx {
* *
* By default, uCode issues this notification after receiving a beacon * By default, uCode issues this notification after receiving a beacon
* while associated. To disable this behavior, set DISABLE_NOTIF flag in the * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
* REPLY_STATISTICS_CMD 0x9c, above. * STATISTICS_CMD (0x9c), below.
*
* Statistics counters continue to increment beacon after beacon, but are
* cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
* 0x9c with CLEAR_STATS bit set (see above).
*
* uCode also issues this notification during scans. uCode clears statistics
* appropriately so that each notification contains statistics for only the
* one channel that has just been scanned.
*/ */
struct iwl_notif_statistics_v8 { struct iwl_notif_statistics_v8 {
...@@ -315,4 +307,11 @@ struct iwl_notif_statistics_v10 { ...@@ -315,4 +307,11 @@ struct iwl_notif_statistics_v10 {
struct mvm_statistics_general_v8 general; struct mvm_statistics_general_v8 general;
} __packed; /* STATISTICS_NTFY_API_S_VER_10 */ } __packed; /* STATISTICS_NTFY_API_S_VER_10 */
#define IWL_STATISTICS_FLG_CLEAR 0x1
#define IWL_STATISTICS_FLG_DISABLE_NOTIF 0x2
struct iwl_statistics_cmd {
__le32 flags;
} __packed; /* STATISTICS_CMD_API_S_VER_1 */
#endif /* __fw_api_stats_h__ */ #endif /* __fw_api_stats_h__ */
...@@ -192,6 +192,7 @@ enum { ...@@ -192,6 +192,7 @@ enum {
BEACON_NOTIFICATION = 0x90, BEACON_NOTIFICATION = 0x90,
BEACON_TEMPLATE_CMD = 0x91, BEACON_TEMPLATE_CMD = 0x91,
TX_ANT_CONFIGURATION_CMD = 0x98, TX_ANT_CONFIGURATION_CMD = 0x98,
STATISTICS_CMD = 0x9c,
STATISTICS_NOTIFICATION = 0x9d, STATISTICS_NOTIFICATION = 0x9d,
EOSP_NOTIFICATION = 0x9e, EOSP_NOTIFICATION = 0x9e,
REDUCE_TX_POWER_CMD = 0x9f, REDUCE_TX_POWER_CMD = 0x9f,
......
...@@ -1091,6 +1091,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) ...@@ -1091,6 +1091,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
mvm->vif_count = 0; mvm->vif_count = 0;
mvm->rx_ba_sessions = 0; mvm->rx_ba_sessions = 0;
/* keep statistics ticking */
iwl_mvm_accu_radio_stats(mvm);
} }
int __iwl_mvm_mac_start(struct iwl_mvm *mvm) int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
...@@ -1213,6 +1216,11 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) ...@@ -1213,6 +1216,11 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
{ {
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
/* firmware counters are obviously reset now, but we shouldn't
* partially track so also clear the fw_reset_accu counters.
*/
memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats));
/* /*
* Disallow low power states when the FW is down by taking * Disallow low power states when the FW is down by taking
* the UCODE_DOWN ref. in case of ongoing hw restart the * the UCODE_DOWN ref. in case of ongoing hw restart the
...@@ -3581,6 +3589,55 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, ...@@ -3581,6 +3589,55 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
} }
} }
static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
memset(survey, 0, sizeof(*survey));
/* only support global statistics right now */
if (idx != 0)
return -ENOENT;
if (!(mvm->fw->ucode_capa.capa[0] &
IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return -ENOENT;
mutex_lock(&mvm->mutex);
if (mvm->ucode_loaded) {
ret = iwl_mvm_request_statistics(mvm);
if (ret)
goto out;
}
survey->filled = SURVEY_INFO_TIME |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_SCAN;
survey->time = mvm->accu_radio_stats.on_time_rf +
mvm->radio_stats.on_time_rf;
do_div(survey->time, USEC_PER_MSEC);
survey->time_rx = mvm->accu_radio_stats.rx_time +
mvm->radio_stats.rx_time;
do_div(survey->time_rx, USEC_PER_MSEC);
survey->time_tx = mvm->accu_radio_stats.tx_time +
mvm->radio_stats.tx_time;
do_div(survey->time_tx, USEC_PER_MSEC);
survey->time_scan = mvm->accu_radio_stats.on_time_scan +
mvm->radio_stats.on_time_scan;
do_div(survey->time_scan, USEC_PER_MSEC);
out:
mutex_unlock(&mvm->mutex);
return ret;
}
const struct ieee80211_ops iwl_mvm_hw_ops = { const struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx, .tx = iwl_mvm_mac_tx,
.ampdu_action = iwl_mvm_mac_ampdu_action, .ampdu_action = iwl_mvm_mac_ampdu_action,
...@@ -3647,4 +3704,5 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { ...@@ -3647,4 +3704,5 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
#endif #endif
.set_default_unicast_key = iwl_mvm_set_default_unicast_key, .set_default_unicast_key = iwl_mvm_set_default_unicast_key,
#endif #endif
.get_survey = iwl_mvm_mac_get_survey,
}; };
...@@ -593,6 +593,13 @@ struct iwl_mvm { ...@@ -593,6 +593,13 @@ struct iwl_mvm {
struct mvm_statistics_rx rx_stats; struct mvm_statistics_rx rx_stats;
struct {
u64 rx_time;
u64 tx_time;
u64 on_time_rf;
u64 on_time_scan;
} radio_stats, accu_radio_stats;
u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES]; atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
...@@ -951,12 +958,13 @@ static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm) ...@@ -951,12 +958,13 @@ static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm)
} }
/* Statistics */ /* Statistics */
int iwl_mvm_rx_reply_statistics(struct iwl_mvm *mvm, void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_packet *pkt);
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd); struct iwl_device_cmd *cmd);
int iwl_mvm_request_statistics(struct iwl_mvm *mvm);
void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm);
/* NVM */ /* NVM */
int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
......
...@@ -311,6 +311,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { ...@@ -311,6 +311,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(REPLY_RX_MPDU_CMD), CMD(REPLY_RX_MPDU_CMD),
CMD(BEACON_NOTIFICATION), CMD(BEACON_NOTIFICATION),
CMD(BEACON_TEMPLATE_CMD), CMD(BEACON_TEMPLATE_CMD),
CMD(STATISTICS_CMD),
CMD(STATISTICS_NOTIFICATION), CMD(STATISTICS_NOTIFICATION),
CMD(EOSP_NOTIFICATION), CMD(EOSP_NOTIFICATION),
CMD(REDUCE_TX_POWER_CMD), CMD(REDUCE_TX_POWER_CMD),
......
...@@ -496,16 +496,9 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac, ...@@ -496,16 +496,9 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
} }
} }
/* void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
* iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler struct iwl_rx_packet *pkt)
*
* TODO: This handler is implemented partially.
*/
int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{ {
struct iwl_rx_packet *pkt = rxb_addr(rxb);
size_t v8_len = sizeof(struct iwl_notif_statistics_v8); size_t v8_len = sizeof(struct iwl_notif_statistics_v8);
size_t v10_len = sizeof(struct iwl_notif_statistics_v10); size_t v10_len = sizeof(struct iwl_notif_statistics_v10);
struct iwl_mvm_stat_data data = { struct iwl_mvm_stat_data data = {
...@@ -525,6 +518,13 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, ...@@ -525,6 +518,13 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
stats->general.beacon_filter_average_energy; stats->general.beacon_filter_average_energy;
iwl_mvm_update_rx_statistics(mvm, &stats->rx); iwl_mvm_update_rx_statistics(mvm, &stats->rx);
mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time);
mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time);
mvm->radio_stats.on_time_rf =
le64_to_cpu(stats->general.on_time_rf);
mvm->radio_stats.on_time_scan =
le64_to_cpu(stats->general.on_time_scan);
} else { } else {
struct iwl_notif_statistics_v8 *stats = (void *)&pkt->data; struct iwl_notif_statistics_v8 *stats = (void *)&pkt->data;
...@@ -549,9 +549,16 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, ...@@ -549,9 +549,16 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
IEEE80211_IFACE_ITER_NORMAL, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_stat_iterator, iwl_mvm_stat_iterator,
&data); &data);
return 0; return;
invalid: invalid:
IWL_ERR(mvm, "received invalid statistics size (%d)!\n", IWL_ERR(mvm, "received invalid statistics size (%d)!\n",
iwl_rx_packet_payload_len(pkt)); iwl_rx_packet_payload_len(pkt));
}
int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb));
return 0; return 0;
} }
...@@ -643,6 +643,35 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ...@@ -643,6 +643,35 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
ieee80211_request_smps(vif, smps_mode); ieee80211_request_smps(vif, smps_mode);
} }
int iwl_mvm_request_statistics(struct iwl_mvm *mvm)
{
struct iwl_statistics_cmd scmd = {};
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;
iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt);
iwl_free_resp(&cmd);
return 0;
}
void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm)
{
mvm->accu_radio_stats.rx_time += mvm->radio_stats.rx_time;
mvm->accu_radio_stats.tx_time += mvm->radio_stats.tx_time;
mvm->accu_radio_stats.on_time_rf += mvm->radio_stats.on_time_rf;
mvm->accu_radio_stats.on_time_scan += mvm->radio_stats.on_time_scan;
}
static void iwl_mvm_diversity_iter(void *_data, u8 *mac, static void iwl_mvm_diversity_iter(void *_data, u8 *mac,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
......
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