Skip to content

Commit

Permalink
iwlwifi: mvm: disconnect in case of bad channel switch parameters
Browse files Browse the repository at this point in the history
In case we receive channel switch announcement with immediate
quiet and unknown switching time, we will switch when FW identifies
AP left channel. However, if AP remains on channel, we will
eventually get TX queue hang. Init a work to disconnect if
switch doesn't occur within 1500 milliseconds. Do it also
for a too long channel switch.

Signed-off-by: Sara Sharon <[email protected]>
Signed-off-by: Luca Coelho <[email protected]>
  • Loading branch information
sara-s authored and lucacoelho committed Mar 22, 2019
1 parent 7773886 commit f678061
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 77 deletions.
7 changes: 4 additions & 3 deletions drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright(c) 2015 - 2017 Intel Deutschland GmbH
* Copyright(c) 2018 Intel Corporation
* Copyright(c) 2018 - 2019 Intel Corporation
*
* 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
Expand All @@ -31,7 +31,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright(c) 2015 - 2017 Intel Deutschland GmbH
* Copyright(c) 2018 Intel Corporation
* Copyright(c) 2018 - 2019 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -1573,6 +1573,7 @@ void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm,

rcu_read_lock();
vif = rcu_dereference(mvm->vif_id_to_mac[mac_id]);
mvmvif = iwl_mvm_vif_from_mac80211(vif);

switch (vif->type) {
case NL80211_IFTYPE_AP:
Expand All @@ -1581,7 +1582,6 @@ void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm,
csa_vif != vif))
goto out_unlock;

mvmvif = iwl_mvm_vif_from_mac80211(csa_vif);
csa_id = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color);
if (WARN(csa_id != id_n_color,
"channel switch noa notification on unexpected vif (csa_vif=%d, notif=%d)",
Expand All @@ -1602,6 +1602,7 @@ void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm,
return;
case NL80211_IFTYPE_STATION:
iwl_mvm_csa_client_absent(mvm, vif);
cancel_delayed_work_sync(&mvmvif->csa_work);
ieee80211_chswitch_done(vif, true);
break;
default:
Expand Down
171 changes: 99 additions & 72 deletions drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,91 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd);
}

static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;

mutex_lock(&mvm->mutex);

if (mvmvif->csa_failed) {
mvmvif->csa_failed = false;
ret = -EIO;
goto out_unlock;
}

if (vif->type == NL80211_IFTYPE_STATION) {
struct iwl_mvm_sta *mvmsta;

mvmvif->csa_bcn_pending = false;
mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
mvmvif->ap_sta_id);

if (WARN_ON(!mvmsta)) {
ret = -EIO;
goto out_unlock;
}

iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);

iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);

ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
if (ret)
goto out_unlock;

iwl_mvm_stop_session_protection(mvm, vif);
}

mvmvif->ps_disabled = false;

ret = iwl_mvm_power_update_ps(mvm);

out_unlock:
mutex_unlock(&mvm->mutex);

return ret;
}

static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_chan_switch_te_cmd cmd = {
.mac_id = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
mvmvif->color)),
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
};

IWL_DEBUG_MAC80211(mvm, "Abort CSA on mac %d\n", mvmvif->id);

mutex_lock(&mvm->mutex);
WARN_ON(iwl_mvm_send_cmd_pdu(mvm,
WIDE_ID(MAC_CONF_GROUP,
CHANNEL_SWITCH_TIME_EVENT_CMD),
0, sizeof(cmd), &cmd));
mutex_unlock(&mvm->mutex);

WARN_ON(iwl_mvm_post_channel_switch(hw, vif));
}

static void iwl_mvm_channel_switch_disconnect_wk(struct work_struct *wk)
{
struct iwl_mvm *mvm;
struct iwl_mvm_vif *mvmvif;
struct ieee80211_vif *vif;

mvmvif = container_of(wk, struct iwl_mvm_vif, csa_work.work);
vif = container_of((void *)mvmvif, struct ieee80211_vif, drv_priv);
mvm = mvmvif->mvm;

iwl_mvm_abort_channel_switch(mvm->hw, vif);
ieee80211_chswitch_done(vif, false);
}

static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
Expand Down Expand Up @@ -1626,6 +1711,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
}

