Add kernel tags to the patches that got accepted upstream.
Signed-off-by: Nick Hainke <vincent@systemli.org>
--- /dev/null
+From: Alexander Wetzel <alexander@wetzel-home.de>
+Date: Sun, 9 Oct 2022 18:30:38 +0200
+Subject: [PATCH] wifi: mac80211: add internal handler for wake_tx_queue
+
+Start to align the TX handling to only use internal TX queues (iTXQs):
+
+Provide a handler for drivers not having a custom wake_tx_queue
+callback and update the documentation.
+
+Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -89,15 +89,13 @@
+ /**
+ * DOC: mac80211 software tx queueing
+ *
+- * mac80211 provides an optional intermediate queueing implementation designed
+- * to allow the driver to keep hardware queues short and provide some fairness
+- * between different stations/interfaces.
+- * In this model, the driver pulls data frames from the mac80211 queue instead
+- * of letting mac80211 push them via drv_tx().
+- * Other frames (e.g. control or management) are still pushed using drv_tx().
++ * mac80211 uses an intermediate queueing implementation, designed to allow the
++ * driver to keep hardware queues short and to provide some fairness between
++ * different stations/interfaces.
+ *
+- * Drivers indicate that they use this model by implementing the .wake_tx_queue
+- * driver operation.
++ * Drivers must provide the .wake_tx_queue driver operation by either
++ * 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
+@@ -106,9 +104,12 @@
+ * The driver is expected to initialize its private per-queue data for stations
+ * and interfaces in the .add_interface and .sta_add ops.
+ *
+- * The driver can't access the queue directly. To dequeue a frame from a
+- * txq, it calls ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a
+- * queue, it calls the .wake_tx_queue driver op.
++ * The driver can't access the internal TX queues (iTXQs) directly.
++ * Whenever mac80211 adds a new frame to a queue, it calls the .wake_tx_queue
++ * driver op.
++ * Drivers implementing a custom .wake_tx_queue op can get them by calling
++ * ieee80211_tx_dequeue(). Drivers using ieee80211_handle_wake_tx_queue() will
++ * simply get the individual frames pushed via the .tx driver operation.
+ *
+ * Drivers can optionally delegate responsibility for scheduling queues to
+ * mac80211, to take advantage of airtime fairness accounting. In this case, to
+@@ -1826,7 +1827,7 @@ struct ieee80211_vif_cfg {
+ * for this interface.
+ * @drv_priv: data area for driver use, will always be aligned to
+ * sizeof(void \*).
+- * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
++ * @txq: the multicast data TX queue
+ * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped,
+ * protected by fq->lock.
+ * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
+@@ -2252,8 +2253,8 @@ 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 (if driver uses the TXQ abstraction); note that
+- * the last entry (%IEEE80211_NUM_TIDS) is used for non-data frames
++ * @txq: per-TID data TX queues; note that the last entry (%IEEE80211_NUM_TIDS)
++ * is used for non-data frames
+ * @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
+@@ -5691,7 +5692,7 @@ void ieee80211_key_replay(struct ieee802
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+- * Drivers should use this function instead of netif_wake_queue.
++ * Drivers must use this function instead of netif_wake_queue.
+ */
+ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue);
+
+@@ -5700,7 +5701,7 @@ void ieee80211_wake_queue(struct ieee802
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+- * Drivers should use this function instead of netif_stop_queue.
++ * Drivers must use this function instead of netif_stop_queue.
+ */
+ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue);
+
+@@ -5709,7 +5710,7 @@ void ieee80211_stop_queue(struct ieee802
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+- * Drivers should use this function instead of netif_stop_queue.
++ * Drivers must use this function instead of netif_queue_stopped.
+ *
+ * Return: %true if the queue is stopped. %false otherwise.
+ */
+@@ -5720,7 +5721,7 @@ int ieee80211_queue_stopped(struct ieee8
+ * ieee80211_stop_queues - stop all queues
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ *
+- * Drivers should use this function instead of netif_stop_queue.
++ * Drivers must use this function instead of netif_tx_stop_all_queues.
+ */
+ void ieee80211_stop_queues(struct ieee80211_hw *hw);
+
+@@ -5728,7 +5729,7 @@ void ieee80211_stop_queues(struct ieee80
+ * ieee80211_wake_queues - wake all queues
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ *
+- * Drivers should use this function instead of netif_wake_queue.
++ * Drivers must use this function instead of netif_tx_wake_all_queues.
+ */
+ void ieee80211_wake_queues(struct ieee80211_hw *hw);
+
+@@ -6950,6 +6951,18 @@ static inline struct sk_buff *ieee80211_
+ }
+
+ /**
++ * ieee80211_handle_wake_tx_queue - mac80211 handler for wake_tx_queue callback
++ *
++ * @hw: pointer as obtained from wake_tx_queue() callback().
++ * @txq: pointer as obtained from wake_tx_queue() callback().
++ *
++ * Drivers can use this function for the mandatory mac80211 wake_tx_queue
++ * callback in struct ieee80211_ops. They should not call this function.
++ */
++void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
++ struct ieee80211_txq *txq);
++
++/**
+ * ieee80211_next_txq - get next tx queue to pull packets from
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -288,6 +288,52 @@ __le16 ieee80211_ctstoself_duration(stru
+ }
+ EXPORT_SYMBOL(ieee80211_ctstoself_duration);
+
++static void wake_tx_push_queue(struct ieee80211_local *local,
++ struct ieee80211_sub_if_data *sdata,
++ struct ieee80211_txq *queue)
++{
++ int q = sdata->vif.hw_queue[queue->ac];
++ struct ieee80211_tx_control control = {
++ .sta = queue->sta,
++ };
++ struct sk_buff *skb;
++ unsigned long flags;
++ bool q_stopped;
++
++ while (1) {
++ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
++ q_stopped = local->queue_stop_reasons[q];
++ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
++
++ if (q_stopped)
++ break;
++
++ skb = ieee80211_tx_dequeue(&local->hw, queue);
++ if (!skb)
++ break;
++
++ drv_tx(local, &control, skb);
++ }
++}
++
++/* wake_tx_queue handler for driver not implementing a custom one*/
++void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
++ struct ieee80211_txq *txq)
++{
++ struct ieee80211_local *local = hw_to_local(hw);
++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
++ struct ieee80211_txq *queue;
++
++ /* Use ieee80211_next_txq() for airtime fairness accounting */
++ ieee80211_txq_schedule_start(hw, txq->ac);
++ while ((queue = ieee80211_next_txq(hw, txq->ac))) {
++ wake_tx_push_queue(local, sdata, queue);
++ ieee80211_return_txq(hw, queue, false);
++ }
++ ieee80211_txq_schedule_end(hw, txq->ac);
++}
++EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue);
++
+ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
+ {
+ struct ieee80211_local *local = sdata->local;
+++ /dev/null
-From: Alexander Wetzel <alexander@wetzel-home.de>
-Date: Sun, 9 Oct 2022 18:30:38 +0200
-Subject: [PATCH] wifi: mac80211: add internal handler for wake_tx_queue
-
-Start to align the TX handling to only use internal TX queues (iTXQs):
-
-Provide a handler for drivers not having a custom wake_tx_queue
-callback and update the documentation.
-
-Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -89,15 +89,13 @@
- /**
- * DOC: mac80211 software tx queueing
- *
-- * mac80211 provides an optional intermediate queueing implementation designed
-- * to allow the driver to keep hardware queues short and provide some fairness
-- * between different stations/interfaces.
-- * In this model, the driver pulls data frames from the mac80211 queue instead
-- * of letting mac80211 push them via drv_tx().
-- * Other frames (e.g. control or management) are still pushed using drv_tx().
-+ * mac80211 uses an intermediate queueing implementation, designed to allow the
-+ * driver to keep hardware queues short and to provide some fairness between
-+ * different stations/interfaces.
- *
-- * Drivers indicate that they use this model by implementing the .wake_tx_queue
-- * driver operation.
-+ * Drivers must provide the .wake_tx_queue driver operation by either
-+ * 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
-@@ -106,9 +104,12 @@
- * The driver is expected to initialize its private per-queue data for stations
- * and interfaces in the .add_interface and .sta_add ops.
- *
-- * The driver can't access the queue directly. To dequeue a frame from a
-- * txq, it calls ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a
-- * queue, it calls the .wake_tx_queue driver op.
-+ * The driver can't access the internal TX queues (iTXQs) directly.
-+ * Whenever mac80211 adds a new frame to a queue, it calls the .wake_tx_queue
-+ * driver op.
-+ * Drivers implementing a custom .wake_tx_queue op can get them by calling
-+ * ieee80211_tx_dequeue(). Drivers using ieee80211_handle_wake_tx_queue() will
-+ * simply get the individual frames pushed via the .tx driver operation.
- *
- * Drivers can optionally delegate responsibility for scheduling queues to
- * mac80211, to take advantage of airtime fairness accounting. In this case, to
-@@ -1826,7 +1827,7 @@ struct ieee80211_vif_cfg {
- * for this interface.
- * @drv_priv: data area for driver use, will always be aligned to
- * sizeof(void \*).
-- * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
-+ * @txq: the multicast data TX queue
- * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped,
- * protected by fq->lock.
- * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
-@@ -2252,8 +2253,8 @@ 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 (if driver uses the TXQ abstraction); note that
-- * the last entry (%IEEE80211_NUM_TIDS) is used for non-data frames
-+ * @txq: per-TID data TX queues; note that the last entry (%IEEE80211_NUM_TIDS)
-+ * is used for non-data frames
- * @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
-@@ -5691,7 +5692,7 @@ void ieee80211_key_replay(struct ieee802
- * @hw: pointer as obtained from ieee80211_alloc_hw().
- * @queue: queue number (counted from zero).
- *
-- * Drivers should use this function instead of netif_wake_queue.
-+ * Drivers must use this function instead of netif_wake_queue.
- */
- void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue);
-
-@@ -5700,7 +5701,7 @@ void ieee80211_wake_queue(struct ieee802
- * @hw: pointer as obtained from ieee80211_alloc_hw().
- * @queue: queue number (counted from zero).
- *
-- * Drivers should use this function instead of netif_stop_queue.
-+ * Drivers must use this function instead of netif_stop_queue.
- */
- void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue);
-
-@@ -5709,7 +5710,7 @@ void ieee80211_stop_queue(struct ieee802
- * @hw: pointer as obtained from ieee80211_alloc_hw().
- * @queue: queue number (counted from zero).
- *
-- * Drivers should use this function instead of netif_stop_queue.
-+ * Drivers must use this function instead of netif_queue_stopped.
- *
- * Return: %true if the queue is stopped. %false otherwise.
- */
-@@ -5720,7 +5721,7 @@ int ieee80211_queue_stopped(struct ieee8
- * ieee80211_stop_queues - stop all queues
- * @hw: pointer as obtained from ieee80211_alloc_hw().
- *
-- * Drivers should use this function instead of netif_stop_queue.
-+ * Drivers must use this function instead of netif_tx_stop_all_queues.
- */
- void ieee80211_stop_queues(struct ieee80211_hw *hw);
-
-@@ -5728,7 +5729,7 @@ void ieee80211_stop_queues(struct ieee80
- * ieee80211_wake_queues - wake all queues
- * @hw: pointer as obtained from ieee80211_alloc_hw().
- *
-- * Drivers should use this function instead of netif_wake_queue.
-+ * Drivers must use this function instead of netif_tx_wake_all_queues.
- */
- void ieee80211_wake_queues(struct ieee80211_hw *hw);
-
-@@ -6950,6 +6951,18 @@ static inline struct sk_buff *ieee80211_
- }
-
- /**
-+ * ieee80211_handle_wake_tx_queue - mac80211 handler for wake_tx_queue callback
-+ *
-+ * @hw: pointer as obtained from wake_tx_queue() callback().
-+ * @txq: pointer as obtained from wake_tx_queue() callback().
-+ *
-+ * Drivers can use this function for the mandatory mac80211 wake_tx_queue
-+ * callback in struct ieee80211_ops. They should not call this function.
-+ */
-+void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq);
-+
-+/**
- * ieee80211_next_txq - get next tx queue to pull packets from
- *
- * @hw: pointer as obtained from ieee80211_alloc_hw()
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -288,6 +288,52 @@ __le16 ieee80211_ctstoself_duration(stru
- }
- EXPORT_SYMBOL(ieee80211_ctstoself_duration);
-
-+static void wake_tx_push_queue(struct ieee80211_local *local,
-+ struct ieee80211_sub_if_data *sdata,
-+ struct ieee80211_txq *queue)
-+{
-+ int q = sdata->vif.hw_queue[queue->ac];
-+ struct ieee80211_tx_control control = {
-+ .sta = queue->sta,
-+ };
-+ struct sk_buff *skb;
-+ unsigned long flags;
-+ bool q_stopped;
-+
-+ while (1) {
-+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-+ q_stopped = local->queue_stop_reasons[q];
-+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-+
-+ if (q_stopped)
-+ break;
-+
-+ skb = ieee80211_tx_dequeue(&local->hw, queue);
-+ if (!skb)
-+ break;
-+
-+ drv_tx(local, &control, skb);
-+ }
-+}
-+
-+/* wake_tx_queue handler for driver not implementing a custom one*/
-+void ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
-+ struct ieee80211_txq *txq)
-+{
-+ struct ieee80211_local *local = hw_to_local(hw);
-+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
-+ struct ieee80211_txq *queue;
-+
-+ /* Use ieee80211_next_txq() for airtime fairness accounting */
-+ ieee80211_txq_schedule_start(hw, txq->ac);
-+ while ((queue = ieee80211_next_txq(hw, txq->ac))) {
-+ wake_tx_push_queue(local, sdata, queue);
-+ ieee80211_return_txq(hw, queue, false);
-+ }
-+ ieee80211_txq_schedule_end(hw, txq->ac);
-+}
-+EXPORT_SYMBOL(ieee80211_handle_wake_tx_queue);
-+
- static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
- {
- struct ieee80211_local *local = sdata->local;
--- /dev/null
+From: Alexander Wetzel <alexander@wetzel-home.de>
+Date: Sun, 9 Oct 2022 18:30:39 +0200
+Subject: [PATCH] wifi: mac80211: add wake_tx_queue callback to drivers
+
+mac80211 is fully switching over to the internal TX queue (iTXQ)
+implementation. Update all drivers not yet providing the now mandatory
+wake_tx_queue() callback.
+
+As an side effect the netdev interfaces of all updated drivers will
+switch to the noqueue qdisc.
+
+Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
+[add staging drivers]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/drivers/net/wireless/admtek/adm8211.c
++++ b/drivers/net/wireless/admtek/adm8211.c
+@@ -1760,6 +1760,7 @@ static int adm8211_alloc_rings(struct ie
+
+ static const struct ieee80211_ops adm8211_ops = {
+ .tx = adm8211_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = adm8211_start,
+ .stop = adm8211_stop,
+ .add_interface = adm8211_add_interface,
+--- a/drivers/net/wireless/ath/ar5523/ar5523.c
++++ b/drivers/net/wireless/ath/ar5523/ar5523.c
+@@ -1355,6 +1355,7 @@ static const struct ieee80211_ops ar5523
+ .start = ar5523_start,
+ .stop = ar5523_stop,
+ .tx = ar5523_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .set_rts_threshold = ar5523_set_rts_threshold,
+ .add_interface = ar5523_add_interface,
+ .remove_interface = ar5523_remove_interface,
+--- a/drivers/net/wireless/ath/ath11k/mac.c
++++ b/drivers/net/wireless/ath/ath11k/mac.c
+@@ -8539,6 +8539,7 @@ err_fallback:
+
+ static const struct ieee80211_ops ath11k_ops = {
+ .tx = ath11k_mac_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = ath11k_mac_op_start,
+ .stop = ath11k_mac_op_stop,
+ .reconfig_complete = ath11k_mac_op_reconfig_complete,
+--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
++++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+@@ -781,6 +781,7 @@ static int ath5k_set_ringparam(struct ie
+
+ const struct ieee80211_ops ath5k_hw_ops = {
+ .tx = ath5k_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = ath5k_start,
+ .stop = ath5k_stop,
+ .add_interface = ath5k_add_interface,
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+@@ -1870,6 +1870,7 @@ static void ath9k_htc_channel_switch_bea
+
+ struct ieee80211_ops ath9k_htc_ops = {
+ .tx = ath9k_htc_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = ath9k_htc_start,
+ .stop = ath9k_htc_stop,
+ .add_interface = ath9k_htc_add_interface,
+--- a/drivers/net/wireless/ath/carl9170/main.c
++++ b/drivers/net/wireless/ath/carl9170/main.c
+@@ -1715,6 +1715,7 @@ static const struct ieee80211_ops carl91
+ .start = carl9170_op_start,
+ .stop = carl9170_op_stop,
+ .tx = carl9170_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .flush = carl9170_op_flush,
+ .add_interface = carl9170_op_add_interface,
+ .remove_interface = carl9170_op_remove_interface,
+--- a/drivers/net/wireless/ath/wcn36xx/main.c
++++ b/drivers/net/wireless/ath/wcn36xx/main.c
+@@ -1362,6 +1362,7 @@ static const struct ieee80211_ops wcn36x
+ .prepare_multicast = wcn36xx_prepare_multicast,
+ .configure_filter = wcn36xx_configure_filter,
+ .tx = wcn36xx_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .set_key = wcn36xx_set_key,
+ .hw_scan = wcn36xx_hw_scan,
+ .cancel_hw_scan = wcn36xx_cancel_hw_scan,
+--- a/drivers/net/wireless/atmel/at76c50x-usb.c
++++ b/drivers/net/wireless/atmel/at76c50x-usb.c
+@@ -2187,6 +2187,7 @@ static int at76_set_key(struct ieee80211
+
+ static const struct ieee80211_ops at76_ops = {
+ .tx = at76_mac80211_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .add_interface = at76_add_interface,
+ .remove_interface = at76_remove_interface,
+ .config = at76_config,
+--- a/drivers/net/wireless/broadcom/b43/main.c
++++ b/drivers/net/wireless/broadcom/b43/main.c
+@@ -5171,6 +5171,7 @@ static int b43_op_get_survey(struct ieee
+
+ static const struct ieee80211_ops b43_hw_ops = {
+ .tx = b43_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .conf_tx = b43_op_conf_tx,
+ .add_interface = b43_op_add_interface,
+ .remove_interface = b43_op_remove_interface,
+--- a/drivers/net/wireless/broadcom/b43legacy/main.c
++++ b/drivers/net/wireless/broadcom/b43legacy/main.c
+@@ -3532,6 +3532,7 @@ static int b43legacy_op_get_survey(struc
+
+ static const struct ieee80211_ops b43legacy_hw_ops = {
+ .tx = b43legacy_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .conf_tx = b43legacy_op_conf_tx,
+ .add_interface = b43legacy_op_add_interface,
+ .remove_interface = b43legacy_op_remove_interface,
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
+@@ -962,6 +962,7 @@ static int brcms_ops_beacon_set_tim(stru
+
+ static const struct ieee80211_ops brcms_ops = {
+ .tx = brcms_ops_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = brcms_ops_start,
+ .stop = brcms_ops_stop,
+ .add_interface = brcms_ops_add_interface,
+--- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
++++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
+@@ -3435,6 +3435,7 @@ static const struct attribute_group il39
+
+ static struct ieee80211_ops il3945_mac_ops __ro_after_init = {
+ .tx = il3945_mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = il3945_mac_start,
+ .stop = il3945_mac_stop,
+ .add_interface = il_mac_add_interface,
+--- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c
++++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
+@@ -6304,6 +6304,7 @@ il4965_tx_queue_set_status(struct il_pri
+
+ static const struct ieee80211_ops il4965_mac_ops = {
+ .tx = il4965_mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = il4965_mac_start,
+ .stop = il4965_mac_stop,
+ .add_interface = il_mac_add_interface,
+--- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
++++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
+@@ -1571,6 +1571,7 @@ static void iwlagn_mac_sta_notify(struct
+
+ const struct ieee80211_ops iwlagn_hw_ops = {
+ .tx = iwlagn_mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = iwlagn_mac_start,
+ .stop = iwlagn_mac_stop,
+ #ifdef CONFIG_PM_SLEEP
+--- a/drivers/net/wireless/intersil/p54/main.c
++++ b/drivers/net/wireless/intersil/p54/main.c
+@@ -705,6 +705,7 @@ static void p54_set_coverage_class(struc
+
+ static const struct ieee80211_ops p54_ops = {
+ .tx = p54_tx_80211,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = p54_start,
+ .stop = p54_stop,
+ .add_interface = p54_add_interface,
+--- a/drivers/net/wireless/mac80211_hwsim.c
++++ b/drivers/net/wireless/mac80211_hwsim.c
+@@ -3109,6 +3109,7 @@ static int mac80211_hwsim_change_sta_lin
+
+ #define HWSIM_COMMON_OPS \
+ .tx = mac80211_hwsim_tx, \
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue, \
+ .start = mac80211_hwsim_start, \
+ .stop = mac80211_hwsim_stop, \
+ .add_interface = mac80211_hwsim_add_interface, \
+--- a/drivers/net/wireless/marvell/libertas_tf/main.c
++++ b/drivers/net/wireless/marvell/libertas_tf/main.c
+@@ -474,6 +474,7 @@ static int lbtf_op_get_survey(struct iee
+
+ static const struct ieee80211_ops lbtf_ops = {
+ .tx = lbtf_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = lbtf_op_start,
+ .stop = lbtf_op_stop,
+ .add_interface = lbtf_op_add_interface,
+--- a/drivers/net/wireless/marvell/mwl8k.c
++++ b/drivers/net/wireless/marvell/mwl8k.c
+@@ -5611,6 +5611,7 @@ static void mwl8k_sw_scan_complete(struc
+
+ static const struct ieee80211_ops mwl8k_ops = {
+ .tx = mwl8k_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = mwl8k_start,
+ .stop = mwl8k_stop,
+ .add_interface = mwl8k_add_interface,
+--- a/drivers/net/wireless/mediatek/mt7601u/main.c
++++ b/drivers/net/wireless/mediatek/mt7601u/main.c
+@@ -406,6 +406,7 @@ out:
+
+ const struct ieee80211_ops mt7601u_ops = {
+ .tx = mt7601u_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = mt7601u_start,
+ .stop = mt7601u_stop,
+ .add_interface = mt7601u_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
+@@ -1706,6 +1706,7 @@ static int rt2400pci_tx_last_beacon(stru
+
+ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
+@@ -2004,6 +2004,7 @@ static int rt2500pci_tx_last_beacon(stru
+
+ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
+@@ -1795,6 +1795,7 @@ static int rt2500usb_probe_hw(struct rt2
+
+ static const struct ieee80211_ops rt2500usb_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+@@ -288,6 +288,7 @@ static int rt2800pci_read_eeprom(struct
+
+ static const struct ieee80211_ops rt2800pci_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+@@ -133,6 +133,7 @@ static int rt2800soc_write_firmware(stru
+
+ static const struct ieee80211_ops rt2800soc_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+@@ -630,6 +630,7 @@ static int rt2800usb_probe_hw(struct rt2
+
+ static const struct ieee80211_ops rt2800usb_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
+@@ -2873,6 +2873,7 @@ static u64 rt61pci_get_tsf(struct ieee80
+
+ static const struct ieee80211_ops rt61pci_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
+@@ -2292,6 +2292,7 @@ static u64 rt73usb_get_tsf(struct ieee80
+
+ static const struct ieee80211_ops rt73usb_mac80211_ops = {
+ .tx = rt2x00mac_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+--- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
++++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
+@@ -1608,6 +1608,7 @@ static void rtl8180_configure_filter(str
+
+ static const struct ieee80211_ops rtl8180_ops = {
+ .tx = rtl8180_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rtl8180_start,
+ .stop = rtl8180_stop,
+ .add_interface = rtl8180_add_interface,
+--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
++++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
+@@ -1378,6 +1378,7 @@ static int rtl8187_conf_tx(struct ieee80
+
+ static const struct ieee80211_ops rtl8187_ops = {
+ .tx = rtl8187_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rtl8187_start,
+ .stop = rtl8187_stop,
+ .add_interface = rtl8187_add_interface,
+--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
++++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+@@ -6561,6 +6561,7 @@ static void rtl8xxxu_stop(struct ieee802
+
+ static const struct ieee80211_ops rtl8xxxu_ops = {
+ .tx = rtl8xxxu_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .add_interface = rtl8xxxu_add_interface,
+ .remove_interface = rtl8xxxu_remove_interface,
+ .config = rtl8xxxu_config,
+--- a/drivers/net/wireless/realtek/rtlwifi/core.c
++++ b/drivers/net/wireless/realtek/rtlwifi/core.c
+@@ -1912,6 +1912,7 @@ const struct ieee80211_ops rtl_ops = {
+ .start = rtl_op_start,
+ .stop = rtl_op_stop,
+ .tx = rtl_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .add_interface = rtl_op_add_interface,
+ .remove_interface = rtl_op_remove_interface,
+ .change_interface = rtl_op_change_interface,
+--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
+@@ -896,6 +896,7 @@ static void rtw_ops_sta_rc_update(struct
+
+ const struct ieee80211_ops rtw_ops = {
+ .tx = rtw_ops_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = rtw_ops_wake_tx_queue,
+ .start = rtw_ops_start,
+ .stop = rtw_ops_stop,
+--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
+@@ -918,6 +918,7 @@ static int rtw89_ops_set_tid_config(stru
+
+ const struct ieee80211_ops rtw89_ops = {
+ .tx = rtw89_ops_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = rtw89_ops_wake_tx_queue,
+ .start = rtw89_ops_start,
+ .stop = rtw89_ops_stop,
+--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
++++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+@@ -1958,6 +1958,7 @@ static int rsi_mac80211_resume(struct ie
+
+ static const struct ieee80211_ops mac80211_ops = {
+ .tx = rsi_mac80211_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = rsi_mac80211_start,
+ .stop = rsi_mac80211_stop,
+ .add_interface = rsi_mac80211_add_interface,
+--- a/drivers/net/wireless/st/cw1200/main.c
++++ b/drivers/net/wireless/st/cw1200/main.c
+@@ -209,6 +209,7 @@ static const struct ieee80211_ops cw1200
+ .remove_interface = cw1200_remove_interface,
+ .change_interface = cw1200_change_interface,
+ .tx = cw1200_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .hw_scan = cw1200_hw_scan,
+ .set_tim = cw1200_set_tim,
+ .sta_notify = cw1200_sta_notify,
+--- a/drivers/net/wireless/ti/wl1251/main.c
++++ b/drivers/net/wireless/ti/wl1251/main.c
+@@ -1359,6 +1359,7 @@ static const struct ieee80211_ops wl1251
+ .prepare_multicast = wl1251_op_prepare_multicast,
+ .configure_filter = wl1251_op_configure_filter,
+ .tx = wl1251_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .set_key = wl1251_op_set_key,
+ .hw_scan = wl1251_op_hw_scan,
+ .bss_info_changed = wl1251_op_bss_info_changed,
+--- a/drivers/net/wireless/ti/wlcore/main.c
++++ b/drivers/net/wireless/ti/wlcore/main.c
+@@ -5942,6 +5942,7 @@ static const struct ieee80211_ops wl1271
+ .prepare_multicast = wl1271_op_prepare_multicast,
+ .configure_filter = wl1271_op_configure_filter,
+ .tx = wl1271_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .set_key = wlcore_op_set_key,
+ .hw_scan = wl1271_op_hw_scan,
+ .cancel_hw_scan = wl1271_op_cancel_hw_scan,
+--- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
++++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
+@@ -1344,6 +1344,7 @@ static u64 zd_op_get_tsf(struct ieee8021
+
+ static const struct ieee80211_ops zd_ops = {
+ .tx = zd_op_tx,
++ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .start = zd_op_start,
+ .stop = zd_op_stop,
+ .add_interface = zd_op_add_interface,
+++ /dev/null
-From: Alexander Wetzel <alexander@wetzel-home.de>
-Date: Sun, 9 Oct 2022 18:30:39 +0200
-Subject: [PATCH] wifi: mac80211: add wake_tx_queue callback to drivers
-
-mac80211 is fully switching over to the internal TX queue (iTXQ)
-implementation. Update all drivers not yet providing the now mandatory
-wake_tx_queue() callback.
-
-As an side effect the netdev interfaces of all updated drivers will
-switch to the noqueue qdisc.
-
-Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
-[add staging drivers]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/drivers/net/wireless/admtek/adm8211.c
-+++ b/drivers/net/wireless/admtek/adm8211.c
-@@ -1760,6 +1760,7 @@ static int adm8211_alloc_rings(struct ie
-
- static const struct ieee80211_ops adm8211_ops = {
- .tx = adm8211_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = adm8211_start,
- .stop = adm8211_stop,
- .add_interface = adm8211_add_interface,
---- a/drivers/net/wireless/ath/ar5523/ar5523.c
-+++ b/drivers/net/wireless/ath/ar5523/ar5523.c
-@@ -1355,6 +1355,7 @@ static const struct ieee80211_ops ar5523
- .start = ar5523_start,
- .stop = ar5523_stop,
- .tx = ar5523_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .set_rts_threshold = ar5523_set_rts_threshold,
- .add_interface = ar5523_add_interface,
- .remove_interface = ar5523_remove_interface,
---- a/drivers/net/wireless/ath/ath11k/mac.c
-+++ b/drivers/net/wireless/ath/ath11k/mac.c
-@@ -8539,6 +8539,7 @@ err_fallback:
-
- static const struct ieee80211_ops ath11k_ops = {
- .tx = ath11k_mac_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = ath11k_mac_op_start,
- .stop = ath11k_mac_op_stop,
- .reconfig_complete = ath11k_mac_op_reconfig_complete,
---- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
-+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
-@@ -781,6 +781,7 @@ static int ath5k_set_ringparam(struct ie
-
- const struct ieee80211_ops ath5k_hw_ops = {
- .tx = ath5k_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = ath5k_start,
- .stop = ath5k_stop,
- .add_interface = ath5k_add_interface,
---- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
-+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
-@@ -1870,6 +1870,7 @@ static void ath9k_htc_channel_switch_bea
-
- struct ieee80211_ops ath9k_htc_ops = {
- .tx = ath9k_htc_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = ath9k_htc_start,
- .stop = ath9k_htc_stop,
- .add_interface = ath9k_htc_add_interface,
---- a/drivers/net/wireless/ath/carl9170/main.c
-+++ b/drivers/net/wireless/ath/carl9170/main.c
-@@ -1715,6 +1715,7 @@ static const struct ieee80211_ops carl91
- .start = carl9170_op_start,
- .stop = carl9170_op_stop,
- .tx = carl9170_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .flush = carl9170_op_flush,
- .add_interface = carl9170_op_add_interface,
- .remove_interface = carl9170_op_remove_interface,
---- a/drivers/net/wireless/ath/wcn36xx/main.c
-+++ b/drivers/net/wireless/ath/wcn36xx/main.c
-@@ -1362,6 +1362,7 @@ static const struct ieee80211_ops wcn36x
- .prepare_multicast = wcn36xx_prepare_multicast,
- .configure_filter = wcn36xx_configure_filter,
- .tx = wcn36xx_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .set_key = wcn36xx_set_key,
- .hw_scan = wcn36xx_hw_scan,
- .cancel_hw_scan = wcn36xx_cancel_hw_scan,
---- a/drivers/net/wireless/atmel/at76c50x-usb.c
-+++ b/drivers/net/wireless/atmel/at76c50x-usb.c
-@@ -2187,6 +2187,7 @@ static int at76_set_key(struct ieee80211
-
- static const struct ieee80211_ops at76_ops = {
- .tx = at76_mac80211_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .add_interface = at76_add_interface,
- .remove_interface = at76_remove_interface,
- .config = at76_config,
---- a/drivers/net/wireless/broadcom/b43/main.c
-+++ b/drivers/net/wireless/broadcom/b43/main.c
-@@ -5171,6 +5171,7 @@ static int b43_op_get_survey(struct ieee
-
- static const struct ieee80211_ops b43_hw_ops = {
- .tx = b43_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .conf_tx = b43_op_conf_tx,
- .add_interface = b43_op_add_interface,
- .remove_interface = b43_op_remove_interface,
---- a/drivers/net/wireless/broadcom/b43legacy/main.c
-+++ b/drivers/net/wireless/broadcom/b43legacy/main.c
-@@ -3532,6 +3532,7 @@ static int b43legacy_op_get_survey(struc
-
- static const struct ieee80211_ops b43legacy_hw_ops = {
- .tx = b43legacy_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .conf_tx = b43legacy_op_conf_tx,
- .add_interface = b43legacy_op_add_interface,
- .remove_interface = b43legacy_op_remove_interface,
---- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
-@@ -962,6 +962,7 @@ static int brcms_ops_beacon_set_tim(stru
-
- static const struct ieee80211_ops brcms_ops = {
- .tx = brcms_ops_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = brcms_ops_start,
- .stop = brcms_ops_stop,
- .add_interface = brcms_ops_add_interface,
---- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
-+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
-@@ -3435,6 +3435,7 @@ static const struct attribute_group il39
-
- static struct ieee80211_ops il3945_mac_ops __ro_after_init = {
- .tx = il3945_mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = il3945_mac_start,
- .stop = il3945_mac_stop,
- .add_interface = il_mac_add_interface,
---- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c
-+++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
-@@ -6304,6 +6304,7 @@ il4965_tx_queue_set_status(struct il_pri
-
- static const struct ieee80211_ops il4965_mac_ops = {
- .tx = il4965_mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = il4965_mac_start,
- .stop = il4965_mac_stop,
- .add_interface = il_mac_add_interface,
---- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
-+++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
-@@ -1571,6 +1571,7 @@ static void iwlagn_mac_sta_notify(struct
-
- const struct ieee80211_ops iwlagn_hw_ops = {
- .tx = iwlagn_mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = iwlagn_mac_start,
- .stop = iwlagn_mac_stop,
- #ifdef CONFIG_PM_SLEEP
---- a/drivers/net/wireless/intersil/p54/main.c
-+++ b/drivers/net/wireless/intersil/p54/main.c
-@@ -705,6 +705,7 @@ static void p54_set_coverage_class(struc
-
- static const struct ieee80211_ops p54_ops = {
- .tx = p54_tx_80211,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = p54_start,
- .stop = p54_stop,
- .add_interface = p54_add_interface,
---- a/drivers/net/wireless/mac80211_hwsim.c
-+++ b/drivers/net/wireless/mac80211_hwsim.c
-@@ -3109,6 +3109,7 @@ static int mac80211_hwsim_change_sta_lin
-
- #define HWSIM_COMMON_OPS \
- .tx = mac80211_hwsim_tx, \
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue, \
- .start = mac80211_hwsim_start, \
- .stop = mac80211_hwsim_stop, \
- .add_interface = mac80211_hwsim_add_interface, \
---- a/drivers/net/wireless/marvell/libertas_tf/main.c
-+++ b/drivers/net/wireless/marvell/libertas_tf/main.c
-@@ -474,6 +474,7 @@ static int lbtf_op_get_survey(struct iee
-
- static const struct ieee80211_ops lbtf_ops = {
- .tx = lbtf_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = lbtf_op_start,
- .stop = lbtf_op_stop,
- .add_interface = lbtf_op_add_interface,
---- a/drivers/net/wireless/marvell/mwl8k.c
-+++ b/drivers/net/wireless/marvell/mwl8k.c
-@@ -5611,6 +5611,7 @@ static void mwl8k_sw_scan_complete(struc
-
- static const struct ieee80211_ops mwl8k_ops = {
- .tx = mwl8k_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = mwl8k_start,
- .stop = mwl8k_stop,
- .add_interface = mwl8k_add_interface,
---- a/drivers/net/wireless/mediatek/mt7601u/main.c
-+++ b/drivers/net/wireless/mediatek/mt7601u/main.c
-@@ -406,6 +406,7 @@ out:
-
- const struct ieee80211_ops mt7601u_ops = {
- .tx = mt7601u_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = mt7601u_start,
- .stop = mt7601u_stop,
- .add_interface = mt7601u_add_interface,
---- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
-@@ -1706,6 +1706,7 @@ static int rt2400pci_tx_last_beacon(stru
-
- static const struct ieee80211_ops rt2400pci_mac80211_ops = {
- .tx = rt2x00mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
---- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
-@@ -2004,6 +2004,7 @@ static int rt2500pci_tx_last_beacon(stru
-
- static const struct ieee80211_ops rt2500pci_mac80211_ops = {
- .tx = rt2x00mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
---- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
-@@ -1795,6 +1795,7 @@ static int rt2500usb_probe_hw(struct rt2
-
- static const struct ieee80211_ops rt2500usb_mac80211_ops = {
- .tx = rt2x00mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
---- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
-@@ -288,6 +288,7 @@ static int rt2800pci_read_eeprom(struct
-
- static const struct ieee80211_ops rt2800pci_mac80211_ops = {
- .tx = rt2x00mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
---- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
-@@ -133,6 +133,7 @@ static int rt2800soc_write_firmware(stru
-
- static const struct ieee80211_ops rt2800soc_mac80211_ops = {
- .tx = rt2x00mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
---- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
-@@ -630,6 +630,7 @@ static int rt2800usb_probe_hw(struct rt2
-
- static const struct ieee80211_ops rt2800usb_mac80211_ops = {
- .tx = rt2x00mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
---- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
-@@ -2873,6 +2873,7 @@ static u64 rt61pci_get_tsf(struct ieee80
-
- static const struct ieee80211_ops rt61pci_mac80211_ops = {
- .tx = rt2x00mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
---- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c
-+++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
-@@ -2292,6 +2292,7 @@ static u64 rt73usb_get_tsf(struct ieee80
-
- static const struct ieee80211_ops rt73usb_mac80211_ops = {
- .tx = rt2x00mac_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
---- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
-+++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
-@@ -1608,6 +1608,7 @@ static void rtl8180_configure_filter(str
-
- static const struct ieee80211_ops rtl8180_ops = {
- .tx = rtl8180_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rtl8180_start,
- .stop = rtl8180_stop,
- .add_interface = rtl8180_add_interface,
---- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
-+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
-@@ -1378,6 +1378,7 @@ static int rtl8187_conf_tx(struct ieee80
-
- static const struct ieee80211_ops rtl8187_ops = {
- .tx = rtl8187_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rtl8187_start,
- .stop = rtl8187_stop,
- .add_interface = rtl8187_add_interface,
---- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
-+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
-@@ -6561,6 +6561,7 @@ static void rtl8xxxu_stop(struct ieee802
-
- static const struct ieee80211_ops rtl8xxxu_ops = {
- .tx = rtl8xxxu_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .add_interface = rtl8xxxu_add_interface,
- .remove_interface = rtl8xxxu_remove_interface,
- .config = rtl8xxxu_config,
---- a/drivers/net/wireless/realtek/rtlwifi/core.c
-+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
-@@ -1912,6 +1912,7 @@ const struct ieee80211_ops rtl_ops = {
- .start = rtl_op_start,
- .stop = rtl_op_stop,
- .tx = rtl_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .add_interface = rtl_op_add_interface,
- .remove_interface = rtl_op_remove_interface,
- .change_interface = rtl_op_change_interface,
---- a/drivers/net/wireless/realtek/rtw88/mac80211.c
-+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
-@@ -896,6 +896,7 @@ static void rtw_ops_sta_rc_update(struct
-
- const struct ieee80211_ops rtw_ops = {
- .tx = rtw_ops_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .wake_tx_queue = rtw_ops_wake_tx_queue,
- .start = rtw_ops_start,
- .stop = rtw_ops_stop,
---- a/drivers/net/wireless/realtek/rtw89/mac80211.c
-+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
-@@ -918,6 +918,7 @@ static int rtw89_ops_set_tid_config(stru
-
- const struct ieee80211_ops rtw89_ops = {
- .tx = rtw89_ops_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .wake_tx_queue = rtw89_ops_wake_tx_queue,
- .start = rtw89_ops_start,
- .stop = rtw89_ops_stop,
---- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
-+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
-@@ -1958,6 +1958,7 @@ static int rsi_mac80211_resume(struct ie
-
- static const struct ieee80211_ops mac80211_ops = {
- .tx = rsi_mac80211_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = rsi_mac80211_start,
- .stop = rsi_mac80211_stop,
- .add_interface = rsi_mac80211_add_interface,
---- a/drivers/net/wireless/st/cw1200/main.c
-+++ b/drivers/net/wireless/st/cw1200/main.c
-@@ -209,6 +209,7 @@ static const struct ieee80211_ops cw1200
- .remove_interface = cw1200_remove_interface,
- .change_interface = cw1200_change_interface,
- .tx = cw1200_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .hw_scan = cw1200_hw_scan,
- .set_tim = cw1200_set_tim,
- .sta_notify = cw1200_sta_notify,
---- a/drivers/net/wireless/ti/wl1251/main.c
-+++ b/drivers/net/wireless/ti/wl1251/main.c
-@@ -1359,6 +1359,7 @@ static const struct ieee80211_ops wl1251
- .prepare_multicast = wl1251_op_prepare_multicast,
- .configure_filter = wl1251_op_configure_filter,
- .tx = wl1251_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .set_key = wl1251_op_set_key,
- .hw_scan = wl1251_op_hw_scan,
- .bss_info_changed = wl1251_op_bss_info_changed,
---- a/drivers/net/wireless/ti/wlcore/main.c
-+++ b/drivers/net/wireless/ti/wlcore/main.c
-@@ -5942,6 +5942,7 @@ static const struct ieee80211_ops wl1271
- .prepare_multicast = wl1271_op_prepare_multicast,
- .configure_filter = wl1271_op_configure_filter,
- .tx = wl1271_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .set_key = wlcore_op_set_key,
- .hw_scan = wl1271_op_hw_scan,
- .cancel_hw_scan = wl1271_op_cancel_hw_scan,
---- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
-+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
-@@ -1344,6 +1344,7 @@ static u64 zd_op_get_tsf(struct ieee8021
-
- static const struct ieee80211_ops zd_ops = {
- .tx = zd_op_tx,
-+ .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .start = zd_op_start,
- .stop = zd_op_stop,
- .add_interface = zd_op_add_interface,
--- /dev/null
+From: Alexander Wetzel <alexander@wetzel-home.de>
+Date: Sun, 9 Oct 2022 18:30:40 +0200
+Subject: [PATCH] wifi: mac80211: Drop support for TX push path
+
+All drivers are now using mac80211 internal queues (iTXQs).
+Drop mac80211 internal support for the old push path.
+
+Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4339,9 +4339,6 @@ static int ieee80211_get_txq_stats(struc
+ struct ieee80211_sub_if_data *sdata;
+ int ret = 0;
+
+- if (!local->ops->wake_tx_queue)
+- return 1;
+-
+ spin_lock_bh(&local->fq.lock);
+ rcu_read_lock();
+
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -663,9 +663,7 @@ void debugfs_hw_add(struct ieee80211_loc
+ DEBUGFS_ADD_MODE(force_tx_status, 0600);
+ DEBUGFS_ADD_MODE(aql_enable, 0600);
+ DEBUGFS_ADD(aql_pending);
+-
+- if (local->ops->wake_tx_queue)
+- DEBUGFS_ADD_MODE(aqm, 0600);
++ DEBUGFS_ADD_MODE(aqm, 0600);
+
+ DEBUGFS_ADD_MODE(airtime_flags, 0600);
+
+--- a/net/mac80211/debugfs_netdev.c
++++ b/net/mac80211/debugfs_netdev.c
+@@ -677,8 +677,7 @@ static void add_common_files(struct ieee
+ DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
+ DEBUGFS_ADD(hw_queues);
+
+- if (sdata->local->ops->wake_tx_queue &&
+- sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
++ if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+ sdata->vif.type != NL80211_IFTYPE_NAN)
+ DEBUGFS_ADD(aqm);
+ }
+--- a/net/mac80211/debugfs_sta.c
++++ b/net/mac80211/debugfs_sta.c
+@@ -1056,10 +1056,8 @@ void ieee80211_sta_debugfs_add(struct st
+ DEBUGFS_ADD_COUNTER(rx_fragments, deflink.rx_stats.fragments);
+ DEBUGFS_ADD_COUNTER(tx_filtered, deflink.status_stats.filtered);
+
+- if (local->ops->wake_tx_queue) {
+- DEBUGFS_ADD(aqm);
+- DEBUGFS_ADD(airtime);
+- }
++ DEBUGFS_ADD(aqm);
++ DEBUGFS_ADD(airtime);
+
+ if (wiphy_ext_feature_isset(local->hw.wiphy,
+ NL80211_EXT_FEATURE_AQL))
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2290,7 +2290,6 @@ void ieee80211_wake_queue_by_reason(stru
+ void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason,
+ bool refcounted);
+-void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);
+ void ieee80211_add_pending_skb(struct ieee80211_local *local,
+ struct sk_buff *skb);
+ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -458,12 +458,6 @@ static void ieee80211_do_stop(struct iee
+ if (cancel_scan)
+ ieee80211_scan_cancel(local);
+
+- /*
+- * Stop TX on this interface first.
+- */
+- if (!local->ops->wake_tx_queue && sdata->dev)
+- netif_tx_stop_all_queues(sdata->dev);
+-
+ ieee80211_roc_purge(local, sdata);
+
+ switch (sdata->vif.type) {
+@@ -811,13 +805,6 @@ static void ieee80211_uninit(struct net_
+ ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));
+ }
+
+-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
+- struct sk_buff *skb,
+- struct net_device *sb_dev)
+-{
+- return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
+-}
+-
+ static void
+ ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+ {
+@@ -831,7 +818,6 @@ static const struct net_device_ops ieee8
+ .ndo_start_xmit = ieee80211_subif_start_xmit,
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
+ .ndo_set_mac_address = ieee80211_change_mac,
+- .ndo_select_queue = ieee80211_netdev_select_queue,
+ .ndo_get_stats64 = ieee80211_get_stats64,
+ };
+
+@@ -939,7 +925,6 @@ static const struct net_device_ops ieee8
+ .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
+ .ndo_set_mac_address = ieee80211_change_mac,
+- .ndo_select_queue = ieee80211_netdev_select_queue,
+ .ndo_get_stats64 = ieee80211_get_stats64,
+ .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path,
+ };
+@@ -1441,35 +1426,6 @@ int ieee80211_do_open(struct wireless_de
+
+ ieee80211_recalc_ps(local);
+
+- if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
+- sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+- local->ops->wake_tx_queue) {
+- /* XXX: for AP_VLAN, actually track AP queues */
+- if (dev)
+- netif_tx_start_all_queues(dev);
+- } else if (dev) {
+- unsigned long flags;
+- int n_acs = IEEE80211_NUM_ACS;
+- int ac;
+-
+- if (local->hw.queues < IEEE80211_NUM_ACS)
+- n_acs = 1;
+-
+- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+- if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE ||
+- (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 &&
+- skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) {
+- for (ac = 0; ac < n_acs; ac++) {
+- int ac_queue = sdata->vif.hw_queue[ac];
+-
+- if (local->queue_stop_reasons[ac_queue] == 0 &&
+- skb_queue_empty(&local->pending[ac_queue]))
+- netif_start_subqueue(dev, ac);
+- }
+- }
+- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+- }
+-
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ return 0;
+@@ -1499,17 +1455,12 @@ static void ieee80211_if_setup(struct ne
+ {
+ ether_setup(dev);
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
++ dev->priv_flags |= IFF_NO_QUEUE;
+ dev->netdev_ops = &ieee80211_dataif_ops;
+ dev->needs_free_netdev = true;
+ dev->priv_destructor = ieee80211_if_free;
+ }
+
+-static void ieee80211_if_setup_no_queue(struct net_device *dev)
+-{
+- ieee80211_if_setup(dev);
+- dev->priv_flags |= IFF_NO_QUEUE;
+-}
+-
+ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+@@ -2094,9 +2045,7 @@ int ieee80211_if_add(struct ieee80211_lo
+ struct net_device *ndev = NULL;
+ struct ieee80211_sub_if_data *sdata = NULL;
+ struct txq_info *txqi;
+- void (*if_setup)(struct net_device *dev);
+ int ret, i;
+- int txqs = 1;
+
+ ASSERT_RTNL();
+
+@@ -2119,30 +2068,18 @@ int ieee80211_if_add(struct ieee80211_lo
+ sizeof(void *));
+ int txq_size = 0;
+
+- if (local->ops->wake_tx_queue &&
+- type != NL80211_IFTYPE_AP_VLAN &&
++ 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 (local->ops->wake_tx_queue) {
+- if_setup = ieee80211_if_setup_no_queue;
+- } else {
+- if_setup = ieee80211_if_setup;
+- if (local->hw.queues >= IEEE80211_NUM_ACS)
+- txqs = IEEE80211_NUM_ACS;
+- }
+-
+ ndev = alloc_netdev_mqs(size + txq_size,
+ name, name_assign_type,
+- if_setup, txqs, 1);
++ ieee80211_if_setup, 1, 1);
+ if (!ndev)
+ return -ENOMEM;
+
+- if (!local->ops->wake_tx_queue && local->hw.wiphy->tx_queue_len)
+- ndev->tx_queue_len = local->hw.wiphy->tx_queue_len;
+-
+ dev_net_set(ndev, wiphy_net(local->hw.wiphy));
+
+ ndev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -630,7 +630,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+
+ if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config ||
+ !ops->add_interface || !ops->remove_interface ||
+- !ops->configure_filter))
++ !ops->configure_filter || !ops->wake_tx_queue))
+ return NULL;
+
+ if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
+@@ -719,9 +719,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+ if (!ops->set_key)
+ wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
+- if (ops->wake_tx_queue)
+- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS);
+-
++ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
+
+ wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
+@@ -834,10 +832,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+ atomic_set(&local->agg_queue_stop[i], 0);
+ }
+ tasklet_setup(&local->tx_pending_tasklet, ieee80211_tx_pending);
+-
+- if (ops->wake_tx_queue)
+- tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs);
+-
++ tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs);
+ tasklet_setup(&local->tasklet, ieee80211_tasklet_handler);
+
+ skb_queue_head_init(&local->skb_queue);
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1571,9 +1571,6 @@ static void sta_ps_start(struct sta_info
+
+ ieee80211_clear_fast_xmit(sta);
+
+- if (!sta->sta.txq[0])
+- return;
+-
+ for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
+ struct ieee80211_txq *txq = sta->sta.txq[tid];
+ struct txq_info *txqi = to_txq_info(txq);
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -140,17 +140,15 @@ static void __cleanup_single_sta(struct
+ atomic_dec(&ps->num_sta_ps);
+ }
+
+- if (sta->sta.txq[0]) {
+- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+- struct txq_info *txqi;
++ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
++ struct txq_info *txqi;
+
+- if (!sta->sta.txq[i])
+- continue;
++ if (!sta->sta.txq[i])
++ continue;
+
+- txqi = to_txq_info(sta->sta.txq[i]);
++ txqi = to_txq_info(sta->sta.txq[i]);
+
+- ieee80211_txq_purge(local, txqi);
+- }
++ ieee80211_txq_purge(local, txqi);
+ }
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+@@ -425,8 +423,7 @@ void sta_info_free(struct ieee80211_loca
+
+ sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
+
+- if (sta->sta.txq[0])
+- kfree(to_txq_info(sta->sta.txq[0]));
++ kfree(to_txq_info(sta->sta.txq[0]));
+ kfree(rcu_dereference_raw(sta->sta.rates));
+ #ifdef CPTCFG_MAC80211_MESH
+ kfree(sta->mesh);
+@@ -527,6 +524,8 @@ __sta_info_alloc(struct ieee80211_sub_if
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hw *hw = &local->hw;
+ struct sta_info *sta;
++ void *txq_data;
++ int size;
+ int i;
+
+ sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
+@@ -597,21 +596,18 @@ __sta_info_alloc(struct ieee80211_sub_if
+
+ sta->last_connected = ktime_get_seconds();
+
+- if (local->ops->wake_tx_queue) {
+- void *txq_data;
+- int size = sizeof(struct txq_info) +
+- ALIGN(hw->txq_data_size, sizeof(void *));
++ size = sizeof(struct txq_info) +
++ ALIGN(hw->txq_data_size, sizeof(void *));
+
+- txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
+- if (!txq_data)
+- goto free;
++ txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
++ if (!txq_data)
++ goto free;
+
+- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+- struct txq_info *txq = txq_data + i * size;
++ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
++ struct txq_info *txq = txq_data + i * size;
+
+- /* might not do anything for the bufferable MMPDU TXQ */
+- ieee80211_txq_init(sdata, sta, txq, i);
+- }
++ /* might not do anything for the (bufferable) MMPDU TXQ */
++ ieee80211_txq_init(sdata, sta, txq, i);
+ }
+
+ if (sta_prepare_rate_control(local, sta, gfp))
+@@ -685,8 +681,7 @@ __sta_info_alloc(struct ieee80211_sub_if
+ return sta;
+
+ free_txq:
+- if (sta->sta.txq[0])
+- kfree(to_txq_info(sta->sta.txq[0]));
++ kfree(to_txq_info(sta->sta.txq[0]));
+ free:
+ sta_info_free_link(&sta->deflink);
+ #ifdef CPTCFG_MAC80211_MESH
+@@ -1959,9 +1954,6 @@ ieee80211_sta_ps_deliver_response(struct
+ * TIM recalculation.
+ */
+
+- if (!sta->sta.txq[0])
+- return;
+-
+ for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+ if (!sta->sta.txq[tid] ||
+ !(driver_release_tids & BIT(tid)) ||
+@@ -2446,7 +2438,7 @@ static void sta_set_tidstats(struct sta_
+ tidstats->tx_msdu_failed = sta->deflink.status_stats.msdu_failed[tid];
+ }
+
+- if (local->ops->wake_tx_queue && tid < IEEE80211_NUM_TIDS) {
++ if (tid < IEEE80211_NUM_TIDS) {
+ spin_lock_bh(&local->fq.lock);
+ rcu_read_lock();
+
+@@ -2774,9 +2766,6 @@ unsigned long ieee80211_sta_last_active(
+
+ static void sta_update_codel_params(struct sta_info *sta, u32 thr)
+ {
+- if (!sta->sdata->local->ops->wake_tx_queue)
+- return;
+-
+ if (thr && thr < STA_SLOW_THRESHOLD * sta->local->num_sta) {
+ sta->cparams.target = MS2TIME(50);
+ sta->cparams.interval = MS2TIME(300);
+--- a/net/mac80211/tdls.c
++++ b/net/mac80211/tdls.c
+@@ -1016,7 +1016,6 @@ ieee80211_tdls_prep_mgmt_packet(struct w
+ skb->priority = 256 + 5;
+ break;
+ }
+- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb));
+
+ /*
+ * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1599,9 +1599,6 @@ int ieee80211_txq_setup_flows(struct iee
+ bool supp_vht = false;
+ enum nl80211_band band;
+
+- if (!local->ops->wake_tx_queue)
+- return 0;
+-
+ ret = fq_init(fq, 4096);
+ if (ret)
+ return ret;
+@@ -1649,9 +1646,6 @@ void ieee80211_txq_teardown_flows(struct
+ {
+ struct fq *fq = &local->fq;
+
+- if (!local->ops->wake_tx_queue)
+- return;
+-
+ kfree(local->cvars);
+ local->cvars = NULL;
+
+@@ -1668,8 +1662,7 @@ static bool ieee80211_queue_skb(struct i
+ struct ieee80211_vif *vif;
+ struct txq_info *txqi;
+
+- if (!local->ops->wake_tx_queue ||
+- sdata->vif.type == NL80211_IFTYPE_MONITOR)
++ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ return false;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+@@ -4185,12 +4178,7 @@ void __ieee80211_subif_start_xmit(struct
+ if (IS_ERR(sta))
+ sta = NULL;
+
+- if (local->ops->wake_tx_queue) {
+- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
+- skb_set_queue_mapping(skb, queue);
+- skb_get_hash(skb);
+- }
+-
++ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
+ ieee80211_aggr_check(sdata, sta, skb);
+
+ sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);
+@@ -4501,11 +4489,7 @@ static void ieee80211_8023_xmit(struct i
+ struct tid_ampdu_tx *tid_tx;
+ u8 tid;
+
+- if (local->ops->wake_tx_queue) {
+- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
+- skb_set_queue_mapping(skb, queue);
+- skb_get_hash(skb);
+- }
++ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
+
+ if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
+ test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
+@@ -4759,9 +4743,6 @@ void ieee80211_tx_pending(struct tasklet
+ if (!txok)
+ break;
+ }
+-
+- if (skb_queue_empty(&local->pending[i]))
+- ieee80211_propagate_queue_wake(local, i);
+ }
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+@@ -5954,10 +5935,9 @@ int ieee80211_tx_control_port(struct wip
+ }
+
+ if (!IS_ERR(sta)) {
+- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
++ u16 queue = ieee80211_select_queue(sdata, sta, skb);
+
+ skb_set_queue_mapping(skb, queue);
+- skb_get_hash(skb);
+
+ /*
+ * for MLO STA, the SA should be the AP MLD address, but
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -446,39 +446,6 @@ void ieee80211_wake_txqs(struct tasklet_
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ }
+
+-void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
+-{
+- struct ieee80211_sub_if_data *sdata;
+- int n_acs = IEEE80211_NUM_ACS;
+-
+- if (local->ops->wake_tx_queue)
+- return;
+-
+- if (local->hw.queues < IEEE80211_NUM_ACS)
+- n_acs = 1;
+-
+- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+- int ac;
+-
+- if (!sdata->dev)
+- continue;
+-
+- if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
+- local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
+- continue;
+-
+- for (ac = 0; ac < n_acs; ac++) {
+- int ac_queue = sdata->vif.hw_queue[ac];
+-
+- if (ac_queue == queue ||
+- (sdata->vif.cab_queue == queue &&
+- local->queue_stop_reasons[ac_queue] == 0 &&
+- skb_queue_empty(&local->pending[ac_queue])))
+- netif_wake_subqueue(sdata->dev, ac);
+- }
+- }
+-}
+-
+ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason,
+ bool refcounted,
+@@ -509,11 +476,7 @@ static void __ieee80211_wake_queue(struc
+ /* someone still has this queue stopped */
+ return;
+
+- if (skb_queue_empty(&local->pending[queue])) {
+- rcu_read_lock();
+- ieee80211_propagate_queue_wake(local, queue);
+- rcu_read_unlock();
+- } else
++ if (!skb_queue_empty(&local->pending[queue]))
+ tasklet_schedule(&local->tx_pending_tasklet);
+
+ /*
+@@ -523,12 +486,10 @@ static void __ieee80211_wake_queue(struc
+ * release someone's lock, but it is fine because all the callers of
+ * __ieee80211_wake_queue call it right before releasing the lock.
+ */
+- if (local->ops->wake_tx_queue) {
+- if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
+- tasklet_schedule(&local->wake_txqs_tasklet);
+- else
+- _ieee80211_wake_txqs(local, flags);
+- }
++ if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
++ tasklet_schedule(&local->wake_txqs_tasklet);
++ else
++ _ieee80211_wake_txqs(local, flags);
+ }
+
+ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+@@ -585,10 +546,6 @@ static void __ieee80211_stop_queue(struc
+ for (ac = 0; ac < n_acs; ac++) {
+ if (sdata->vif.hw_queue[ac] == queue ||
+ sdata->vif.cab_queue == queue) {
+- if (!local->ops->wake_tx_queue) {
+- netif_stop_subqueue(sdata->dev, ac);
+- continue;
+- }
+ spin_lock(&local->fq.lock);
+ sdata->vif.txqs_stopped[ac] = true;
+ spin_unlock(&local->fq.lock);
+--- a/net/mac80211/wme.c
++++ b/net/mac80211/wme.c
+@@ -122,6 +122,9 @@ u16 ieee80211_select_queue_80211(struct
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ u8 *p;
+
++ /* Ensure hash is set prior to potential SW encryption */
++ skb_get_hash(skb);
++
+ if ((info->control.flags & IEEE80211_TX_CTRL_DONT_REORDER) ||
+ local->hw.queues < IEEE80211_NUM_ACS)
+ return 0;
+@@ -141,12 +144,15 @@ u16 ieee80211_select_queue_80211(struct
+ return ieee80211_downgrade_queue(sdata, NULL, skb);
+ }
+
+-u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+- struct sta_info *sta, struct sk_buff *skb)
++u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta, struct sk_buff *skb)
+ {
+ struct mac80211_qos_map *qos_map;
+ bool qos;
+
++ /* Ensure hash is set prior to potential SW encryption */
++ skb_get_hash(skb);
++
+ /* all mesh/ocb stations are required to support WME */
+ if (sta && (sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
+ sdata->vif.type == NL80211_IFTYPE_OCB))
+@@ -176,59 +182,6 @@ u16 __ieee80211_select_queue(struct ieee
+ return ieee80211_downgrade_queue(sdata, sta, skb);
+ }
+
+-
+-/* Indicate which queue to use. */
+-u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+- struct sk_buff *skb)
+-{
+- struct ieee80211_local *local = sdata->local;
+- struct sta_info *sta = NULL;
+- const u8 *ra = NULL;
+- u16 ret;
+-
+- /* when using iTXQ, we can do this later */
+- if (local->ops->wake_tx_queue)
+- return 0;
+-
+- if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
+- skb->priority = 0; /* required for correct WPA/11i MIC */
+- return 0;
+- }
+-
+- rcu_read_lock();
+- switch (sdata->vif.type) {
+- case NL80211_IFTYPE_AP_VLAN:
+- sta = rcu_dereference(sdata->u.vlan.sta);
+- if (sta)
+- break;
+- fallthrough;
+- case NL80211_IFTYPE_AP:
+- ra = skb->data;
+- break;
+- case NL80211_IFTYPE_STATION:
+- /* might be a TDLS station */
+- sta = sta_info_get(sdata, skb->data);
+- if (sta)
+- break;
+-
+- ra = sdata->deflink.u.mgd.bssid;
+- break;
+- case NL80211_IFTYPE_ADHOC:
+- ra = skb->data;
+- break;
+- default:
+- break;
+- }
+-
+- if (!sta && ra && !is_multicast_ether_addr(ra))
+- sta = sta_info_get(sdata, ra);
+-
+- ret = __ieee80211_select_queue(sdata, sta, skb);
+-
+- rcu_read_unlock();
+- return ret;
+-}
+-
+ /**
+ * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
+ *
+--- a/net/mac80211/wme.h
++++ b/net/mac80211/wme.h
+@@ -13,10 +13,8 @@
+ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb,
+ struct ieee80211_hdr *hdr);
+-u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+- struct sta_info *sta, struct sk_buff *skb);
+ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+- struct sk_buff *skb);
++ struct sta_info *sta, struct sk_buff *skb);
+ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
+
+++ /dev/null
-From: Alexander Wetzel <alexander@wetzel-home.de>
-Date: Sun, 9 Oct 2022 18:30:40 +0200
-Subject: [PATCH] wifi: mac80211: Drop support for TX push path
-
-All drivers are now using mac80211 internal queues (iTXQs).
-Drop mac80211 internal support for the old push path.
-
-Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4339,9 +4339,6 @@ static int ieee80211_get_txq_stats(struc
- struct ieee80211_sub_if_data *sdata;
- int ret = 0;
-
-- if (!local->ops->wake_tx_queue)
-- return 1;
--
- spin_lock_bh(&local->fq.lock);
- rcu_read_lock();
-
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -663,9 +663,7 @@ void debugfs_hw_add(struct ieee80211_loc
- DEBUGFS_ADD_MODE(force_tx_status, 0600);
- DEBUGFS_ADD_MODE(aql_enable, 0600);
- DEBUGFS_ADD(aql_pending);
--
-- if (local->ops->wake_tx_queue)
-- DEBUGFS_ADD_MODE(aqm, 0600);
-+ DEBUGFS_ADD_MODE(aqm, 0600);
-
- DEBUGFS_ADD_MODE(airtime_flags, 0600);
-
---- a/net/mac80211/debugfs_netdev.c
-+++ b/net/mac80211/debugfs_netdev.c
-@@ -677,8 +677,7 @@ static void add_common_files(struct ieee
- DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
- DEBUGFS_ADD(hw_queues);
-
-- if (sdata->local->ops->wake_tx_queue &&
-- sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-+ if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
- sdata->vif.type != NL80211_IFTYPE_NAN)
- DEBUGFS_ADD(aqm);
- }
---- a/net/mac80211/debugfs_sta.c
-+++ b/net/mac80211/debugfs_sta.c
-@@ -1056,10 +1056,8 @@ void ieee80211_sta_debugfs_add(struct st
- DEBUGFS_ADD_COUNTER(rx_fragments, deflink.rx_stats.fragments);
- DEBUGFS_ADD_COUNTER(tx_filtered, deflink.status_stats.filtered);
-
-- if (local->ops->wake_tx_queue) {
-- DEBUGFS_ADD(aqm);
-- DEBUGFS_ADD(airtime);
-- }
-+ DEBUGFS_ADD(aqm);
-+ DEBUGFS_ADD(airtime);
-
- if (wiphy_ext_feature_isset(local->hw.wiphy,
- NL80211_EXT_FEATURE_AQL))
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -2290,7 +2290,6 @@ void ieee80211_wake_queue_by_reason(stru
- void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
- enum queue_stop_reason reason,
- bool refcounted);
--void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);
- void ieee80211_add_pending_skb(struct ieee80211_local *local,
- struct sk_buff *skb);
- void ieee80211_add_pending_skbs(struct ieee80211_local *local,
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -458,12 +458,6 @@ static void ieee80211_do_stop(struct iee
- if (cancel_scan)
- ieee80211_scan_cancel(local);
-
-- /*
-- * Stop TX on this interface first.
-- */
-- if (!local->ops->wake_tx_queue && sdata->dev)
-- netif_tx_stop_all_queues(sdata->dev);
--
- ieee80211_roc_purge(local, sdata);
-
- switch (sdata->vif.type) {
-@@ -811,13 +805,6 @@ static void ieee80211_uninit(struct net_
- ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));
- }
-
--static u16 ieee80211_netdev_select_queue(struct net_device *dev,
-- struct sk_buff *skb,
-- struct net_device *sb_dev)
--{
-- return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
--}
--
- static void
- ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
- {
-@@ -831,7 +818,6 @@ static const struct net_device_ops ieee8
- .ndo_start_xmit = ieee80211_subif_start_xmit,
- .ndo_set_rx_mode = ieee80211_set_multicast_list,
- .ndo_set_mac_address = ieee80211_change_mac,
-- .ndo_select_queue = ieee80211_netdev_select_queue,
- .ndo_get_stats64 = ieee80211_get_stats64,
- };
-
-@@ -939,7 +925,6 @@ static const struct net_device_ops ieee8
- .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
- .ndo_set_rx_mode = ieee80211_set_multicast_list,
- .ndo_set_mac_address = ieee80211_change_mac,
-- .ndo_select_queue = ieee80211_netdev_select_queue,
- .ndo_get_stats64 = ieee80211_get_stats64,
- .ndo_fill_forward_path = ieee80211_netdev_fill_forward_path,
- };
-@@ -1441,35 +1426,6 @@ int ieee80211_do_open(struct wireless_de
-
- ieee80211_recalc_ps(local);
-
-- if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
-- sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
-- local->ops->wake_tx_queue) {
-- /* XXX: for AP_VLAN, actually track AP queues */
-- if (dev)
-- netif_tx_start_all_queues(dev);
-- } else if (dev) {
-- unsigned long flags;
-- int n_acs = IEEE80211_NUM_ACS;
-- int ac;
--
-- if (local->hw.queues < IEEE80211_NUM_ACS)
-- n_acs = 1;
--
-- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-- if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE ||
-- (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 &&
-- skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) {
-- for (ac = 0; ac < n_acs; ac++) {
-- int ac_queue = sdata->vif.hw_queue[ac];
--
-- if (local->queue_stop_reasons[ac_queue] == 0 &&
-- skb_queue_empty(&local->pending[ac_queue]))
-- netif_start_subqueue(dev, ac);
-- }
-- }
-- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-- }
--
- set_bit(SDATA_STATE_RUNNING, &sdata->state);
-
- return 0;
-@@ -1499,17 +1455,12 @@ static void ieee80211_if_setup(struct ne
- {
- ether_setup(dev);
- dev->priv_flags &= ~IFF_TX_SKB_SHARING;
-+ dev->priv_flags |= IFF_NO_QUEUE;
- dev->netdev_ops = &ieee80211_dataif_ops;
- dev->needs_free_netdev = true;
- dev->priv_destructor = ieee80211_if_free;
- }
-
--static void ieee80211_if_setup_no_queue(struct net_device *dev)
--{
-- ieee80211_if_setup(dev);
-- dev->priv_flags |= IFF_NO_QUEUE;
--}
--
- static void ieee80211_iface_process_skb(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb)
-@@ -2094,9 +2045,7 @@ int ieee80211_if_add(struct ieee80211_lo
- struct net_device *ndev = NULL;
- struct ieee80211_sub_if_data *sdata = NULL;
- struct txq_info *txqi;
-- void (*if_setup)(struct net_device *dev);
- int ret, i;
-- int txqs = 1;
-
- ASSERT_RTNL();
-
-@@ -2119,30 +2068,18 @@ int ieee80211_if_add(struct ieee80211_lo
- sizeof(void *));
- int txq_size = 0;
-
-- if (local->ops->wake_tx_queue &&
-- type != NL80211_IFTYPE_AP_VLAN &&
-+ 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 (local->ops->wake_tx_queue) {
-- if_setup = ieee80211_if_setup_no_queue;
-- } else {
-- if_setup = ieee80211_if_setup;
-- if (local->hw.queues >= IEEE80211_NUM_ACS)
-- txqs = IEEE80211_NUM_ACS;
-- }
--
- ndev = alloc_netdev_mqs(size + txq_size,
- name, name_assign_type,
-- if_setup, txqs, 1);
-+ ieee80211_if_setup, 1, 1);
- if (!ndev)
- return -ENOMEM;
-
-- if (!local->ops->wake_tx_queue && local->hw.wiphy->tx_queue_len)
-- ndev->tx_queue_len = local->hw.wiphy->tx_queue_len;
--
- dev_net_set(ndev, wiphy_net(local->hw.wiphy));
-
- ndev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -630,7 +630,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
-
- if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config ||
- !ops->add_interface || !ops->remove_interface ||
-- !ops->configure_filter))
-+ !ops->configure_filter || !ops->wake_tx_queue))
- return NULL;
-
- if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
-@@ -719,9 +719,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
- if (!ops->set_key)
- wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
-
-- if (ops->wake_tx_queue)
-- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS);
--
-+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_TXQS);
- wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
-
- wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
-@@ -834,10 +832,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
- atomic_set(&local->agg_queue_stop[i], 0);
- }
- tasklet_setup(&local->tx_pending_tasklet, ieee80211_tx_pending);
--
-- if (ops->wake_tx_queue)
-- tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs);
--
-+ tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs);
- tasklet_setup(&local->tasklet, ieee80211_tasklet_handler);
-
- skb_queue_head_init(&local->skb_queue);
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -1571,9 +1571,6 @@ static void sta_ps_start(struct sta_info
-
- ieee80211_clear_fast_xmit(sta);
-
-- if (!sta->sta.txq[0])
-- return;
--
- for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
- struct ieee80211_txq *txq = sta->sta.txq[tid];
- struct txq_info *txqi = to_txq_info(txq);
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -140,17 +140,15 @@ static void __cleanup_single_sta(struct
- atomic_dec(&ps->num_sta_ps);
- }
-
-- if (sta->sta.txq[0]) {
-- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-- struct txq_info *txqi;
-+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-+ struct txq_info *txqi;
-
-- if (!sta->sta.txq[i])
-- continue;
-+ if (!sta->sta.txq[i])
-+ continue;
-
-- txqi = to_txq_info(sta->sta.txq[i]);
-+ txqi = to_txq_info(sta->sta.txq[i]);
-
-- ieee80211_txq_purge(local, txqi);
-- }
-+ ieee80211_txq_purge(local, txqi);
- }
-
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-@@ -425,8 +423,7 @@ void sta_info_free(struct ieee80211_loca
-
- sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
-
-- if (sta->sta.txq[0])
-- kfree(to_txq_info(sta->sta.txq[0]));
-+ kfree(to_txq_info(sta->sta.txq[0]));
- kfree(rcu_dereference_raw(sta->sta.rates));
- #ifdef CPTCFG_MAC80211_MESH
- kfree(sta->mesh);
-@@ -527,6 +524,8 @@ __sta_info_alloc(struct ieee80211_sub_if
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_hw *hw = &local->hw;
- struct sta_info *sta;
-+ void *txq_data;
-+ int size;
- int i;
-
- sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
-@@ -597,21 +596,18 @@ __sta_info_alloc(struct ieee80211_sub_if
-
- sta->last_connected = ktime_get_seconds();
-
-- if (local->ops->wake_tx_queue) {
-- void *txq_data;
-- int size = sizeof(struct txq_info) +
-- ALIGN(hw->txq_data_size, sizeof(void *));
-+ size = sizeof(struct txq_info) +
-+ ALIGN(hw->txq_data_size, sizeof(void *));
-
-- txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
-- if (!txq_data)
-- goto free;
-+ txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
-+ if (!txq_data)
-+ goto free;
-
-- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-- struct txq_info *txq = txq_data + i * size;
-+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-+ struct txq_info *txq = txq_data + i * size;
-
-- /* might not do anything for the bufferable MMPDU TXQ */
-- ieee80211_txq_init(sdata, sta, txq, i);
-- }
-+ /* might not do anything for the (bufferable) MMPDU TXQ */
-+ ieee80211_txq_init(sdata, sta, txq, i);
- }
-
- if (sta_prepare_rate_control(local, sta, gfp))
-@@ -685,8 +681,7 @@ __sta_info_alloc(struct ieee80211_sub_if
- return sta;
-
- free_txq:
-- if (sta->sta.txq[0])
-- kfree(to_txq_info(sta->sta.txq[0]));
-+ kfree(to_txq_info(sta->sta.txq[0]));
- free:
- sta_info_free_link(&sta->deflink);
- #ifdef CPTCFG_MAC80211_MESH
-@@ -1959,9 +1954,6 @@ ieee80211_sta_ps_deliver_response(struct
- * TIM recalculation.
- */
-
-- if (!sta->sta.txq[0])
-- return;
--
- for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
- if (!sta->sta.txq[tid] ||
- !(driver_release_tids & BIT(tid)) ||
-@@ -2446,7 +2438,7 @@ static void sta_set_tidstats(struct sta_
- tidstats->tx_msdu_failed = sta->deflink.status_stats.msdu_failed[tid];
- }
-
-- if (local->ops->wake_tx_queue && tid < IEEE80211_NUM_TIDS) {
-+ if (tid < IEEE80211_NUM_TIDS) {
- spin_lock_bh(&local->fq.lock);
- rcu_read_lock();
-
-@@ -2774,9 +2766,6 @@ unsigned long ieee80211_sta_last_active(
-
- static void sta_update_codel_params(struct sta_info *sta, u32 thr)
- {
-- if (!sta->sdata->local->ops->wake_tx_queue)
-- return;
--
- if (thr && thr < STA_SLOW_THRESHOLD * sta->local->num_sta) {
- sta->cparams.target = MS2TIME(50);
- sta->cparams.interval = MS2TIME(300);
---- a/net/mac80211/tdls.c
-+++ b/net/mac80211/tdls.c
-@@ -1016,7 +1016,6 @@ ieee80211_tdls_prep_mgmt_packet(struct w
- skb->priority = 256 + 5;
- break;
- }
-- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb));
-
- /*
- * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -1599,9 +1599,6 @@ int ieee80211_txq_setup_flows(struct iee
- bool supp_vht = false;
- enum nl80211_band band;
-
-- if (!local->ops->wake_tx_queue)
-- return 0;
--
- ret = fq_init(fq, 4096);
- if (ret)
- return ret;
-@@ -1649,9 +1646,6 @@ void ieee80211_txq_teardown_flows(struct
- {
- struct fq *fq = &local->fq;
-
-- if (!local->ops->wake_tx_queue)
-- return;
--
- kfree(local->cvars);
- local->cvars = NULL;
-
-@@ -1668,8 +1662,7 @@ static bool ieee80211_queue_skb(struct i
- struct ieee80211_vif *vif;
- struct txq_info *txqi;
-
-- if (!local->ops->wake_tx_queue ||
-- sdata->vif.type == NL80211_IFTYPE_MONITOR)
-+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
- return false;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-@@ -4185,12 +4178,7 @@ void __ieee80211_subif_start_xmit(struct
- if (IS_ERR(sta))
- sta = NULL;
-
-- if (local->ops->wake_tx_queue) {
-- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
-- skb_set_queue_mapping(skb, queue);
-- skb_get_hash(skb);
-- }
--
-+ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
- ieee80211_aggr_check(sdata, sta, skb);
-
- sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);
-@@ -4501,11 +4489,7 @@ static void ieee80211_8023_xmit(struct i
- struct tid_ampdu_tx *tid_tx;
- u8 tid;
-
-- if (local->ops->wake_tx_queue) {
-- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
-- skb_set_queue_mapping(skb, queue);
-- skb_get_hash(skb);
-- }
-+ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
-
- if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
- test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
-@@ -4759,9 +4743,6 @@ void ieee80211_tx_pending(struct tasklet
- if (!txok)
- break;
- }
--
-- if (skb_queue_empty(&local->pending[i]))
-- ieee80211_propagate_queue_wake(local, i);
- }
- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-
-@@ -5954,10 +5935,9 @@ int ieee80211_tx_control_port(struct wip
- }
-
- if (!IS_ERR(sta)) {
-- u16 queue = __ieee80211_select_queue(sdata, sta, skb);
-+ u16 queue = ieee80211_select_queue(sdata, sta, skb);
-
- skb_set_queue_mapping(skb, queue);
-- skb_get_hash(skb);
-
- /*
- * for MLO STA, the SA should be the AP MLD address, but
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -446,39 +446,6 @@ void ieee80211_wake_txqs(struct tasklet_
- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
- }
-
--void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
--{
-- struct ieee80211_sub_if_data *sdata;
-- int n_acs = IEEE80211_NUM_ACS;
--
-- if (local->ops->wake_tx_queue)
-- return;
--
-- if (local->hw.queues < IEEE80211_NUM_ACS)
-- n_acs = 1;
--
-- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-- int ac;
--
-- if (!sdata->dev)
-- continue;
--
-- if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
-- local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
-- continue;
--
-- for (ac = 0; ac < n_acs; ac++) {
-- int ac_queue = sdata->vif.hw_queue[ac];
--
-- if (ac_queue == queue ||
-- (sdata->vif.cab_queue == queue &&
-- local->queue_stop_reasons[ac_queue] == 0 &&
-- skb_queue_empty(&local->pending[ac_queue])))
-- netif_wake_subqueue(sdata->dev, ac);
-- }
-- }
--}
--
- static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
- enum queue_stop_reason reason,
- bool refcounted,
-@@ -509,11 +476,7 @@ static void __ieee80211_wake_queue(struc
- /* someone still has this queue stopped */
- return;
-
-- if (skb_queue_empty(&local->pending[queue])) {
-- rcu_read_lock();
-- ieee80211_propagate_queue_wake(local, queue);
-- rcu_read_unlock();
-- } else
-+ if (!skb_queue_empty(&local->pending[queue]))
- tasklet_schedule(&local->tx_pending_tasklet);
-
- /*
-@@ -523,12 +486,10 @@ static void __ieee80211_wake_queue(struc
- * release someone's lock, but it is fine because all the callers of
- * __ieee80211_wake_queue call it right before releasing the lock.
- */
-- if (local->ops->wake_tx_queue) {
-- if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
-- tasklet_schedule(&local->wake_txqs_tasklet);
-- else
-- _ieee80211_wake_txqs(local, flags);
-- }
-+ if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
-+ tasklet_schedule(&local->wake_txqs_tasklet);
-+ else
-+ _ieee80211_wake_txqs(local, flags);
- }
-
- void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
-@@ -585,10 +546,6 @@ static void __ieee80211_stop_queue(struc
- for (ac = 0; ac < n_acs; ac++) {
- if (sdata->vif.hw_queue[ac] == queue ||
- sdata->vif.cab_queue == queue) {
-- if (!local->ops->wake_tx_queue) {
-- netif_stop_subqueue(sdata->dev, ac);
-- continue;
-- }
- spin_lock(&local->fq.lock);
- sdata->vif.txqs_stopped[ac] = true;
- spin_unlock(&local->fq.lock);
---- a/net/mac80211/wme.c
-+++ b/net/mac80211/wme.c
-@@ -122,6 +122,9 @@ u16 ieee80211_select_queue_80211(struct
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- u8 *p;
-
-+ /* Ensure hash is set prior to potential SW encryption */
-+ skb_get_hash(skb);
-+
- if ((info->control.flags & IEEE80211_TX_CTRL_DONT_REORDER) ||
- local->hw.queues < IEEE80211_NUM_ACS)
- return 0;
-@@ -141,12 +144,15 @@ u16 ieee80211_select_queue_80211(struct
- return ieee80211_downgrade_queue(sdata, NULL, skb);
- }
-
--u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
-- struct sta_info *sta, struct sk_buff *skb)
-+u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
-+ struct sta_info *sta, struct sk_buff *skb)
- {
- struct mac80211_qos_map *qos_map;
- bool qos;
-
-+ /* Ensure hash is set prior to potential SW encryption */
-+ skb_get_hash(skb);
-+
- /* all mesh/ocb stations are required to support WME */
- if (sta && (sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
- sdata->vif.type == NL80211_IFTYPE_OCB))
-@@ -176,59 +182,6 @@ u16 __ieee80211_select_queue(struct ieee
- return ieee80211_downgrade_queue(sdata, sta, skb);
- }
-
--
--/* Indicate which queue to use. */
--u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
-- struct sk_buff *skb)
--{
-- struct ieee80211_local *local = sdata->local;
-- struct sta_info *sta = NULL;
-- const u8 *ra = NULL;
-- u16 ret;
--
-- /* when using iTXQ, we can do this later */
-- if (local->ops->wake_tx_queue)
-- return 0;
--
-- if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
-- skb->priority = 0; /* required for correct WPA/11i MIC */
-- return 0;
-- }
--
-- rcu_read_lock();
-- switch (sdata->vif.type) {
-- case NL80211_IFTYPE_AP_VLAN:
-- sta = rcu_dereference(sdata->u.vlan.sta);
-- if (sta)
-- break;
-- fallthrough;
-- case NL80211_IFTYPE_AP:
-- ra = skb->data;
-- break;
-- case NL80211_IFTYPE_STATION:
-- /* might be a TDLS station */
-- sta = sta_info_get(sdata, skb->data);
-- if (sta)
-- break;
--
-- ra = sdata->deflink.u.mgd.bssid;
-- break;
-- case NL80211_IFTYPE_ADHOC:
-- ra = skb->data;
-- break;
-- default:
-- break;
-- }
--
-- if (!sta && ra && !is_multicast_ether_addr(ra))
-- sta = sta_info_get(sdata, ra);
--
-- ret = __ieee80211_select_queue(sdata, sta, skb);
--
-- rcu_read_unlock();
-- return ret;
--}
--
- /**
- * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
- *
---- a/net/mac80211/wme.h
-+++ b/net/mac80211/wme.h
-@@ -13,10 +13,8 @@
- u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb,
- struct ieee80211_hdr *hdr);
--u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
-- struct sta_info *sta, struct sk_buff *skb);
- u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
-- struct sk_buff *skb);
-+ struct sta_info *sta, struct sk_buff *skb);
- void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb);
-
--- /dev/null
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Mon, 10 Oct 2022 19:17:46 +0200
+Subject: [PATCH] wifi: realtek: remove duplicated wake_tx_queue
+
+By accident, the previous patch duplicated the initialization
+of the wake_tx_queue callback. Fix that by removing the new
+initializations.
+
+Fixes: a790cc3a4fad ("wifi: mac80211: add wake_tx_queue callback to drivers")
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
+@@ -896,7 +896,6 @@ static void rtw_ops_sta_rc_update(struct
+
+ const struct ieee80211_ops rtw_ops = {
+ .tx = rtw_ops_tx,
+- .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = rtw_ops_wake_tx_queue,
+ .start = rtw_ops_start,
+ .stop = rtw_ops_stop,
+--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
++++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
+@@ -918,7 +918,6 @@ static int rtw89_ops_set_tid_config(stru
+
+ const struct ieee80211_ops rtw89_ops = {
+ .tx = rtw89_ops_tx,
+- .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = rtw89_ops_wake_tx_queue,
+ .start = rtw89_ops_start,
+ .stop = rtw89_ops_stop,
+++ /dev/null
-From: Johannes Berg <johannes.berg@intel.com>
-Date: Mon, 10 Oct 2022 19:17:46 +0200
-Subject: [PATCH] wifi: realtek: remove duplicated wake_tx_queue
-
-By accident, the previous patch duplicated the initialization
-of the wake_tx_queue callback. Fix that by removing the new
-initializations.
-
-Fixes: a790cc3a4fad ("wifi: mac80211: add wake_tx_queue callback to drivers")
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/drivers/net/wireless/realtek/rtw88/mac80211.c
-+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
-@@ -896,7 +896,6 @@ static void rtw_ops_sta_rc_update(struct
-
- const struct ieee80211_ops rtw_ops = {
- .tx = rtw_ops_tx,
-- .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .wake_tx_queue = rtw_ops_wake_tx_queue,
- .start = rtw_ops_start,
- .stop = rtw_ops_stop,
---- a/drivers/net/wireless/realtek/rtw89/mac80211.c
-+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
-@@ -918,7 +918,6 @@ static int rtw89_ops_set_tid_config(stru
-
- const struct ieee80211_ops rtw89_ops = {
- .tx = rtw89_ops_tx,
-- .wake_tx_queue = ieee80211_handle_wake_tx_queue,
- .wake_tx_queue = rtw89_ops_wake_tx_queue,
- .start = rtw89_ops_start,
- .stop = rtw89_ops_stop,
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 13 Dec 2022 21:03:19 +0100
+Subject: [PATCH] wifi: mac80211: fix initialization of rx->link and
+ rx->link_sta
+
+There are some codepaths that do not initialize rx->link_sta properly. This
+causes a crash in places which assume that rx->link_sta is valid if rx->sta
+is valid.
+One known instance is triggered by __ieee80211_rx_h_amsdu being called from
+fast-rx.
+
+Since the initialization of rx->link and rx->link_sta is rather convoluted
+and duplicated in many places, clean it up by using a helper function to
+set it.
+
+Fixes: ccdde7c74ffd ("wifi: mac80211: properly implement MLO key handling")
+Fixes: b320d6c456ff ("wifi: mac80211: use correct rx link_sta instead of default")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -4067,6 +4067,58 @@ static void ieee80211_invoke_rx_handlers
+ #undef CALL_RXH
+ }
+
++static bool
++ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
++{
++ if (!sta->mlo)
++ return false;
++
++ return !!(sta->valid_links & BIT(link_id));
++}
++
++static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
++ u8 link_id)
++{
++ rx->link_id = link_id;
++ rx->link = rcu_dereference(rx->sdata->link[link_id]);
++
++ if (!rx->sta || !rx->sta->sta.mlo)
++ return rx->link;
++
++ if (!ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta, link_id))
++ return false;
++
++ rx->link_sta = rcu_dereference(rx->sta->link[link_id]);
++
++ return rx->link && rx->link_sta;
++}
++
++static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx,
++ struct ieee80211_sta *pubsta,
++ int link_id)
++{
++ struct sta_info *sta;
++
++ sta = container_of(pubsta, struct sta_info, sta);
++
++ rx->link_id = link_id;
++ rx->sta = sta;
++
++ if (sta) {
++ rx->local = sta->sdata->local;
++ if (!rx->sdata)
++ rx->sdata = sta->sdata;
++ rx->link_sta = &sta->deflink;
++ }
++
++ if (link_id < 0)
++ rx->link = &rx->sdata->deflink;
++ else if (!ieee80211_rx_data_set_link(rx, link_id))
++ return false;
++
++ return true;
++}
++
+ /*
+ * This function makes calls into the RX path, therefore
+ * it has to be invoked under RCU read lock.
+@@ -4075,16 +4127,19 @@ void ieee80211_release_reorder_timeout(s
+ {
+ struct sk_buff_head frames;
+ struct ieee80211_rx_data rx = {
+- .sta = sta,
+- .sdata = sta->sdata,
+- .local = sta->local,
+ /* This is OK -- must be QoS data frame */
+ .security_idx = tid,
+ .seqno_idx = tid,
+- .link_id = -1,
+ };
+ struct tid_ampdu_rx *tid_agg_rx;
+- u8 link_id;
++ int link_id = -1;
++
++ /* FIXME: statistics won't be right with this */
++ if (sta->sta.valid_links)
++ link_id = ffs(sta->sta.valid_links) - 1;
++
++ if (!ieee80211_rx_data_set_sta(&rx, &sta->sta, link_id))
++ return;
+
+ tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
+ if (!tid_agg_rx)
+@@ -4104,10 +4159,6 @@ void ieee80211_release_reorder_timeout(s
+ };
+ drv_event_callback(rx.local, rx.sdata, &event);
+ }
+- /* FIXME: statistics won't be right with this */
+- link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
+- rx.link = rcu_dereference(sta->sdata->link[link_id]);
+- rx.link_sta = rcu_dereference(sta->link[link_id]);
+
+ ieee80211_rx_handlers(&rx, &frames);
+ }
+@@ -4123,7 +4174,6 @@ void ieee80211_mark_rx_ba_filtered_frame
+ /* This is OK -- must be QoS data frame */
+ .security_idx = tid,
+ .seqno_idx = tid,
+- .link_id = -1,
+ };
+ int i, diff;
+
+@@ -4134,10 +4184,8 @@ void ieee80211_mark_rx_ba_filtered_frame
+
+ sta = container_of(pubsta, struct sta_info, sta);
+
+- rx.sta = sta;
+- rx.sdata = sta->sdata;
+- rx.link = &rx.sdata->deflink;
+- rx.local = sta->local;
++ if (!ieee80211_rx_data_set_sta(&rx, pubsta, -1))
++ return;
+
+ rcu_read_lock();
+ tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
+@@ -4524,15 +4572,6 @@ void ieee80211_check_fast_rx_iface(struc
+ mutex_unlock(&local->sta_mtx);
+ }
+
+-static bool
+-ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
+-{
+- if (!sta->mlo)
+- return false;
+-
+- return !!(sta->valid_links & BIT(link_id));
+-}
+-
+ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
+ struct ieee80211_fast_rx *fast_rx,
+ int orig_len)
+@@ -4643,7 +4682,6 @@ static bool ieee80211_invoke_fast_rx(str
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+- struct sta_info *sta = rx->sta;
+ int orig_len = skb->len;
+ int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ int snap_offs = hdrlen;
+@@ -4655,7 +4693,6 @@ static bool ieee80211_invoke_fast_rx(str
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+ } addrs __aligned(2);
+- struct link_sta_info *link_sta;
+ struct ieee80211_sta_rx_stats *stats;
+
+ /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
+@@ -4758,18 +4795,10 @@ static bool ieee80211_invoke_fast_rx(str
+ drop:
+ dev_kfree_skb(skb);
+
+- if (rx->link_id >= 0) {
+- link_sta = rcu_dereference(sta->link[rx->link_id]);
+- if (!link_sta)
+- return true;
+- } else {
+- link_sta = &sta->deflink;
+- }
+-
+ if (fast_rx->uses_rss)
+- stats = this_cpu_ptr(link_sta->pcpu_rx_stats);
++ stats = this_cpu_ptr(rx->link_sta->pcpu_rx_stats);
+ else
+- stats = &link_sta->rx_stats;
++ stats = &rx->link_sta->rx_stats;
+
+ stats->dropped++;
+ return true;
+@@ -4787,8 +4816,8 @@ static bool ieee80211_prepare_and_rx_han
+ struct ieee80211_local *local = rx->local;
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+- struct link_sta_info *link_sta = NULL;
+- struct ieee80211_link_data *link;
++ struct link_sta_info *link_sta = rx->link_sta;
++ struct ieee80211_link_data *link = rx->link;
+
+ rx->skb = skb;
+
+@@ -4810,35 +4839,6 @@ static bool ieee80211_prepare_and_rx_han
+ if (!ieee80211_accept_frame(rx))
+ return false;
+
+- if (rx->link_id >= 0) {
+- link = rcu_dereference(rx->sdata->link[rx->link_id]);
+-
+- /* we might race link removal */
+- if (!link)
+- return true;
+- rx->link = link;
+-
+- if (rx->sta) {
+- rx->link_sta =
+- rcu_dereference(rx->sta->link[rx->link_id]);
+- if (!rx->link_sta)
+- return true;
+- }
+- } else {
+- if (rx->sta)
+- rx->link_sta = &rx->sta->deflink;
+-
+- rx->link = &sdata->deflink;
+- }
+-
+- if (unlikely(!is_multicast_ether_addr(hdr->addr1) &&
+- rx->link_id >= 0 && rx->sta && rx->sta->sta.mlo)) {
+- link_sta = rcu_dereference(rx->sta->link[rx->link_id]);
+-
+- if (WARN_ON_ONCE(!link_sta))
+- return true;
+- }
+-
+ if (!consume) {
+ struct skb_shared_hwtstamps *shwt;
+
+@@ -4858,7 +4858,7 @@ static bool ieee80211_prepare_and_rx_han
+ shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
+ }
+
+- if (unlikely(link_sta)) {
++ if (unlikely(rx->sta && rx->sta->sta.mlo)) {
+ /* translate to MLD addresses */
+ if (ether_addr_equal(link->conf->addr, hdr->addr1))
+ ether_addr_copy(hdr->addr1, rx->sdata->vif.addr);
+@@ -4888,6 +4888,7 @@ static void __ieee80211_rx_handle_8023(s
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_fast_rx *fast_rx;
+ struct ieee80211_rx_data rx;
++ int link_id = -1;
+
+ memset(&rx, 0, sizeof(rx));
+ rx.skb = skb;
+@@ -4904,12 +4905,8 @@ static void __ieee80211_rx_handle_8023(s
+ if (!pubsta)
+ goto drop;
+
+- rx.sta = container_of(pubsta, struct sta_info, sta);
+- rx.sdata = rx.sta->sdata;
+-
+- if (status->link_valid &&
+- !ieee80211_rx_is_valid_sta_link_id(pubsta, status->link_id))
+- goto drop;
++ if (status->link_valid)
++ link_id = status->link_id;
+
+ /*
+ * TODO: Should the frame be dropped if the right link_id is not
+@@ -4918,19 +4915,8 @@ static void __ieee80211_rx_handle_8023(s
+ * link_id is used only for stats purpose and updating the stats on
+ * the deflink is fine?
+ */
+- if (status->link_valid)
+- rx.link_id = status->link_id;
+-
+- if (rx.link_id >= 0) {
+- struct ieee80211_link_data *link;
+-
+- link = rcu_dereference(rx.sdata->link[rx.link_id]);
+- if (!link)
+- goto drop;
+- rx.link = link;
+- } else {
+- rx.link = &rx.sdata->deflink;
+- }
++ if (!ieee80211_rx_data_set_sta(&rx, pubsta, link_id))
++ goto drop;
+
+ fast_rx = rcu_dereference(rx.sta->fast_rx);
+ if (!fast_rx)
+@@ -4948,6 +4934,8 @@ static bool ieee80211_rx_for_interface(s
+ {
+ struct link_sta_info *link_sta;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
++ struct sta_info *sta;
++ int link_id = -1;
+
+ /*
+ * Look up link station first, in case there's a
+@@ -4957,24 +4945,19 @@ static bool ieee80211_rx_for_interface(s
+ */
+ link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2);
+ if (link_sta) {
+- rx->sta = link_sta->sta;
+- rx->link_id = link_sta->link_id;
++ sta = link_sta->sta;
++ link_id = link_sta->link_id;
+ } else {
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
+- rx->sta = sta_info_get_bss(rx->sdata, hdr->addr2);
+- if (rx->sta) {
+- if (status->link_valid &&
+- !ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta,
+- status->link_id))
+- return false;
+-
+- rx->link_id = status->link_valid ? status->link_id : -1;
+- } else {
+- rx->link_id = -1;
+- }
++ sta = sta_info_get_bss(rx->sdata, hdr->addr2);
++ if (status->link_valid)
++ link_id = status->link_id;
+ }
+
++ if (!ieee80211_rx_data_set_sta(rx, &sta->sta, link_id))
++ return false;
++
+ return ieee80211_prepare_and_rx_handle(rx, skb, consume);
+ }
+
+@@ -5033,19 +5016,15 @@ static void __ieee80211_rx_handle_packet
+
+ if (ieee80211_is_data(fc)) {
+ struct sta_info *sta, *prev_sta;
+- u8 link_id = status->link_id;
++ int link_id = -1;
+
+- if (pubsta) {
+- rx.sta = container_of(pubsta, struct sta_info, sta);
+- rx.sdata = rx.sta->sdata;
++ if (status->link_valid)
++ link_id = status->link_id;
+
+- if (status->link_valid &&
+- !ieee80211_rx_is_valid_sta_link_id(pubsta, link_id))
++ if (pubsta) {
++ if (!ieee80211_rx_data_set_sta(&rx, pubsta, link_id))
+ goto out;
+
+- if (status->link_valid)
+- rx.link_id = status->link_id;
+-
+ /*
+ * In MLO connection, fetch the link_id using addr2
+ * when the driver does not pass link_id in status.
+@@ -5063,7 +5042,7 @@ static void __ieee80211_rx_handle_packet
+ if (!link_sta)
+ goto out;
+
+- rx.link_id = link_sta->link_id;
++ ieee80211_rx_data_set_link(&rx, link_sta->link_id);
+ }
+
+ if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
+@@ -5079,30 +5058,27 @@ static void __ieee80211_rx_handle_packet
+ continue;
+ }
+
+- if ((status->link_valid &&
+- !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
+- link_id)) ||
+- (!status->link_valid && prev_sta->sta.mlo))
++ rx.sdata = prev_sta->sdata;
++ if (!ieee80211_rx_data_set_sta(&rx, &prev_sta->sta,
++ link_id))
++ goto out;
++
++ if (!status->link_valid && prev_sta->sta.mlo)
+ continue;
+
+- rx.link_id = status->link_valid ? link_id : -1;
+- rx.sta = prev_sta;
+- rx.sdata = prev_sta->sdata;
+ ieee80211_prepare_and_rx_handle(&rx, skb, false);
+
+ prev_sta = sta;
+ }
+
+ if (prev_sta) {
+- if ((status->link_valid &&
+- !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
+- link_id)) ||
+- (!status->link_valid && prev_sta->sta.mlo))
++ rx.sdata = prev_sta->sdata;
++ if (!ieee80211_rx_data_set_sta(&rx, &prev_sta->sta,
++ link_id))
+ goto out;
+
+- rx.link_id = status->link_valid ? link_id : -1;
+- rx.sta = prev_sta;
+- rx.sdata = prev_sta->sdata;
++ if (!status->link_valid && prev_sta->sta.mlo)
++ goto out;
+
+ if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
+ return;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 13 Dec 2022 21:03:19 +0100
-Subject: [PATCH] wifi: mac80211: fix initialization of rx->link and
- rx->link_sta
-
-There are some codepaths that do not initialize rx->link_sta properly. This
-causes a crash in places which assume that rx->link_sta is valid if rx->sta
-is valid.
-One known instance is triggered by __ieee80211_rx_h_amsdu being called from
-fast-rx.
-
-Since the initialization of rx->link and rx->link_sta is rather convoluted
-and duplicated in many places, clean it up by using a helper function to
-set it.
-
-Fixes: ccdde7c74ffd ("wifi: mac80211: properly implement MLO key handling")
-Fixes: b320d6c456ff ("wifi: mac80211: use correct rx link_sta instead of default")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -4067,6 +4067,58 @@ static void ieee80211_invoke_rx_handlers
- #undef CALL_RXH
- }
-
-+static bool
-+ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
-+{
-+ if (!sta->mlo)
-+ return false;
-+
-+ return !!(sta->valid_links & BIT(link_id));
-+}
-+
-+static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
-+ u8 link_id)
-+{
-+ rx->link_id = link_id;
-+ rx->link = rcu_dereference(rx->sdata->link[link_id]);
-+
-+ if (!rx->sta || !rx->sta->sta.mlo)
-+ return rx->link;
-+
-+ if (!ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta, link_id))
-+ return false;
-+
-+ rx->link_sta = rcu_dereference(rx->sta->link[link_id]);
-+
-+ return rx->link && rx->link_sta;
-+}
-+
-+static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx,
-+ struct ieee80211_sta *pubsta,
-+ int link_id)
-+{
-+ struct sta_info *sta;
-+
-+ sta = container_of(pubsta, struct sta_info, sta);
-+
-+ rx->link_id = link_id;
-+ rx->sta = sta;
-+
-+ if (sta) {
-+ rx->local = sta->sdata->local;
-+ if (!rx->sdata)
-+ rx->sdata = sta->sdata;
-+ rx->link_sta = &sta->deflink;
-+ }
-+
-+ if (link_id < 0)
-+ rx->link = &rx->sdata->deflink;
-+ else if (!ieee80211_rx_data_set_link(rx, link_id))
-+ return false;
-+
-+ return true;
-+}
-+
- /*
- * This function makes calls into the RX path, therefore
- * it has to be invoked under RCU read lock.
-@@ -4075,16 +4127,19 @@ void ieee80211_release_reorder_timeout(s
- {
- struct sk_buff_head frames;
- struct ieee80211_rx_data rx = {
-- .sta = sta,
-- .sdata = sta->sdata,
-- .local = sta->local,
- /* This is OK -- must be QoS data frame */
- .security_idx = tid,
- .seqno_idx = tid,
-- .link_id = -1,
- };
- struct tid_ampdu_rx *tid_agg_rx;
-- u8 link_id;
-+ int link_id = -1;
-+
-+ /* FIXME: statistics won't be right with this */
-+ if (sta->sta.valid_links)
-+ link_id = ffs(sta->sta.valid_links) - 1;
-+
-+ if (!ieee80211_rx_data_set_sta(&rx, &sta->sta, link_id))
-+ return;
-
- tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
- if (!tid_agg_rx)
-@@ -4104,10 +4159,6 @@ void ieee80211_release_reorder_timeout(s
- };
- drv_event_callback(rx.local, rx.sdata, &event);
- }
-- /* FIXME: statistics won't be right with this */
-- link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
-- rx.link = rcu_dereference(sta->sdata->link[link_id]);
-- rx.link_sta = rcu_dereference(sta->link[link_id]);
-
- ieee80211_rx_handlers(&rx, &frames);
- }
-@@ -4123,7 +4174,6 @@ void ieee80211_mark_rx_ba_filtered_frame
- /* This is OK -- must be QoS data frame */
- .security_idx = tid,
- .seqno_idx = tid,
-- .link_id = -1,
- };
- int i, diff;
-
-@@ -4134,10 +4184,8 @@ void ieee80211_mark_rx_ba_filtered_frame
-
- sta = container_of(pubsta, struct sta_info, sta);
-
-- rx.sta = sta;
-- rx.sdata = sta->sdata;
-- rx.link = &rx.sdata->deflink;
-- rx.local = sta->local;
-+ if (!ieee80211_rx_data_set_sta(&rx, pubsta, -1))
-+ return;
-
- rcu_read_lock();
- tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
-@@ -4524,15 +4572,6 @@ void ieee80211_check_fast_rx_iface(struc
- mutex_unlock(&local->sta_mtx);
- }
-
--static bool
--ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
--{
-- if (!sta->mlo)
-- return false;
--
-- return !!(sta->valid_links & BIT(link_id));
--}
--
- static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
- struct ieee80211_fast_rx *fast_rx,
- int orig_len)
-@@ -4643,7 +4682,6 @@ static bool ieee80211_invoke_fast_rx(str
- struct sk_buff *skb = rx->skb;
- struct ieee80211_hdr *hdr = (void *)skb->data;
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-- struct sta_info *sta = rx->sta;
- int orig_len = skb->len;
- int hdrlen = ieee80211_hdrlen(hdr->frame_control);
- int snap_offs = hdrlen;
-@@ -4655,7 +4693,6 @@ static bool ieee80211_invoke_fast_rx(str
- u8 da[ETH_ALEN];
- u8 sa[ETH_ALEN];
- } addrs __aligned(2);
-- struct link_sta_info *link_sta;
- struct ieee80211_sta_rx_stats *stats;
-
- /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
-@@ -4758,18 +4795,10 @@ static bool ieee80211_invoke_fast_rx(str
- drop:
- dev_kfree_skb(skb);
-
-- if (rx->link_id >= 0) {
-- link_sta = rcu_dereference(sta->link[rx->link_id]);
-- if (!link_sta)
-- return true;
-- } else {
-- link_sta = &sta->deflink;
-- }
--
- if (fast_rx->uses_rss)
-- stats = this_cpu_ptr(link_sta->pcpu_rx_stats);
-+ stats = this_cpu_ptr(rx->link_sta->pcpu_rx_stats);
- else
-- stats = &link_sta->rx_stats;
-+ stats = &rx->link_sta->rx_stats;
-
- stats->dropped++;
- return true;
-@@ -4787,8 +4816,8 @@ static bool ieee80211_prepare_and_rx_han
- struct ieee80211_local *local = rx->local;
- struct ieee80211_sub_if_data *sdata = rx->sdata;
- struct ieee80211_hdr *hdr = (void *)skb->data;
-- struct link_sta_info *link_sta = NULL;
-- struct ieee80211_link_data *link;
-+ struct link_sta_info *link_sta = rx->link_sta;
-+ struct ieee80211_link_data *link = rx->link;
-
- rx->skb = skb;
-
-@@ -4810,35 +4839,6 @@ static bool ieee80211_prepare_and_rx_han
- if (!ieee80211_accept_frame(rx))
- return false;
-
-- if (rx->link_id >= 0) {
-- link = rcu_dereference(rx->sdata->link[rx->link_id]);
--
-- /* we might race link removal */
-- if (!link)
-- return true;
-- rx->link = link;
--
-- if (rx->sta) {
-- rx->link_sta =
-- rcu_dereference(rx->sta->link[rx->link_id]);
-- if (!rx->link_sta)
-- return true;
-- }
-- } else {
-- if (rx->sta)
-- rx->link_sta = &rx->sta->deflink;
--
-- rx->link = &sdata->deflink;
-- }
--
-- if (unlikely(!is_multicast_ether_addr(hdr->addr1) &&
-- rx->link_id >= 0 && rx->sta && rx->sta->sta.mlo)) {
-- link_sta = rcu_dereference(rx->sta->link[rx->link_id]);
--
-- if (WARN_ON_ONCE(!link_sta))
-- return true;
-- }
--
- if (!consume) {
- struct skb_shared_hwtstamps *shwt;
-
-@@ -4858,7 +4858,7 @@ static bool ieee80211_prepare_and_rx_han
- shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
- }
-
-- if (unlikely(link_sta)) {
-+ if (unlikely(rx->sta && rx->sta->sta.mlo)) {
- /* translate to MLD addresses */
- if (ether_addr_equal(link->conf->addr, hdr->addr1))
- ether_addr_copy(hdr->addr1, rx->sdata->vif.addr);
-@@ -4888,6 +4888,7 @@ static void __ieee80211_rx_handle_8023(s
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_fast_rx *fast_rx;
- struct ieee80211_rx_data rx;
-+ int link_id = -1;
-
- memset(&rx, 0, sizeof(rx));
- rx.skb = skb;
-@@ -4904,12 +4905,8 @@ static void __ieee80211_rx_handle_8023(s
- if (!pubsta)
- goto drop;
-
-- rx.sta = container_of(pubsta, struct sta_info, sta);
-- rx.sdata = rx.sta->sdata;
--
-- if (status->link_valid &&
-- !ieee80211_rx_is_valid_sta_link_id(pubsta, status->link_id))
-- goto drop;
-+ if (status->link_valid)
-+ link_id = status->link_id;
-
- /*
- * TODO: Should the frame be dropped if the right link_id is not
-@@ -4918,19 +4915,8 @@ static void __ieee80211_rx_handle_8023(s
- * link_id is used only for stats purpose and updating the stats on
- * the deflink is fine?
- */
-- if (status->link_valid)
-- rx.link_id = status->link_id;
--
-- if (rx.link_id >= 0) {
-- struct ieee80211_link_data *link;
--
-- link = rcu_dereference(rx.sdata->link[rx.link_id]);
-- if (!link)
-- goto drop;
-- rx.link = link;
-- } else {
-- rx.link = &rx.sdata->deflink;
-- }
-+ if (!ieee80211_rx_data_set_sta(&rx, pubsta, link_id))
-+ goto drop;
-
- fast_rx = rcu_dereference(rx.sta->fast_rx);
- if (!fast_rx)
-@@ -4948,6 +4934,8 @@ static bool ieee80211_rx_for_interface(s
- {
- struct link_sta_info *link_sta;
- struct ieee80211_hdr *hdr = (void *)skb->data;
-+ struct sta_info *sta;
-+ int link_id = -1;
-
- /*
- * Look up link station first, in case there's a
-@@ -4957,24 +4945,19 @@ static bool ieee80211_rx_for_interface(s
- */
- link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2);
- if (link_sta) {
-- rx->sta = link_sta->sta;
-- rx->link_id = link_sta->link_id;
-+ sta = link_sta->sta;
-+ link_id = link_sta->link_id;
- } else {
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
-- rx->sta = sta_info_get_bss(rx->sdata, hdr->addr2);
-- if (rx->sta) {
-- if (status->link_valid &&
-- !ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta,
-- status->link_id))
-- return false;
--
-- rx->link_id = status->link_valid ? status->link_id : -1;
-- } else {
-- rx->link_id = -1;
-- }
-+ sta = sta_info_get_bss(rx->sdata, hdr->addr2);
-+ if (status->link_valid)
-+ link_id = status->link_id;
- }
-
-+ if (!ieee80211_rx_data_set_sta(rx, &sta->sta, link_id))
-+ return false;
-+
- return ieee80211_prepare_and_rx_handle(rx, skb, consume);
- }
-
-@@ -5033,19 +5016,15 @@ static void __ieee80211_rx_handle_packet
-
- if (ieee80211_is_data(fc)) {
- struct sta_info *sta, *prev_sta;
-- u8 link_id = status->link_id;
-+ int link_id = -1;
-
-- if (pubsta) {
-- rx.sta = container_of(pubsta, struct sta_info, sta);
-- rx.sdata = rx.sta->sdata;
-+ if (status->link_valid)
-+ link_id = status->link_id;
-
-- if (status->link_valid &&
-- !ieee80211_rx_is_valid_sta_link_id(pubsta, link_id))
-+ if (pubsta) {
-+ if (!ieee80211_rx_data_set_sta(&rx, pubsta, link_id))
- goto out;
-
-- if (status->link_valid)
-- rx.link_id = status->link_id;
--
- /*
- * In MLO connection, fetch the link_id using addr2
- * when the driver does not pass link_id in status.
-@@ -5063,7 +5042,7 @@ static void __ieee80211_rx_handle_packet
- if (!link_sta)
- goto out;
-
-- rx.link_id = link_sta->link_id;
-+ ieee80211_rx_data_set_link(&rx, link_sta->link_id);
- }
-
- if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
-@@ -5079,30 +5058,27 @@ static void __ieee80211_rx_handle_packet
- continue;
- }
-
-- if ((status->link_valid &&
-- !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
-- link_id)) ||
-- (!status->link_valid && prev_sta->sta.mlo))
-+ rx.sdata = prev_sta->sdata;
-+ if (!ieee80211_rx_data_set_sta(&rx, &prev_sta->sta,
-+ link_id))
-+ goto out;
-+
-+ if (!status->link_valid && prev_sta->sta.mlo)
- continue;
-
-- rx.link_id = status->link_valid ? link_id : -1;
-- rx.sta = prev_sta;
-- rx.sdata = prev_sta->sdata;
- ieee80211_prepare_and_rx_handle(&rx, skb, false);
-
- prev_sta = sta;
- }
-
- if (prev_sta) {
-- if ((status->link_valid &&
-- !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
-- link_id)) ||
-- (!status->link_valid && prev_sta->sta.mlo))
-+ rx.sdata = prev_sta->sdata;
-+ if (!ieee80211_rx_data_set_sta(&rx, &prev_sta->sta,
-+ link_id))
- goto out;
-
-- rx.link_id = status->link_valid ? link_id : -1;
-- rx.sta = prev_sta;
-- rx.sdata = prev_sta->sdata;
-+ if (!status->link_valid && prev_sta->sta.mlo)
-+ goto out;
-
- if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
- return;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 14 Dec 2022 13:46:38 +0100
+Subject: [PATCH] wifi: mac80211: fix MLO + AP_VLAN check
+
+Instead of preventing adding AP_VLAN to MLO enabled APs, this check was
+preventing adding more than one 4-addr AP_VLAN regardless of the MLO status.
+Fix this by adding missing extra checks.
+
+Fixes: ae960ee90bb1 ("wifi: mac80211: prevent VLANs on MLDs")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -364,7 +364,9 @@ static int ieee80211_check_concurrent_if
+
+ /* No support for VLAN with MLO yet */
+ if (iftype == NL80211_IFTYPE_AP_VLAN &&
+- nsdata->wdev.use_4addr)
++ sdata->wdev.use_4addr &&
++ nsdata->vif.type == NL80211_IFTYPE_AP &&
++ nsdata->vif.valid_links)
+ return -EOPNOTSUPP;
+
+ /*
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 14 Dec 2022 13:46:38 +0100
-Subject: [PATCH] wifi: mac80211: fix MLO + AP_VLAN check
-
-Instead of preventing adding AP_VLAN to MLO enabled APs, this check was
-preventing adding more than one 4-addr AP_VLAN regardless of the MLO status.
-Fix this by adding missing extra checks.
-
-Fixes: ae960ee90bb1 ("wifi: mac80211: prevent VLANs on MLDs")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -364,7 +364,9 @@ static int ieee80211_check_concurrent_if
-
- /* No support for VLAN with MLO yet */
- if (iftype == NL80211_IFTYPE_AP_VLAN &&
-- nsdata->wdev.use_4addr)
-+ sdata->wdev.use_4addr &&
-+ nsdata->vif.type == NL80211_IFTYPE_AP &&
-+ nsdata->vif.valid_links)
- return -EOPNOTSUPP;
-
- /*
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 9 Oct 2022 20:15:46 +0200
-Subject: [PATCH] mac80211: add support for restricting netdev features per vif
-
-This can be used to selectively disable feature flags for checksum offload,
-scatter/gather or GSO by changing vif->netdev_features.
-Removing features from vif->netdev_features does not affect the netdev
-features themselves, but instead fixes up skbs in the tx path so that the
-offloads are not needed in the driver.
-
-Aside from making it easier to deal with vif type based hardware limitations,
-this also makes it possible to optimize performance on hardware without native
-GSO support by declaring GSO support in hw->netdev_features and removing it
-from vif->netdev_features. This allows mac80211 to handle GSO segmentation
-after the sta lookup, but before itxq enqueue, thus reducing the number of
-unnecessary sta lookups, as well as some other per-packet processing.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/fq_impl.h
-+++ b/include/net/fq_impl.h
-@@ -200,6 +200,7 @@ static void fq_tin_enqueue(struct fq *fq
- fq_skb_free_t free_func)
- {
- struct fq_flow *flow;
-+ struct sk_buff *next;
- bool oom;
-
- lockdep_assert_held(&fq->lock);
-@@ -214,11 +215,15 @@ static void fq_tin_enqueue(struct fq *fq
- }
-
- flow->tin = tin;
-- flow->backlog += skb->len;
-- tin->backlog_bytes += skb->len;
-- tin->backlog_packets++;
-- fq->memory_usage += skb->truesize;
-- fq->backlog++;
-+ skb_list_walk_safe(skb, skb, next) {
-+ skb_mark_not_on_list(skb);
-+ flow->backlog += skb->len;
-+ tin->backlog_bytes += skb->len;
-+ tin->backlog_packets++;
-+ fq->memory_usage += skb->truesize;
-+ fq->backlog++;
-+ __skb_queue_tail(&flow->queue, skb);
-+ }
-
- if (list_empty(&flow->flowchain)) {
- flow->deficit = fq->quantum;
-@@ -226,7 +231,6 @@ static void fq_tin_enqueue(struct fq *fq
- &tin->new_flows);
- }
-
-- __skb_queue_tail(&flow->queue, skb);
- oom = (fq->memory_usage > fq->memory_limit);
- while (fq->backlog > fq->limit || oom) {
- flow = fq_find_fattest_flow(fq);
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1807,6 +1807,10 @@ struct ieee80211_vif_cfg {
- * @addr: address of this interface
- * @p2p: indicates whether this AP or STA interface is a p2p
- * interface, i.e. a GO or p2p-sta respectively
-+ * @netdev_features: tx netdev features supported by the hardware for this
-+ * vif. mac80211 initializes this to hw->netdev_features, and the driver
-+ * can mask out specific tx features. mac80211 will handle software fixup
-+ * for masked offloads (GSO, CSUM)
- * @driver_flags: flags/capabilities the driver has for this interface,
- * these need to be set (or cleared) when the interface is added
- * or, if supported by the driver, the interface type is changed
-@@ -1848,6 +1852,7 @@ struct ieee80211_vif {
-
- struct ieee80211_txq *txq;
-
-+ netdev_features_t netdev_features;
- u32 driver_flags;
- u32 offload_flags;
-
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -2181,6 +2181,7 @@ int ieee80211_if_add(struct ieee80211_lo
- ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
- ndev->hw_features |= ndev->features &
- MAC80211_SUPPORTED_FEATURES_TX;
-+ sdata->vif.netdev_features = local->hw.netdev_features;
-
- netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops);
-
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -1355,7 +1355,11 @@ static struct txq_info *ieee80211_get_tx
-
- static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
- {
-- IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
-+ struct sk_buff *next;
-+ codel_time_t now = codel_get_time();
-+
-+ skb_list_walk_safe(skb, skb, next)
-+ IEEE80211_SKB_CB(skb)->control.enqueue_time = now;
- }
-
- static u32 codel_skb_len_func(const struct sk_buff *skb)
-@@ -3578,55 +3582,79 @@ ieee80211_xmit_fast_finish(struct ieee80
- return TX_CONTINUE;
- }
-
--static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-- struct sta_info *sta,
-- struct ieee80211_fast_tx *fast_tx,
-- struct sk_buff *skb)
-+static netdev_features_t
-+ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata)
- {
-- struct ieee80211_local *local = sdata->local;
-- u16 ethertype = (skb->data[12] << 8) | skb->data[13];
-- int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
-- int hw_headroom = sdata->local->hw.extra_tx_headroom;
-- struct ethhdr eth;
-- struct ieee80211_tx_info *info;
-- struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
-- struct ieee80211_tx_data tx;
-- ieee80211_tx_result r;
-- struct tid_ampdu_tx *tid_tx = NULL;
-- u8 tid = IEEE80211_NUM_TIDS;
-+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
-+ return sdata->vif.netdev_features;
-
-- /* control port protocol needs a lot of special handling */
-- if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
-- return false;
-+ if (!sdata->bss)
-+ return 0;
-
-- /* only RFC 1042 SNAP */
-- if (ethertype < ETH_P_802_3_MIN)
-- return false;
-+ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
-+ return sdata->vif.netdev_features;
-+}
-
-- /* don't handle TX status request here either */
-- if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
-- return false;
-+static struct sk_buff *
-+ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features)
-+{
-+ if (skb_is_gso(skb)) {
-+ struct sk_buff *segs;
-
-- if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
-- if (tid_tx) {
-- if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
-- return false;
-- if (tid_tx->timeout)
-- tid_tx->last_tx = jiffies;
-- }
-+ segs = skb_gso_segment(skb, features);
-+ if (!segs)
-+ return skb;
-+ if (IS_ERR(segs))
-+ goto free;
-+
-+ consume_skb(skb);
-+ return segs;
- }
-
-- /* after this point (skb is modified) we cannot return false */
-+ if (skb_needs_linearize(skb, features) && __skb_linearize(skb))
-+ goto free;
-+
-+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
-+ int ofs = skb_checksum_start_offset(skb);
-+
-+ if (skb->encapsulation)
-+ skb_set_inner_transport_header(skb, ofs);
-+ else
-+ skb_set_transport_header(skb, ofs);
-+
-+ if (skb_csum_hwoffload_help(skb, features))
-+ goto free;
-+ }
-+
-+ skb_mark_not_on_list(skb);
-+ return skb;
-+
-+free:
-+ kfree_skb(skb);
-+ return NULL;
-+}
-+
-+static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+ struct sta_info *sta,
-+ struct ieee80211_fast_tx *fast_tx,
-+ struct sk_buff *skb, u8 tid, bool ampdu)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
-+ struct ieee80211_tx_info *info;
-+ struct ieee80211_tx_data tx;
-+ ieee80211_tx_result r;
-+ int hw_headroom = sdata->local->hw.extra_tx_headroom;
-+ int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
-+ struct ethhdr eth;
-
- skb = skb_share_check(skb, GFP_ATOMIC);
- if (unlikely(!skb))
-- return true;
-+ return;
-
- if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
- ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
-- return true;
-+ return;
-
- /* will not be crypto-handled beyond what we do here, so use false
- * as the may-encrypt argument for the resize to not account for
-@@ -3635,10 +3663,8 @@ static bool ieee80211_xmit_fast(struct i
- if (unlikely(ieee80211_skb_resize(sdata, skb,
- max_t(int, extra_head + hw_headroom -
- skb_headroom(skb), 0),
-- ENCRYPT_NO))) {
-- kfree_skb(skb);
-- return true;
-- }
-+ ENCRYPT_NO)))
-+ goto free;
-
- memcpy(ð, skb->data, ETH_HLEN - 2);
- hdr = skb_push(skb, extra_head);
-@@ -3652,7 +3678,7 @@ static bool ieee80211_xmit_fast(struct i
- info->control.vif = &sdata->vif;
- info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
- IEEE80211_TX_CTL_DONTFRAG |
-- (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
-+ (ampdu ? IEEE80211_TX_CTL_AMPDU : 0);
- info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT |
- u32_encode_bits(IEEE80211_LINK_UNSPECIFIED,
- IEEE80211_TX_CTRL_MLO_LINK);
-@@ -3676,16 +3702,14 @@ static bool ieee80211_xmit_fast(struct i
- tx.key = fast_tx->key;
-
- if (ieee80211_queue_skb(local, sdata, sta, skb))
-- return true;
-+ return;
-
- tx.skb = skb;
- r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
- fast_tx->key, &tx);
- tx.skb = NULL;
-- if (r == TX_DROP) {
-- kfree_skb(skb);
-- return true;
-- }
-+ if (r == TX_DROP)
-+ goto free;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- sdata = container_of(sdata->bss,
-@@ -3693,6 +3717,56 @@ static bool ieee80211_xmit_fast(struct i
-
- __skb_queue_tail(&tx.skbs, skb);
- ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false);
-+ return;
-+
-+free:
-+ kfree_skb(skb);
-+}
-+
-+static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+ struct sta_info *sta,
-+ struct ieee80211_fast_tx *fast_tx,
-+ struct sk_buff *skb)
-+{
-+ u16 ethertype = (skb->data[12] << 8) | skb->data[13];
-+ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
-+ struct tid_ampdu_tx *tid_tx = NULL;
-+ struct sk_buff *next;
-+ u8 tid = IEEE80211_NUM_TIDS;
-+
-+ /* control port protocol needs a lot of special handling */
-+ if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
-+ return false;
-+
-+ /* only RFC 1042 SNAP */
-+ if (ethertype < ETH_P_802_3_MIN)
-+ return false;
-+
-+ /* don't handle TX status request here either */
-+ if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
-+ return false;
-+
-+ if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-+ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
-+ if (tid_tx) {
-+ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
-+ return false;
-+ if (tid_tx->timeout)
-+ tid_tx->last_tx = jiffies;
-+ }
-+ }
-+
-+ /* after this point (skb is modified) we cannot return false */
-+ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
-+ if (!skb)
-+ return true;
-+
-+ skb_list_walk_safe(skb, skb, next) {
-+ skb_mark_not_on_list(skb);
-+ __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx);
-+ }
-+
- return true;
- }
-
-@@ -4193,31 +4267,14 @@ void __ieee80211_subif_start_xmit(struct
- goto out;
- }
-
-- if (skb_is_gso(skb)) {
-- struct sk_buff *segs;
--
-- segs = skb_gso_segment(skb, 0);
-- if (IS_ERR(segs)) {
-- goto out_free;
-- } else if (segs) {
-- consume_skb(skb);
-- skb = segs;
-- }
-- } else {
-- /* we cannot process non-linear frames on this path */
-- if (skb_linearize(skb))
-- goto out_free;
--
-- /* the frame could be fragmented, software-encrypted, and other
-- * things so we cannot really handle checksum offload with it -
-- * fix it up in software before we handle anything else.
-- */
-- if (skb->ip_summed == CHECKSUM_PARTIAL) {
-- skb_set_transport_header(skb,
-- skb_checksum_start_offset(skb));
-- if (skb_checksum_help(skb))
-- goto out_free;
-- }
-+ /* the frame could be fragmented, software-encrypted, and other
-+ * things so we cannot really handle checksum or GSO offload.
-+ * fix it up in software before we handle anything else.
-+ */
-+ skb = ieee80211_tx_skb_fixup(skb, 0);
-+ if (!skb) {
-+ len = 0;
-+ goto out;
- }
-
- skb_list_walk_safe(skb, skb, next) {
-@@ -4435,9 +4492,11 @@ normal:
- return NETDEV_TX_OK;
- }
-
--static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
-- struct sk_buff *skb, struct sta_info *sta,
-- bool txpending)
-+
-+
-+static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
-+ struct sk_buff *skb, struct sta_info *sta,
-+ bool txpending)
- {
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_tx_control control = {};
-@@ -4446,14 +4505,6 @@ static bool ieee80211_tx_8023(struct iee
- unsigned long flags;
- int q = info->hw_queue;
-
-- if (sta)
-- sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift);
--
-- ieee80211_tpt_led_trig_tx(local, skb->len);
--
-- if (ieee80211_queue_skb(local, sdata, sta, skb))
-- return true;
--
- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-
- if (local->queue_stop_reasons[q] ||
-@@ -4480,6 +4531,26 @@ static bool ieee80211_tx_8023(struct iee
- return true;
- }
-
-+static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
-+ struct sk_buff *skb, struct sta_info *sta,
-+ bool txpending)
-+{
-+ struct ieee80211_local *local = sdata->local;
-+ struct sk_buff *next;
-+ bool ret = true;
-+
-+ if (ieee80211_queue_skb(local, sdata, sta, skb))
-+ return true;
-+
-+ skb_list_walk_safe(skb, skb, next) {
-+ skb_mark_not_on_list(skb);
-+ if (!__ieee80211_tx_8023(sdata, skb, sta, txpending))
-+ ret = false;
-+ }
-+
-+ return ret;
-+}
-+
- static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
- struct net_device *dev, struct sta_info *sta,
- struct ieee80211_key *key, struct sk_buff *skb)
-@@ -4487,9 +4558,13 @@ static void ieee80211_8023_xmit(struct i
- struct ieee80211_tx_info *info;
- struct ieee80211_local *local = sdata->local;
- struct tid_ampdu_tx *tid_tx;
-+ struct sk_buff *seg, *next;
-+ unsigned int skbs = 0, len = 0;
-+ u16 queue;
- u8 tid;
-
-- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
-+ queue = ieee80211_select_queue(sdata, sta, skb);
-+ skb_set_queue_mapping(skb, queue);
-
- if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
- test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
-@@ -4499,9 +4574,6 @@ static void ieee80211_8023_xmit(struct i
- if (unlikely(!skb))
- return;
-
-- info = IEEE80211_SKB_CB(skb);
-- memset(info, 0, sizeof(*info));
--
- ieee80211_aggr_check(sdata, sta, skb);
-
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-@@ -4515,22 +4587,20 @@ static void ieee80211_8023_xmit(struct i
- return;
- }
-
-- info->flags |= IEEE80211_TX_CTL_AMPDU;
- if (tid_tx->timeout)
- tid_tx->last_tx = jiffies;
- }
-
-- if (unlikely(skb->sk &&
-- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
-- info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
-- &info->flags, NULL);
-+ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
-+ if (!skb)
-+ return;
-
-- info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-+ info = IEEE80211_SKB_CB(skb);
-+ memset(info, 0, sizeof(*info));
-+ if (tid_tx)
-+ info->flags |= IEEE80211_TX_CTL_AMPDU;
-
-- dev_sw_netstats_tx_add(dev, 1, skb->len);
--
-- sta->deflink.tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
-- sta->deflink.tx_stats.packets[skb_get_queue_mapping(skb)]++;
-+ info->hw_queue = sdata->vif.hw_queue[queue];
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- sdata = container_of(sdata->bss,
-@@ -4542,6 +4612,24 @@ static void ieee80211_8023_xmit(struct i
- if (key)
- info->control.hw_key = &key->conf;
-
-+ skb_list_walk_safe(skb, seg, next) {
-+ skbs++;
-+ len += seg->len;
-+ if (seg != skb)
-+ memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info));
-+ }
-+
-+ if (unlikely(skb->sk &&
-+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
-+ info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
-+ &info->flags, NULL);
-+
-+ dev_sw_netstats_tx_add(dev, skbs, len);
-+ sta->deflink.tx_stats.packets[queue] += skbs;
-+ sta->deflink.tx_stats.bytes[queue] += len;
-+
-+ ieee80211_tpt_led_trig_tx(local, len);
-+
- ieee80211_tx_8023(sdata, skb, sta, false);
-
- return;
-@@ -4583,6 +4671,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8
- key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
- goto skip_offload;
-
-+ sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);
- ieee80211_8023_xmit(sdata, dev, sta, key, skb);
- goto out;
-
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 9 Oct 2022 20:15:46 +0200
+Subject: [PATCH] mac80211: add support for restricting netdev features per vif
+
+This can be used to selectively disable feature flags for checksum offload,
+scatter/gather or GSO by changing vif->netdev_features.
+Removing features from vif->netdev_features does not affect the netdev
+features themselves, but instead fixes up skbs in the tx path so that the
+offloads are not needed in the driver.
+
+Aside from making it easier to deal with vif type based hardware limitations,
+this also makes it possible to optimize performance on hardware without native
+GSO support by declaring GSO support in hw->netdev_features and removing it
+from vif->netdev_features. This allows mac80211 to handle GSO segmentation
+after the sta lookup, but before itxq enqueue, thus reducing the number of
+unnecessary sta lookups, as well as some other per-packet processing.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/fq_impl.h
++++ b/include/net/fq_impl.h
+@@ -200,6 +200,7 @@ static void fq_tin_enqueue(struct fq *fq
+ fq_skb_free_t free_func)
+ {
+ struct fq_flow *flow;
++ struct sk_buff *next;
+ bool oom;
+
+ lockdep_assert_held(&fq->lock);
+@@ -214,11 +215,15 @@ static void fq_tin_enqueue(struct fq *fq
+ }
+
+ flow->tin = tin;
+- flow->backlog += skb->len;
+- tin->backlog_bytes += skb->len;
+- tin->backlog_packets++;
+- fq->memory_usage += skb->truesize;
+- fq->backlog++;
++ skb_list_walk_safe(skb, skb, next) {
++ skb_mark_not_on_list(skb);
++ flow->backlog += skb->len;
++ tin->backlog_bytes += skb->len;
++ tin->backlog_packets++;
++ fq->memory_usage += skb->truesize;
++ fq->backlog++;
++ __skb_queue_tail(&flow->queue, skb);
++ }
+
+ if (list_empty(&flow->flowchain)) {
+ flow->deficit = fq->quantum;
+@@ -226,7 +231,6 @@ static void fq_tin_enqueue(struct fq *fq
+ &tin->new_flows);
+ }
+
+- __skb_queue_tail(&flow->queue, skb);
+ oom = (fq->memory_usage > fq->memory_limit);
+ while (fq->backlog > fq->limit || oom) {
+ flow = fq_find_fattest_flow(fq);
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1807,6 +1807,10 @@ struct ieee80211_vif_cfg {
+ * @addr: address of this interface
+ * @p2p: indicates whether this AP or STA interface is a p2p
+ * interface, i.e. a GO or p2p-sta respectively
++ * @netdev_features: tx netdev features supported by the hardware for this
++ * vif. mac80211 initializes this to hw->netdev_features, and the driver
++ * can mask out specific tx features. mac80211 will handle software fixup
++ * for masked offloads (GSO, CSUM)
+ * @driver_flags: flags/capabilities the driver has for this interface,
+ * these need to be set (or cleared) when the interface is added
+ * or, if supported by the driver, the interface type is changed
+@@ -1848,6 +1852,7 @@ struct ieee80211_vif {
+
+ struct ieee80211_txq *txq;
+
++ netdev_features_t netdev_features;
+ u32 driver_flags;
+ u32 offload_flags;
+
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -2181,6 +2181,7 @@ int ieee80211_if_add(struct ieee80211_lo
+ ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ ndev->hw_features |= ndev->features &
+ MAC80211_SUPPORTED_FEATURES_TX;
++ sdata->vif.netdev_features = local->hw.netdev_features;
+
+ netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops);
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1355,7 +1355,11 @@ static struct txq_info *ieee80211_get_tx
+
+ static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
+ {
+- IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
++ struct sk_buff *next;
++ codel_time_t now = codel_get_time();
++
++ skb_list_walk_safe(skb, skb, next)
++ IEEE80211_SKB_CB(skb)->control.enqueue_time = now;
+ }
+
+ static u32 codel_skb_len_func(const struct sk_buff *skb)
+@@ -3578,55 +3582,79 @@ ieee80211_xmit_fast_finish(struct ieee80
+ return TX_CONTINUE;
+ }
+
+-static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
+- struct sta_info *sta,
+- struct ieee80211_fast_tx *fast_tx,
+- struct sk_buff *skb)
++static netdev_features_t
++ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata)
+ {
+- struct ieee80211_local *local = sdata->local;
+- u16 ethertype = (skb->data[12] << 8) | skb->data[13];
+- int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
+- int hw_headroom = sdata->local->hw.extra_tx_headroom;
+- struct ethhdr eth;
+- struct ieee80211_tx_info *info;
+- struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
+- struct ieee80211_tx_data tx;
+- ieee80211_tx_result r;
+- struct tid_ampdu_tx *tid_tx = NULL;
+- u8 tid = IEEE80211_NUM_TIDS;
++ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
++ return sdata->vif.netdev_features;
+
+- /* control port protocol needs a lot of special handling */
+- if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
+- return false;
++ if (!sdata->bss)
++ return 0;
+
+- /* only RFC 1042 SNAP */
+- if (ethertype < ETH_P_802_3_MIN)
+- return false;
++ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
++ return sdata->vif.netdev_features;
++}
+
+- /* don't handle TX status request here either */
+- if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
+- return false;
++static struct sk_buff *
++ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features)
++{
++ if (skb_is_gso(skb)) {
++ struct sk_buff *segs;
+
+- if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
+- if (tid_tx) {
+- if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
+- return false;
+- if (tid_tx->timeout)
+- tid_tx->last_tx = jiffies;
+- }
++ segs = skb_gso_segment(skb, features);
++ if (!segs)
++ return skb;
++ if (IS_ERR(segs))
++ goto free;
++
++ consume_skb(skb);
++ return segs;
+ }
+
+- /* after this point (skb is modified) we cannot return false */
++ if (skb_needs_linearize(skb, features) && __skb_linearize(skb))
++ goto free;
++
++ if (skb->ip_summed == CHECKSUM_PARTIAL) {
++ int ofs = skb_checksum_start_offset(skb);
++
++ if (skb->encapsulation)
++ skb_set_inner_transport_header(skb, ofs);
++ else
++ skb_set_transport_header(skb, ofs);
++
++ if (skb_csum_hwoffload_help(skb, features))
++ goto free;
++ }
++
++ skb_mark_not_on_list(skb);
++ return skb;
++
++free:
++ kfree_skb(skb);
++ return NULL;
++}
++
++static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta,
++ struct ieee80211_fast_tx *fast_tx,
++ struct sk_buff *skb, u8 tid, bool ampdu)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
++ struct ieee80211_tx_info *info;
++ struct ieee80211_tx_data tx;
++ ieee80211_tx_result r;
++ int hw_headroom = sdata->local->hw.extra_tx_headroom;
++ int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
++ struct ethhdr eth;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (unlikely(!skb))
+- return true;
++ return;
+
+ if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
+ ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
+- return true;
++ return;
+
+ /* will not be crypto-handled beyond what we do here, so use false
+ * as the may-encrypt argument for the resize to not account for
+@@ -3635,10 +3663,8 @@ static bool ieee80211_xmit_fast(struct i
+ if (unlikely(ieee80211_skb_resize(sdata, skb,
+ max_t(int, extra_head + hw_headroom -
+ skb_headroom(skb), 0),
+- ENCRYPT_NO))) {
+- kfree_skb(skb);
+- return true;
+- }
++ ENCRYPT_NO)))
++ goto free;
+
+ memcpy(ð, skb->data, ETH_HLEN - 2);
+ hdr = skb_push(skb, extra_head);
+@@ -3652,7 +3678,7 @@ static bool ieee80211_xmit_fast(struct i
+ info->control.vif = &sdata->vif;
+ info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
+ IEEE80211_TX_CTL_DONTFRAG |
+- (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
++ (ampdu ? IEEE80211_TX_CTL_AMPDU : 0);
+ info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT |
+ u32_encode_bits(IEEE80211_LINK_UNSPECIFIED,
+ IEEE80211_TX_CTRL_MLO_LINK);
+@@ -3676,16 +3702,14 @@ static bool ieee80211_xmit_fast(struct i
+ tx.key = fast_tx->key;
+
+ if (ieee80211_queue_skb(local, sdata, sta, skb))
+- return true;
++ return;
+
+ tx.skb = skb;
+ r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
+ fast_tx->key, &tx);
+ tx.skb = NULL;
+- if (r == TX_DROP) {
+- kfree_skb(skb);
+- return true;
+- }
++ if (r == TX_DROP)
++ goto free;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+@@ -3693,6 +3717,56 @@ static bool ieee80211_xmit_fast(struct i
+
+ __skb_queue_tail(&tx.skbs, skb);
+ ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false);
++ return;
++
++free:
++ kfree_skb(skb);
++}
++
++static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
++ struct sta_info *sta,
++ struct ieee80211_fast_tx *fast_tx,
++ struct sk_buff *skb)
++{
++ u16 ethertype = (skb->data[12] << 8) | skb->data[13];
++ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
++ struct tid_ampdu_tx *tid_tx = NULL;
++ struct sk_buff *next;
++ u8 tid = IEEE80211_NUM_TIDS;
++
++ /* control port protocol needs a lot of special handling */
++ if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
++ return false;
++
++ /* only RFC 1042 SNAP */
++ if (ethertype < ETH_P_802_3_MIN)
++ return false;
++
++ /* don't handle TX status request here either */
++ if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
++ return false;
++
++ if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
++ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
++ if (tid_tx) {
++ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
++ return false;
++ if (tid_tx->timeout)
++ tid_tx->last_tx = jiffies;
++ }
++ }
++
++ /* after this point (skb is modified) we cannot return false */
++ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
++ if (!skb)
++ return true;
++
++ skb_list_walk_safe(skb, skb, next) {
++ skb_mark_not_on_list(skb);
++ __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx);
++ }
++
+ return true;
+ }
+
+@@ -4193,31 +4267,14 @@ void __ieee80211_subif_start_xmit(struct
+ goto out;
+ }
+
+- if (skb_is_gso(skb)) {
+- struct sk_buff *segs;
+-
+- segs = skb_gso_segment(skb, 0);
+- if (IS_ERR(segs)) {
+- goto out_free;
+- } else if (segs) {
+- consume_skb(skb);
+- skb = segs;
+- }
+- } else {
+- /* we cannot process non-linear frames on this path */
+- if (skb_linearize(skb))
+- goto out_free;
+-
+- /* the frame could be fragmented, software-encrypted, and other
+- * things so we cannot really handle checksum offload with it -
+- * fix it up in software before we handle anything else.
+- */
+- if (skb->ip_summed == CHECKSUM_PARTIAL) {
+- skb_set_transport_header(skb,
+- skb_checksum_start_offset(skb));
+- if (skb_checksum_help(skb))
+- goto out_free;
+- }
++ /* the frame could be fragmented, software-encrypted, and other
++ * things so we cannot really handle checksum or GSO offload.
++ * fix it up in software before we handle anything else.
++ */
++ skb = ieee80211_tx_skb_fixup(skb, 0);
++ if (!skb) {
++ len = 0;
++ goto out;
+ }
+
+ skb_list_walk_safe(skb, skb, next) {
+@@ -4435,9 +4492,11 @@ normal:
+ return NETDEV_TX_OK;
+ }
+
+-static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
+- struct sk_buff *skb, struct sta_info *sta,
+- bool txpending)
++
++
++static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, struct sta_info *sta,
++ bool txpending)
+ {
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_tx_control control = {};
+@@ -4446,14 +4505,6 @@ static bool ieee80211_tx_8023(struct iee
+ unsigned long flags;
+ int q = info->hw_queue;
+
+- if (sta)
+- sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift);
+-
+- ieee80211_tpt_led_trig_tx(local, skb->len);
+-
+- if (ieee80211_queue_skb(local, sdata, sta, skb))
+- return true;
+-
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+ if (local->queue_stop_reasons[q] ||
+@@ -4480,6 +4531,26 @@ static bool ieee80211_tx_8023(struct iee
+ return true;
+ }
+
++static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
++ struct sk_buff *skb, struct sta_info *sta,
++ bool txpending)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct sk_buff *next;
++ bool ret = true;
++
++ if (ieee80211_queue_skb(local, sdata, sta, skb))
++ return true;
++
++ skb_list_walk_safe(skb, skb, next) {
++ skb_mark_not_on_list(skb);
++ if (!__ieee80211_tx_8023(sdata, skb, sta, txpending))
++ ret = false;
++ }
++
++ return ret;
++}
++
+ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+ struct net_device *dev, struct sta_info *sta,
+ struct ieee80211_key *key, struct sk_buff *skb)
+@@ -4487,9 +4558,13 @@ static void ieee80211_8023_xmit(struct i
+ struct ieee80211_tx_info *info;
+ struct ieee80211_local *local = sdata->local;
+ struct tid_ampdu_tx *tid_tx;
++ struct sk_buff *seg, *next;
++ unsigned int skbs = 0, len = 0;
++ u16 queue;
+ u8 tid;
+
+- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
++ queue = ieee80211_select_queue(sdata, sta, skb);
++ skb_set_queue_mapping(skb, queue);
+
+ if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
+ test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
+@@ -4499,9 +4574,6 @@ static void ieee80211_8023_xmit(struct i
+ if (unlikely(!skb))
+ return;
+
+- info = IEEE80211_SKB_CB(skb);
+- memset(info, 0, sizeof(*info));
+-
+ ieee80211_aggr_check(sdata, sta, skb);
+
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+@@ -4515,22 +4587,20 @@ static void ieee80211_8023_xmit(struct i
+ return;
+ }
+
+- info->flags |= IEEE80211_TX_CTL_AMPDU;
+ if (tid_tx->timeout)
+ tid_tx->last_tx = jiffies;
+ }
+
+- if (unlikely(skb->sk &&
+- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
+- info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
+- &info->flags, NULL);
++ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
++ if (!skb)
++ return;
+
+- info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
++ info = IEEE80211_SKB_CB(skb);
++ memset(info, 0, sizeof(*info));
++ if (tid_tx)
++ info->flags |= IEEE80211_TX_CTL_AMPDU;
+
+- dev_sw_netstats_tx_add(dev, 1, skb->len);
+-
+- sta->deflink.tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
+- sta->deflink.tx_stats.packets[skb_get_queue_mapping(skb)]++;
++ info->hw_queue = sdata->vif.hw_queue[queue];
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+@@ -4542,6 +4612,24 @@ static void ieee80211_8023_xmit(struct i
+ if (key)
+ info->control.hw_key = &key->conf;
+
++ skb_list_walk_safe(skb, seg, next) {
++ skbs++;
++ len += seg->len;
++ if (seg != skb)
++ memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info));
++ }
++
++ if (unlikely(skb->sk &&
++ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
++ info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
++ &info->flags, NULL);
++
++ dev_sw_netstats_tx_add(dev, skbs, len);
++ sta->deflink.tx_stats.packets[queue] += skbs;
++ sta->deflink.tx_stats.bytes[queue] += len;
++
++ ieee80211_tpt_led_trig_tx(local, len);
++
+ ieee80211_tx_8023(sdata, skb, sta, false);
+
+ return;
+@@ -4583,6 +4671,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+ key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
+ goto skip_offload;
+
++ sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);
+ ieee80211_8023_xmit(sdata, dev, sta, key, skb);
+ goto out;
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 1 Dec 2022 14:57:30 +0100
+Subject: [PATCH] wifi: mac80211: fix and simplify unencrypted drop check for
+ mesh
+
+ieee80211_drop_unencrypted is called from ieee80211_rx_h_mesh_fwding and
+ieee80211_frame_allowed.
+
+Since ieee80211_rx_h_mesh_fwding can forward packets for other mesh nodes
+and is called earlier, it needs to check the decryptions status and if the
+packet is using the control protocol on its own, instead of deferring to
+the later call from ieee80211_frame_allowed.
+
+Because of that, ieee80211_drop_unencrypted has a mesh specific check
+that skips over the mesh header in order to check the payload protocol.
+This code is invalid when called from ieee80211_frame_allowed, since that
+happens after the 802.11->802.3 conversion.
+
+Fix this by moving the mesh specific check directly into
+ieee80211_rx_h_mesh_fwding.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Link: https://lore.kernel.org/r/20221201135730.19723-1-nbd@nbd.name
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2403,7 +2403,6 @@ static int ieee80211_802_1x_port_control
+
+ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
+ {
+- struct ieee80211_hdr *hdr = (void *)rx->skb->data;
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
+@@ -2414,31 +2413,6 @@ static int ieee80211_drop_unencrypted(st
+ if (status->flag & RX_FLAG_DECRYPTED)
+ return 0;
+
+- /* check mesh EAPOL frames first */
+- if (unlikely(rx->sta && ieee80211_vif_is_mesh(&rx->sdata->vif) &&
+- ieee80211_is_data(fc))) {
+- struct ieee80211s_hdr *mesh_hdr;
+- u16 hdr_len = ieee80211_hdrlen(fc);
+- u16 ethertype_offset;
+- __be16 ethertype;
+-
+- if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr))
+- goto drop_check;
+-
+- /* make sure fixed part of mesh header is there, also checks skb len */
+- if (!pskb_may_pull(rx->skb, hdr_len + 6))
+- goto drop_check;
+-
+- mesh_hdr = (struct ieee80211s_hdr *)(skb->data + hdr_len);
+- ethertype_offset = hdr_len + ieee80211_get_mesh_hdrlen(mesh_hdr) +
+- sizeof(rfc1042_header);
+-
+- if (skb_copy_bits(rx->skb, ethertype_offset, ðertype, 2) == 0 &&
+- ethertype == rx->sdata->control_port_protocol)
+- return 0;
+- }
+-
+-drop_check:
+ /* Drop unencrypted frames if key is set. */
+ if (unlikely(!ieee80211_has_protected(fc) &&
+ !ieee80211_is_any_nullfunc(fc) &&
+@@ -2892,8 +2866,16 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
+
+- if (ieee80211_drop_unencrypted(rx, hdr->frame_control))
+- return RX_DROP_MONITOR;
++ if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) {
++ int offset = hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr) +
++ sizeof(rfc1042_header);
++ __be16 ethertype;
++
++ if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr) ||
++ skb_copy_bits(rx->skb, offset, ðertype, 2) != 0 ||
++ ethertype != rx->sdata->control_port_protocol)
++ return RX_DROP_MONITOR;
++ }
+
+ /* frame is in RMC, don't forward */
+ if (ieee80211_is_data(hdr->frame_control) &&
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 1 Dec 2022 14:57:30 +0100
-Subject: [PATCH] wifi: mac80211: fix and simplify unencrypted drop check for
- mesh
-
-ieee80211_drop_unencrypted is called from ieee80211_rx_h_mesh_fwding and
-ieee80211_frame_allowed.
-
-Since ieee80211_rx_h_mesh_fwding can forward packets for other mesh nodes
-and is called earlier, it needs to check the decryptions status and if the
-packet is using the control protocol on its own, instead of deferring to
-the later call from ieee80211_frame_allowed.
-
-Because of that, ieee80211_drop_unencrypted has a mesh specific check
-that skips over the mesh header in order to check the payload protocol.
-This code is invalid when called from ieee80211_frame_allowed, since that
-happens after the 802.11->802.3 conversion.
-
-Fix this by moving the mesh specific check directly into
-ieee80211_rx_h_mesh_fwding.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-Link: https://lore.kernel.org/r/20221201135730.19723-1-nbd@nbd.name
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2403,7 +2403,6 @@ static int ieee80211_802_1x_port_control
-
- static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
- {
-- struct ieee80211_hdr *hdr = (void *)rx->skb->data;
- struct sk_buff *skb = rx->skb;
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
-@@ -2414,31 +2413,6 @@ static int ieee80211_drop_unencrypted(st
- if (status->flag & RX_FLAG_DECRYPTED)
- return 0;
-
-- /* check mesh EAPOL frames first */
-- if (unlikely(rx->sta && ieee80211_vif_is_mesh(&rx->sdata->vif) &&
-- ieee80211_is_data(fc))) {
-- struct ieee80211s_hdr *mesh_hdr;
-- u16 hdr_len = ieee80211_hdrlen(fc);
-- u16 ethertype_offset;
-- __be16 ethertype;
--
-- if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr))
-- goto drop_check;
--
-- /* make sure fixed part of mesh header is there, also checks skb len */
-- if (!pskb_may_pull(rx->skb, hdr_len + 6))
-- goto drop_check;
--
-- mesh_hdr = (struct ieee80211s_hdr *)(skb->data + hdr_len);
-- ethertype_offset = hdr_len + ieee80211_get_mesh_hdrlen(mesh_hdr) +
-- sizeof(rfc1042_header);
--
-- if (skb_copy_bits(rx->skb, ethertype_offset, ðertype, 2) == 0 &&
-- ethertype == rx->sdata->control_port_protocol)
-- return 0;
-- }
--
--drop_check:
- /* Drop unencrypted frames if key is set. */
- if (unlikely(!ieee80211_has_protected(fc) &&
- !ieee80211_is_any_nullfunc(fc) &&
-@@ -2892,8 +2866,16 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
- hdr = (struct ieee80211_hdr *) skb->data;
- mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
-
-- if (ieee80211_drop_unencrypted(rx, hdr->frame_control))
-- return RX_DROP_MONITOR;
-+ if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) {
-+ int offset = hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr) +
-+ sizeof(rfc1042_header);
-+ __be16 ethertype;
-+
-+ if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr) ||
-+ skb_copy_bits(rx->skb, offset, ðertype, 2) != 0 ||
-+ ethertype != rx->sdata->control_port_protocol)
-+ return RX_DROP_MONITOR;
-+ }
-
- /* frame is in RMC, don't forward */
- if (ieee80211_is_data(hdr->frame_control) &&
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 2 Dec 2022 13:53:11 +0100
+Subject: [PATCH] wifi: cfg80211: move A-MSDU check in
+ ieee80211_data_to_8023_exthdr
+
+When parsing the outer A-MSDU header, don't check for inner bridge tunnel
+or RFC1042 headers. This is handled by ieee80211_amsdu_to_8023s already.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -631,8 +631,9 @@ int ieee80211_data_to_8023_exthdr(struct
+ break;
+ }
+
+- if (likely(skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
+- ((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) &&
++ if (likely(!is_amsdu &&
++ skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
++ ((ether_addr_equal(payload.hdr, rfc1042_header) &&
+ payload.proto != htons(ETH_P_AARP) &&
+ payload.proto != htons(ETH_P_IPX)) ||
+ ether_addr_equal(payload.hdr, bridge_tunnel_header)))) {
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 2 Dec 2022 13:53:11 +0100
-Subject: [PATCH] wifi: cfg80211: move A-MSDU check in
- ieee80211_data_to_8023_exthdr
-
-When parsing the outer A-MSDU header, don't check for inner bridge tunnel
-or RFC1042 headers. This is handled by ieee80211_amsdu_to_8023s already.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -631,8 +631,9 @@ int ieee80211_data_to_8023_exthdr(struct
- break;
- }
-
-- if (likely(skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
-- ((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) &&
-+ if (likely(!is_amsdu &&
-+ skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
-+ ((ether_addr_equal(payload.hdr, rfc1042_header) &&
- payload.proto != htons(ETH_P_AARP) &&
- payload.proto != htons(ETH_P_IPX)) ||
- ether_addr_equal(payload.hdr, bridge_tunnel_header)))) {
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 2 Dec 2022 13:54:15 +0100
+Subject: [PATCH] wifi: cfg80211: factor out bridge tunnel / RFC1042 header
+ check
+
+The same check is done in multiple places, unify it.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -542,6 +542,21 @@ unsigned int ieee80211_get_mesh_hdrlen(s
+ }
+ EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
+
++static bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto)
++{
++ const __be16 *hdr_proto = hdr + ETH_ALEN;
++
++ if (!(ether_addr_equal(hdr, rfc1042_header) &&
++ *hdr_proto != htons(ETH_P_AARP) &&
++ *hdr_proto != htons(ETH_P_IPX)) &&
++ !ether_addr_equal(hdr, bridge_tunnel_header))
++ return false;
++
++ *proto = *hdr_proto;
++
++ return true;
++}
++
+ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+ const u8 *addr, enum nl80211_iftype iftype,
+ u8 data_offset, bool is_amsdu)
+@@ -633,14 +648,9 @@ int ieee80211_data_to_8023_exthdr(struct
+
+ if (likely(!is_amsdu &&
+ skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
+- ((ether_addr_equal(payload.hdr, rfc1042_header) &&
+- payload.proto != htons(ETH_P_AARP) &&
+- payload.proto != htons(ETH_P_IPX)) ||
+- ether_addr_equal(payload.hdr, bridge_tunnel_header)))) {
+- /* remove RFC1042 or Bridge-Tunnel encapsulation and
+- * replace EtherType */
++ ieee80211_get_8023_tunnel_proto(&payload, &tmp.h_proto))) {
++ /* remove RFC1042 or Bridge-Tunnel encapsulation */
+ hdrlen += ETH_ALEN + 2;
+- tmp.h_proto = payload.proto;
+ skb_postpull_rcsum(skb, &payload, ETH_ALEN + 2);
+ } else {
+ tmp.h_proto = htons(skb->len - hdrlen);
+@@ -756,8 +766,6 @@ void ieee80211_amsdu_to_8023s(struct sk_
+ {
+ unsigned int hlen = ALIGN(extra_headroom, 4);
+ struct sk_buff *frame = NULL;
+- u16 ethertype;
+- u8 *payload;
+ int offset = 0, remaining;
+ struct ethhdr eth;
+ bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
+@@ -811,14 +819,8 @@ void ieee80211_amsdu_to_8023s(struct sk_
+ frame->dev = skb->dev;
+ frame->priority = skb->priority;
+
+- payload = frame->data;
+- ethertype = (payload[6] << 8) | payload[7];
+- if (likely((ether_addr_equal(payload, rfc1042_header) &&
+- ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+- ether_addr_equal(payload, bridge_tunnel_header))) {
+- eth.h_proto = htons(ethertype);
++ if (likely(ieee80211_get_8023_tunnel_proto(frame->data, ð.h_proto)))
+ skb_pull(frame, ETH_ALEN + 2);
+- }
+
+ memcpy(skb_push(frame, sizeof(eth)), ð, sizeof(eth));
+ __skb_queue_tail(list, frame);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 2 Dec 2022 13:54:15 +0100
-Subject: [PATCH] wifi: cfg80211: factor out bridge tunnel / RFC1042 header
- check
-
-The same check is done in multiple places, unify it.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -542,6 +542,21 @@ unsigned int ieee80211_get_mesh_hdrlen(s
- }
- EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
-
-+static bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto)
-+{
-+ const __be16 *hdr_proto = hdr + ETH_ALEN;
-+
-+ if (!(ether_addr_equal(hdr, rfc1042_header) &&
-+ *hdr_proto != htons(ETH_P_AARP) &&
-+ *hdr_proto != htons(ETH_P_IPX)) &&
-+ !ether_addr_equal(hdr, bridge_tunnel_header))
-+ return false;
-+
-+ *proto = *hdr_proto;
-+
-+ return true;
-+}
-+
- int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
- const u8 *addr, enum nl80211_iftype iftype,
- u8 data_offset, bool is_amsdu)
-@@ -633,14 +648,9 @@ int ieee80211_data_to_8023_exthdr(struct
-
- if (likely(!is_amsdu &&
- skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
-- ((ether_addr_equal(payload.hdr, rfc1042_header) &&
-- payload.proto != htons(ETH_P_AARP) &&
-- payload.proto != htons(ETH_P_IPX)) ||
-- ether_addr_equal(payload.hdr, bridge_tunnel_header)))) {
-- /* remove RFC1042 or Bridge-Tunnel encapsulation and
-- * replace EtherType */
-+ ieee80211_get_8023_tunnel_proto(&payload, &tmp.h_proto))) {
-+ /* remove RFC1042 or Bridge-Tunnel encapsulation */
- hdrlen += ETH_ALEN + 2;
-- tmp.h_proto = payload.proto;
- skb_postpull_rcsum(skb, &payload, ETH_ALEN + 2);
- } else {
- tmp.h_proto = htons(skb->len - hdrlen);
-@@ -756,8 +766,6 @@ void ieee80211_amsdu_to_8023s(struct sk_
- {
- unsigned int hlen = ALIGN(extra_headroom, 4);
- struct sk_buff *frame = NULL;
-- u16 ethertype;
-- u8 *payload;
- int offset = 0, remaining;
- struct ethhdr eth;
- bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
-@@ -811,14 +819,8 @@ void ieee80211_amsdu_to_8023s(struct sk_
- frame->dev = skb->dev;
- frame->priority = skb->priority;
-
-- payload = frame->data;
-- ethertype = (payload[6] << 8) | payload[7];
-- if (likely((ether_addr_equal(payload, rfc1042_header) &&
-- ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
-- ether_addr_equal(payload, bridge_tunnel_header))) {
-- eth.h_proto = htons(ethertype);
-+ if (likely(ieee80211_get_8023_tunnel_proto(frame->data, ð.h_proto)))
- skb_pull(frame, ETH_ALEN + 2);
-- }
-
- memcpy(skb_push(frame, sizeof(eth)), ð, sizeof(eth));
- __skb_queue_tail(list, frame);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 2 Dec 2022 17:01:46 +0100
+Subject: [PATCH] wifi: mac80211: remove mesh forwarding congestion check
+
+Now that all drivers use iTXQ, it does not make sense to check to drop
+tx forwarding packets when the driver has stopped the queues.
+fq_codel will take care of dropping packets when the queues fill up
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/debugfs_netdev.c
++++ b/net/mac80211/debugfs_netdev.c
+@@ -603,8 +603,6 @@ IEEE80211_IF_FILE(fwded_mcast, u.mesh.ms
+ IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC);
+ IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC);
+ IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC);
+-IEEE80211_IF_FILE(dropped_frames_congestion,
+- u.mesh.mshstats.dropped_frames_congestion, DEC);
+ IEEE80211_IF_FILE(dropped_frames_no_route,
+ u.mesh.mshstats.dropped_frames_no_route, DEC);
+
+@@ -740,7 +738,6 @@ static void add_mesh_stats(struct ieee80
+ MESHSTATS_ADD(fwded_frames);
+ MESHSTATS_ADD(dropped_frames_ttl);
+ MESHSTATS_ADD(dropped_frames_no_route);
+- MESHSTATS_ADD(dropped_frames_congestion);
+ #undef MESHSTATS_ADD
+ }
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -329,7 +329,6 @@ struct mesh_stats {
+ __u32 fwded_frames; /* Mesh total forwarded frames */
+ __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/
+ __u32 dropped_frames_no_route; /* Not transmitted, no route found */
+- __u32 dropped_frames_congestion;/* Not forwarded due to congestion */
+ };
+
+ #define PREQ_Q_F_START 0x1
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2926,11 +2926,6 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
+ return RX_CONTINUE;
+
+ ac = ieee802_1d_to_ac[skb->priority];
+- q = sdata->vif.hw_queue[ac];
+- if (ieee80211_queue_stopped(&local->hw, q)) {
+- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion);
+- return RX_DROP_MONITOR;
+- }
+ skb_set_queue_mapping(skb, ac);
+
+ if (!--mesh_hdr->ttl) {
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 2 Dec 2022 17:01:46 +0100
-Subject: [PATCH] wifi: mac80211: remove mesh forwarding congestion check
-
-Now that all drivers use iTXQ, it does not make sense to check to drop
-tx forwarding packets when the driver has stopped the queues.
-fq_codel will take care of dropping packets when the queues fill up
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/debugfs_netdev.c
-+++ b/net/mac80211/debugfs_netdev.c
-@@ -603,8 +603,6 @@ IEEE80211_IF_FILE(fwded_mcast, u.mesh.ms
- IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC);
- IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC);
- IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC);
--IEEE80211_IF_FILE(dropped_frames_congestion,
-- u.mesh.mshstats.dropped_frames_congestion, DEC);
- IEEE80211_IF_FILE(dropped_frames_no_route,
- u.mesh.mshstats.dropped_frames_no_route, DEC);
-
-@@ -740,7 +738,6 @@ static void add_mesh_stats(struct ieee80
- MESHSTATS_ADD(fwded_frames);
- MESHSTATS_ADD(dropped_frames_ttl);
- MESHSTATS_ADD(dropped_frames_no_route);
-- MESHSTATS_ADD(dropped_frames_congestion);
- #undef MESHSTATS_ADD
- }
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -329,7 +329,6 @@ struct mesh_stats {
- __u32 fwded_frames; /* Mesh total forwarded frames */
- __u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/
- __u32 dropped_frames_no_route; /* Not transmitted, no route found */
-- __u32 dropped_frames_congestion;/* Not forwarded due to congestion */
- };
-
- #define PREQ_Q_F_START 0x1
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2926,11 +2926,6 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
- return RX_CONTINUE;
-
- ac = ieee802_1d_to_ac[skb->priority];
-- q = sdata->vif.hw_queue[ac];
-- if (ieee80211_queue_stopped(&local->hw, q)) {
-- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion);
-- return RX_DROP_MONITOR;
-- }
- skb_set_queue_mapping(skb, ac);
-
- if (!--mesh_hdr->ttl) {
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 6 Dec 2022 11:15:02 +0100
+Subject: [PATCH] wifi: mac80211: fix receiving A-MSDU frames on mesh
+ interfaces
+
+The current mac80211 mesh A-MSDU receive path fails to parse A-MSDU packets
+on mesh interfaces, because it assumes that the Mesh Control field is always
+directly after the 802.11 header.
+802.11-2020 9.3.2.2.2 Figure 9-70 shows that the Mesh Control field is
+actually part of the A-MSDU subframe header.
+This makes more sense, since it allows packets for multiple different
+destinations to be included in the same A-MSDU, as long as RA and TID are
+still the same.
+Another issue is the fact that the A-MSDU subframe length field was apparently
+accidentally defined as little-endian in the standard.
+
+In order to fix this, the mesh forwarding path needs happen at a different
+point in the receive path.
+
+ieee80211_data_to_8023_exthdr is changed to ignore the mesh control field
+and leave it in after the ethernet header. This also affects the source/dest
+MAC address fields, which now in the case of mesh point to the mesh SA/DA.
+
+ieee80211_amsdu_to_8023s is changed to deal with the endian difference and
+to add the Mesh Control length to the subframe length, since it's not covered
+by the MSDU length field.
+
+With these changes, the mac80211 will get the same packet structure for
+converted regular data packets and unpacked A-MSDU subframes.
+
+The mesh forwarding checks are now only performed after the A-MSDU decap.
+For locally received packets, the Mesh Control header is stripped away.
+For forwarded packets, a new 802.11 header gets added.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
++++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
+@@ -33,7 +33,7 @@ static int mwifiex_11n_dispatch_amsdu_pk
+ skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
+
+ ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
+- priv->wdev.iftype, 0, NULL, NULL);
++ priv->wdev.iftype, 0, NULL, NULL, false);
+
+ while (!skb_queue_empty(&list)) {
+ struct rx_packet_hdr *rx_hdr;
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -6208,11 +6208,36 @@ static inline int ieee80211_data_to_8023
+ * @extra_headroom: The hardware extra headroom for SKBs in the @list.
+ * @check_da: DA to check in the inner ethernet header, or NULL
+ * @check_sa: SA to check in the inner ethernet header, or NULL
++ * @mesh_control: A-MSDU subframe header includes the mesh control field
+ */
+ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
+ const u8 *addr, enum nl80211_iftype iftype,
+ const unsigned int extra_headroom,
+- const u8 *check_da, const u8 *check_sa);
++ const u8 *check_da, const u8 *check_sa,
++ bool mesh_control);
++
++/**
++ * ieee80211_get_8023_tunnel_proto - get RFC1042 or bridge tunnel encap protocol
++ *
++ * Check for RFC1042 or bridge tunnel header and fetch the encapsulated
++ * protocol.
++ *
++ * @hdr: pointer to the MSDU payload
++ * @proto: destination pointer to store the protocol
++ * Return: true if encapsulation was found
++ */
++bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto);
++
++/**
++ * ieee80211_strip_8023_mesh_hdr - strip mesh header from converted 802.3 frames
++ *
++ * Strip the mesh header, which was left in by ieee80211_data_to_8023 as part
++ * of the MSDU data. Also move any source/destination addresses from the mesh
++ * header to the ethernet header (if present).
++ *
++ * @skb: The 802.3 frame with embedded mesh header
++ */
++int ieee80211_strip_8023_mesh_hdr(struct sk_buff *skb);
+
+ /**
+ * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2720,6 +2720,174 @@ ieee80211_deliver_skb(struct ieee80211_r
+ }
+ }
+
++static ieee80211_rx_result
++ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta,
++ struct sk_buff *skb)
++{
++#ifdef CPTCFG_MAC80211_MESH
++ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
++ struct ieee80211_local *local = sdata->local;
++ uint16_t fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA;
++ struct ieee80211_hdr hdr = {
++ .frame_control = cpu_to_le16(fc)
++ };
++ struct ieee80211_hdr *fwd_hdr;
++ struct ieee80211s_hdr *mesh_hdr;
++ struct ieee80211_tx_info *info;
++ struct sk_buff *fwd_skb;
++ struct ethhdr *eth;
++ bool multicast;
++ int tailroom = 0;
++ int hdrlen, mesh_hdrlen;
++ u8 *qos;
++
++ if (!ieee80211_vif_is_mesh(&sdata->vif))
++ return RX_CONTINUE;
++
++ if (!pskb_may_pull(skb, sizeof(*eth) + 6))
++ return RX_DROP_MONITOR;
++
++ mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(*eth));
++ mesh_hdrlen = ieee80211_get_mesh_hdrlen(mesh_hdr);
++
++ if (!pskb_may_pull(skb, sizeof(*eth) + mesh_hdrlen))
++ return RX_DROP_MONITOR;
++
++ eth = (struct ethhdr *)skb->data;
++ multicast = is_multicast_ether_addr(eth->h_dest);
++
++ mesh_hdr = (struct ieee80211s_hdr *)(eth + 1);
++ if (!mesh_hdr->ttl)
++ return RX_DROP_MONITOR;
++
++ /* frame is in RMC, don't forward */
++ if (is_multicast_ether_addr(eth->h_dest) &&
++ mesh_rmc_check(sdata, eth->h_source, mesh_hdr))
++ return RX_DROP_MONITOR;
++
++ /* Frame has reached destination. Don't forward */
++ if (ether_addr_equal(sdata->vif.addr, eth->h_dest))
++ goto rx_accept;
++
++ if (!ifmsh->mshcfg.dot11MeshForwarding) {
++ if (is_multicast_ether_addr(eth->h_dest))
++ goto rx_accept;
++
++ return RX_DROP_MONITOR;
++ }
++
++ /* forward packet */
++ if (sdata->crypto_tx_tailroom_needed_cnt)
++ tailroom = IEEE80211_ENCRYPT_TAILROOM;
++
++ if (!--mesh_hdr->ttl) {
++ if (multicast)
++ goto rx_accept;
++
++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl);
++ return RX_DROP_MONITOR;
++ }
++
++ if (mesh_hdr->flags & MESH_FLAGS_AE) {
++ struct mesh_path *mppath;
++ char *proxied_addr;
++
++ if (multicast)
++ proxied_addr = mesh_hdr->eaddr1;
++ else if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
++ /* has_a4 already checked in ieee80211_rx_mesh_check */
++ proxied_addr = mesh_hdr->eaddr2;
++ else
++ return RX_DROP_MONITOR;
++
++ rcu_read_lock();
++ mppath = mpp_path_lookup(sdata, proxied_addr);
++ if (!mppath) {
++ mpp_path_add(sdata, proxied_addr, eth->h_source);
++ } else {
++ spin_lock_bh(&mppath->state_lock);
++ if (!ether_addr_equal(mppath->mpp, eth->h_source))
++ memcpy(mppath->mpp, eth->h_source, ETH_ALEN);
++ mppath->exp_time = jiffies;
++ spin_unlock_bh(&mppath->state_lock);
++ }
++ rcu_read_unlock();
++ }
++
++ skb_set_queue_mapping(skb, ieee802_1d_to_ac[skb->priority]);
++
++ ieee80211_fill_mesh_addresses(&hdr, &hdr.frame_control,
++ eth->h_dest, eth->h_source);
++ hdrlen = ieee80211_hdrlen(hdr.frame_control);
++ if (multicast) {
++ int extra_head = sizeof(struct ieee80211_hdr) - sizeof(*eth);
++
++ fwd_skb = skb_copy_expand(skb, local->tx_headroom + extra_head +
++ IEEE80211_ENCRYPT_HEADROOM,
++ tailroom, GFP_ATOMIC);
++ if (!fwd_skb)
++ goto rx_accept;
++ } else {
++ fwd_skb = skb;
++ skb = NULL;
++
++ if (skb_cow_head(fwd_skb, hdrlen - sizeof(struct ethhdr)))
++ return RX_DROP_UNUSABLE;
++ }
++
++ fwd_hdr = skb_push(fwd_skb, hdrlen - sizeof(struct ethhdr));
++ memcpy(fwd_hdr, &hdr, hdrlen - 2);
++ qos = ieee80211_get_qos_ctl(fwd_hdr);
++ qos[0] = qos[1] = 0;
++
++ skb_reset_mac_header(fwd_skb);
++ hdrlen += mesh_hdrlen;
++ if (ieee80211_get_8023_tunnel_proto(fwd_skb->data + hdrlen,
++ &fwd_skb->protocol))
++ hdrlen += ETH_ALEN;
++ else
++ fwd_skb->protocol = htons(fwd_skb->len - hdrlen);
++ skb_set_network_header(fwd_skb, hdrlen);
++
++ info = IEEE80211_SKB_CB(fwd_skb);
++ memset(info, 0, sizeof(*info));
++ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
++ info->control.vif = &sdata->vif;
++ info->control.jiffies = jiffies;
++ if (multicast) {
++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
++ memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
++ /* update power mode indication when forwarding */
++ ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
++ } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) {
++ /* mesh power mode flags updated in mesh_nexthop_lookup */
++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
++ } else {
++ /* unable to resolve next hop */
++ if (sta)
++ mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
++ hdr.addr3, 0,
++ WLAN_REASON_MESH_PATH_NOFORWARD,
++ sta->sta.addr);
++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
++ kfree_skb(fwd_skb);
++ goto rx_accept;
++ }
++
++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
++ fwd_skb->dev = sdata->dev;
++ ieee80211_add_pending_skb(local, fwd_skb);
++
++rx_accept:
++ if (!skb)
++ return RX_QUEUED;
++
++ ieee80211_strip_8023_mesh_hdr(skb);
++#endif
++
++ return RX_CONTINUE;
++}
++
+ static ieee80211_rx_result debug_noinline
+ __ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx, u8 data_offset)
+ {
+@@ -2728,8 +2896,10 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ __le16 fc = hdr->frame_control;
+ struct sk_buff_head frame_list;
++ static ieee80211_rx_result res;
+ struct ethhdr ethhdr;
+ const u8 *check_da = ethhdr.h_dest, *check_sa = ethhdr.h_source;
++ bool mesh = false;
+
+ if (unlikely(ieee80211_has_a4(hdr->frame_control))) {
+ check_da = NULL;
+@@ -2746,6 +2916,8 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ check_sa = NULL;
++ check_da = NULL;
++ mesh = true;
+ break;
+ default:
+ break;
+@@ -2763,17 +2935,29 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
+ ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
+ rx->sdata->vif.type,
+ rx->local->hw.extra_tx_headroom,
+- check_da, check_sa);
++ check_da, check_sa, mesh);
+
+ while (!skb_queue_empty(&frame_list)) {
+ rx->skb = __skb_dequeue(&frame_list);
+
+- if (!ieee80211_frame_allowed(rx, fc)) {
+- dev_kfree_skb(rx->skb);
++ res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb);
++ switch (res) {
++ case RX_QUEUED:
+ continue;
++ case RX_CONTINUE:
++ break;
++ default:
++ goto free;
+ }
+
++ if (!ieee80211_frame_allowed(rx, fc))
++ goto free;
++
+ ieee80211_deliver_skb(rx);
++ continue;
++
++free:
++ dev_kfree_skb(rx->skb);
+ }
+
+ return RX_QUEUED;
+@@ -2806,6 +2990,8 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx
+ if (!rx->sdata->u.mgd.use_4addr)
+ return RX_DROP_UNUSABLE;
+ break;
++ case NL80211_IFTYPE_MESH_POINT:
++ break;
+ default:
+ return RX_DROP_UNUSABLE;
+ }
+@@ -2834,155 +3020,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx
+ return __ieee80211_rx_h_amsdu(rx, 0);
+ }
+
+-#ifdef CPTCFG_MAC80211_MESH
+-static ieee80211_rx_result
+-ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
+-{
+- struct ieee80211_hdr *fwd_hdr, *hdr;
+- struct ieee80211_tx_info *info;
+- struct ieee80211s_hdr *mesh_hdr;
+- struct sk_buff *skb = rx->skb, *fwd_skb;
+- struct ieee80211_local *local = rx->local;
+- struct ieee80211_sub_if_data *sdata = rx->sdata;
+- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+- u16 ac, q, hdrlen;
+- int tailroom = 0;
+-
+- hdr = (struct ieee80211_hdr *) skb->data;
+- hdrlen = ieee80211_hdrlen(hdr->frame_control);
+-
+- /* make sure fixed part of mesh header is there, also checks skb len */
+- if (!pskb_may_pull(rx->skb, hdrlen + 6))
+- return RX_DROP_MONITOR;
+-
+- mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
+-
+- /* make sure full mesh header is there, also checks skb len */
+- if (!pskb_may_pull(rx->skb,
+- hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr)))
+- return RX_DROP_MONITOR;
+-
+- /* reload pointers */
+- hdr = (struct ieee80211_hdr *) skb->data;
+- mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
+-
+- if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) {
+- int offset = hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr) +
+- sizeof(rfc1042_header);
+- __be16 ethertype;
+-
+- if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr) ||
+- skb_copy_bits(rx->skb, offset, ðertype, 2) != 0 ||
+- ethertype != rx->sdata->control_port_protocol)
+- return RX_DROP_MONITOR;
+- }
+-
+- /* frame is in RMC, don't forward */
+- if (ieee80211_is_data(hdr->frame_control) &&
+- is_multicast_ether_addr(hdr->addr1) &&
+- mesh_rmc_check(rx->sdata, hdr->addr3, mesh_hdr))
+- return RX_DROP_MONITOR;
+-
+- if (!ieee80211_is_data(hdr->frame_control))
+- return RX_CONTINUE;
+-
+- if (!mesh_hdr->ttl)
+- return RX_DROP_MONITOR;
+-
+- if (mesh_hdr->flags & MESH_FLAGS_AE) {
+- struct mesh_path *mppath;
+- char *proxied_addr;
+- char *mpp_addr;
+-
+- if (is_multicast_ether_addr(hdr->addr1)) {
+- mpp_addr = hdr->addr3;
+- proxied_addr = mesh_hdr->eaddr1;
+- } else if ((mesh_hdr->flags & MESH_FLAGS_AE) ==
+- MESH_FLAGS_AE_A5_A6) {
+- /* has_a4 already checked in ieee80211_rx_mesh_check */
+- mpp_addr = hdr->addr4;
+- proxied_addr = mesh_hdr->eaddr2;
+- } else {
+- return RX_DROP_MONITOR;
+- }
+-
+- rcu_read_lock();
+- mppath = mpp_path_lookup(sdata, proxied_addr);
+- if (!mppath) {
+- mpp_path_add(sdata, proxied_addr, mpp_addr);
+- } else {
+- spin_lock_bh(&mppath->state_lock);
+- if (!ether_addr_equal(mppath->mpp, mpp_addr))
+- memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
+- mppath->exp_time = jiffies;
+- spin_unlock_bh(&mppath->state_lock);
+- }
+- rcu_read_unlock();
+- }
+-
+- /* Frame has reached destination. Don't forward */
+- if (!is_multicast_ether_addr(hdr->addr1) &&
+- ether_addr_equal(sdata->vif.addr, hdr->addr3))
+- return RX_CONTINUE;
+-
+- ac = ieee802_1d_to_ac[skb->priority];
+- skb_set_queue_mapping(skb, ac);
+-
+- if (!--mesh_hdr->ttl) {
+- if (!is_multicast_ether_addr(hdr->addr1))
+- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh,
+- dropped_frames_ttl);
+- goto out;
+- }
+-
+- if (!ifmsh->mshcfg.dot11MeshForwarding)
+- goto out;
+-
+- if (sdata->crypto_tx_tailroom_needed_cnt)
+- tailroom = IEEE80211_ENCRYPT_TAILROOM;
+-
+- fwd_skb = skb_copy_expand(skb, local->tx_headroom +
+- IEEE80211_ENCRYPT_HEADROOM,
+- tailroom, GFP_ATOMIC);
+- if (!fwd_skb)
+- goto out;
+-
+- fwd_skb->dev = sdata->dev;
+- fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
+- fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
+- info = IEEE80211_SKB_CB(fwd_skb);
+- memset(info, 0, sizeof(*info));
+- info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+- info->control.vif = &rx->sdata->vif;
+- info->control.jiffies = jiffies;
+- if (is_multicast_ether_addr(fwd_hdr->addr1)) {
+- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
+- memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
+- /* update power mode indication when forwarding */
+- ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
+- } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) {
+- /* mesh power mode flags updated in mesh_nexthop_lookup */
+- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
+- } else {
+- /* unable to resolve next hop */
+- mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
+- fwd_hdr->addr3, 0,
+- WLAN_REASON_MESH_PATH_NOFORWARD,
+- fwd_hdr->addr2);
+- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
+- kfree_skb(fwd_skb);
+- return RX_DROP_MONITOR;
+- }
+-
+- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
+- ieee80211_add_pending_skb(local, fwd_skb);
+- out:
+- if (is_multicast_ether_addr(hdr->addr1))
+- return RX_CONTINUE;
+- return RX_DROP_MONITOR;
+-}
+-#endif
+-
+ static ieee80211_rx_result debug_noinline
+ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
+ {
+@@ -2991,6 +3028,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_
+ struct net_device *dev = sdata->dev;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ __le16 fc = hdr->frame_control;
++ static ieee80211_rx_result res;
+ bool port_control;
+ int err;
+
+@@ -3017,6 +3055,10 @@ ieee80211_rx_h_data(struct ieee80211_rx_
+ if (unlikely(err))
+ return RX_DROP_UNUSABLE;
+
++ res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb);
++ if (res != RX_CONTINUE)
++ return res;
++
+ if (!ieee80211_frame_allowed(rx, fc))
+ return RX_DROP_MONITOR;
+
+@@ -3987,10 +4029,6 @@ static void ieee80211_rx_handlers(struct
+ CALL_RXH(ieee80211_rx_h_defragment);
+ CALL_RXH(ieee80211_rx_h_michael_mic_verify);
+ /* must be after MMIC verify so header is counted in MPDU mic */
+-#ifdef CPTCFG_MAC80211_MESH
+- if (ieee80211_vif_is_mesh(&rx->sdata->vif))
+- CALL_RXH(ieee80211_rx_h_mesh_fwding);
+-#endif
+ CALL_RXH(ieee80211_rx_h_amsdu);
+ CALL_RXH(ieee80211_rx_h_data);
+
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -542,7 +542,7 @@ unsigned int ieee80211_get_mesh_hdrlen(s
+ }
+ EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
+
+-static bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto)
++bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto)
+ {
+ const __be16 *hdr_proto = hdr + ETH_ALEN;
+
+@@ -556,6 +556,49 @@ static bool ieee80211_get_8023_tunnel_pr
+
+ return true;
+ }
++EXPORT_SYMBOL(ieee80211_get_8023_tunnel_proto);
++
++int ieee80211_strip_8023_mesh_hdr(struct sk_buff *skb)
++{
++ const void *mesh_addr;
++ struct {
++ struct ethhdr eth;
++ u8 flags;
++ } payload;
++ int hdrlen;
++ int ret;
++
++ ret = skb_copy_bits(skb, 0, &payload, sizeof(payload));
++ if (ret)
++ return ret;
++
++ hdrlen = sizeof(payload.eth) + __ieee80211_get_mesh_hdrlen(payload.flags);
++
++ if (likely(pskb_may_pull(skb, hdrlen + 8) &&
++ ieee80211_get_8023_tunnel_proto(skb->data + hdrlen,
++ &payload.eth.h_proto)))
++ hdrlen += ETH_ALEN + 2;
++ else if (!pskb_may_pull(skb, hdrlen))
++ return -EINVAL;
++
++ mesh_addr = skb->data + sizeof(payload.eth) + ETH_ALEN;
++ switch (payload.flags & MESH_FLAGS_AE) {
++ case MESH_FLAGS_AE_A4:
++ memcpy(&payload.eth.h_source, mesh_addr, ETH_ALEN);
++ break;
++ case MESH_FLAGS_AE_A5_A6:
++ memcpy(&payload.eth.h_dest, mesh_addr, 2 * ETH_ALEN);
++ break;
++ default:
++ break;
++ }
++
++ pskb_pull(skb, hdrlen - sizeof(payload.eth));
++ memcpy(skb->data, &payload.eth, sizeof(payload.eth));
++
++ return 0;
++}
++EXPORT_SYMBOL(ieee80211_strip_8023_mesh_hdr);
+
+ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+ const u8 *addr, enum nl80211_iftype iftype,
+@@ -568,7 +611,6 @@ int ieee80211_data_to_8023_exthdr(struct
+ } payload;
+ struct ethhdr tmp;
+ u16 hdrlen;
+- u8 mesh_flags = 0;
+
+ if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
+ return -1;
+@@ -589,12 +631,6 @@ int ieee80211_data_to_8023_exthdr(struct
+ memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
+ memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
+
+- if (iftype == NL80211_IFTYPE_MESH_POINT &&
+- skb_copy_bits(skb, hdrlen, &mesh_flags, 1) < 0)
+- return -1;
+-
+- mesh_flags &= MESH_FLAGS_AE;
+-
+ switch (hdr->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+ case cpu_to_le16(IEEE80211_FCTL_TODS):
+@@ -608,17 +644,6 @@ int ieee80211_data_to_8023_exthdr(struct
+ iftype != NL80211_IFTYPE_AP_VLAN &&
+ iftype != NL80211_IFTYPE_STATION))
+ return -1;
+- if (iftype == NL80211_IFTYPE_MESH_POINT) {
+- if (mesh_flags == MESH_FLAGS_AE_A4)
+- return -1;
+- if (mesh_flags == MESH_FLAGS_AE_A5_A6 &&
+- skb_copy_bits(skb, hdrlen +
+- offsetof(struct ieee80211s_hdr, eaddr1),
+- tmp.h_dest, 2 * ETH_ALEN) < 0)
+- return -1;
+-
+- hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
+- }
+ break;
+ case cpu_to_le16(IEEE80211_FCTL_FROMDS):
+ if ((iftype != NL80211_IFTYPE_STATION &&
+@@ -627,16 +652,6 @@ int ieee80211_data_to_8023_exthdr(struct
+ (is_multicast_ether_addr(tmp.h_dest) &&
+ ether_addr_equal(tmp.h_source, addr)))
+ return -1;
+- if (iftype == NL80211_IFTYPE_MESH_POINT) {
+- if (mesh_flags == MESH_FLAGS_AE_A5_A6)
+- return -1;
+- if (mesh_flags == MESH_FLAGS_AE_A4 &&
+- skb_copy_bits(skb, hdrlen +
+- offsetof(struct ieee80211s_hdr, eaddr1),
+- tmp.h_source, ETH_ALEN) < 0)
+- return -1;
+- hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
+- }
+ break;
+ case cpu_to_le16(0):
+ if (iftype != NL80211_IFTYPE_ADHOC &&
+@@ -646,7 +661,7 @@ int ieee80211_data_to_8023_exthdr(struct
+ break;
+ }
+
+- if (likely(!is_amsdu &&
++ if (likely(!is_amsdu && iftype != NL80211_IFTYPE_MESH_POINT &&
+ skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
+ ieee80211_get_8023_tunnel_proto(&payload, &tmp.h_proto))) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation */
+@@ -722,7 +737,8 @@ __ieee80211_amsdu_copy_frag(struct sk_bu
+
+ static struct sk_buff *
+ __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
+- int offset, int len, bool reuse_frag)
++ int offset, int len, bool reuse_frag,
++ int min_len)
+ {
+ struct sk_buff *frame;
+ int cur_len = len;
+@@ -736,7 +752,7 @@ __ieee80211_amsdu_copy(struct sk_buff *s
+ * in the stack later.
+ */
+ if (reuse_frag)
+- cur_len = min_t(int, len, 32);
++ cur_len = min_t(int, len, min_len);
+
+ /*
+ * Allocate and reserve two bytes more for payload
+@@ -746,6 +762,7 @@ __ieee80211_amsdu_copy(struct sk_buff *s
+ if (!frame)
+ return NULL;
+
++ frame->priority = skb->priority;
+ skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
+ skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
+
+@@ -762,23 +779,37 @@ __ieee80211_amsdu_copy(struct sk_buff *s
+ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
+ const u8 *addr, enum nl80211_iftype iftype,
+ const unsigned int extra_headroom,
+- const u8 *check_da, const u8 *check_sa)
++ const u8 *check_da, const u8 *check_sa,
++ bool mesh_control)
+ {
+ unsigned int hlen = ALIGN(extra_headroom, 4);
+ struct sk_buff *frame = NULL;
+ int offset = 0, remaining;
+- struct ethhdr eth;
++ struct {
++ struct ethhdr eth;
++ uint8_t flags;
++ } hdr;
+ bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
+ bool reuse_skb = false;
+ bool last = false;
++ int copy_len = sizeof(hdr.eth);
++
++ if (iftype == NL80211_IFTYPE_MESH_POINT)
++ copy_len = sizeof(hdr);
+
+ while (!last) {
+ unsigned int subframe_len;
+- int len;
++ int len, mesh_len = 0;
+ u8 padding;
+
+- skb_copy_bits(skb, offset, ð, sizeof(eth));
+- len = ntohs(eth.h_proto);
++ skb_copy_bits(skb, offset, &hdr, copy_len);
++ if (iftype == NL80211_IFTYPE_MESH_POINT)
++ mesh_len = __ieee80211_get_mesh_hdrlen(hdr.flags);
++ if (mesh_control)
++ len = le16_to_cpu(*(__le16 *)&hdr.eth.h_proto) + mesh_len;
++ else
++ len = ntohs(hdr.eth.h_proto);
++
+ subframe_len = sizeof(struct ethhdr) + len;
+ padding = (4 - subframe_len) & 0x3;
+
+@@ -787,16 +818,16 @@ void ieee80211_amsdu_to_8023s(struct sk_
+ if (subframe_len > remaining)
+ goto purge;
+ /* mitigate A-MSDU aggregation injection attacks */
+- if (ether_addr_equal(eth.h_dest, rfc1042_header))
++ if (ether_addr_equal(hdr.eth.h_dest, rfc1042_header))
+ goto purge;
+
+ offset += sizeof(struct ethhdr);
+ last = remaining <= subframe_len + padding;
+
+ /* FIXME: should we really accept multicast DA? */
+- if ((check_da && !is_multicast_ether_addr(eth.h_dest) &&
+- !ether_addr_equal(check_da, eth.h_dest)) ||
+- (check_sa && !ether_addr_equal(check_sa, eth.h_source))) {
++ if ((check_da && !is_multicast_ether_addr(hdr.eth.h_dest) &&
++ !ether_addr_equal(check_da, hdr.eth.h_dest)) ||
++ (check_sa && !ether_addr_equal(check_sa, hdr.eth.h_source))) {
+ offset += len + padding;
+ continue;
+ }
+@@ -808,7 +839,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
+ reuse_skb = true;
+ } else {
+ frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
+- reuse_frag);
++ reuse_frag, 32 + mesh_len);
+ if (!frame)
+ goto purge;
+
+@@ -819,10 +850,11 @@ void ieee80211_amsdu_to_8023s(struct sk_
+ frame->dev = skb->dev;
+ frame->priority = skb->priority;
+
+- if (likely(ieee80211_get_8023_tunnel_proto(frame->data, ð.h_proto)))
++ if (likely(iftype != NL80211_IFTYPE_MESH_POINT &&
++ ieee80211_get_8023_tunnel_proto(frame->data, &hdr.eth.h_proto)))
+ skb_pull(frame, ETH_ALEN + 2);
+
+- memcpy(skb_push(frame, sizeof(eth)), ð, sizeof(eth));
++ memcpy(skb_push(frame, sizeof(hdr.eth)), &hdr.eth, sizeof(hdr.eth));
+ __skb_queue_tail(list, frame);
+ }
+
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Tue, 6 Dec 2022 11:15:02 +0100
-Subject: [PATCH] wifi: mac80211: fix receiving A-MSDU frames on mesh
- interfaces
-
-The current mac80211 mesh A-MSDU receive path fails to parse A-MSDU packets
-on mesh interfaces, because it assumes that the Mesh Control field is always
-directly after the 802.11 header.
-802.11-2020 9.3.2.2.2 Figure 9-70 shows that the Mesh Control field is
-actually part of the A-MSDU subframe header.
-This makes more sense, since it allows packets for multiple different
-destinations to be included in the same A-MSDU, as long as RA and TID are
-still the same.
-Another issue is the fact that the A-MSDU subframe length field was apparently
-accidentally defined as little-endian in the standard.
-
-In order to fix this, the mesh forwarding path needs happen at a different
-point in the receive path.
-
-ieee80211_data_to_8023_exthdr is changed to ignore the mesh control field
-and leave it in after the ethernet header. This also affects the source/dest
-MAC address fields, which now in the case of mesh point to the mesh SA/DA.
-
-ieee80211_amsdu_to_8023s is changed to deal with the endian difference and
-to add the Mesh Control length to the subframe length, since it's not covered
-by the MSDU length field.
-
-With these changes, the mac80211 will get the same packet structure for
-converted regular data packets and unpacked A-MSDU subframes.
-
-The mesh forwarding checks are now only performed after the A-MSDU decap.
-For locally received packets, the Mesh Control header is stripped away.
-For forwarded packets, a new 802.11 header gets added.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
-+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
-@@ -33,7 +33,7 @@ static int mwifiex_11n_dispatch_amsdu_pk
- skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
-
- ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
-- priv->wdev.iftype, 0, NULL, NULL);
-+ priv->wdev.iftype, 0, NULL, NULL, false);
-
- while (!skb_queue_empty(&list)) {
- struct rx_packet_hdr *rx_hdr;
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -6208,11 +6208,36 @@ static inline int ieee80211_data_to_8023
- * @extra_headroom: The hardware extra headroom for SKBs in the @list.
- * @check_da: DA to check in the inner ethernet header, or NULL
- * @check_sa: SA to check in the inner ethernet header, or NULL
-+ * @mesh_control: A-MSDU subframe header includes the mesh control field
- */
- void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
- const u8 *addr, enum nl80211_iftype iftype,
- const unsigned int extra_headroom,
-- const u8 *check_da, const u8 *check_sa);
-+ const u8 *check_da, const u8 *check_sa,
-+ bool mesh_control);
-+
-+/**
-+ * ieee80211_get_8023_tunnel_proto - get RFC1042 or bridge tunnel encap protocol
-+ *
-+ * Check for RFC1042 or bridge tunnel header and fetch the encapsulated
-+ * protocol.
-+ *
-+ * @hdr: pointer to the MSDU payload
-+ * @proto: destination pointer to store the protocol
-+ * Return: true if encapsulation was found
-+ */
-+bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto);
-+
-+/**
-+ * ieee80211_strip_8023_mesh_hdr - strip mesh header from converted 802.3 frames
-+ *
-+ * Strip the mesh header, which was left in by ieee80211_data_to_8023 as part
-+ * of the MSDU data. Also move any source/destination addresses from the mesh
-+ * header to the ethernet header (if present).
-+ *
-+ * @skb: The 802.3 frame with embedded mesh header
-+ */
-+int ieee80211_strip_8023_mesh_hdr(struct sk_buff *skb);
-
- /**
- * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2720,6 +2720,174 @@ ieee80211_deliver_skb(struct ieee80211_r
- }
- }
-
-+static ieee80211_rx_result
-+ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta,
-+ struct sk_buff *skb)
-+{
-+#ifdef CPTCFG_MAC80211_MESH
-+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-+ struct ieee80211_local *local = sdata->local;
-+ uint16_t fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA;
-+ struct ieee80211_hdr hdr = {
-+ .frame_control = cpu_to_le16(fc)
-+ };
-+ struct ieee80211_hdr *fwd_hdr;
-+ struct ieee80211s_hdr *mesh_hdr;
-+ struct ieee80211_tx_info *info;
-+ struct sk_buff *fwd_skb;
-+ struct ethhdr *eth;
-+ bool multicast;
-+ int tailroom = 0;
-+ int hdrlen, mesh_hdrlen;
-+ u8 *qos;
-+
-+ if (!ieee80211_vif_is_mesh(&sdata->vif))
-+ return RX_CONTINUE;
-+
-+ if (!pskb_may_pull(skb, sizeof(*eth) + 6))
-+ return RX_DROP_MONITOR;
-+
-+ mesh_hdr = (struct ieee80211s_hdr *)(skb->data + sizeof(*eth));
-+ mesh_hdrlen = ieee80211_get_mesh_hdrlen(mesh_hdr);
-+
-+ if (!pskb_may_pull(skb, sizeof(*eth) + mesh_hdrlen))
-+ return RX_DROP_MONITOR;
-+
-+ eth = (struct ethhdr *)skb->data;
-+ multicast = is_multicast_ether_addr(eth->h_dest);
-+
-+ mesh_hdr = (struct ieee80211s_hdr *)(eth + 1);
-+ if (!mesh_hdr->ttl)
-+ return RX_DROP_MONITOR;
-+
-+ /* frame is in RMC, don't forward */
-+ if (is_multicast_ether_addr(eth->h_dest) &&
-+ mesh_rmc_check(sdata, eth->h_source, mesh_hdr))
-+ return RX_DROP_MONITOR;
-+
-+ /* Frame has reached destination. Don't forward */
-+ if (ether_addr_equal(sdata->vif.addr, eth->h_dest))
-+ goto rx_accept;
-+
-+ if (!ifmsh->mshcfg.dot11MeshForwarding) {
-+ if (is_multicast_ether_addr(eth->h_dest))
-+ goto rx_accept;
-+
-+ return RX_DROP_MONITOR;
-+ }
-+
-+ /* forward packet */
-+ if (sdata->crypto_tx_tailroom_needed_cnt)
-+ tailroom = IEEE80211_ENCRYPT_TAILROOM;
-+
-+ if (!--mesh_hdr->ttl) {
-+ if (multicast)
-+ goto rx_accept;
-+
-+ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl);
-+ return RX_DROP_MONITOR;
-+ }
-+
-+ if (mesh_hdr->flags & MESH_FLAGS_AE) {
-+ struct mesh_path *mppath;
-+ char *proxied_addr;
-+
-+ if (multicast)
-+ proxied_addr = mesh_hdr->eaddr1;
-+ else if ((mesh_hdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
-+ /* has_a4 already checked in ieee80211_rx_mesh_check */
-+ proxied_addr = mesh_hdr->eaddr2;
-+ else
-+ return RX_DROP_MONITOR;
-+
-+ rcu_read_lock();
-+ mppath = mpp_path_lookup(sdata, proxied_addr);
-+ if (!mppath) {
-+ mpp_path_add(sdata, proxied_addr, eth->h_source);
-+ } else {
-+ spin_lock_bh(&mppath->state_lock);
-+ if (!ether_addr_equal(mppath->mpp, eth->h_source))
-+ memcpy(mppath->mpp, eth->h_source, ETH_ALEN);
-+ mppath->exp_time = jiffies;
-+ spin_unlock_bh(&mppath->state_lock);
-+ }
-+ rcu_read_unlock();
-+ }
-+
-+ skb_set_queue_mapping(skb, ieee802_1d_to_ac[skb->priority]);
-+
-+ ieee80211_fill_mesh_addresses(&hdr, &hdr.frame_control,
-+ eth->h_dest, eth->h_source);
-+ hdrlen = ieee80211_hdrlen(hdr.frame_control);
-+ if (multicast) {
-+ int extra_head = sizeof(struct ieee80211_hdr) - sizeof(*eth);
-+
-+ fwd_skb = skb_copy_expand(skb, local->tx_headroom + extra_head +
-+ IEEE80211_ENCRYPT_HEADROOM,
-+ tailroom, GFP_ATOMIC);
-+ if (!fwd_skb)
-+ goto rx_accept;
-+ } else {
-+ fwd_skb = skb;
-+ skb = NULL;
-+
-+ if (skb_cow_head(fwd_skb, hdrlen - sizeof(struct ethhdr)))
-+ return RX_DROP_UNUSABLE;
-+ }
-+
-+ fwd_hdr = skb_push(fwd_skb, hdrlen - sizeof(struct ethhdr));
-+ memcpy(fwd_hdr, &hdr, hdrlen - 2);
-+ qos = ieee80211_get_qos_ctl(fwd_hdr);
-+ qos[0] = qos[1] = 0;
-+
-+ skb_reset_mac_header(fwd_skb);
-+ hdrlen += mesh_hdrlen;
-+ if (ieee80211_get_8023_tunnel_proto(fwd_skb->data + hdrlen,
-+ &fwd_skb->protocol))
-+ hdrlen += ETH_ALEN;
-+ else
-+ fwd_skb->protocol = htons(fwd_skb->len - hdrlen);
-+ skb_set_network_header(fwd_skb, hdrlen);
-+
-+ info = IEEE80211_SKB_CB(fwd_skb);
-+ memset(info, 0, sizeof(*info));
-+ info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
-+ info->control.vif = &sdata->vif;
-+ info->control.jiffies = jiffies;
-+ if (multicast) {
-+ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
-+ memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
-+ /* update power mode indication when forwarding */
-+ ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
-+ } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) {
-+ /* mesh power mode flags updated in mesh_nexthop_lookup */
-+ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
-+ } else {
-+ /* unable to resolve next hop */
-+ if (sta)
-+ mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
-+ hdr.addr3, 0,
-+ WLAN_REASON_MESH_PATH_NOFORWARD,
-+ sta->sta.addr);
-+ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
-+ kfree_skb(fwd_skb);
-+ goto rx_accept;
-+ }
-+
-+ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
-+ fwd_skb->dev = sdata->dev;
-+ ieee80211_add_pending_skb(local, fwd_skb);
-+
-+rx_accept:
-+ if (!skb)
-+ return RX_QUEUED;
-+
-+ ieee80211_strip_8023_mesh_hdr(skb);
-+#endif
-+
-+ return RX_CONTINUE;
-+}
-+
- static ieee80211_rx_result debug_noinline
- __ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx, u8 data_offset)
- {
-@@ -2728,8 +2896,10 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- __le16 fc = hdr->frame_control;
- struct sk_buff_head frame_list;
-+ static ieee80211_rx_result res;
- struct ethhdr ethhdr;
- const u8 *check_da = ethhdr.h_dest, *check_sa = ethhdr.h_source;
-+ bool mesh = false;
-
- if (unlikely(ieee80211_has_a4(hdr->frame_control))) {
- check_da = NULL;
-@@ -2746,6 +2916,8 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
- break;
- case NL80211_IFTYPE_MESH_POINT:
- check_sa = NULL;
-+ check_da = NULL;
-+ mesh = true;
- break;
- default:
- break;
-@@ -2763,17 +2935,29 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
- ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
- rx->sdata->vif.type,
- rx->local->hw.extra_tx_headroom,
-- check_da, check_sa);
-+ check_da, check_sa, mesh);
-
- while (!skb_queue_empty(&frame_list)) {
- rx->skb = __skb_dequeue(&frame_list);
-
-- if (!ieee80211_frame_allowed(rx, fc)) {
-- dev_kfree_skb(rx->skb);
-+ res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb);
-+ switch (res) {
-+ case RX_QUEUED:
- continue;
-+ case RX_CONTINUE:
-+ break;
-+ default:
-+ goto free;
- }
-
-+ if (!ieee80211_frame_allowed(rx, fc))
-+ goto free;
-+
- ieee80211_deliver_skb(rx);
-+ continue;
-+
-+free:
-+ dev_kfree_skb(rx->skb);
- }
-
- return RX_QUEUED;
-@@ -2806,6 +2990,8 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx
- if (!rx->sdata->u.mgd.use_4addr)
- return RX_DROP_UNUSABLE;
- break;
-+ case NL80211_IFTYPE_MESH_POINT:
-+ break;
- default:
- return RX_DROP_UNUSABLE;
- }
-@@ -2834,155 +3020,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx
- return __ieee80211_rx_h_amsdu(rx, 0);
- }
-
--#ifdef CPTCFG_MAC80211_MESH
--static ieee80211_rx_result
--ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
--{
-- struct ieee80211_hdr *fwd_hdr, *hdr;
-- struct ieee80211_tx_info *info;
-- struct ieee80211s_hdr *mesh_hdr;
-- struct sk_buff *skb = rx->skb, *fwd_skb;
-- struct ieee80211_local *local = rx->local;
-- struct ieee80211_sub_if_data *sdata = rx->sdata;
-- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-- u16 ac, q, hdrlen;
-- int tailroom = 0;
--
-- hdr = (struct ieee80211_hdr *) skb->data;
-- hdrlen = ieee80211_hdrlen(hdr->frame_control);
--
-- /* make sure fixed part of mesh header is there, also checks skb len */
-- if (!pskb_may_pull(rx->skb, hdrlen + 6))
-- return RX_DROP_MONITOR;
--
-- mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
--
-- /* make sure full mesh header is there, also checks skb len */
-- if (!pskb_may_pull(rx->skb,
-- hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr)))
-- return RX_DROP_MONITOR;
--
-- /* reload pointers */
-- hdr = (struct ieee80211_hdr *) skb->data;
-- mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
--
-- if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) {
-- int offset = hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr) +
-- sizeof(rfc1042_header);
-- __be16 ethertype;
--
-- if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr) ||
-- skb_copy_bits(rx->skb, offset, ðertype, 2) != 0 ||
-- ethertype != rx->sdata->control_port_protocol)
-- return RX_DROP_MONITOR;
-- }
--
-- /* frame is in RMC, don't forward */
-- if (ieee80211_is_data(hdr->frame_control) &&
-- is_multicast_ether_addr(hdr->addr1) &&
-- mesh_rmc_check(rx->sdata, hdr->addr3, mesh_hdr))
-- return RX_DROP_MONITOR;
--
-- if (!ieee80211_is_data(hdr->frame_control))
-- return RX_CONTINUE;
--
-- if (!mesh_hdr->ttl)
-- return RX_DROP_MONITOR;
--
-- if (mesh_hdr->flags & MESH_FLAGS_AE) {
-- struct mesh_path *mppath;
-- char *proxied_addr;
-- char *mpp_addr;
--
-- if (is_multicast_ether_addr(hdr->addr1)) {
-- mpp_addr = hdr->addr3;
-- proxied_addr = mesh_hdr->eaddr1;
-- } else if ((mesh_hdr->flags & MESH_FLAGS_AE) ==
-- MESH_FLAGS_AE_A5_A6) {
-- /* has_a4 already checked in ieee80211_rx_mesh_check */
-- mpp_addr = hdr->addr4;
-- proxied_addr = mesh_hdr->eaddr2;
-- } else {
-- return RX_DROP_MONITOR;
-- }
--
-- rcu_read_lock();
-- mppath = mpp_path_lookup(sdata, proxied_addr);
-- if (!mppath) {
-- mpp_path_add(sdata, proxied_addr, mpp_addr);
-- } else {
-- spin_lock_bh(&mppath->state_lock);
-- if (!ether_addr_equal(mppath->mpp, mpp_addr))
-- memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
-- mppath->exp_time = jiffies;
-- spin_unlock_bh(&mppath->state_lock);
-- }
-- rcu_read_unlock();
-- }
--
-- /* Frame has reached destination. Don't forward */
-- if (!is_multicast_ether_addr(hdr->addr1) &&
-- ether_addr_equal(sdata->vif.addr, hdr->addr3))
-- return RX_CONTINUE;
--
-- ac = ieee802_1d_to_ac[skb->priority];
-- skb_set_queue_mapping(skb, ac);
--
-- if (!--mesh_hdr->ttl) {
-- if (!is_multicast_ether_addr(hdr->addr1))
-- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh,
-- dropped_frames_ttl);
-- goto out;
-- }
--
-- if (!ifmsh->mshcfg.dot11MeshForwarding)
-- goto out;
--
-- if (sdata->crypto_tx_tailroom_needed_cnt)
-- tailroom = IEEE80211_ENCRYPT_TAILROOM;
--
-- fwd_skb = skb_copy_expand(skb, local->tx_headroom +
-- IEEE80211_ENCRYPT_HEADROOM,
-- tailroom, GFP_ATOMIC);
-- if (!fwd_skb)
-- goto out;
--
-- fwd_skb->dev = sdata->dev;
-- fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
-- fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
-- info = IEEE80211_SKB_CB(fwd_skb);
-- memset(info, 0, sizeof(*info));
-- info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
-- info->control.vif = &rx->sdata->vif;
-- info->control.jiffies = jiffies;
-- if (is_multicast_ether_addr(fwd_hdr->addr1)) {
-- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
-- memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
-- /* update power mode indication when forwarding */
-- ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
-- } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) {
-- /* mesh power mode flags updated in mesh_nexthop_lookup */
-- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
-- } else {
-- /* unable to resolve next hop */
-- mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
-- fwd_hdr->addr3, 0,
-- WLAN_REASON_MESH_PATH_NOFORWARD,
-- fwd_hdr->addr2);
-- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
-- kfree_skb(fwd_skb);
-- return RX_DROP_MONITOR;
-- }
--
-- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
-- ieee80211_add_pending_skb(local, fwd_skb);
-- out:
-- if (is_multicast_ether_addr(hdr->addr1))
-- return RX_CONTINUE;
-- return RX_DROP_MONITOR;
--}
--#endif
--
- static ieee80211_rx_result debug_noinline
- ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
- {
-@@ -2991,6 +3028,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_
- struct net_device *dev = sdata->dev;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
- __le16 fc = hdr->frame_control;
-+ static ieee80211_rx_result res;
- bool port_control;
- int err;
-
-@@ -3017,6 +3055,10 @@ ieee80211_rx_h_data(struct ieee80211_rx_
- if (unlikely(err))
- return RX_DROP_UNUSABLE;
-
-+ res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb);
-+ if (res != RX_CONTINUE)
-+ return res;
-+
- if (!ieee80211_frame_allowed(rx, fc))
- return RX_DROP_MONITOR;
-
-@@ -3987,10 +4029,6 @@ static void ieee80211_rx_handlers(struct
- CALL_RXH(ieee80211_rx_h_defragment);
- CALL_RXH(ieee80211_rx_h_michael_mic_verify);
- /* must be after MMIC verify so header is counted in MPDU mic */
--#ifdef CPTCFG_MAC80211_MESH
-- if (ieee80211_vif_is_mesh(&rx->sdata->vif))
-- CALL_RXH(ieee80211_rx_h_mesh_fwding);
--#endif
- CALL_RXH(ieee80211_rx_h_amsdu);
- CALL_RXH(ieee80211_rx_h_data);
-
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -542,7 +542,7 @@ unsigned int ieee80211_get_mesh_hdrlen(s
- }
- EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
-
--static bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto)
-+bool ieee80211_get_8023_tunnel_proto(const void *hdr, __be16 *proto)
- {
- const __be16 *hdr_proto = hdr + ETH_ALEN;
-
-@@ -556,6 +556,49 @@ static bool ieee80211_get_8023_tunnel_pr
-
- return true;
- }
-+EXPORT_SYMBOL(ieee80211_get_8023_tunnel_proto);
-+
-+int ieee80211_strip_8023_mesh_hdr(struct sk_buff *skb)
-+{
-+ const void *mesh_addr;
-+ struct {
-+ struct ethhdr eth;
-+ u8 flags;
-+ } payload;
-+ int hdrlen;
-+ int ret;
-+
-+ ret = skb_copy_bits(skb, 0, &payload, sizeof(payload));
-+ if (ret)
-+ return ret;
-+
-+ hdrlen = sizeof(payload.eth) + __ieee80211_get_mesh_hdrlen(payload.flags);
-+
-+ if (likely(pskb_may_pull(skb, hdrlen + 8) &&
-+ ieee80211_get_8023_tunnel_proto(skb->data + hdrlen,
-+ &payload.eth.h_proto)))
-+ hdrlen += ETH_ALEN + 2;
-+ else if (!pskb_may_pull(skb, hdrlen))
-+ return -EINVAL;
-+
-+ mesh_addr = skb->data + sizeof(payload.eth) + ETH_ALEN;
-+ switch (payload.flags & MESH_FLAGS_AE) {
-+ case MESH_FLAGS_AE_A4:
-+ memcpy(&payload.eth.h_source, mesh_addr, ETH_ALEN);
-+ break;
-+ case MESH_FLAGS_AE_A5_A6:
-+ memcpy(&payload.eth.h_dest, mesh_addr, 2 * ETH_ALEN);
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ pskb_pull(skb, hdrlen - sizeof(payload.eth));
-+ memcpy(skb->data, &payload.eth, sizeof(payload.eth));
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL(ieee80211_strip_8023_mesh_hdr);
-
- int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
- const u8 *addr, enum nl80211_iftype iftype,
-@@ -568,7 +611,6 @@ int ieee80211_data_to_8023_exthdr(struct
- } payload;
- struct ethhdr tmp;
- u16 hdrlen;
-- u8 mesh_flags = 0;
-
- if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
- return -1;
-@@ -589,12 +631,6 @@ int ieee80211_data_to_8023_exthdr(struct
- memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
- memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
-
-- if (iftype == NL80211_IFTYPE_MESH_POINT &&
-- skb_copy_bits(skb, hdrlen, &mesh_flags, 1) < 0)
-- return -1;
--
-- mesh_flags &= MESH_FLAGS_AE;
--
- switch (hdr->frame_control &
- cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
- case cpu_to_le16(IEEE80211_FCTL_TODS):
-@@ -608,17 +644,6 @@ int ieee80211_data_to_8023_exthdr(struct
- iftype != NL80211_IFTYPE_AP_VLAN &&
- iftype != NL80211_IFTYPE_STATION))
- return -1;
-- if (iftype == NL80211_IFTYPE_MESH_POINT) {
-- if (mesh_flags == MESH_FLAGS_AE_A4)
-- return -1;
-- if (mesh_flags == MESH_FLAGS_AE_A5_A6 &&
-- skb_copy_bits(skb, hdrlen +
-- offsetof(struct ieee80211s_hdr, eaddr1),
-- tmp.h_dest, 2 * ETH_ALEN) < 0)
-- return -1;
--
-- hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
-- }
- break;
- case cpu_to_le16(IEEE80211_FCTL_FROMDS):
- if ((iftype != NL80211_IFTYPE_STATION &&
-@@ -627,16 +652,6 @@ int ieee80211_data_to_8023_exthdr(struct
- (is_multicast_ether_addr(tmp.h_dest) &&
- ether_addr_equal(tmp.h_source, addr)))
- return -1;
-- if (iftype == NL80211_IFTYPE_MESH_POINT) {
-- if (mesh_flags == MESH_FLAGS_AE_A5_A6)
-- return -1;
-- if (mesh_flags == MESH_FLAGS_AE_A4 &&
-- skb_copy_bits(skb, hdrlen +
-- offsetof(struct ieee80211s_hdr, eaddr1),
-- tmp.h_source, ETH_ALEN) < 0)
-- return -1;
-- hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
-- }
- break;
- case cpu_to_le16(0):
- if (iftype != NL80211_IFTYPE_ADHOC &&
-@@ -646,7 +661,7 @@ int ieee80211_data_to_8023_exthdr(struct
- break;
- }
-
-- if (likely(!is_amsdu &&
-+ if (likely(!is_amsdu && iftype != NL80211_IFTYPE_MESH_POINT &&
- skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)) == 0 &&
- ieee80211_get_8023_tunnel_proto(&payload, &tmp.h_proto))) {
- /* remove RFC1042 or Bridge-Tunnel encapsulation */
-@@ -722,7 +737,8 @@ __ieee80211_amsdu_copy_frag(struct sk_bu
-
- static struct sk_buff *
- __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
-- int offset, int len, bool reuse_frag)
-+ int offset, int len, bool reuse_frag,
-+ int min_len)
- {
- struct sk_buff *frame;
- int cur_len = len;
-@@ -736,7 +752,7 @@ __ieee80211_amsdu_copy(struct sk_buff *s
- * in the stack later.
- */
- if (reuse_frag)
-- cur_len = min_t(int, len, 32);
-+ cur_len = min_t(int, len, min_len);
-
- /*
- * Allocate and reserve two bytes more for payload
-@@ -746,6 +762,7 @@ __ieee80211_amsdu_copy(struct sk_buff *s
- if (!frame)
- return NULL;
-
-+ frame->priority = skb->priority;
- skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
- skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
-
-@@ -762,23 +779,37 @@ __ieee80211_amsdu_copy(struct sk_buff *s
- void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
- const u8 *addr, enum nl80211_iftype iftype,
- const unsigned int extra_headroom,
-- const u8 *check_da, const u8 *check_sa)
-+ const u8 *check_da, const u8 *check_sa,
-+ bool mesh_control)
- {
- unsigned int hlen = ALIGN(extra_headroom, 4);
- struct sk_buff *frame = NULL;
- int offset = 0, remaining;
-- struct ethhdr eth;
-+ struct {
-+ struct ethhdr eth;
-+ uint8_t flags;
-+ } hdr;
- bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
- bool reuse_skb = false;
- bool last = false;
-+ int copy_len = sizeof(hdr.eth);
-+
-+ if (iftype == NL80211_IFTYPE_MESH_POINT)
-+ copy_len = sizeof(hdr);
-
- while (!last) {
- unsigned int subframe_len;
-- int len;
-+ int len, mesh_len = 0;
- u8 padding;
-
-- skb_copy_bits(skb, offset, ð, sizeof(eth));
-- len = ntohs(eth.h_proto);
-+ skb_copy_bits(skb, offset, &hdr, copy_len);
-+ if (iftype == NL80211_IFTYPE_MESH_POINT)
-+ mesh_len = __ieee80211_get_mesh_hdrlen(hdr.flags);
-+ if (mesh_control)
-+ len = le16_to_cpu(*(__le16 *)&hdr.eth.h_proto) + mesh_len;
-+ else
-+ len = ntohs(hdr.eth.h_proto);
-+
- subframe_len = sizeof(struct ethhdr) + len;
- padding = (4 - subframe_len) & 0x3;
-
-@@ -787,16 +818,16 @@ void ieee80211_amsdu_to_8023s(struct sk_
- if (subframe_len > remaining)
- goto purge;
- /* mitigate A-MSDU aggregation injection attacks */
-- if (ether_addr_equal(eth.h_dest, rfc1042_header))
-+ if (ether_addr_equal(hdr.eth.h_dest, rfc1042_header))
- goto purge;
-
- offset += sizeof(struct ethhdr);
- last = remaining <= subframe_len + padding;
-
- /* FIXME: should we really accept multicast DA? */
-- if ((check_da && !is_multicast_ether_addr(eth.h_dest) &&
-- !ether_addr_equal(check_da, eth.h_dest)) ||
-- (check_sa && !ether_addr_equal(check_sa, eth.h_source))) {
-+ if ((check_da && !is_multicast_ether_addr(hdr.eth.h_dest) &&
-+ !ether_addr_equal(check_da, hdr.eth.h_dest)) ||
-+ (check_sa && !ether_addr_equal(check_sa, hdr.eth.h_source))) {
- offset += len + padding;
- continue;
- }
-@@ -808,7 +839,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
- reuse_skb = true;
- } else {
- frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
-- reuse_frag);
-+ reuse_frag, 32 + mesh_len);
- if (!frame)
- goto purge;
-
-@@ -819,10 +850,11 @@ void ieee80211_amsdu_to_8023s(struct sk_
- frame->dev = skb->dev;
- frame->priority = skb->priority;
-
-- if (likely(ieee80211_get_8023_tunnel_proto(frame->data, ð.h_proto)))
-+ if (likely(iftype != NL80211_IFTYPE_MESH_POINT &&
-+ ieee80211_get_8023_tunnel_proto(frame->data, &hdr.eth.h_proto)))
- skb_pull(frame, ETH_ALEN + 2);
-
-- memcpy(skb_push(frame, sizeof(eth)), ð, sizeof(eth));
-+ memcpy(skb_push(frame, sizeof(hdr.eth)), &hdr.eth, sizeof(hdr.eth));
- __skb_queue_tail(list, frame);
- }
-
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 9 Dec 2022 21:15:04 +0100
+Subject: [PATCH] wifi: mac80211: add a workaround for receiving
+ non-standard mesh A-MSDU
+
+At least ath10k and ath11k supported hardware (maybe more) does not implement
+mesh A-MSDU aggregation in a standard compliant way.
+802.11-2020 9.3.2.2.2 declares that the Mesh Control field is part of the
+A-MSDU header. As such, its length must not be included in the subframe
+length field.
+Hardware affected by this bug treats the mesh control field as part of the
+MSDU data and sets the length accordingly.
+In order to avoid packet loss, keep track of which stations are affected
+by this and take it into account when converting A-MSDU to 802.3 + mesh control
+packets.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -6194,6 +6194,19 @@ static inline int ieee80211_data_to_8023
+ }
+
+ /**
++ * ieee80211_is_valid_amsdu - check if subframe lengths of an A-MSDU are valid
++ *
++ * This is used to detect non-standard A-MSDU frames, e.g. the ones generated
++ * by ath10k and ath11k, where the subframe length includes the length of the
++ * mesh control field.
++ *
++ * @skb: The input A-MSDU frame without any headers.
++ * @mesh_hdr: use standard compliant mesh A-MSDU subframe header
++ * Returns: true if subframe header lengths are valid for the @mesh_hdr mode
++ */
++bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr);
++
++/**
+ * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
+ *
+ * Decode an IEEE 802.11 A-MSDU and convert it to a list of 802.3 frames.
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2899,7 +2899,6 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
+ static ieee80211_rx_result res;
+ struct ethhdr ethhdr;
+ const u8 *check_da = ethhdr.h_dest, *check_sa = ethhdr.h_source;
+- bool mesh = false;
+
+ if (unlikely(ieee80211_has_a4(hdr->frame_control))) {
+ check_da = NULL;
+@@ -2917,7 +2916,6 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
+ case NL80211_IFTYPE_MESH_POINT:
+ check_sa = NULL;
+ check_da = NULL;
+- mesh = true;
+ break;
+ default:
+ break;
+@@ -2932,10 +2930,21 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
+ data_offset, true))
+ return RX_DROP_UNUSABLE;
+
++ if (rx->sta && rx->sta->amsdu_mesh_control < 0) {
++ bool valid_std = ieee80211_is_valid_amsdu(skb, true);
++ bool valid_nonstd = ieee80211_is_valid_amsdu(skb, false);
++
++ if (valid_std && !valid_nonstd)
++ rx->sta->amsdu_mesh_control = 1;
++ else if (valid_nonstd && !valid_std)
++ rx->sta->amsdu_mesh_control = 0;
++ }
++
+ ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
+ rx->sdata->vif.type,
+ rx->local->hw.extra_tx_headroom,
+- check_da, check_sa, mesh);
++ check_da, check_sa,
++ rx->sta->amsdu_mesh_control);
+
+ while (!skb_queue_empty(&frame_list)) {
+ rx->skb = __skb_dequeue(&frame_list);
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -591,6 +591,9 @@ __sta_info_alloc(struct ieee80211_sub_if
+
+ sta->sta_state = IEEE80211_STA_NONE;
+
++ if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
++ sta->amsdu_mesh_control = -1;
++
+ /* Mark TID as unreserved */
+ sta->reserved_tid = IEEE80211_TID_UNRESERVED;
+
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -702,6 +702,7 @@ struct sta_info {
+ struct codel_params cparams;
+
+ u8 reserved_tid;
++ s8 amsdu_mesh_control;
+
+ struct cfg80211_chan_def tdls_chandef;
+
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -776,6 +776,38 @@ __ieee80211_amsdu_copy(struct sk_buff *s
+ return frame;
+ }
+
++bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr)
++{
++ int offset = 0, remaining, subframe_len, padding;
++
++ for (offset = 0; offset < skb->len; offset += subframe_len + padding) {
++ struct {
++ __be16 len;
++ u8 mesh_flags;
++ } hdr;
++ u16 len;
++
++ if (skb_copy_bits(skb, offset + 2 * ETH_ALEN, &hdr, sizeof(hdr)) < 0)
++ return false;
++
++ if (mesh_hdr)
++ len = le16_to_cpu(*(__le16 *)&hdr.len) +
++ __ieee80211_get_mesh_hdrlen(hdr.mesh_flags);
++ else
++ len = ntohs(hdr.len);
++
++ subframe_len = sizeof(struct ethhdr) + len;
++ padding = (4 - subframe_len) & 0x3;
++ remaining = skb->len - offset;
++
++ if (subframe_len > remaining)
++ return false;
++ }
++
++ return true;
++}
++EXPORT_SYMBOL(ieee80211_is_valid_amsdu);
++
+ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
+ const u8 *addr, enum nl80211_iftype iftype,
+ const unsigned int extra_headroom,
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 9 Dec 2022 21:15:04 +0100
-Subject: [PATCH] wifi: mac80211: add a workaround for receiving
- non-standard mesh A-MSDU
-
-At least ath10k and ath11k supported hardware (maybe more) does not implement
-mesh A-MSDU aggregation in a standard compliant way.
-802.11-2020 9.3.2.2.2 declares that the Mesh Control field is part of the
-A-MSDU header. As such, its length must not be included in the subframe
-length field.
-Hardware affected by this bug treats the mesh control field as part of the
-MSDU data and sets the length accordingly.
-In order to avoid packet loss, keep track of which stations are affected
-by this and take it into account when converting A-MSDU to 802.3 + mesh control
-packets.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -6194,6 +6194,19 @@ static inline int ieee80211_data_to_8023
- }
-
- /**
-+ * ieee80211_is_valid_amsdu - check if subframe lengths of an A-MSDU are valid
-+ *
-+ * This is used to detect non-standard A-MSDU frames, e.g. the ones generated
-+ * by ath10k and ath11k, where the subframe length includes the length of the
-+ * mesh control field.
-+ *
-+ * @skb: The input A-MSDU frame without any headers.
-+ * @mesh_hdr: use standard compliant mesh A-MSDU subframe header
-+ * Returns: true if subframe header lengths are valid for the @mesh_hdr mode
-+ */
-+bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr);
-+
-+/**
- * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
- *
- * Decode an IEEE 802.11 A-MSDU and convert it to a list of 802.3 frames.
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -2899,7 +2899,6 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
- static ieee80211_rx_result res;
- struct ethhdr ethhdr;
- const u8 *check_da = ethhdr.h_dest, *check_sa = ethhdr.h_source;
-- bool mesh = false;
-
- if (unlikely(ieee80211_has_a4(hdr->frame_control))) {
- check_da = NULL;
-@@ -2917,7 +2916,6 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
- case NL80211_IFTYPE_MESH_POINT:
- check_sa = NULL;
- check_da = NULL;
-- mesh = true;
- break;
- default:
- break;
-@@ -2932,10 +2930,21 @@ __ieee80211_rx_h_amsdu(struct ieee80211_
- data_offset, true))
- return RX_DROP_UNUSABLE;
-
-+ if (rx->sta && rx->sta->amsdu_mesh_control < 0) {
-+ bool valid_std = ieee80211_is_valid_amsdu(skb, true);
-+ bool valid_nonstd = ieee80211_is_valid_amsdu(skb, false);
-+
-+ if (valid_std && !valid_nonstd)
-+ rx->sta->amsdu_mesh_control = 1;
-+ else if (valid_nonstd && !valid_std)
-+ rx->sta->amsdu_mesh_control = 0;
-+ }
-+
- ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
- rx->sdata->vif.type,
- rx->local->hw.extra_tx_headroom,
-- check_da, check_sa, mesh);
-+ check_da, check_sa,
-+ rx->sta->amsdu_mesh_control);
-
- while (!skb_queue_empty(&frame_list)) {
- rx->skb = __skb_dequeue(&frame_list);
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -591,6 +591,9 @@ __sta_info_alloc(struct ieee80211_sub_if
-
- sta->sta_state = IEEE80211_STA_NONE;
-
-+ if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
-+ sta->amsdu_mesh_control = -1;
-+
- /* Mark TID as unreserved */
- sta->reserved_tid = IEEE80211_TID_UNRESERVED;
-
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -702,6 +702,7 @@ struct sta_info {
- struct codel_params cparams;
-
- u8 reserved_tid;
-+ s8 amsdu_mesh_control;
-
- struct cfg80211_chan_def tdls_chandef;
-
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -776,6 +776,38 @@ __ieee80211_amsdu_copy(struct sk_buff *s
- return frame;
- }
-
-+bool ieee80211_is_valid_amsdu(struct sk_buff *skb, bool mesh_hdr)
-+{
-+ int offset = 0, remaining, subframe_len, padding;
-+
-+ for (offset = 0; offset < skb->len; offset += subframe_len + padding) {
-+ struct {
-+ __be16 len;
-+ u8 mesh_flags;
-+ } hdr;
-+ u16 len;
-+
-+ if (skb_copy_bits(skb, offset + 2 * ETH_ALEN, &hdr, sizeof(hdr)) < 0)
-+ return false;
-+
-+ if (mesh_hdr)
-+ len = le16_to_cpu(*(__le16 *)&hdr.len) +
-+ __ieee80211_get_mesh_hdrlen(hdr.mesh_flags);
-+ else
-+ len = ntohs(hdr.len);
-+
-+ subframe_len = sizeof(struct ethhdr) + len;
-+ padding = (4 - subframe_len) & 0x3;
-+ remaining = skb->len - offset;
-+
-+ if (subframe_len > remaining)
-+ return false;
-+ }
-+
-+ return true;
-+}
-+EXPORT_SYMBOL(ieee80211_is_valid_amsdu);
-+
- void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
- const u8 *addr, enum nl80211_iftype iftype,
- const unsigned int extra_headroom,
--- /dev/null
+From: Muna Sinada <quic_msinada@quicinc.com>
+Date: Wed, 5 Oct 2022 14:54:45 -0700
+Subject: [PATCH] wifi: mac80211: Add VHT MU-MIMO related flags in
+ ieee80211_bss_conf
+
+Adding flags for SU Beamformer, SU Beamformee, MU Beamformer and
+MU Beamformee for VHT. This is utilized to pass MU-MIMO
+configurations from user space to driver in AP mode.
+
+Signed-off-by: Muna Sinada <quic_msinada@quicinc.com>
+Link: https://lore.kernel.org/r/1665006886-23874-1-git-send-email-quic_msinada@quicinc.com
+[fixed indentation, removed redundant !!]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -653,6 +653,14 @@ struct ieee80211_fils_discovery {
+ * write-protected by sdata_lock and local->mtx so holding either is fine
+ * for read access.
+ * @color_change_color: the bss color that will be used after the change.
++ * @vht_su_beamformer: in AP mode, does this BSS support operation as an VHT SU
++ * beamformer
++ * @vht_su_beamformee: in AP mode, does this BSS support operation as an VHT SU
++ * beamformee
++ * @vht_mu_beamformer: in AP mode, does this BSS support operation as an VHT MU
++ * beamformer
++ * @vht_mu_beamformee: in AP mode, does this BSS support operation as an VHT MU
++ * beamformee
+ */
+ struct ieee80211_bss_conf {
+ const u8 *bssid;
+@@ -726,6 +734,11 @@ struct ieee80211_bss_conf {
+
+ bool color_change_active;
+ u8 color_change_color;
++
++ bool vht_su_beamformer;
++ bool vht_su_beamformee;
++ bool vht_mu_beamformer;
++ bool vht_mu_beamformee;
+ };
+
+ /**
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1251,6 +1251,21 @@ static int ieee80211_start_ap(struct wip
+ prev_beacon_int = link_conf->beacon_int;
+ link_conf->beacon_int = params->beacon_interval;
+
++ if (params->vht_cap) {
++ link_conf->vht_su_beamformer =
++ params->vht_cap->vht_cap_info &
++ cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
++ link_conf->vht_su_beamformee =
++ params->vht_cap->vht_cap_info &
++ cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
++ link_conf->vht_mu_beamformer =
++ params->vht_cap->vht_cap_info &
++ cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
++ link_conf->vht_mu_beamformee =
++ params->vht_cap->vht_cap_info &
++ cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
++ }
++
+ if (params->he_cap && params->he_oper) {
+ link_conf->he_support = true;
+ link_conf->htc_trig_based_pkt_ext =
+++ /dev/null
-From: Muna Sinada <quic_msinada@quicinc.com>
-Date: Wed, 5 Oct 2022 14:54:45 -0700
-Subject: [PATCH] wifi: mac80211: Add VHT MU-MIMO related flags in
- ieee80211_bss_conf
-
-Adding flags for SU Beamformer, SU Beamformee, MU Beamformer and
-MU Beamformee for VHT. This is utilized to pass MU-MIMO
-configurations from user space to driver in AP mode.
-
-Signed-off-by: Muna Sinada <quic_msinada@quicinc.com>
-Link: https://lore.kernel.org/r/1665006886-23874-1-git-send-email-quic_msinada@quicinc.com
-[fixed indentation, removed redundant !!]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -653,6 +653,14 @@ struct ieee80211_fils_discovery {
- * write-protected by sdata_lock and local->mtx so holding either is fine
- * for read access.
- * @color_change_color: the bss color that will be used after the change.
-+ * @vht_su_beamformer: in AP mode, does this BSS support operation as an VHT SU
-+ * beamformer
-+ * @vht_su_beamformee: in AP mode, does this BSS support operation as an VHT SU
-+ * beamformee
-+ * @vht_mu_beamformer: in AP mode, does this BSS support operation as an VHT MU
-+ * beamformer
-+ * @vht_mu_beamformee: in AP mode, does this BSS support operation as an VHT MU
-+ * beamformee
- */
- struct ieee80211_bss_conf {
- const u8 *bssid;
-@@ -726,6 +734,11 @@ struct ieee80211_bss_conf {
-
- bool color_change_active;
- u8 color_change_color;
-+
-+ bool vht_su_beamformer;
-+ bool vht_su_beamformee;
-+ bool vht_mu_beamformer;
-+ bool vht_mu_beamformee;
- };
-
- /**
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1251,6 +1251,21 @@ static int ieee80211_start_ap(struct wip
- prev_beacon_int = link_conf->beacon_int;
- link_conf->beacon_int = params->beacon_interval;
-
-+ if (params->vht_cap) {
-+ link_conf->vht_su_beamformer =
-+ params->vht_cap->vht_cap_info &
-+ cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
-+ link_conf->vht_su_beamformee =
-+ params->vht_cap->vht_cap_info &
-+ cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
-+ link_conf->vht_mu_beamformer =
-+ params->vht_cap->vht_cap_info &
-+ cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
-+ link_conf->vht_mu_beamformee =
-+ params->vht_cap->vht_cap_info &
-+ cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
-+ }
-+
- if (params->he_cap && params->he_oper) {
- link_conf->he_support = true;
- link_conf->htc_trig_based_pkt_ext =
--- /dev/null
+From: Muna Sinada <quic_msinada@quicinc.com>
+Date: Wed, 5 Oct 2022 14:54:46 -0700
+Subject: [PATCH] wifi: mac80211: Add HE MU-MIMO related flags in
+ ieee80211_bss_conf
+
+Adding flags for SU Beamformer, SU Beamformee, MU Beamformer and Full
+Bandwidth UL MU-MIMO for HE. This is utilized to pass MU-MIMO
+configurations from user space to driver in AP mode.
+
+Signed-off-by: Muna Sinada <quic_msinada@quicinc.com>
+Link: https://lore.kernel.org/r/1665006886-23874-2-git-send-email-quic_msinada@quicinc.com
+[fixed indentation, removed redundant !!]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -661,6 +661,15 @@ struct ieee80211_fils_discovery {
+ * beamformer
+ * @vht_mu_beamformee: in AP mode, does this BSS support operation as an VHT MU
+ * beamformee
++ * @he_su_beamformer: in AP-mode, does this BSS support operation as an HE SU
++ * beamformer
++ * @he_su_beamformee: in AP-mode, does this BSS support operation as an HE SU
++ * beamformee
++ * @he_mu_beamformer: in AP-mode, does this BSS support operation as an HE MU
++ * beamformer
++ * @he_full_ul_mumimo: does this BSS support the reception (AP) or transmission
++ * (non-AP STA) of an HE TB PPDU on an RU that spans the entire PPDU
++ * bandwidth
+ */
+ struct ieee80211_bss_conf {
+ const u8 *bssid;
+@@ -739,6 +748,10 @@ struct ieee80211_bss_conf {
+ bool vht_su_beamformee;
+ bool vht_mu_beamformer;
+ bool vht_mu_beamformee;
++ bool he_su_beamformer;
++ bool he_su_beamformee;
++ bool he_mu_beamformer;
++ bool he_full_ul_mumimo;
+ };
+
+ /**
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1280,6 +1280,21 @@ static int ieee80211_start_ap(struct wip
+ changed |= BSS_CHANGED_HE_BSS_COLOR;
+ }
+
++ if (params->he_cap) {
++ link_conf->he_su_beamformer =
++ params->he_cap->phy_cap_info[3] &
++ IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
++ link_conf->he_su_beamformee =
++ params->he_cap->phy_cap_info[4] &
++ IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
++ link_conf->he_mu_beamformer =
++ params->he_cap->phy_cap_info[4] &
++ IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
++ link_conf->he_full_ul_mumimo =
++ params->he_cap->phy_cap_info[2] &
++ IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
++ }
++
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ params->mbssid_config.tx_wdev) {
+ err = ieee80211_set_ap_mbssid_options(sdata,
+++ /dev/null
-From: Muna Sinada <quic_msinada@quicinc.com>
-Date: Wed, 5 Oct 2022 14:54:46 -0700
-Subject: [PATCH] wifi: mac80211: Add HE MU-MIMO related flags in
- ieee80211_bss_conf
-
-Adding flags for SU Beamformer, SU Beamformee, MU Beamformer and Full
-Bandwidth UL MU-MIMO for HE. This is utilized to pass MU-MIMO
-configurations from user space to driver in AP mode.
-
-Signed-off-by: Muna Sinada <quic_msinada@quicinc.com>
-Link: https://lore.kernel.org/r/1665006886-23874-2-git-send-email-quic_msinada@quicinc.com
-[fixed indentation, removed redundant !!]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -661,6 +661,15 @@ struct ieee80211_fils_discovery {
- * beamformer
- * @vht_mu_beamformee: in AP mode, does this BSS support operation as an VHT MU
- * beamformee
-+ * @he_su_beamformer: in AP-mode, does this BSS support operation as an HE SU
-+ * beamformer
-+ * @he_su_beamformee: in AP-mode, does this BSS support operation as an HE SU
-+ * beamformee
-+ * @he_mu_beamformer: in AP-mode, does this BSS support operation as an HE MU
-+ * beamformer
-+ * @he_full_ul_mumimo: does this BSS support the reception (AP) or transmission
-+ * (non-AP STA) of an HE TB PPDU on an RU that spans the entire PPDU
-+ * bandwidth
- */
- struct ieee80211_bss_conf {
- const u8 *bssid;
-@@ -739,6 +748,10 @@ struct ieee80211_bss_conf {
- bool vht_su_beamformee;
- bool vht_mu_beamformer;
- bool vht_mu_beamformee;
-+ bool he_su_beamformer;
-+ bool he_su_beamformee;
-+ bool he_mu_beamformer;
-+ bool he_full_ul_mumimo;
- };
-
- /**
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1280,6 +1280,21 @@ static int ieee80211_start_ap(struct wip
- changed |= BSS_CHANGED_HE_BSS_COLOR;
- }
-
-+ if (params->he_cap) {
-+ link_conf->he_su_beamformer =
-+ params->he_cap->phy_cap_info[3] &
-+ IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
-+ link_conf->he_su_beamformee =
-+ params->he_cap->phy_cap_info[4] &
-+ IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
-+ link_conf->he_mu_beamformer =
-+ params->he_cap->phy_cap_info[4] &
-+ IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
-+ link_conf->he_full_ul_mumimo =
-+ params->he_cap->phy_cap_info[2] &
-+ IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
-+ }
-+
- if (sdata->vif.type == NL80211_IFTYPE_AP &&
- params->mbssid_config.tx_wdev) {
- err = ieee80211_set_ap_mbssid_options(sdata,