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.
Unified · Split
| @@ -1832,8 +1832,6 @@ struct ieee80211_vif_cfg { · 1831 unmodified lines | ||
| 1832 | 1832 | * @drv_priv: data area for driver use, will always be aligned to |
| 1833 | 1833 | * sizeof(void \*). |
| 1834 | 1834 | * @txq: the multicast data TX queue |
| 1835 | − * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped, | |
| 1836 | − * protected by fq->lock. | |
| 1837 | 1835 | * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see |
| 1838 | 1836 | * &enum ieee80211_offload_flags. |
| 1839 | 1837 | * @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled. |
| @@ -1863,8 +1861,6 @@ struct ieee80211_vif { · 23 unmodified lines | ||
| 1863 | 1861 | bool probe_req_reg; |
| 1864 | 1862 | bool rx_mcast_action_reg; |
| 1865 | 1863 | |
| 1866 | − bool txqs_stopped[IEEE80211_NUM_ACS]; | |
| 1867 | − | |
| 1868 | 1864 | struct ieee80211_vif *mbssid_tx_vif; |
| 1869 | 1865 | |
| 1870 | 1866 | /* must be last */ |
| @@ -167,7 +167,7 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf, · 166 unmodified lines | ||
| 167 | 167 | continue; |
| 168 | 168 | txqi = to_txq_info(sta->sta.txq[i]); |
| 169 | 169 | p += scnprintf(p, bufsz + buf - p, |
| 170 | − "%d %d %u %u %u %u %u %u %u %u %u 0x%lx(%s%s%s)\n", | |
| 170 | + "%d %d %u %u %u %u %u %u %u %u %u 0x%lx(%s%s%s%s)\n", | |
| 171 | 171 | txqi->txq.tid, |
| 172 | 172 | txqi->txq.ac, |
| 173 | 173 | txqi->tin.backlog_bytes, |
| @@ -182,7 +182,8 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf, · 8 unmodified lines | ||
| 182 | 182 | txqi->flags, |
| 183 | 183 | test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN", |
| 184 | 184 | test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "", |
| 185 | − test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : ""); | |
| 185 | + test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "", | |
| 186 | + test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ? " DIRTY" : ""); | |
| 186 | 187 | } |
| 187 | 188 | |
| 188 | 189 | rcu_read_unlock(); |
| @@ -1199,7 +1199,7 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local, · 1198 unmodified lines | ||
| 1199 | 1199 | |
| 1200 | 1200 | /* In reconfig don't transmit now, but mark for waking later */ |
| 1201 | 1201 | if (local->in_reconfig) { |
| 1202 | − set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txq->flags); | |
| 1202 | + set_bit(IEEE80211_TXQ_DIRTY, &txq->flags); | |
| 1203 | 1203 | return; |
| 1204 | 1204 | } |
| 1205 | 1205 | |
| @@ -838,7 +838,7 @@ enum txq_info_flags { · 837 unmodified lines | ||
| 838 | 838 | IEEE80211_TXQ_STOP, |
| 839 | 839 | IEEE80211_TXQ_AMPDU, |
| 840 | 840 | IEEE80211_TXQ_NO_AMSDU, |
| 841 | − IEEE80211_TXQ_STOP_NETIF_TX, | |
| 841 | + IEEE80211_TXQ_DIRTY, | |
| 842 | 842 | }; |
| 843 | 843 | |
| 844 | 844 | /** |
| @@ -3783,6 +3783,8 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, · 3782 unmodified lines | ||
| 3783 | 3783 | struct ieee80211_tx_data tx; |
| 3784 | 3784 | ieee80211_tx_result r; |
| 3785 | 3785 | struct ieee80211_vif *vif = txq->vif; |
| 3786 | + int q = vif->hw_queue[txq->ac]; | |
| 3787 | + bool q_stopped; | |
| 3786 | 3788 | |
| 3787 | 3789 | WARN_ON_ONCE(softirq_count() == 0); |
| 3788 | 3790 | |
| @@ -3790,16 +3792,20 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, · 1 unmodified line | ||
| 3790 | 3792 | return NULL; |
| 3791 | 3793 | |
| 3792 | 3794 | begin: |
| 3793 | − spin_lock_bh(&fq->lock); | |
| 3795 | + spin_lock(&local->queue_stop_reason_lock); | |
| 3796 | + q_stopped = local->queue_stop_reasons[q]; | |
| 3797 | + spin_unlock(&local->queue_stop_reason_lock); | |
| 3794 | 3798 | |
| 3795 | − if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags) || | |
| 3796 | − test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags)) | |
| 3797 | − goto out; | |
| 3799 | + if (unlikely(q_stopped)) { | |
| 3800 | + /* mark for waking later */ | |
| 3801 | + set_bit(IEEE80211_TXQ_DIRTY, &txqi->flags); | |
| 3802 | + return NULL; | |
| 3803 | + } | |
| 3798 | 3804 | |
| 3799 | − if (vif->txqs_stopped[txq->ac]) { | |
| 3800 | − set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags); | |
| 3805 | + spin_lock_bh(&fq->lock); | |
| 3806 | + | |
| 3807 | + if (unlikely(test_bit(IEEE80211_TXQ_STOP, &txqi->flags))) | |
| 3801 | 3808 | goto out; |
| 3802 | − } | |
| 3803 | 3809 | |
| 3804 | 3810 | /* Make sure fragments stay together. */ |
| 3805 | 3811 | skb = __skb_dequeue(&txqi->frags); |
| @@ -292,22 +292,12 @@ static void wake_tx_push_queue(struct ieee80211_local *local, · 291 unmodified lines | ||
| 292 | 292 | struct ieee80211_sub_if_data *sdata, |
| 293 | 293 | struct ieee80211_txq *queue) |
| 294 | 294 | { |
| 295 | − int q = sdata->vif.hw_queue[queue->ac]; | |
| 296 | 295 | struct ieee80211_tx_control control = { |
| 297 | 296 | .sta = queue->sta, |
| 298 | 297 | }; |
| 299 | 298 | struct sk_buff *skb; |
| 300 | − unsigned long flags; | |
| 301 | − bool q_stopped; | |
| 302 | 299 | |
| 303 | 300 | while (1) { |
| 304 | − spin_lock_irqsave(&local->queue_stop_reason_lock, flags); | |
| 305 | − q_stopped = local->queue_stop_reasons[q]; | |
| 306 | − spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | |
| 307 | − | |
| 308 | − if (q_stopped) | |
| 309 | − break; | |
| 310 | − | |
| 311 | 301 | skb = ieee80211_tx_dequeue(&local->hw, queue); |
| 312 | 302 | if (!skb) |
| 313 | 303 | break; |
| @@ -347,8 +337,6 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) · 33 unmodified lines | ||
| 347 | 337 | local_bh_disable(); |
| 348 | 338 | spin_lock(&fq->lock); |
| 349 | 339 | |
| 350 | − sdata->vif.txqs_stopped[ac] = false; | |
| 351 | − | |
| 352 | 340 | if (!test_bit(SDATA_STATE_RUNNING, &sdata->state)) |
| 353 | 341 | goto out; |
| 354 | 342 | |
| @@ -370,7 +358,7 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) · 15 unmodified lines | ||
| 370 | 358 | if (ac != txq->ac) |
| 371 | 359 | continue; |
| 372 | 360 | |
| 373 | − if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, | |
| 361 | + if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, | |
| 374 | 362 | &txqi->flags)) |
| 375 | 363 | continue; |
| 376 | 364 | |
| @@ -385,7 +373,7 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) · 8 unmodified lines | ||
| 385 | 373 | |
| 386 | 374 | txqi = to_txq_info(vif->txq); |
| 387 | 375 | |
| 388 | − if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags) || | |
| 376 | + if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) || | |
| 389 | 377 | (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) |
| 390 | 378 | goto out; |
| 391 | 379 | |
| @@ -517,8 +505,6 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, · 125 unmodified lines | ||
| 517 | 505 | bool refcounted) |
| 518 | 506 | { |
| 519 | 507 | struct ieee80211_local *local = hw_to_local(hw); |
| 520 | − struct ieee80211_sub_if_data *sdata; | |
| 521 | − int n_acs = IEEE80211_NUM_ACS; | |
| 522 | 508 | |
| 523 | 509 | trace_stop_queue(local, queue, reason); |
| 524 | 510 | |
| @@ -530,29 +516,7 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, · 5 unmodified lines | ||
| 530 | 516 | else |
| 531 | 517 | local->q_stop_reasons[queue][reason]++; |
| 532 | 518 | |
| 533 | − if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue])) | |
| 534 | − return; | |
| 535 | − | |
| 536 | − if (local->hw.queues < IEEE80211_NUM_ACS) | |
| 537 | − n_acs = 1; | |
| 538 | − | |
| 539 | − rcu_read_lock(); | |
| 540 | − list_for_each_entry_rcu(sdata, &local->interfaces, list) { | |
| 541 | − int ac; | |
| 542 | − | |
| 543 | − if (!sdata->dev) | |
| 544 | − continue; | |
| 545 | − | |
| 546 | − for (ac = 0; ac < n_acs; ac++) { | |
| 547 | − if (sdata->vif.hw_queue[ac] == queue || | |
| 548 | − sdata->vif.cab_queue == queue) { | |
| 549 | − spin_lock(&local->fq.lock); | |
| 550 | − sdata->vif.txqs_stopped[ac] = true; | |
| 551 | − spin_unlock(&local->fq.lock); | |
| 552 | − } | |
| 553 | − } | |
| 554 | − } | |
| 555 | − rcu_read_unlock(); | |
| 519 | + set_bit(reason, &local->queue_stop_reasons[queue]); | |
| 556 | 520 | } |
| 557 | 521 | |
| 558 | 522 | void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, |