struct ieee80211_tx_rate *dest,
                            int max_rates);
 
+/**
+ * ieee80211_sta_set_expected_throughput - set the expected tpt for a station
+ *
+ * Call this function to notify mac80211 about a change in expected throughput
+ * to a station. A driver for a device that does rate control in firmware can
+ * call this function when the expected throughput estimate towards a station
+ * changes. The information is used to tune the CoDel AQM applied to traffic
+ * going towards that station (which can otherwise be too aggressive and cause
+ * slow stations to starve).
+ *
+ * @pubsta: the station to set throughput for.
+ * @thr: the current expected throughput in kbps.
+ */
+void ieee80211_sta_set_expected_throughput(struct ieee80211_sta *pubsta,
+                                          u32 thr);
+
 /**
  * ieee80211_tx_status - transmit status callback
  *
 
        spin_lock_bh(&local->fq.lock);
        rcu_read_lock();
 
+       p += scnprintf(p,
+                      bufsz+buf-p,
+                      "target %uus interval %uus ecn %s\n",
+                      codel_time_to_us(sta->cparams.target),
+                      codel_time_to_us(sta->cparams.interval),
+                      sta->cparams.ecn ? "yes" : "no");
        p += scnprintf(p,
                       bufsz+buf-p,
                       "tid ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets\n");
 
 
        drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta);
 
+       ieee80211_sta_set_expected_throughput(pubsta, sta_get_expected_throughput(sta));
+
        return 0;
 }
 EXPORT_SYMBOL(rate_control_set_rates);
        local->rate_ctrl = NULL;
        rate_control_free(local, ref);
 }
-
 
 #include <linux/timer.h>
 #include <linux/rtnetlink.h>
 
+#include <net/codel.h>
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
 #include "driver-ops.h"
 
        sta->sta.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
 
+       sta->cparams.ce_threshold = CODEL_DISABLED_THRESHOLD;
+       sta->cparams.target = MS2TIME(20);
+       sta->cparams.interval = MS2TIME(100);
+       sta->cparams.ecn = true;
+
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 
        return sta;
                return stats->last_rx;
        return sta->status_stats.last_ack;
 }
+
+static void sta_update_codel_params(struct sta_info *sta, u32 thr)
+{
+       if (!sta->sdata->local->ops->wake_tx_queue)
+               return;
+
+       if (thr && thr < STA_SLOW_THRESHOLD * sta->local->num_sta) {
+               sta->cparams.target = MS2TIME(50);
+               sta->cparams.interval = MS2TIME(300);
+               sta->cparams.ecn = false;
+       } else {
+               sta->cparams.target = MS2TIME(20);
+               sta->cparams.interval = MS2TIME(100);
+               sta->cparams.ecn = true;
+       }
+}
+
+void ieee80211_sta_set_expected_throughput(struct ieee80211_sta *pubsta,
+                                          u32 thr)
+{
+       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+
+       sta_update_codel_params(sta, thr);
+}
 
        u64 msdu[IEEE80211_NUM_TIDS + 1];
 };
 
+/**
+ * The bandwidth threshold below which the per-station CoDel parameters will be
+ * scaled to be more lenient (to prevent starvation of slow stations). This
+ * value will be scaled by the number of active stations when it is being
+ * applied.
+ */
+#define STA_SLOW_THRESHOLD 6000 /* 6 Mbps */
+
 /**
  * struct sta_info - STA information
  *
  * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
  *     AP only.
  * @cipher_scheme: optional cipher scheme for this station
+ * @cparams: CoDel parameters for this station.
  * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
  * @fast_tx: TX fastpath information
  * @fast_rx: RX fastpath information
        enum ieee80211_smps_mode known_smps_mode;
        const struct ieee80211_cipher_scheme *cipher_scheme;
 
+       struct codel_params cparams;
+
        u8 reserved_tid;
 
        struct cfg80211_chan_def tdls_chandef;
 
 
        local = container_of(fq, struct ieee80211_local, fq);
        txqi = container_of(tin, struct txq_info, tin);
-       cparams = &local->cparams;
        cstats = &txqi->cstats;
 
+       if (txqi->txq.sta) {
+               struct sta_info *sta = container_of(txqi->txq.sta,
+                                                   struct sta_info, sta);
+               cparams = &sta->cparams;
+       } else {
+               cparams = &local->cparams;
+       }
+
        if (flow == &txqi->def_flow)
                cvars = &txqi->def_cvars;
        else