Commit 4203263d authored by Emmanuel Grumbach's avatar Emmanuel Grumbach

iwlwifi: mvm: implement the BlockAck related debug triggers

BlockAck sessions can have events that are interesting to
debug. When we send or receive a BAR, it is may indicate
that something bad is happening. Even more so when mac80211
tells us that a frame timed out in the reodering buffer.
Add a few triggers for BlockAck session debugging.
Allow per-TID debugging.
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 31eb07f5
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY * GPL LICENSE SUMMARY
* *
* Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
* BSD LICENSE * BSD LICENSE
* *
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -1239,6 +1239,8 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) ...@@ -1239,6 +1239,8 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
sizeof(struct iwl_fw_dbg_trigger_txq_timer); sizeof(struct iwl_fw_dbg_trigger_txq_timer);
trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] = trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] =
sizeof(struct iwl_fw_dbg_trigger_time_event); sizeof(struct iwl_fw_dbg_trigger_time_event);
trigger_tlv_sz[FW_DBG_TRIGGER_BA] =
sizeof(struct iwl_fw_dbg_trigger_ba);
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) { for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
if (pieces->dbg_trigger_tlv[i]) { if (pieces->dbg_trigger_tlv[i]) {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY * GPL LICENSE SUMMARY
* *
* Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2014 Intel Mobile Communications GmbH * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
* BSD LICENSE * BSD LICENSE
* *
* Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2014 Intel Mobile Communications GmbH * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -254,6 +254,7 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) ...@@ -254,6 +254,7 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
* detection. * detection.
* @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related
* events. * events.
* @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events.
*/ */
enum iwl_fw_dbg_trigger { enum iwl_fw_dbg_trigger {
FW_DBG_TRIGGER_INVALID = 0, FW_DBG_TRIGGER_INVALID = 0,
...@@ -267,6 +268,7 @@ enum iwl_fw_dbg_trigger { ...@@ -267,6 +268,7 @@ enum iwl_fw_dbg_trigger {
FW_DBG_TRIGGER_RSSI, FW_DBG_TRIGGER_RSSI,
FW_DBG_TRIGGER_TXQ_TIMERS, FW_DBG_TRIGGER_TXQ_TIMERS,
FW_DBG_TRIGGER_TIME_EVENT, FW_DBG_TRIGGER_TIME_EVENT,
FW_DBG_TRIGGER_BA,
/* must be last */ /* must be last */
FW_DBG_TRIGGER_MAX, FW_DBG_TRIGGER_MAX,
......
...@@ -663,6 +663,33 @@ struct iwl_fw_dbg_trigger_time_event { ...@@ -663,6 +663,33 @@ struct iwl_fw_dbg_trigger_time_event {
} __packed time_events[16]; } __packed time_events[16];
} __packed; } __packed;
/**
* struct iwl_fw_dbg_trigger_ba - configures BlockAck related trigger
* rx_ba_start: tid bitmap to configure on what tid the trigger should occur
* when an Rx BlockAck session is started.
* rx_ba_stop: tid bitmap to configure on what tid the trigger should occur
* when an Rx BlockAck session is stopped.
* tx_ba_start: tid bitmap to configure on what tid the trigger should occur
* when a Tx BlockAck session is started.
* tx_ba_stop: tid bitmap to configure on what tid the trigger should occur
* when a Tx BlockAck session is stopped.
* rx_bar: tid bitmap to configure on what tid the trigger should occur
* when a BAR is received (for a Tx BlockAck session).
* tx_bar: tid bitmap to configure on what tid the trigger should occur
* when a BAR is send (for an Rx BlocAck session).
* frame_timeout: tid bitmap to configure on what tid the trigger should occur
* when a frame times out in the reodering buffer.
*/
struct iwl_fw_dbg_trigger_ba {
__le16 rx_ba_start;
__le16 rx_ba_stop;
__le16 tx_ba_start;
__le16 tx_ba_stop;
__le16 rx_bar;
__le16 tx_bar;
__le16 frame_timeout;
} __packed;
/** /**
* struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration. * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
* @id: conf id * @id: conf id
......
...@@ -737,6 +737,60 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) ...@@ -737,6 +737,60 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg)
return true; return true;
} }
#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \
do { \
if (!(le16_to_cpu(_tid_bm) & BIT(_tid))) \
break; \
iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt); \
} while (0)
static void
iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 rx_ba_ssn,
enum ieee80211_ampdu_mlme_action action)
{
struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_fw_dbg_trigger_ba *ba_trig;
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
return;
trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
ba_trig = (void *)trig->data;
if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
return;
switch (action) {
case IEEE80211_AMPDU_TX_OPERATIONAL: {
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_start, tid,
"TX AGG START: MAC %pM tid %d ssn %d\n",
sta->addr, tid, tid_data->ssn);
break;
}
case IEEE80211_AMPDU_TX_STOP_CONT:
CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_stop, tid,
"TX AGG STOP: MAC %pM tid %d\n",
sta->addr, tid);
break;
case IEEE80211_AMPDU_RX_START:
CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_start, tid,
"RX AGG START: MAC %pM tid %d ssn %d\n",
sta->addr, tid, rx_ba_ssn);
break;
case IEEE80211_AMPDU_RX_STOP:
CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_stop, tid,
"RX AGG STOP: MAC %pM tid %d\n",
sta->addr, tid);
break;
default:
break;
}
}
static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action, enum ieee80211_ampdu_mlme_action action,
...@@ -813,6 +867,16 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, ...@@ -813,6 +867,16 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
if (!ret) {
u16 rx_ba_ssn = 0;
if (action == IEEE80211_AMPDU_RX_START)
rx_ba_ssn = *ssn;
iwl_mvm_ampdu_check_trigger(mvm, vif, sta, tid,
rx_ba_ssn, action);
}
mutex_unlock(&mvm->mutex); mutex_unlock(&mvm->mutex);
/* /*
...@@ -3904,9 +3968,9 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, ...@@ -3904,9 +3968,9 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
mutex_unlock(&mvm->mutex); mutex_unlock(&mvm->mutex);
} }
static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
const struct ieee80211_event *event) const struct ieee80211_event *event)
{ {
#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \ #define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \
do { \ do { \
...@@ -3915,7 +3979,6 @@ static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, ...@@ -3915,7 +3979,6 @@ static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\ iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\
} while (0) } while (0)
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_fw_dbg_trigger_tlv *trig; struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_fw_dbg_trigger_mlme *trig_mlme; struct iwl_fw_dbg_trigger_mlme *trig_mlme;
...@@ -3959,6 +4022,75 @@ static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, ...@@ -3959,6 +4022,75 @@ static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
#undef CHECK_MLME_TRIGGER #undef CHECK_MLME_TRIGGER
} }
static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
const struct ieee80211_event *event)
{
struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_fw_dbg_trigger_ba *ba_trig;
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
return;
trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
ba_trig = (void *)trig->data;
if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
return;
if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid)))
return;
iwl_mvm_fw_dbg_collect_trig(mvm, trig,
"BAR received from %pM, tid %d, ssn %d",
event->u.ba.sta->addr, event->u.ba.tid,
event->u.ba.ssn);
}
static void
iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
const struct ieee80211_event *event)
{
struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_fw_dbg_trigger_ba *ba_trig;
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
return;
trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
ba_trig = (void *)trig->data;
if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
return;
if (!(le16_to_cpu(ba_trig->frame_timeout) & BIT(event->u.ba.tid)))
return;
iwl_mvm_fw_dbg_collect_trig(mvm, trig,
"Frame from %pM timed out, tid %d",
event->u.ba.sta->addr, event->u.ba.tid);
}
static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
const struct ieee80211_event *event)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
switch (event->type) {
case MLME_EVENT:
iwl_mvm_event_mlme_callback(mvm, vif, event);
break;
case BAR_RX_EVENT:
iwl_mvm_event_bar_rx_callback(mvm, vif, event);
break;
case BA_FRAME_TIMEOUT:
iwl_mvm_event_frame_timeout_callback(mvm, vif, event);
break;
default:
break;
}
}
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,
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY * GPL LICENSE SUMMARY
* *
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
* BSD LICENSE * BSD LICENSE
* *
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -70,6 +70,30 @@ ...@@ -70,6 +70,30 @@
#include "mvm.h" #include "mvm.h"
#include "sta.h" #include "sta.h"
static void
iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
u16 tid, u16 ssn)
{
struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_fw_dbg_trigger_ba *ba_trig;
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
return;
trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
ba_trig = (void *)trig->data;
if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
return;
if (!(le16_to_cpu(ba_trig->tx_bar) & BIT(tid)))
return;
iwl_mvm_fw_dbg_collect_trig(mvm, trig,
"BAR sent to %pM, tid %d, ssn %d",
addr, tid, ssn);
}
/* /*
* Sets most of the Tx cmd's fields * Sets most of the Tx cmd's fields
*/ */
...@@ -101,12 +125,15 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, ...@@ -101,12 +125,15 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
} else if (ieee80211_is_back_req(fc)) { } else if (ieee80211_is_back_req(fc)) {
struct ieee80211_bar *bar = (void *)skb->data; struct ieee80211_bar *bar = (void *)skb->data;
u16 control = le16_to_cpu(bar->control); u16 control = le16_to_cpu(bar->control);
u16 ssn = le16_to_cpu(bar->start_seq_num);
tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
tx_cmd->tid_tspec = (control & tx_cmd->tid_tspec = (control &
IEEE80211_BAR_CTRL_TID_INFO_MASK) >> IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
IEEE80211_BAR_CTRL_TID_INFO_SHIFT; IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT); WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT);
iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec,
ssn);
} else { } else {
tx_cmd->tid_tspec = IWL_TID_NON_QOS; tx_cmd->tid_tspec = IWL_TID_NON_QOS;
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
......
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