]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/sta_info.c
mac80211: clear sequence/fragment number in QoS-null frames
[~andy/linux] / net / mac80211 / sta_info.c
index 1eb66e26e49d0a02869ec5e5ff4789583e9760d1..137a192e64bc3c2aa61cc9c5912a89bd3008cbe3 100644 (file)
@@ -91,7 +91,7 @@ static int sta_info_hash_del(struct ieee80211_local *local,
        return -ENOENT;
 }
 
-static void cleanup_single_sta(struct sta_info *sta)
+static void __cleanup_single_sta(struct sta_info *sta)
 {
        int ac, i;
        struct tid_ampdu_tx *tid_tx;
@@ -99,24 +99,8 @@ static void cleanup_single_sta(struct sta_info *sta)
        struct ieee80211_local *local = sdata->local;
        struct ps_data *ps;
 
-       /*
-        * At this point, when being called as call_rcu callback,
-        * neither mac80211 nor the driver can reference this
-        * sta struct any more except by still existing timers
-        * associated with this station that we clean up below.
-        *
-        * Note though that this still uses the sdata and even
-        * calls the driver in AP and mesh mode, so interfaces
-        * of those types mush use call sta_info_flush_cleanup()
-        * (typically via sta_info_flush()) before deconfiguring
-        * the driver.
-        *
-        * In station mode, nothing happens here so it doesn't
-        * have to (and doesn't) do that, this is intentional to
-        * speed up roaming.
-        */
-
-       if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
+       if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
+           test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
                    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                        ps = &sdata->bss->ps;
@@ -126,6 +110,7 @@ static void cleanup_single_sta(struct sta_info *sta)
                        return;
 
                clear_sta_flag(sta, WLAN_STA_PS_STA);
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
 
                atomic_dec(&ps->num_sta_ps);
                sta_info_recalc_tim(sta);
@@ -156,39 +141,15 @@ static void cleanup_single_sta(struct sta_info *sta)
                ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);
                kfree(tid_tx);
        }
-
-       sta_info_free(local, sta);
-}
-
-void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata)
-{
-       struct sta_info *sta;
-
-       spin_lock_bh(&sdata->cleanup_stations_lock);
-       while (!list_empty(&sdata->cleanup_stations)) {
-               sta = list_first_entry(&sdata->cleanup_stations,
-                                      struct sta_info, list);
-               list_del(&sta->list);
-               spin_unlock_bh(&sdata->cleanup_stations_lock);
-
-               cleanup_single_sta(sta);
-
-               spin_lock_bh(&sdata->cleanup_stations_lock);
-       }
-
-       spin_unlock_bh(&sdata->cleanup_stations_lock);
 }
 
-static void free_sta_rcu(struct rcu_head *h)
+static void cleanup_single_sta(struct sta_info *sta)
 {
-       struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
        struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
 
-       spin_lock(&sdata->cleanup_stations_lock);
-       list_add_tail(&sta->list, &sdata->cleanup_stations);
-       spin_unlock(&sdata->cleanup_stations_lock);
-
-       ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk);
+       __cleanup_single_sta(sta);
+       sta_info_free(local, sta);
 }
 
 /* protected by RCU */
@@ -266,9 +227,17 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
  */
 void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 {
+       int i;
+
        if (sta->rate_ctrl)
                rate_control_free_sta(sta);
 
+       if (sta->tx_lat) {
+               for (i = 0; i < IEEE80211_NUM_TIDS; i++)
+                       kfree(sta->tx_lat[i].bins);
+               kfree(sta->tx_lat);
+       }
+
        sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
 
        kfree(sta);
@@ -333,13 +302,44 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
        struct timespec uptime;
+       struct ieee80211_tx_latency_bin_ranges *tx_latency;
        int i;
 
        sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
        if (!sta)
                return NULL;
 
+       rcu_read_lock();
+       tx_latency = rcu_dereference(local->tx_latency);
+       /* init stations Tx latency statistics && TID bins */
+       if (tx_latency) {
+               sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS *
+                                     sizeof(struct ieee80211_tx_latency_stat),
+                                     GFP_ATOMIC);
+               if (!sta->tx_lat) {
+                       rcu_read_unlock();
+                       goto free;
+               }
+
+               if (tx_latency->n_ranges) {
+                       for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+                               /* size of bins is size of the ranges +1 */
+                               sta->tx_lat[i].bin_count =
+                                       tx_latency->n_ranges + 1;
+                               sta->tx_lat[i].bins =
+                                       kcalloc(sta->tx_lat[i].bin_count,
+                                               sizeof(u32), GFP_ATOMIC);
+                               if (!sta->tx_lat[i].bins) {
+                                       rcu_read_unlock();
+                                       goto free;
+                               }
+                       }
+               }
+       }
+       rcu_read_unlock();
+
        spin_lock_init(&sta->lock);
