mac80211: wait for the first beacon on the new channel after CSA
authorLuciano Coelho <luciano.coelho@intel.com>
Wed, 8 Oct 2014 06:48:39 +0000 (09:48 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 9 Oct 2014 09:30:09 +0000 (11:30 +0200)
Instead of immediately reopening the queues (in case of block_tx),
calling the post_channel_switch operation and sending the
notification, wait for the first beacon on the new channel.  This
makes sure that we don't lose packets if the AP/GO is not on the new
channel yet.

Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mlme.c

index a9cc4912898045a21e36b3f7a06fd19159f71d97..78d6121eb372d15397864bc20bfcd1908e4f26ee 100644 (file)
@@ -434,6 +434,8 @@ struct ieee80211_if_managed {
 
        unsigned int flags;
 
+       bool csa_waiting_bcn;
+
        bool beacon_crc_valid;
        u32 beacon_crc;
 
index af237223a8cd9bd3aa6121576650238d42e3bbb0..e469b3390f2a614ab9628ca927be8ba7bcf2a509 100644 (file)
@@ -842,6 +842,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        sdata_lock(sdata);
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               sdata->u.mgd.csa_waiting_bcn = false;
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
index cb1a8c3bc73f67e1b3c83fb41dd9c302f1440652..148253c1bd783f89a510471570df6b40db48aec6 100644 (file)
@@ -1001,31 +1001,44 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->csa_chandef.chan;
 
-       sdata->vif.csa_active = false;
+       ifmgd->csa_waiting_bcn = true;
+
+       ieee80211_sta_reset_beacon_monitor(sdata);
+       ieee80211_sta_reset_conn_monitor(sdata);
+
+out:
+       mutex_unlock(&local->chanctx_mtx);
+       mutex_unlock(&local->mtx);
+       sdata_unlock(sdata);
+}
+
+static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       int ret;
+
+       sdata_assert_lock(sdata);
+
+       WARN_ON(!sdata->vif.csa_active);
 
-       /* XXX: wait for a beacon first? */
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
                sdata->csa_block_tx = false;
        }
 
+       sdata->vif.csa_active = false;
+       ifmgd->csa_waiting_bcn = false;
+
        ret = drv_post_channel_switch(sdata);
        if (ret) {
                sdata_info(sdata,
                           "driver post channel switch failed, disconnecting\n");
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
-               goto out;
+               return;
        }
-
-       ieee80211_sta_reset_beacon_monitor(sdata);
-       ieee80211_sta_reset_conn_monitor(sdata);
-
-out:
-       mutex_unlock(&local->chanctx_mtx);
-       mutex_unlock(&local->mtx);
-       sdata_unlock(sdata);
 }
 
 void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
@@ -1943,6 +1956,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_vif_release_channel(sdata);
 
        sdata->vif.csa_active = false;
+       ifmgd->csa_waiting_bcn = false;
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -2191,6 +2205,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                               true, frame_buf);
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
+       ifmgd->csa_waiting_bcn = false;
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -3215,6 +3230,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                }
        }
 
+       if (ifmgd->csa_waiting_bcn)
+               ieee80211_chswitch_post_beacon(sdata);
+
        if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
                return;
        ifmgd->beacon_crc = ncrc;
@@ -3687,11 +3705,12 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        if (local->quiescing)
                return;
 
-       if (sdata->vif.csa_active)
+       if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
                return;
 
        sdata->u.mgd.connection_loss = false;
@@ -3709,7 +3728,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
        if (local->quiescing)
                return;
 
-       if (sdata->vif.csa_active)
+       if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
                return;
 
        ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);