]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/mac80211/util.c
mac80211: fix aggregation for hardware with ampdu queues
[mirror_ubuntu-artful-kernel.git] / net / mac80211 / util.c
index 505d68f344ceb386486e7963ea425715f4804b6f..92ea1770461b5b85db3e827f68502bbb9b34ac89 100644 (file)
@@ -41,6 +41,15 @@ const unsigned char rfc1042_header[] __aligned(2) =
 const unsigned char bridge_tunnel_header[] __aligned(2) =
        { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
 
+struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
+{
+       struct ieee80211_local *local;
+       BUG_ON(!wiphy);
+
+       local = wiphy_priv(wiphy);
+       return &local->hw;
+}
+EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
 
 u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
                        enum nl80211_iftype type)
@@ -330,10 +339,41 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_ctstoself_duration);
 
-void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
+                                  enum queue_stop_reason reason)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
+       if (queue >= hw->queues) {
+               if (local->ampdu_ac_queue[queue - hw->queues] < 0)
+                       return;
+
+               /*
+                * for virtual aggregation queues, we need to refcount the
+                * internal mac80211 disable (multiple times!), keep track of
+                * driver disable _and_ make sure the regular queue is
+                * actually enabled.
+                */
+               if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
+                       local->amdpu_ac_stop_refcnt[queue - hw->queues]--;
+               else
+                       __clear_bit(reason, &local->queue_stop_reasons[queue]);
+
+               if (local->queue_stop_reasons[queue] ||
+                   local->amdpu_ac_stop_refcnt[queue - hw->queues])
+                       return;
+
+               /* now go on to treat the corresponding regular queue */
+               queue = local->ampdu_ac_queue[queue - hw->queues];
+               reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
+       }
+
+       __clear_bit(reason, &local->queue_stop_reasons[queue]);
+
+       if (local->queue_stop_reasons[queue] != 0)
+               /* someone still has this queue stopped */
+               return;
+
        if (test_bit(queue, local->queues_pending)) {
                set_bit(queue, local->queues_pending_run);
                tasklet_schedule(&local->tx_pending_tasklet);
@@ -341,38 +381,130 @@ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
                netif_wake_subqueue(local->mdev, queue);
        }
 }
+
+void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+                                   enum queue_stop_reason reason)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       __ieee80211_wake_queue(hw, queue, reason);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+{
+       ieee80211_wake_queue_by_reason(hw, queue,
+                                      IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
 EXPORT_SYMBOL(ieee80211_wake_queue);
 
-void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
+                                  enum queue_stop_reason reason)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
+       if (queue >= hw->queues) {
+               if (local->ampdu_ac_queue[queue - hw->queues] < 0)
+                       return;
+
+               /*
+                * for virtual aggregation queues, we need to refcount the
+                * internal mac80211 disable (multiple times!), keep track of
+                * driver disable _and_ make sure the regular queue is
+                * actually enabled.
+                */
+               if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
+                       local->amdpu_ac_stop_refcnt[queue - hw->queues]++;
+               else
+                       __set_bit(reason, &local->queue_stop_reasons[queue]);
+
+               /* now go on to treat the corresponding regular queue */
+               queue = local->ampdu_ac_queue[queue - hw->queues];
+               reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
+       }
+
+       __set_bit(reason, &local->queue_stop_reasons[queue]);
+
        netif_stop_subqueue(local->mdev, queue);
 }
+
+void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+                                   enum queue_stop_reason reason)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       __ieee80211_stop_queue(hw, queue, reason);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+{
+       ieee80211_stop_queue_by_reason(hw, queue,
+                                      IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
 EXPORT_SYMBOL(ieee80211_stop_queue);
 
-void ieee80211_stop_queues(struct ieee80211_hw *hw)
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+                                   enum queue_stop_reason reason)
 {
+       struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
        int i;
 
-       for (i = 0; i < ieee80211_num_queues(hw); i++)
-               ieee80211_stop_queue(hw, i);
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+       for (i = 0; i < hw->queues; i++)
+               __ieee80211_stop_queue(hw, i, reason);
+
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+       ieee80211_stop_queues_by_reason(hw,
+                                       IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_stop_queues);
 
 int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
+
+       if (queue >= hw->queues) {
+               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+               queue = local->ampdu_ac_queue[queue - hw->queues];
+               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+               if (queue < 0)
+                       return true;
+       }
+
        return __netif_subqueue_stopped(local->mdev, queue);
 }
 EXPORT_SYMBOL(ieee80211_queue_stopped);
 
-void ieee80211_wake_queues(struct ieee80211_hw *hw)
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+                                    enum queue_stop_reason reason)
 {
+       struct ieee80211_local *local = hw_to_local(hw);
+       unsigned long flags;
        int i;
 
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
        for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
-               ieee80211_wake_queue(hw, i);
+               __ieee80211_wake_queue(hw, i, reason);
+
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+       ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER);
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
@@ -385,7 +517,7 @@ void ieee80211_iterate_active_interfaces(
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
 
-       rtnl_lock();
+       mutex_lock(&local->iflist_mtx);
 
        list_for_each_entry(sdata, &local->interfaces, list) {
                switch (sdata->vif.type) {
@@ -406,7 +538,7 @@ void ieee80211_iterate_active_interfaces(
                                 &sdata->vif);
        }
 
-       rtnl_unlock();
+       mutex_unlock(&local->iflist_mtx);
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
 
@@ -579,6 +711,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
                        elems->pwr_constr_elem = pos;
                        elems->pwr_constr_elem_len = elen;
                        break;
+               case WLAN_EID_TIMEOUT_INTERVAL:
+                       elems->timeout_int = pos;
+                       elems->timeout_int_len = elen;
+                       break;
                default:
                        break;
                }
@@ -641,7 +777,7 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)
                    chan->flags & IEEE80211_CHAN_NO_IBSS)
                        return ret;
                local->oper_channel = chan;
-               local->oper_sec_chan_offset = NL80211_SEC_CHAN_NO_HT;
+               local->oper_channel_type = NL80211_CHAN_NO_HT;
 
                if (local->sw_scanning || local->hw_scanning)
                        ret = 0;
@@ -653,12 +789,12 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)
        return ret;
 }
 
-u64 ieee80211_mandatory_rates(struct ieee80211_local *local,
+u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
                              enum ieee80211_band band)
 {
        struct ieee80211_supported_band *sband;
        struct ieee80211_rate *bitrates;
-       u64 mandatory_rates;
+       u32 mandatory_rates;
        enum ieee80211_rate_flags mandatory_flag;
        int i;