@@ -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();
}
@@ -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)
@@ -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,
@@ -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);
@@ -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);
@@ -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;
@@ -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)
Use a per-phy kthread to handle all TX operations except IEEE80211_TX_INTFL_NOQUEUE_TX. Signed-off-by: Alexander Wetzel <Alexander@wetzel-home.de> --- 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(-)