+       spin_lock_init(&sta->ps_lock);
        INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
@@ -363,10 +363,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
                ewma_init(&sta->chain_signal_avg[i], 1024, 8);
 
-       if (sta_prepare_rate_control(local, sta, gfp)) {
-               kfree(sta);
-               return NULL;
-       }
+       if (sta_prepare_rate_control(local, sta, gfp))
+               goto free;
 
        for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
                /*
@@ -411,8 +409,16 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        }
 
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
-
        return sta;
+
+free:
+       if (sta->tx_lat) {
+               for (i = 0; i < IEEE80211_NUM_TIDS; i++)
+                       kfree(sta->tx_lat[i].bins);
+               kfree(sta->tx_lat);
+       }
+       kfree(sta);
+       return NULL;
 }
 
 static int sta_info_insert_check(struct sta_info *sta)
@@ -491,22 +497,28 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
                goto out_err;
        }
 
-       /* notify driver */
-       err = sta_info_insert_drv_state(local, sdata, sta);
-       if (err)
-               goto out_err;
-
        local->num_sta++;
        local->sta_generation++;
        smp_mb();
 
+       /* simplify things and don't accept BA sessions yet */
+       set_sta_flag(sta, WLAN_STA_BLOCK_BA);
+
        /* make the station visible */
        sta_info_hash_add(local, sta);
 
        list_add_rcu(&sta->list, &local->sta_list);
 
+       /* notify driver */
+       err = sta_info_insert_drv_state(local, sdata, sta);
+       if (err)
+               goto out_remove;
+
        set_sta_flag(sta, WLAN_STA_INSERTED);
+       /* accept BA sessions now */
+       clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
 
+       ieee80211_recalc_min_chandef(sdata);
        ieee80211_sta_debugfs_add(sta);
        rate_control_add_sta_debugfs(sta);
 
@@ -525,6 +537,12 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
                mesh_accept_plinks_update(sdata);
 
        return 0;
+ out_remove:
+       sta_info_hash_del(local, sta);
+       list_del_rcu(&sta->list);
+       local->num_sta--;
+       synchronize_net();
+       __cleanup_single_sta(sta);
  out_err:
        mutex_unlock(&local->sta_mtx);
        rcu_read_lock();
@@ -630,8 +648,8 @@ void sta_info_recalc_tim(struct sta_info *sta)
 #ifdef CONFIG_MAC80211_MESH
        } else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
                ps = &sta->sdata->u.mesh.ps;
-               /* TIM map only for PLID <= IEEE80211_MAX_AID */
-               id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID;
+               /* TIM map only for 1 <= PLID <= IEEE80211_MAX_AID */
+               id = sta->plid % (IEEE80211_MAX_AID + 1);
 #endif
        } else {
                return;
@@ -807,7 +825,7 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
        return have_buffered;
 }
 
