OSDN Git Service

Merge tag 'dax-fix-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm...
[uclinux-h8/linux.git] / net / mac80211 / util.c
index bec4243..d0eb38b 100644 (file)
@@ -299,16 +299,16 @@ out:
        spin_unlock_bh(&fq->lock);
 }
 
-void ieee80211_wake_txqs(unsigned long data)
+static void
+__releases(&local->queue_stop_reason_lock)
+__acquires(&local->queue_stop_reason_lock)
+_ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags)
 {
-       struct ieee80211_local *local = (struct ieee80211_local *)data;
        struct ieee80211_sub_if_data *sdata;
        int n_acs = IEEE80211_NUM_ACS;
-       unsigned long flags;
        int i;
 
        rcu_read_lock();
-       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
        if (local->hw.queues < IEEE80211_NUM_ACS)
                n_acs = 1;
@@ -317,7 +317,7 @@ void ieee80211_wake_txqs(unsigned long data)
                if (local->queue_stop_reasons[i])
                        continue;
 
-               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+               spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags);
                list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                        int ac;
 
@@ -329,13 +329,22 @@ void ieee80211_wake_txqs(unsigned long data)
                                        __ieee80211_wake_txqs(sdata, ac);
                        }
                }
-               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+               spin_lock_irqsave(&local->queue_stop_reason_lock, *flags);
        }
 
-       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
        rcu_read_unlock();
 }
 
+void ieee80211_wake_txqs(unsigned long data)
+{
+       struct ieee80211_local *local = (struct ieee80211_local *)data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+       _ieee80211_wake_txqs(local, &flags);
+       spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
 void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
 {
        struct ieee80211_sub_if_data *sdata;
@@ -371,7 +380,8 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
 
 static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
                                   enum queue_stop_reason reason,
-                                  bool refcounted)
+                                  bool refcounted,
+                                  unsigned long *flags)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
@@ -405,8 +415,19 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
        } else
                tasklet_schedule(&local->tx_pending_tasklet);
 
-       if (local->ops->wake_tx_queue)
-               tasklet_schedule(&local->wake_txqs_tasklet);
+       /*
+        * Calling _ieee80211_wake_txqs here can be a problem because it may
+        * release queue_stop_reason_lock which has been taken by
+        * __ieee80211_wake_queue's caller. It is certainly not very nice to
+        * release someone's lock, but it is fine because all the callers of
+        * __ieee80211_wake_queue call it right before releasing the lock.
+        */
+       if (local->ops->wake_tx_queue) {
+               if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
+                       tasklet_schedule(&local->wake_txqs_tasklet);
+               else
+                       _ieee80211_wake_txqs(local, flags);
+       }
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -417,7 +438,7 @@ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
        unsigned long flags;
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-       __ieee80211_wake_queue(hw, queue, reason, refcounted);
+       __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
@@ -514,7 +535,7 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
                               false);
        __skb_queue_tail(&local->pending[queue], skb);
        __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
-                              false);
+                              false, &flags);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
@@ -547,7 +568,7 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
        for (i = 0; i < hw->queues; i++)
                __ieee80211_wake_queue(hw, i,
                        IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
-                       false);
+                       false, &flags);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
@@ -605,7 +626,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
        for_each_set_bit(i, &queues, hw->queues)
-               __ieee80211_wake_queue(hw, i, reason, refcounted);
+               __ieee80211_wake_queue(hw, i, reason, refcounted, &flags);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
@@ -1202,6 +1223,8 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                        if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA &&
                            elen >= (sizeof(*elems->mu_edca_param_set) + 1)) {
                                elems->mu_edca_param_set = (void *)&pos[1];
+                               if (calc_crc)
+                                       crc = crc32_be(crc, pos - 2, elen + 2);
                        } else if (pos[0] == WLAN_EID_EXT_HE_CAPABILITY) {
                                elems->he_cap = (void *)&pos[1];
                                elems->he_cap_len = elen - 1;