Use a per-phy kthread to handle all TX operations except IEEE80211_TX_INTFL_NOQUEUE_TX. Signed-off-by: Alexander Wetzel <Alexander@xxxxxxxxxxxxxx> --- net/mac80211/agg-tx.c | 2 +- net/mac80211/driver-ops.h | 7 ----- net/mac80211/ieee80211_i.h | 7 +++-- net/mac80211/main.c | 7 +++-- net/mac80211/sta_info.c | 2 +- net/mac80211/tx.c | 14 +++++++-- net/mac80211/util.c | 64 +++++++++++++++++++++++++++++++++----- 7 files changed, 80 insertions(+), 23 deletions(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 69663e51d2bb..ec00e5730f1c 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -180,7 +180,7 @@ ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable) clear_bit(IEEE80211_TXQ_STOP, &txqi->flags); local_bh_disable(); rcu_read_lock(); - schedule_and_wake_txq(sta->sdata->local, txqi); + ieee80211_schedule_txq(&sta->sdata->local->hw, &txqi->txq); rcu_read_unlock(); local_bh_enable(); } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 3a7980b5a00a..0ddfa16bca7b 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1388,13 +1388,6 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local, local->ops->wake_tx_queue(&local->hw, &txq->txq); } -static inline void schedule_and_wake_txq(struct ieee80211_local *local, - struct txq_info *txqi) -{ - ieee80211_schedule_txq(&local->hw, &txqi->txq); - drv_wake_tx_queue(local, txqi); -} - static inline int drv_can_aggregate_in_amsdu(struct ieee80211_local *local, struct sk_buff *head, struct sk_buff *skb) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9c00c4e60cfc..8bd1e4f0e5fd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1341,9 +1341,7 @@ struct ieee80211_local { spinlock_t active_txq_lock[IEEE80211_NUM_ACS]; struct list_head active_txqs[IEEE80211_NUM_ACS]; u16 schedule_round[IEEE80211_NUM_ACS]; - - /* serializes ieee80211_handle_wake_tx_queue */ - spinlock_t handle_wake_tx_queue_lock; + bool txq_scheduler_used; u16 airtime_flags; u32 aql_txq_limit_low[IEEE80211_NUM_ACS]; @@ -1464,6 +1462,8 @@ struct ieee80211_local { struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; struct tasklet_struct wake_txqs_tasklet; + struct task_struct *mac80211_tsk; + wait_queue_head_t mac80211_tsk_wq; /* number of interfaces with allmulti RX */ atomic_t iff_allmultis; @@ -2541,6 +2541,7 @@ void ieee80211_txq_remove_vlan(struct ieee80211_local *local, void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats, struct txq_info *txqi); void ieee80211_wake_txqs(struct tasklet_struct *t); +int mac80211_thread(void *data); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, const u8 *extra, size_t extra_len, const u8 *bssid, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0416ab7e1ca7..25d5a57a74ca 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -972,8 +972,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, local->aql_threshold = IEEE80211_AQL_THRESHOLD; atomic_set(&local->aql_total_pending_airtime, 0); - spin_lock_init(&local->handle_wake_tx_queue_lock); - INIT_LIST_HEAD(&local->chanctx_list); wiphy_delayed_work_init(&local->scan_work, ieee80211_scan_work); @@ -1004,6 +1002,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs); tasklet_setup(&local->tasklet, ieee80211_tasklet_handler); + init_waitqueue_head(&local->mac80211_tsk_wq); + local->mac80211_tsk = kthread_run(mac80211_thread, local, + "mac80211-%s", wiphy_name(wiphy)); + skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue_unreliable); @@ -1655,6 +1657,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); + kthread_stop(local->mac80211_tsk); tasklet_kill(&local->tx_pending_tasklet); tasklet_kill(&local->tasklet); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 46695bcac4a7..7342f9466299 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1714,7 +1714,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) if (!sta->sta.txq[i] || !txq_has_queue(sta->sta.txq[i])) continue; - schedule_and_wake_txq(local, to_txq_info(sta->sta.txq[i])); + ieee80211_schedule_txq(&local->hw, sta->sta.txq[i]); } skb_queue_head_init(&pending); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d7cd27320df2..bd2f3c183e49 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1567,7 +1567,7 @@ static struct txq_info *ieee80211_queue_skb(struct ieee80211_local *local, ieee80211_txq_enqueue(local, txqi, skb); if (likely(txqi->txq.tid != IEEE80211_TXQ_NOQUEUE)) - schedule_and_wake_txq(local, txqi); + ieee80211_schedule_txq(&local->hw, &txqi->txq); return txqi; } @@ -3823,6 +3823,11 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) found_eligible_txq = false; } + if (test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags)) { + list_del_init(&txqi->schedule_order); + goto begin; + } + if (!head) head = txqi; @@ -3889,8 +3894,9 @@ void __ieee80211_schedule_txq(struct ieee80211_hw *hw, &local->active_txqs[txq->ac]); if (has_queue) ieee80211_txq_set_active(txqi); - } + wake_up_interruptible(&local->mac80211_tsk_wq); + } spin_unlock_bh(&local->active_txq_lock[txq->ac]); } EXPORT_SYMBOL(__ieee80211_schedule_txq); @@ -4006,6 +4012,8 @@ void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac) { struct ieee80211_local *local = hw_to_local(hw); + local->txq_scheduler_used = true; + spin_lock_bh(&local->active_txq_lock[ac]); if (ieee80211_txq_schedule_airtime_check(local, ac)) { @@ -4032,6 +4040,8 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, struct sk_buff *next; int len = skb->len; + skb->dev = dev; + if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { kfree_skb(skb); return; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ef7d482d1984..1f84b83b0ea4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -307,16 +307,14 @@ void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif); struct ieee80211_txq *queue; - spin_lock(&local->handle_wake_tx_queue_lock); - /* Use ieee80211_next_txq() for airtime fairness accounting */ ieee80211_txq_schedule_start(hw, txq->ac); - while ((queue = ieee80211_next_txq(hw, txq->ac))) { + queue = ieee80211_next_txq(hw, txq->ac); + if (queue) { wake_tx_push_queue(local, sdata, queue); ieee80211_return_txq(hw, queue, false); } ieee80211_txq_schedule_end(hw, txq->ac); - spin_unlock(&local->handle_wake_tx_queue_lock); } EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue); @@ -326,11 +324,9 @@ static void __ieee80211_wake_txq(struct ieee80211_local *local, struct txq_info *txqi = to_txq_info(txq); struct fq *fq = &local->fq; - if (WARN_ON(!txq)) - return; if (test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags)) { spin_unlock(&fq->lock); - drv_wake_tx_queue(local, txqi); + ieee80211_schedule_txq(&local->hw, txq); spin_lock(&fq->lock); } } @@ -486,6 +482,60 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, _ieee80211_wake_txqs(local, flags); } +static int ac_has_active_txq(struct ieee80211_local *local) +{ + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + spin_lock_bh(&local->active_txq_lock[ac]); + if (!list_empty(&local->active_txqs[ac])) + goto out; + spin_unlock_bh(&local->active_txq_lock[ac]); + } + return IEEE80211_NUM_ACS; +out: + spin_unlock_bh(&local->active_txq_lock[ac]); + return ac; +} + +int mac80211_thread(void *data) +{ + struct ieee80211_local *local = data; + struct txq_info *txqi, *tmp; + unsigned int ac = IEEE80211_NUM_ACS; + + while (1) { + wait_event_interruptible(local->mac80211_tsk_wq, + (ac = ac_has_active_txq(local)) + != IEEE80211_NUM_ACS); + if (kthread_should_stop()) + break; + + rcu_read_lock(); + + list_for_each_entry_safe(txqi, tmp, + &local->active_txqs[ac], + schedule_order) { + local->txq_scheduler_used = false; + local_bh_disable(); + drv_wake_tx_queue(local, txqi); + local_bh_enable(); + if (!local->txq_scheduler_used) { + /* Driver is not using + * ieee80211_txq_schedule_start(). + * Take over cleaning up the schedule. + */ + spin_lock_bh(&local->active_txq_lock[ac]); + list_del_init(&txqi->schedule_order); + spin_unlock_bh(&local->active_txq_lock[ac]); + } + } + + rcu_read_unlock(); + } + return 0; +} + void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason, bool refcounted) -- 2.48.1