-int __must_check __sta_info_destroy(struct sta_info *sta)
+static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
 {
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
@@ -833,12 +851,35 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
        ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
 
        ret = sta_info_hash_del(local, sta);
-       if (ret)
+       if (WARN_ON(ret))
                return ret;
 
        list_del_rcu(&sta->list);
 
-       /* this always calls synchronize_net() */
+       drv_sta_pre_rcu_remove(local, sta->sdata, sta);
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+           rcu_access_pointer(sdata->u.vlan.sta) == sta)
+               RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
+
+       return 0;
+}
+
+static void __sta_info_destroy_part2(struct sta_info *sta)
+{
+       struct ieee80211_local *local = sta->local;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       int ret;
+
+       /*
+        * NOTE: This assumes at least synchronize_net() was done
+        *       after _part1 and before _part2!
+        */
+
+       might_sleep();
+       lockdep_assert_held(&local->sta_mtx);
+
+       /* now keys can no longer be reached */
        ieee80211_free_sta_keys(local, sta);
 
        sta->dead = true;
@@ -846,9 +887,6 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
        local->num_sta--;
        local->sta_generation++;
 
-       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-               RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
-
        while (sta->sta_state > IEEE80211_STA_NONE) {
                ret = sta_info_move_state(sta, sta->sta_state - 1);
                if (ret) {
@@ -869,8 +907,21 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
 
        rate_control_remove_sta_debugfs(sta);
        ieee80211_sta_debugfs_remove(sta);
+       ieee80211_recalc_min_chandef(sdata);
+
+       cleanup_single_sta(sta);
+}
+
+int __must_check __sta_info_destroy(struct sta_info *sta)
+{
+       int err = __sta_info_destroy_part1(sta);
+
+       if (err)
+               return err;
 
-       call_rcu(&sta->rcu_head, free_sta_rcu);
+       synchronize_net();
+
+       __sta_info_destroy_part2(sta);
 
        return 0;
 }
@@ -940,32 +991,38 @@ void sta_info_stop(struct ieee80211_local *local)
 }
 
 
-int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata)
+int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans)
 {
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta, *tmp;
+       LIST_HEAD(free_list);
        int ret = 0;
 
        might_sleep();
 
+       WARN_ON(vlans && sdata->vif.type != NL80211_IFTYPE_AP);
+       WARN_ON(vlans && !sdata->bss);
+
        mutex_lock(&local->sta_mtx);
        list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
-               if (sdata == sta->sdata) {
-                       WARN_ON(__sta_info_destroy(sta));
+               if (sdata == sta->sdata ||
+                   (vlans && sdata->bss == sta->sdata->bss)) {
+                       if (!WARN_ON(__sta_info_destroy_part1(sta)))
+                               list_add(&sta->free_list, &free_list);
                        ret++;
                }
        }
+
+       if (!list_empty(&free_list)) {
+               synchronize_net();
+               list_for_each_entry_safe(sta, tmp, &free_list, free_list)
+                       __sta_info_destroy_part2(sta);
+       }
        mutex_unlock(&local->sta_mtx);
 
        return ret;
 }
 
-void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata)
-{
-       ieee80211_cleanup_sdata_stas(sdata);
-       cancel_work_sync(&sdata->cleanup_stations_wk);
-}
-
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time)
 {
@@ -1035,10 +1092,14 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
 }
 EXPORT_SYMBOL(ieee80211_find_sta);
 
-static void clear_sta_ps_flags(void *_sta)
+/* powersave support code */
+void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 {
-       struct sta_info *sta = _sta;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff_head pending;
+       int filtered = 0, buffered = 0, ac;
+       unsigned long flags;
        struct ps_data *ps;
 
        if (sdata->vif.type == NL80211_IFTYPE_AP ||
@@ -1049,20 +1110,6 @@ static void clear_sta_ps_flags(void *_sta)
        else
                return;
 
-       clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-       if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
-               atomic_dec(&ps->num_sta_ps);
-}
-
-/* powersave support code */
-void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
-{
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff_head pending;
-       int filtered = 0, buffered = 0, ac;
-       unsigned long flags;
-
        clear_sta_flag(sta, WLAN_STA_SP);
 
        BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
@@ -1073,6 +1120,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
        skb_queue_head_init(&pending);
 
+       /* sync with ieee80211_tx_h_unicast_ps_buf */
+       spin_lock(&sta->ps_lock);
        /* Send all buffered frames to the station */
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                int count = skb_queue_len(&pending), tmp;
@@ -1091,7 +1140,12 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
                buffered += tmp - count;
        }
 
-       ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
+       ieee80211_add_pending_skbs(local, &pending);
+       clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       clear_sta_flag(sta, WLAN_STA_PS_STA);
+       spin_unlock(&sta->ps_lock);
+
+       atomic_dec(&ps->num_sta_ps);
 
        /* This station just woke up and isn't aware of our SMPS state */
        if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,
@@ -1117,7 +1171,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
 static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
                                         struct sta_info *sta, int tid,
-                                        enum ieee80211_frame_release_type reason)
+                                        enum ieee80211_frame_release_type reason,
+                                        bool call_driver)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_qos_hdr *nullfunc;
@@ -1151,6 +1206,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
        memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
        memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
        memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
+       nullfunc->seq_ctrl = 0;
 
        skb->priority = tid;
        skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]);
@@ -1175,7 +1231,9 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
                       IEEE80211_TX_STATUS_EOSP |
                       IEEE80211_TX_CTL_REQ_TX_STATUS;
 
