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 | * @drv_priv: data area for driver use, will always be aligned to | 1832 | * @drv_priv: data area for driver use, will always be aligned to |
| 1833 | * sizeof(void \*). | 1833 | * sizeof(void \*). |
| 1834 | * @txq: the multicast data TX queue | 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 | * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see | 1835 | * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see |
| 1838 | * &enum ieee80211_offload_flags. | 1836 | * &enum ieee80211_offload_flags. |
| 1839 | * @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled. | 1837 | * @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled. |
| @@ -1863,8 +1861,6 @@ struct ieee80211_vif { · 23 unmodified lines | |||
| 1863 | bool probe_req_reg; | 1861 | bool probe_req_reg; |
| 1864 | bool rx_mcast_action_reg; | 1862 | bool rx_mcast_action_reg; |
| 1865 | 1863 | ||
| 1866 | bool txqs_stopped[IEEE80211_NUM_ACS]; | ||
| 1867 | |||
| 1868 | struct ieee80211_vif *mbssid_tx_vif; | 1864 | struct ieee80211_vif *mbssid_tx_vif; |
| 1869 | 1865 | ||
| 1870 | /* must be last */ | 1866 | /* must be last */ |
| @@ -167,7 +167,7 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf, · 166 unmodified lines | |||
| 167 | continue; | 167 | continue; |
| 168 | txqi = to_txq_info(sta->sta.txq[i]); | 168 | txqi = to_txq_info(sta->sta.txq[i]); |
| 169 | p += scnprintf(p, bufsz + buf - p, | 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 | txqi->txq.tid, | 171 | txqi->txq.tid, |
| 172 | txqi->txq.ac, | 172 | txqi->txq.ac, |
| 173 | txqi->tin.backlog_bytes, | 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 | txqi->flags, | 182 | txqi->flags, |
| 183 | test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN", | 183 | test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN", |
| 184 | test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "", | 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 | rcu_read_unlock(); | 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 | /* In reconfig don't transmit now, but mark for waking later */ | 1200 | /* In reconfig don't transmit now, but mark for waking later */ |
| 1201 | if (local->in_reconfig) { | 1201 | if (local->in_reconfig) { |
| 1202 | set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txq->flags); | 1202 | set_bit(IEEE80211_TXQ_DIRTY, &txq->flags); |
| 1203 | return; | 1203 | return; |
| 1204 | } | 1204 | } |
| 1205 | 1205 | ||
| @@ -838,7 +838,7 @@ enum txq_info_flags { · 837 unmodified lines | |||
| 838 | IEEE80211_TXQ_STOP, | 838 | IEEE80211_TXQ_STOP, |
| 839 | IEEE80211_TXQ_AMPDU, | 839 | IEEE80211_TXQ_AMPDU, |
| 840 | IEEE80211_TXQ_NO_AMSDU, | 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 | struct ieee80211_tx_data tx; | 3783 | struct ieee80211_tx_data tx; |
| 3784 | ieee80211_tx_result r; | 3784 | ieee80211_tx_result r; |
| 3785 | struct ieee80211_vif *vif = txq->vif; | 3785 | struct ieee80211_vif *vif = txq->vif; |
| 3786 | int q = vif->hw_queue[txq->ac]; | ||
| 3787 | bool q_stopped; | ||
| 3786 | 3788 | ||
| 3787 | WARN_ON_ONCE(softirq_count() == 0); | 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 | return NULL; | 3792 | return NULL; |
| 3791 | 3793 | ||
| 3792 | begin: | 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) || | 3799 | if (unlikely(q_stopped)) { |
| 3796 | test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags)) | 3800 | /* mark for waking later */ |
| 3797 | goto out; | 3801 | set_bit(IEEE80211_TXQ_DIRTY, &txqi->flags); |
| 3802 | return NULL; | ||
| 3803 | } | ||
| 3798 | 3804 | ||
| 3799 | if (vif->txqs_stopped[txq->ac]) { | 3805 | spin_lock_bh(&fq->lock); |
| 3800 | set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags); | 3806 | |
| 3807 | if (unlikely(test_bit(IEEE80211_TXQ_STOP, &txqi->flags))) | ||
| 3801 | goto out; | 3808 | goto out; |
| 3802 | } | ||
| 3803 | 3809 | ||
| 3804 | /* Make sure fragments stay together. */ | 3810 | /* Make sure fragments stay together. */ |
| 3805 | skb = __skb_dequeue(&txqi->frags); | 3811 | skb = __skb_dequeue(&txqi->frags); |
| @@ -292,22 +292,12 @@ static void wake_tx_push_queue(struct ieee80211_local *local, · 291 unmodified lines | |||
| 292 | struct ieee80211_sub_if_data *sdata, | 292 | struct ieee80211_sub_if_data *sdata, |
| 293 | struct ieee80211_txq *queue) | 293 | struct ieee80211_txq *queue) |
| 294 | { | 294 | { |
| 295 | int q = sdata->vif.hw_queue[queue->ac]; | ||
| 296 | struct ieee80211_tx_control control = { | 295 | struct ieee80211_tx_control control = { |
| 297 | .sta = queue->sta, | 296 | .sta = queue->sta, |
| 298 | }; | 297 | }; |
| 299 | struct sk_buff *skb; | 298 | struct sk_buff *skb; |
| 300 | unsigned long flags; | ||
| 301 | bool q_stopped; | ||
| 302 | 299 | ||
| 303 | while (1) { | 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 | skb = ieee80211_tx_dequeue(&local->hw, queue); | 301 | skb = ieee80211_tx_dequeue(&local->hw, queue); |
| 312 | if (!skb) | 302 | if (!skb) |
| 313 | break; | 303 | break; |
| @@ -347,8 +337,6 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) · 33 unmodified lines | |||
| 347 | local_bh_disable(); | 337 | local_bh_disable(); |
| 348 | spin_lock(&fq->lock); | 338 | spin_lock(&fq->lock); |
| 349 | 339 | ||
| 350 | sdata->vif.txqs_stopped[ac] = false; | ||
| 351 | |||
| 352 | if (!test_bit(SDATA_STATE_RUNNING, &sdata->state)) | 340 | if (!test_bit(SDATA_STATE_RUNNING, &sdata->state)) |
| 353 | goto out; | 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 | if (ac != txq->ac) | 358 | if (ac != txq->ac) |
| 371 | continue; | 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 | &txqi->flags)) | 362 | &txqi->flags)) |
| 375 | continue; | 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 | txqi = to_txq_info(vif->txq); | 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 | (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) | 377 | (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) |
| 390 | goto out; | 378 | goto out; |
| 391 | 379 | ||
| @@ -517,8 +505,6 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, · 125 unmodified lines | |||
| 517 | bool refcounted) | 505 | bool refcounted) |
| 518 | { | 506 | { |
| 519 | struct ieee80211_local *local = hw_to_local(hw); | 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 | trace_stop_queue(local, queue, reason); | 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 | else | 516 | else |
| 531 | local->q_stop_reasons[queue][reason]++; | 517 | local->q_stop_reasons[queue][reason]++; |
| 532 | 518 | ||
| 533 | if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue])) | 519 | 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(); | ||
| 556 | } | 520 | } |
| 557 | 521 | ||
| 558 | void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, | 522 | void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, |