@@ -97,9 +97,14 @@
* linking it to ieee80211_handle_wake_tx_queue() or implementing a custom
* handler.
*
- * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with
- * another per-sta for non-data/non-mgmt and bufferable management frames, and
- * a single per-vif queue for multicast data frames.
+ * Intermediate queues (struct ieee80211_txq) are kept for:
+ * - per-sta per-tid
+ * - per-sta for non-data/non-mgmt and bufferable management frames
+ * - per-sta "noqueue" helper queue for sta (mac80211 internal only)
+ * - per-vif queue for broadcast/multicast data frames
+ * - per-vif fallback queue (mac80211 internal only)
+ * - per-vif "noqueue" helper queue (mac80211 internal only)
+ * The "mac80211 internal only" queues must be ignored by drivers.
*
* The driver is expected to initialize its private per-queue data for stations
* and interfaces in the .add_interface and .sta_add ops.
@@ -1965,9 +1970,18 @@ enum ieee80211_neg_ttlm_res {
* enum ieee80211_vif_txq - per-vif intermediate queues (txqi)
*
* @IEEE80211_VIF_TXQ_MULTICAST: queue for broadcast/multicast data frames.
+ * @IEEE80211_VIF_TXQ_FALLBACK: last resort queue for frames unable to use any
+ * other queue.
+ * @IEEE80211_VIF_TXQ_NOQUEUE: "queue" for non-sta frames which must be
+ * send out immediately, like offchannel, null-func and disassoc frames.
+ * (Queued frames will be transmitted by the queueing process and not wait
+ * for a queue run like normal iTXQs.)
+ *
*/
enum ieee80211_vif_txq {
IEEE80211_VIF_TXQ_MULTICAST,
+ IEEE80211_VIF_TXQ_FALLBACK,
+ IEEE80211_VIF_TXQ_NOQUEUE,
IEEE80211_VIF_TXQ_NUM,
};
@@ -2457,6 +2471,12 @@ struct ieee80211_link_sta {
struct ieee80211_sta_txpwr txpwr;
};
+/* To identify the NOQUEUE iTXQs vif->txq[IEEE80211_VIF_TXQ_NOQUEUE] and
+ * sta->txq[IEEE80211_TXQ_NOQUEUE] we set the tid for these queues to
+ * IEEE80211_TXQ_NOQUEUE (IEEE80211_NUM_TIDS + 1)
+ */
+#define IEEE80211_TXQ_NOQUEUE (IEEE80211_NUM_TIDS + 1)
+
/**
* struct ieee80211_sta - station table entry
*
@@ -2495,8 +2515,10 @@ struct ieee80211_link_sta {
* For non MLO STA it will point to the deflink data. For MLO STA
* ieee80211_sta_recalc_aggregates() must be called to update it.
* @support_p2p_ps: indicates whether the STA supports P2P PS mechanism or not.
- * @txq: per-TID data TX queues; note that the last entry (%IEEE80211_NUM_TIDS)
- * is used for non-data frames
+ * @txq: per-TID data TX queues; note that the last two queues are not for TIDs:
+ * %IEEE80211_NUM_TIDS is used for non-data frames and
+ * %IEEE80211_TXQ_NOQUEUE is for frames which can't wait in any of the
+ * other queues and must be send out immediately.
* @deflink: This holds the default link STA information, for non MLO STA all link
* specific STA information is accessed through @deflink or through
* link[0] which points to address of @deflink. For MLO Link STA
@@ -2530,7 +2552,7 @@ struct ieee80211_sta {
bool support_p2p_ps;
- struct ieee80211_txq *txq[IEEE80211_NUM_TIDS + 1];
+ struct ieee80211_txq *txq[IEEE80211_TXQ_NOQUEUE + 1];
u16 valid_links;
struct ieee80211_link_sta deflink;
@@ -619,28 +619,38 @@ static ssize_t ieee80211_if_fmt_aqm(
struct txq_info *txqi;
int len;
- if (!sdata->vif.txq[IEEE80211_VIF_TXQ_MULTICAST])
- return 0;
-
- txqi = to_txq_info(sdata->vif.txq[IEEE80211_VIF_TXQ_MULTICAST]);
+ len = scnprintf(buf,
+ buflen,
+ "id ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets flags\n");
spin_lock_bh(&local->fq.lock);
rcu_read_lock();
- len = scnprintf(buf,
- buflen,
- "ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets\n"
- "%u %u %u %u %u %u %u %u %u %u\n",
- txqi->txq.ac,
- txqi->tin.backlog_bytes,
- txqi->tin.backlog_packets,
- txqi->tin.flows,
- txqi->cstats.drop_count,
- txqi->cstats.ecn_mark,
- txqi->tin.overlimit,
- txqi->tin.collisions,
- txqi->tin.tx_bytes,
- txqi->tin.tx_packets);
+ for (int i = 0; i < IEEE80211_VIF_TXQ_NUM; i++) {
+ if (!sdata->vif.txq[i])
+ break;
+
+ txqi = to_txq_info(sdata->vif.txq[i]);
+ len += scnprintf(buf + len,
+ buflen - len,
+ "%u %u %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->tin.backlog_packets,
+ txqi->tin.flows,
+ txqi->cstats.drop_count,
+ txqi->cstats.ecn_mark,
+ txqi->tin.overlimit,
+ txqi->tin.collisions,
+ txqi->tin.tx_bytes,
+ txqi->tin.tx_packets,
+ 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_DIRTY, &txqi->flags) ? " DIRTY" : "");
+ }
rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
@@ -1356,18 +1356,31 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local,
return;
}
- if (!check_sdata_in_driver(sdata))
+ if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) {
+ if (local->monitor_sdata) {
+ sdata = local->monitor_sdata;
+ if (!check_sdata_in_driver(sdata))
+ return;
+ }
+ } else if (!check_sdata_in_driver(sdata)) {
return;
+ }
trace_drv_wake_tx_queue(local, sdata, txq);
- /* Driver support for MPDU txqi support is optional */
- if (unlikely(txq->txq.tid == IEEE80211_NUM_TIDS &&
- ((sdata->vif.type == NL80211_IFTYPE_STATION &&
- !ieee80211_hw_check(&sdata->local->hw, STA_MMPDU_TXQ)) ||
- (sdata->vif.type != NL80211_IFTYPE_STATION &&
- !ieee80211_hw_check(&sdata->local->hw,
- BUFF_MMPDU_TXQ))))) {
+ /*
+ * Driver support for MPDU txqi support is optional.
+ * IEEE80211_TXQ_NOQUEUE's txqi's are mac80211 internal and not
+ * intended to be handled by the drivers.
+ */
+ if (unlikely(WARN_ON(txq->txq.tid == IEEE80211_TXQ_NOQUEUE) ||
+ (txq->txq.tid == IEEE80211_NUM_TIDS &&
+ (!txq->txq.sta ||
+ (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ !ieee80211_hw_check(&sdata->local->hw, STA_MMPDU_TXQ)) ||
+ (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ !ieee80211_hw_check(&sdata->local->hw,
+ BUFF_MMPDU_TXQ)))))) {
ieee80211_handle_wake_tx_queue(&local->hw, &txq->txq);
return;
}
@@ -1623,6 +1623,25 @@ IEEE80211_WDEV_TO_SUB_IF(struct wireless_dev *wdev)
return container_of(wdev, struct ieee80211_sub_if_data, wdev);
}
+static inline struct ieee80211_sub_if_data *
+ieee80211_get_tx_sdata(struct ieee80211_sub_if_data *sdata)
+{
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data, u.ap);
+ /*
+ * local->monitor_sdata can only be set without
+ * %IEEE80211_HW_NO_VIRTUAL_MONITOR, no need to check it here.
+ */
+ else if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR) &&
+ sdata->local->monitor_sdata &&
+ !(sdata->u.mntr.flags & (MONITOR_FLAG_ACTIVE |
+ MONITOR_FLAG_COOK_FRAMES)))
+ sdata = rcu_dereference(sdata->local->monitor_sdata);
+
+ return sdata;
+}
+
static inline struct ieee80211_supported_band *
ieee80211_get_sband(struct ieee80211_sub_if_data *sdata)
{
@@ -1124,11 +1124,63 @@ static void ieee80211_sdata_init(struct ieee80211_local *local,
* MLD connection, we get a separate allocation for it.
*/
ieee80211_link_init(sdata, -1, &sdata->deflink, &sdata->vif.bss_conf);
+
+ for (int i = 0; i < NUM_NL80211_BANDS; i++) {
+ struct ieee80211_supported_band *sband;
+
+ sband = local->hw.wiphy->bands[i];
+ sdata->rc_rateidx_mask[i] =
+ sband ? (1 << sband->n_bitrates) - 1 : 0;
+ if (sband) {
+ __le16 cap;
+ u16 *vht_rate_mask;
+
+ memcpy(sdata->rc_rateidx_mcs_mask[i],
+ sband->ht_cap.mcs.rx_mask,
+ sizeof(sdata->rc_rateidx_mcs_mask[i]));
+
+ cap = sband->vht_cap.vht_mcs.rx_mcs_map;
+ vht_rate_mask = sdata->rc_rateidx_vht_mcs_mask[i];
+ ieee80211_get_vht_mask_from_cap(cap, vht_rate_mask);
+ } else {
+ memset(sdata->rc_rateidx_mcs_mask[i], 0,
+ sizeof(sdata->rc_rateidx_mcs_mask[i]));
+ memset(sdata->rc_rateidx_vht_mcs_mask[i], 0,
+ sizeof(sdata->rc_rateidx_vht_mcs_mask[i]));
+ }
+ }
+}
+
+static void ieee80211_vif_txq_init(struct ieee80211_sub_if_data *sdata,
+ int txq_offset, int txq_size, int num_queues)
+{
+ void *buffer = (char *)sdata + txq_offset;
+
+ /* Fallback queue */
+ ieee80211_txq_init(sdata, NULL, buffer, IEEE80211_NUM_TIDS);
+
+ if (num_queues == 1)
+ return;
+
+ /* Immediate TX helper queue */
+ buffer += txq_size;
+ ieee80211_txq_init(sdata, NULL, buffer, IEEE80211_TXQ_NOQUEUE);
+
+ if (num_queues == 2)
+ return;
+
+ /* Multicast queue */
+ buffer += txq_size;
+ ieee80211_txq_init(sdata, NULL, buffer, 0);
}
int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
+ int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size,
+ sizeof(void *));
+ int txq_size = ALIGN(sizeof(struct txq_info) + local->hw.txq_data_size,
+ sizeof(void *));
int ret;
ASSERT_RTNL();
@@ -1138,7 +1190,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
WARN_ON(ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)))
return 0;
- sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
+ sdata = kzalloc(size + txq_size, GFP_KERNEL);
if (!sdata)
return -ENOMEM;
@@ -1150,7 +1202,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
sdata->wdev.wiphy = local->hw.wiphy;
ieee80211_sdata_init(local, sdata);
-
+ ieee80211_vif_txq_init(sdata, size, txq_size, 1);
ieee80211_set_default_queues(sdata);
if (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) {
@@ -2077,8 +2129,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
{
struct net_device *ndev = NULL;
struct ieee80211_sub_if_data *sdata = NULL;
- struct txq_info *txqi;
- int ret, i;
+ int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size,
+ sizeof(void *));
+ int txq_size = ALIGN(sizeof(struct txq_info) + local->hw.txq_data_size,
+ sizeof(void *));
+ int num_txqs = 2;
+ int ret;
ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy);
@@ -2086,8 +2142,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
if (type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN) {
struct wireless_dev *wdev;
- sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size,
- GFP_KERNEL);
+ sdata = kzalloc(size + 2 * txq_size, GFP_KERNEL);
if (!sdata)
return -ENOMEM;
wdev = &sdata->wdev;
@@ -2098,17 +2153,14 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
} else {
- int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size,
- sizeof(void *));
- int txq_size = 0;
-
- if (type != NL80211_IFTYPE_AP_VLAN &&
- (type != NL80211_IFTYPE_MONITOR ||
- (params->flags & MONITOR_FLAG_ACTIVE)))
- txq_size += sizeof(struct txq_info) +
- local->hw.txq_data_size;
+ if (type == NL80211_IFTYPE_AP_VLAN)
+ num_txqs = 0;
+ else if (type == NL80211_IFTYPE_MONITOR)
+ num_txqs = 1;
+ else
+ num_txqs = 3;
- ndev = alloc_netdev_mqs(size + txq_size,
+ ndev = alloc_netdev_mqs(size + num_txqs * txq_size,
name, name_assign_type,
ieee80211_if_setup, 1, 1);
if (!ndev)
@@ -2147,14 +2199,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
memcpy(sdata->name, ndev->name, IFNAMSIZ);
- if (txq_size) {
- txqi = netdev_priv(ndev) + size;
- ieee80211_txq_init(sdata, NULL, txqi, 0);
- }
-
sdata->dev = ndev;
}
+ if (num_txqs)
+ ieee80211_vif_txq_init(sdata, size, txq_size, num_txqs);
+
/* initialise type-independent data */
sdata->wdev.wiphy = local->hw.wiphy;
@@ -2167,30 +2217,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
wiphy_delayed_work_init(&sdata->dec_tailroom_needed_wk,
ieee80211_delayed_tailroom_dec);
- for (i = 0; i < NUM_NL80211_BANDS; i++) {
- struct ieee80211_supported_band *sband;
- sband = local->hw.wiphy->bands[i];
- sdata->rc_rateidx_mask[i] =
- sband ? (1 << sband->n_bitrates) - 1 : 0;
- if (sband) {
- __le16 cap;
- u16 *vht_rate_mask;
-
- memcpy(sdata->rc_rateidx_mcs_mask[i],
- sband->ht_cap.mcs.rx_mask,
- sizeof(sdata->rc_rateidx_mcs_mask[i]));
-
- cap = sband->vht_cap.vht_mcs.rx_mcs_map;
- vht_rate_mask = sdata->rc_rateidx_vht_mcs_mask[i];
- ieee80211_get_vht_mask_from_cap(cap, vht_rate_mask);
- } else {
- memset(sdata->rc_rateidx_mcs_mask[i], 0,
- sizeof(sdata->rc_rateidx_mcs_mask[i]));
- memset(sdata->rc_rateidx_vht_mcs_mask[i], 0,
- sizeof(sdata->rc_rateidx_vht_mcs_mask[i]));
- }
- }
-
ieee80211_set_default_queues(sdata);
/* setup type-dependent data */
@@ -2237,10 +2263,29 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
return 0;
}
-void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_purge_txqs(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_txq *txq = sdata->vif.txq[IEEE80211_VIF_TXQ_MULTICAST];
+ struct sta_info *sta;
+ list_for_each_entry(sta, &sdata->local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+ ieee80211_purge_sta_txqs(sta);
+ }
+
+ for (int i = IEEE80211_VIF_TXQ_MULTICAST;
+ i <= IEEE80211_VIF_TXQ_FALLBACK;
+ i++) {
+ if (sdata->vif.txq[i])
+ ieee80211_txq_purge(sdata->local,
+ to_txq_info(sdata->vif.txq[i]));
+ }
+
+ synchronize_rcu();
+}
+
+void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
+{
ASSERT_RTNL();
lockdep_assert_wiphy(sdata->local->hw.wiphy);
@@ -2248,10 +2293,7 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
list_del_rcu(&sdata->list);
mutex_unlock(&sdata->local->iflist_mtx);
- if (txq)
- ieee80211_txq_purge(sdata->local, to_txq_info(txq));
-
- synchronize_rcu();
+ ieee80211_purge_txqs(sdata);
cfg80211_unregister_wdev(&sdata->wdev);
@@ -2300,6 +2342,8 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
list_for_each_entry_safe(sdata, tmp, &unreg_list, list) {
bool netdev = sdata->dev;
+ ieee80211_purge_txqs(sdata);
+
/*
* Remove IP addresses explicitly, since the notifier will
* skip the callbacks if wdev->registered is false, since
@@ -1300,31 +1300,37 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_txq *txq = NULL;
- if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
- (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
+ if (unlikely(info->flags & IEEE80211_TX_INTFL_OFFCHAN_TX_OK))
+ /* Offchannel queue can't be used, yet */
return NULL;
- if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
- unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
+ if (unlikely(vif->type == NL80211_IFTYPE_MONITOR ||
+ info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM ||
+ info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
+ goto out;
+
+ if (unlikely(!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
+ !ieee80211_is_data_present(hdr->frame_control))) {
if ((!ieee80211_is_mgmt(hdr->frame_control) ||
ieee80211_is_bufferable_mmpdu(skb) ||
vif->type == NL80211_IFTYPE_STATION) &&
sta && sta->uploaded) {
txq = sta->sta.txq[IEEE80211_NUM_TIDS];
}
- } else if (sta) {
+ } else if (likely(sta)) {
u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
- if (!sta->uploaded)
- return NULL;
+ if (unlikely(!sta->uploaded))
+ goto out;
txq = sta->sta.txq[tid];
} else {
txq = vif->txq[IEEE80211_VIF_TXQ_MULTICAST];
}
+out:
if (!txq)
- return NULL;
+ txq = vif->txq[IEEE80211_VIF_TXQ_FALLBACK];
return to_txq_info(txq);
}
@@ -1446,12 +1452,13 @@ static void ieee80211_txq_enqueue(struct ieee80211_local *local,
spin_lock_bh(&fq->lock);
/*
- * For management frames, don't really apply codel etc.,
+ * For management frames (tid set to IEEE80211_NUM_TIDS
+ * or IEEE80211_TXQ_NOQUEUE), don't really apply codel etc.,
* we don't want to apply any shaping or anything we just
- * want to simplify the driver API by having them on the
- * txqi.
+ * want to simplify the driver API and mac80211 internal
+ * handling by having them on the txqi.
*/
- if (unlikely(txqi->txq.tid == IEEE80211_NUM_TIDS)) {
+ if (unlikely(txqi->txq.tid >= IEEE80211_NUM_TIDS)) {
IEEE80211_SKB_CB(skb)->control.flags |=
IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
__skb_queue_tail(&txqi->frags, skb);
@@ -1509,14 +1516,26 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
txqi->txq.vif = &sdata->vif;
if (!sta) {
- sdata->vif.txq[IEEE80211_VIF_TXQ_MULTICAST] = &txqi->txq;
- txqi->txq.tid = 0;
- txqi->txq.ac = IEEE80211_AC_BE;
+ if (tid == IEEE80211_TXQ_NOQUEUE) {
+ sdata->vif.txq[IEEE80211_VIF_TXQ_NOQUEUE] =
+ &txqi->txq;
+ txqi->txq.ac = IEEE80211_AC_VO;
+ } else if (tid == IEEE80211_NUM_TIDS) {
+ sdata->vif.txq[IEEE80211_VIF_TXQ_FALLBACK] =
+ &txqi->txq;
+ txqi->txq.ac = IEEE80211_AC_VO;
+ } else {
+ sdata->vif.txq[IEEE80211_VIF_TXQ_MULTICAST] =
+ &txqi->txq;
+ txqi->txq.ac = IEEE80211_AC_BE;
+ }
+ txqi->txq.tid = tid;
return;
}
- if (tid == IEEE80211_NUM_TIDS)
+ /* for %IEEE80211_NUM_TIDS and %IEEE80211_TXQ_NOQUEUE*/
+ if (tid >= IEEE80211_NUM_TIDS)
txqi->txq.ac = IEEE80211_AC_VO;
else
txqi->txq.ac = ieee80211_ac_from_tid(tid);
@@ -1628,16 +1647,13 @@ static bool ieee80211_queue_skb(struct ieee80211_local *local,
struct sta_info *sta,
struct sk_buff *skb)
{
- struct ieee80211_vif *vif;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_vif *vif = &sdata->vif;
struct txq_info *txqi;
- if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
- return false;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- sdata = container_of(sdata->bss,
- struct ieee80211_sub_if_data, u.ap);
+ info->control.vif = vif;
+ sdata = ieee80211_get_tx_sdata(sdata);
vif = &sdata->vif;
txqi = ieee80211_get_txq(local, vif, sta, skb);
@@ -1645,7 +1661,6 @@ static bool ieee80211_queue_skb(struct ieee80211_local *local,
return false;
ieee80211_txq_enqueue(local, txqi, skb);
-
schedule_and_wake_txq(local, txqi);
return true;
@@ -3794,7 +3809,7 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw);
struct txq_info *txqi = container_of(txq, struct txq_info, txq);
struct ieee80211_hdr *hdr;
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb;
struct fq *fq = &local->fq;
struct fq_tin *tin = &txqi->tin;
struct ieee80211_tx_info *info;
@@ -3803,7 +3818,7 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_vif *vif = txq->vif;
int q = vif->hw_queue[txq->ac];
unsigned long flags;
- bool q_stopped;
+ int qsr;
WARN_ON_ONCE(softirq_count() == 0);
@@ -3812,10 +3827,21 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
begin:
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- q_stopped = local->queue_stop_reasons[q];
+ qsr = local->queue_stop_reasons[q];
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
- if (unlikely(q_stopped)) {
+ if (unlikely(qsr &&
+ (txq->tid != IEEE80211_TXQ_NOQUEUE ||
+ (qsr & ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL))))) {
+ /* Drop off-channel frames if queues are stopped for any reason
+ * other than off-channel operation. Never queue them.
+ */
+ if (unlikely(txq->tid == IEEE80211_TXQ_NOQUEUE)) {
+ WARN(1, "mac80211: Drop noqueue TX. qsr=%i\n", qsr);
+ ieee80211_txq_purge(local, txqi);
+ return NULL;
+ }
+
/* mark for waking later */
set_bit(IEEE80211_TXQ_DIRTY, &txqi->flags);
return NULL;
@@ -3936,36 +3962,6 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
}
}
- switch (tx.sdata->vif.type) {
- case NL80211_IFTYPE_MONITOR:
- if ((tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
- ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
- vif = &tx.sdata->vif;
- break;
- }
- tx.sdata = rcu_dereference(local->monitor_sdata);
- if (tx.sdata &&
- ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) {
- vif = &tx.sdata->vif;
- info->hw_queue =
- vif->hw_queue[skb_get_queue_mapping(skb)];
- } else if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
- ieee80211_free_txskb(&local->hw, skb);
- goto begin;
- } else {
- info->control.vif = NULL;
- return skb;
- }
- break;
- case NL80211_IFTYPE_AP_VLAN:
- tx.sdata = container_of(tx.sdata->bss,
- struct ieee80211_sub_if_data, u.ap);
- fallthrough;
- default:
- vif = &tx.sdata->vif;
- break;
- }
-
encap_out:
info->control.vif = vif;
@@ -4143,7 +4139,7 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
if (!txq->sta)
return true;
- if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
+ if (unlikely(txq->tid >= IEEE80211_NUM_TIDS))
return true;
sta = container_of(txq->sta, struct sta_info, sta);
@@ -4636,7 +4632,6 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sub_if_data, u.ap);
info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
- info->control.vif = &sdata->vif;
if (key)
info->control.hw_key = &key->conf;
@@ -320,65 +320,73 @@ void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue);
+static void __ieee80211_wake_txq(struct ieee80211_local *local,
+ struct ieee80211_txq *txq)
+{
+ 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);
+ spin_lock(&fq->lock);
+ }
+}
+
static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_vif *vif = &sdata->vif;
struct fq *fq = &local->fq;
+ struct ieee80211_vif *vif;
struct ps_data *ps = NULL;
- struct txq_info *txqi;
struct sta_info *sta;
int i;
local_bh_disable();
spin_lock(&fq->lock);
+ if (WARN_ON(!sdata))
+ goto out;
if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
goto out;
+ sdata = ieee80211_get_tx_sdata(sdata);
+ vif = &sdata->vif;
+
if (sdata->vif.type == NL80211_IFTYPE_AP)
ps = &sdata->bss->ps;
+ /* %IEEE80211_VIF_TXQ_NOQUEUE must be ignored here */
+
+ if (ac == IEEE80211_AC_VO) {
+ __ieee80211_wake_txq(local,
+ vif->txq[IEEE80211_VIF_TXQ_FALLBACK]);
+ }
+
+ if (ac == IEEE80211_AC_BE && vif->txq[IEEE80211_VIF_TXQ_MULTICAST] &&
+ (!ps || !atomic_read(&ps->num_sta_ps)))
+ __ieee80211_wake_txq(local,
+ vif->txq[IEEE80211_VIF_TXQ_MULTICAST]);
+
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata)
continue;
- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+ /* IEEE80211_TXQ_NOQUEUE must be ignored here. */
+ for (i = 0; i <= IEEE80211_NUM_TIDS; i++) {
struct ieee80211_txq *txq = sta->sta.txq[i];
if (!txq)
continue;
- txqi = to_txq_info(txq);
-
if (ac != txq->ac)
continue;
- if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY,
- &txqi->flags))
- continue;
-
- spin_unlock(&fq->lock);
- drv_wake_tx_queue(local, txqi);
- spin_lock(&fq->lock);
+ __ieee80211_wake_txq(local, txq);
}
}
-
- if (!vif->txq[IEEE80211_VIF_TXQ_MULTICAST])
- goto out;
-
- txqi = to_txq_info(vif->txq[IEEE80211_VIF_TXQ_MULTICAST]);
-
- if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||
- (ps && atomic_read(&ps->num_sta_ps)) ||
- ac != vif->txq[IEEE80211_VIF_TXQ_MULTICAST]->ac)
- goto out;
-
- spin_unlock(&fq->lock);
-
- drv_wake_tx_queue(local, txqi);
- local_bh_enable();
- return;
out:
spin_unlock(&fq->lock);
local_bh_enable();