-       drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
+       if (call_driver)
+               drv_allow_buffered_frames(local, sta, BIT(tid), 1,
+                                         reason, false);
 
        skb->dev = sdata->dev;
 
@@ -1191,6 +1249,17 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
        rcu_read_unlock();
 }
 
+static int find_highest_prio_tid(unsigned long tids)
+{
+       /* lower 3 TIDs aren't ordered perfectly */
+       if (tids & 0xF8)
+               return fls(tids) - 1;
+       /* TID 0 is BE just like TID 3 */
+       if (tids & BIT(0))
+               return 0;
+       return fls(tids) - 1;
+}
+
 static void
 ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                                  int n_frames, u8 ignored_acs,
@@ -1198,7 +1267,6 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
-       bool found = false;
        bool more_data = false;
        int ac;
        unsigned long driver_release_tids = 0;
@@ -1209,9 +1277,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 
        __skb_queue_head_init(&frames);
 
-       /*
-        * Get response frame(s) and more data bit for it.
-        */
+       /* Get response frame(s) and more data bit for the last one. */
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                unsigned long tids;
 
@@ -1220,43 +1286,48 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 
                tids = ieee80211_tids_for_ac(ac);
 
-               if (!found) {
-                       driver_release_tids = sta->driver_buffered_tids & tids;
-                       if (driver_release_tids) {
-                               found = true;
-                       } else {
-                               struct sk_buff *skb;
-
-                               while (n_frames > 0) {
-                                       skb = skb_dequeue(&sta->tx_filtered[ac]);
-                                       if (!skb) {
-                                               skb = skb_dequeue(
-                                                       &sta->ps_tx_buf[ac]);
-                                               if (skb)
-                                                       local->total_ps_buffered--;
-                                       }
-                                       if (!skb)
-                                               break;
-                                       n_frames--;
-                                       found = true;
-                                       __skb_queue_tail(&frames, skb);
-                               }
-                       }
+               /* if we already have frames from software, then we can't also
+                * release from hardware queues
+                */
+               if (skb_queue_empty(&frames))
+                       driver_release_tids |= sta->driver_buffered_tids & tids;
 
-                       /*
-                        * If the driver has data on more than one TID then
+               if (driver_release_tids) {
+                       /* If the driver has data on more than one TID then
                         * certainly there's more data if we release just a
-                        * single frame now (from a single TID).
+                        * single frame now (from a single TID). This will
+                        * only happen for PS-Poll.
                         */
                        if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
                            hweight16(driver_release_tids) > 1) {
                                more_data = true;
                                driver_release_tids =
-                                       BIT(ffs(driver_release_tids) - 1);
+                                       BIT(find_highest_prio_tid(
+                                               driver_release_tids));
                                break;
                        }
+               } else {
+                       struct sk_buff *skb;
+
+                       while (n_frames > 0) {
+                               skb = skb_dequeue(&sta->tx_filtered[ac]);
+                               if (!skb) {
+                                       skb = skb_dequeue(
+                                               &sta->ps_tx_buf[ac]);
+                                       if (skb)
+                                               local->total_ps_buffered--;
+                               }
+                               if (!skb)
+                                       break;
+                               n_frames--;
+                               __skb_queue_tail(&frames, skb);
+                       }
                }
 
+               /* If we have more frames buffered on this AC, then set the
+                * more-data bit and abort the loop since we can't send more
+                * data from other ACs before the buffered frames from this.
+                */
                if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
                    !skb_queue_empty(&sta->ps_tx_buf[ac])) {
                        more_data = true;
@@ -1264,7 +1335,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                }
        }
 
