When a running wake_tx_queue() call is aborted due to a hw queue stop
the corresponding iTXQ is not always correctly marked for resumption:
wake_tx_push_queue() can stops the queue run without setting
@IEEE80211_TXQ_STOP_NETIF_TX.
Without the @IEEE80211_TXQ_STOP_NETIF_TX flag __ieee80211_wake_txqs()
will not schedule a new queue run and remaining frames in the queue get
stuck till another frame is queued to it.
Fix the issue for all drivers - also the ones with custom wake_tx_queue
callbacks - by moving the logic into ieee80211_tx_dequeue() and drop the
redundant @txqs_stopped.
@IEEE80211_TXQ_STOP_NETIF_TX is also renamed to @IEEE80211_TXQ_DIRTY to
better describe the flag.
Fixes: c850e31f79f0 ("wifi: mac80211: add internal handler for wake_tx_queue")
Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
Link: https://lore.kernel.org/r/20221230121850.218810-1-alexander@wetzel-home.de
Cc: stable@vger.kernel.org
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
 
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void \*).
  * @txq: the multicast data TX queue
- * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped,
- *     protected by fq->lock.
  * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
  *     &enum ieee80211_offload_flags.
  * @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled.
        bool probe_req_reg;
        bool rx_mcast_action_reg;
 
-       bool txqs_stopped[IEEE80211_NUM_ACS];
-
        struct ieee80211_vif *mbssid_tx_vif;
 
        /* must be last */
 
                        continue;
                txqi = to_txq_info(sta->sta.txq[i]);
                p += scnprintf(p, bufsz + buf - p,
-                              "%d %d %u %u %u %u %u %u %u %u %u 0x%lx(%s%s%s)\n",
+                              "%d %d %u %u %u %u %u %u %u %u %u 0x%lx(%s%s%s%s)\n",
                               txqi->txq.tid,
                               txqi->txq.ac,
                               txqi->tin.backlog_bytes,
                               txqi->flags,
                               test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN",
                               test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "",
-                              test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "");
+                              test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "",
+                              test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ? " DIRTY" : "");
        }
 
        rcu_read_unlock();
 
 
        /* In reconfig don't transmit now, but mark for waking later */
        if (local->in_reconfig) {
-               set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txq->flags);
+               set_bit(IEEE80211_TXQ_DIRTY, &txq->flags);
                return;
        }
 
 
        IEEE80211_TXQ_STOP,
        IEEE80211_TXQ_AMPDU,
        IEEE80211_TXQ_NO_AMSDU,
-       IEEE80211_TXQ_STOP_NETIF_TX,
+       IEEE80211_TXQ_DIRTY,
 };
 
 /**
 
        struct ieee80211_tx_data tx;
        ieee80211_tx_result r;
        struct ieee80211_vif *vif = txq->vif;
+       int q = vif->hw_queue[txq->ac];
+       bool q_stopped;
 
        WARN_ON_ONCE(softirq_count() == 0);
 
                return NULL;
 
 begin:
-       spin_lock_bh(&fq->lock);
+       spin_lock(&local->queue_stop_reason_lock);
+       q_stopped = local->queue_stop_reasons[q];
+       spin_unlock(&local->queue_stop_reason_lock);
 
-       if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ||
-           test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags))
-               goto out;
+       if (unlikely(q_stopped)) {
+               /* mark for waking later */
+               set_bit(IEEE80211_TXQ_DIRTY, &txqi->flags);
+               return NULL;
+       }
 
-       if (vif->txqs_stopped[txq->ac]) {
-               set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags);
+       spin_lock_bh(&fq->lock);
+
+       if (unlikely(test_bit(IEEE80211_TXQ_STOP, &txqi->flags)))
                goto out;
-       }
 
        /* Make sure fragments stay together. */
        skb = __skb_dequeue(&txqi->frags);
 
                               struct ieee80211_sub_if_data *sdata,
                               struct ieee80211_txq *queue)
 {
-       int q = sdata->vif.hw_queue[queue->ac];
        struct ieee80211_tx_control control = {
                .sta = queue->sta,
        };
        struct sk_buff *skb;
-       unsigned long flags;
-       bool q_stopped;
 
        while (1) {
-               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-               q_stopped = local->queue_stop_reasons[q];
-               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-
-               if (q_stopped)
-                       break;
-
                skb = ieee80211_tx_dequeue(&local->hw, queue);
                if (!skb)
                        break;
        local_bh_disable();
        spin_lock(&fq->lock);
 
-       sdata->vif.txqs_stopped[ac] = false;
-
        if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
                goto out;
 
                        if (ac != txq->ac)
                                continue;
 
-                       if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX,
+                       if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY,
                                                &txqi->flags))
                                continue;
 
 
        txqi = to_txq_info(vif->txq);
 
-       if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags) ||
+       if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||
            (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac)
                goto out;
 
                                   bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_sub_if_data *sdata;
-       int n_acs = IEEE80211_NUM_ACS;
 
        trace_stop_queue(local, queue, reason);
 
        else
                local->q_stop_reasons[queue][reason]++;
 
-       if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue]))
-               return;
-
-       if (local->hw.queues < IEEE80211_NUM_ACS)
-               n_acs = 1;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               int ac;
-
-               if (!sdata->dev)
-                       continue;
-
-               for (ac = 0; ac < n_acs; ac++) {
-                       if (sdata->vif.hw_queue[ac] == queue ||
-                           sdata->vif.cab_queue == queue) {
-                               spin_lock(&local->fq.lock);
-                               sdata->vif.txqs_stopped[ac] = true;
-                               spin_unlock(&local->fq.lock);
-                       }
-               }
-       }
-       rcu_read_unlock();
+       set_bit(reason, &local->queue_stop_reasons[queue]);
 }
 
 void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,