iwl_mvm_tcm_add_vif(mvm, vif);
INIT_DELAYED_WORK(&mvmvif->csa_work,
iwl_mvm_channel_switch_disconnect_wk);

if (vif->type == NL80211_IFTYPE_MONITOR)
mvm->monitor_on = true;
Expand Down Expand Up @@ -4484,6 +4571,7 @@ static int iwl_mvm_schedule_client_csa(struct iwl_mvm *mvm,
0, sizeof(cmd), &cmd);
}

#define IWL_MAX_CSA_BLOCK_TX 1500
static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
Expand Down Expand Up @@ -4548,8 +4636,18 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
((vif->bss_conf.beacon_int * (chsw->count - 1) -
IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024);

if (chsw->block_tx)
if (chsw->block_tx) {
iwl_mvm_csa_client_absent(mvm, vif);
/*
* In case of undetermined / long time with immediate
* quiet monitor status to gracefully disconnect
*/
if (!chsw->count ||
chsw->count * vif->bss_conf.beacon_int >
IWL_MAX_CSA_BLOCK_TX)
schedule_delayed_work(&mvmvif->csa_work,
msecs_to_jiffies(IWL_MAX_CSA_BLOCK_TX));
}

if (mvmvif->bf_data.bf_enabled) {
ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
Expand Down Expand Up @@ -4584,54 +4682,6 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
return ret;
}

static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;

mutex_lock(&mvm->mutex);

if (mvmvif->csa_failed) {
mvmvif->csa_failed = false;
ret = -EIO;
goto out_unlock;
}

if (vif->type == NL80211_IFTYPE_STATION) {
struct iwl_mvm_sta *mvmsta;

mvmvif->csa_bcn_pending = false;
mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
mvmvif->ap_sta_id);

if (WARN_ON(!mvmsta)) {
ret = -EIO;
goto out_unlock;
}

iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);

iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);

ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
if (ret)
goto out_unlock;

iwl_mvm_stop_session_protection(mvm, vif);
}

mvmvif->ps_disabled = false;

ret = iwl_mvm_power_update_ps(mvm);

out_unlock:
mutex_unlock(&mvm->mutex);

return ret;
}

static void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel_switch *chsw)
Expand All @@ -4658,29 +4708,6 @@ static void iwl_mvm_channel_switch_rx_beacon(struct ieee80211_hw *hw,
CMD_ASYNC, sizeof(cmd), &cmd));
}

static void iwl_mvm_abort_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_chan_switch_te_cmd cmd = {
.mac_id = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
mvmvif->color)),
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
};

IWL_DEBUG_MAC80211(mvm, "Abort CSA on mac %d\n", mvmvif->id);

mutex_lock(&mvm->mutex);
WARN_ON(iwl_mvm_send_cmd_pdu(mvm,
WIDE_ID(MAC_CONF_GROUP,
CHANNEL_SWITCH_TIME_EVENT_CMD),
0, sizeof(cmd), &cmd));
mutex_unlock(&mvm->mutex);

WARN_ON(iwl_mvm_post_channel_switch(hw, vif));
}

static void iwl_mvm_flush_no_vif(struct iwl_mvm *mvm, u32 queues, bool drop)
{
int i;
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ struct iwl_mvm_vif {
bool csa_countdown;
bool csa_failed;
u16 csa_target_freq;
struct delayed_work csa_work;

/* Indicates that we are waiting for a beacon on a new channel */
bool csa_bcn_pending;
Expand Down
5 changes: 3 additions & 2 deletions drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
* Copyright(c) 2018 Intel Corporation
* Copyright(c) 2018 - 2019 Intel Corporation
*
* 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
Expand All @@ -31,7 +31,7 @@
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* Copyright(c) 2017 Intel Deutschland GmbH
* Copyright(c) 2018 Intel Corporation
* Copyright(c) 2018 - 2019 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -234,6 +234,7 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
break;
}
iwl_mvm_csa_client_absent(mvm, te_data->vif);
cancel_delayed_work_sync(&mvmvif->csa_work);
ieee80211_chswitch_done(te_data->vif, true);
break;
default:
Expand Down

0 comments on commit f678061

Please sign in to comment.