-       if (!found) {
+       if (skb_queue_empty(&frames) && !driver_release_tids) {
                int tid;
 
                /*
@@ -1285,15 +1356,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                /* This will evaluate to 1, 3, 5 or 7. */
                tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
 
-               ieee80211_send_null_response(sdata, sta, tid, reason);
-               return;
-       }
-
-       if (!driver_release_tids) {
+               ieee80211_send_null_response(sdata, sta, tid, reason, true);
+       } else if (!driver_release_tids) {
                struct sk_buff_head pending;
                struct sk_buff *skb;
                int num = 0;
                u16 tids = 0;
+               bool need_null = false;
 
                skb_queue_head_init(&pending);
 
@@ -1327,22 +1396,57 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                            ieee80211_is_qos_nullfunc(hdr->frame_control))
                                qoshdr = ieee80211_get_qos_ctl(hdr);
 
-                       /* end service period after last frame */
-                       if (skb_queue_empty(&frames)) {
-                               if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
-                                   qoshdr)
-                                       *qoshdr |= IEEE80211_QOS_CTL_EOSP;
+                       tids |= BIT(skb->priority);
+
+                       __skb_queue_tail(&pending, skb);
 
+                       /* end service period after last frame or add one */
+                       if (!skb_queue_empty(&frames))
+                               continue;
+
+                       if (reason != IEEE80211_FRAME_RELEASE_UAPSD) {
+                               /* for PS-Poll, there's only one frame */
                                info->flags |= IEEE80211_TX_STATUS_EOSP |
                                               IEEE80211_TX_CTL_REQ_TX_STATUS;
+                               break;
                        }
 
-                       if (qoshdr)
-                               tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK);
-                       else
-                               tids |= BIT(0);
+                       /* For uAPSD, things are a bit more complicated. If the
+                        * last frame has a QoS header (i.e. is a QoS-data or
+                        * QoS-nulldata frame) then just set the EOSP bit there
+                        * and be done.
+                        * If the frame doesn't have a QoS header (which means
+                        * it should be a bufferable MMPDU) then we can't set
+                        * the EOSP bit in the QoS header; add a QoS-nulldata
+                        * frame to the list to send it after the MMPDU.
+                        *
+                        * Note that this code is only in the mac80211-release
+                        * code path, we assume that the driver will not buffer
+                        * anything but QoS-data frames, or if it does, will
+                        * create the QoS-nulldata frame by itself if needed.
+                        *
+                        * Cf. 802.11-2012 10.2.1.10 (c).
+                        */
+                       if (qoshdr) {
+                               *qoshdr |= IEEE80211_QOS_CTL_EOSP;
 
-                       __skb_queue_tail(&pending, skb);
+                               info->flags |= IEEE80211_TX_STATUS_EOSP |
+                                              IEEE80211_TX_CTL_REQ_TX_STATUS;
+                       } else {
+                               /* The standard isn't completely clear on this
+                                * as it says the more-data bit should be set
+                                * if there are more BUs. The QoS-Null frame
+                                * we're about to send isn't buffered yet, we
+                                * only create it below, but let's pretend it
+                                * was buffered just in case some clients only
+                                * expect more-data=0 when eosp=1.
+                                */
+                               hdr->frame_control |=
+                                       cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+                               need_null = true;
+                               num++;
+                       }
+                       break;
                }
 
                drv_allow_buffered_frames(local, sta, tids, num,
@@ -1350,17 +1454,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 
                ieee80211_add_pending_skbs(local, &pending);
 
+               if (need_null)
+                       ieee80211_send_null_response(
+                               sdata, sta, find_highest_prio_tid(tids),
+                               reason, false);
+
                sta_info_recalc_tim(sta);
        } else {
                /*
                 * We need to release a frame that is buffered somewhere in the
                 * driver ... it'll have to handle that.
-                * Note that, as per the comment above, it'll also have to see
-                * if there is more than just one frame on the specific TID that
-                * we're releasing from, and it needs to set the more-data bit
-                * accordingly if we tell it that there's no more data. If we do
-                * tell it there's more data, then of course the more-data bit
-                * needs to be set anyway.
+                * Note that the driver also has to check the number of frames
+                * on the TIDs we're releasing from - if there are more than
+                * n_frames it has to set the more-data bit (if we didn't ask
+                * it to set it anyway due to other buffered frames); if there
+                * are fewer than n_frames it has to make sure to adjust that
+                * to allow the service period to end properly.
                 */
                drv_release_buffered_frames(local, sta, driver_release_tids,
                                            n_frames, reason, more_data);
@@ -1368,9 +1477,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                /*
                 * Note that we don't recalculate the TIM bit here as it would
                 * most likely have no effect at all unless the driver told us
-                * that the TID became empty before returning here from the
+                * that the TID(s) became empty before returning here from the
                 * release function.
-                * Either way, however, when the driver tells us that the TID
+                * Either way, however, when the driver tells us that the TID(s)
                 * became empty we'll do the TIM recalculation.
                 */
        }
@@ -1459,6 +1568,8 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
        if (WARN_ON(tid >= IEEE80211_NUM_TIDS))
                return;
 
+       trace_api_sta_set_buffered(sta->local, pubsta, tid, buffered);
+
        if (buffered)
                set_bit(tid, &sta->driver_buffered_tids);
        else