mac80211: merge pending fixes for tx queueing issues
authorFelix Fietkau <nbd@nbd.name>
Thu, 15 Sep 2022 14:38:18 +0000 (16:38 +0200)
committerFelix Fietkau <nbd@nbd.name>
Thu, 15 Sep 2022 14:43:04 +0000 (16:43 +0200)
Fixes a potential deadlock and a tx queue hang on STA assoc

Signed-off-by: Felix Fietkau <nbd@nbd.name>
package/kernel/mac80211/patches/subsys/341-mac80211-Fix-deadlock-Don-t-start-TX-while-holding-f.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/342-mac80211-Ensure-vif-queues-are-operational-after-sta.patch [new file with mode: 0644]

diff --git a/package/kernel/mac80211/patches/subsys/341-mac80211-Fix-deadlock-Don-t-start-TX-while-holding-f.patch b/package/kernel/mac80211/patches/subsys/341-mac80211-Fix-deadlock-Don-t-start-TX-while-holding-f.patch
new file mode 100644 (file)
index 0000000..8c56acb
--- /dev/null
@@ -0,0 +1,40 @@
+From: Alexander Wetzel <alexander@wetzel-home.de>
+Date: Thu, 15 Sep 2022 14:41:20 +0200
+Subject: [PATCH] mac80211: Fix deadlock: Don't start TX while holding
+ fq->lock
+
+ieee80211_txq_purge() calls fq_tin_reset() and
+ieee80211_purge_tx_queue(); Both are then calling
+ieee80211_free_txskb(). Which can decide to TX the skb again.
+
+There are at least two ways to get a deadlock:
+
+1) When we have a TDLS teardown packet queued in either tin or frags
+   ieee80211_tdls_td_tx_handle() will call ieee80211_subif_start_xmit()
+   while we still hold fq->lock. ieee80211_txq_enqueue() will thus
+   deadlock.
+
+2) A variant of the above happens if aggregation is up and running:
+   In that case ieee80211_iface_work() will deadlock with the original
+   task: The original tasks already holds fq->lock and tries to get
+   sta->lock after kicking off ieee80211_iface_work(). But the worker
+   can get sta->lock prior to the original task and will then spin for
+   fq->lock.
+
+Avoid these deadlocks by not sending out any skbs when called via
+ieee80211_free_txskb().
+
+Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -698,7 +698,7 @@ static void ieee80211_report_used_skb(st
+               if (!sdata) {
+                       skb->dev = NULL;
+-              } else {
++              } else if (!dropped) {
+                       unsigned int hdr_size =
+                               ieee80211_hdrlen(hdr->frame_control);
diff --git a/package/kernel/mac80211/patches/subsys/342-mac80211-Ensure-vif-queues-are-operational-after-sta.patch b/package/kernel/mac80211/patches/subsys/342-mac80211-Ensure-vif-queues-are-operational-after-sta.patch
new file mode 100644 (file)
index 0000000..4310329
--- /dev/null
@@ -0,0 +1,47 @@
+From: Alexander Wetzel <alexander@wetzel-home.de>
+Date: Thu, 15 Sep 2022 15:09:46 +0200
+Subject: [PATCH] mac80211: Ensure vif queues are operational after start
+
+Make sure local->queue_stop_reasons and vif.txqs_stopped stay in sync.
+
+When a new vif is created the queues may end up in an inconsistent state
+and be inoperable:
+Communication not using iTXQ will work, allowing to e.g. complete the
+association. But the 4-way handshake will time out. The sta will not
+send out any skbs queued in iTXQs.
+
+All normal attempts to start the queues will fail when reaching this
+state.
+local->queue_stop_reasons will have marked all queues as operational but
+vif.txqs_stopped will still be set, creating an inconsistent internal
+state.
+
+In reality this seems to be race between the mac80211 function
+ieee80211_do_open() setting SDATA_STATE_RUNNING and the wake_txqs_tasklet:
+Depending on the driver and the timing the queues may end up to be
+operational or not.
+
+Cc: stable@vger.kernel.org
+Fixes: f856373e2f31 ("wifi: mac80211: do not wake queues on a vif that is being stopped")
+Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
+---
+
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -301,14 +301,14 @@ static void __ieee80211_wake_txqs(struct
+       local_bh_disable();
+       spin_lock(&fq->lock);
++      sdata->vif.txqs_stopped[ac] = false;
++
+       if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
+               goto out;
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               ps = &sdata->bss->ps;
+-      sdata->vif.txqs_stopped[ac] = false;
+-
+       list_for_each_entry_rcu(sta, &local->sta_list, list) {
+               if (sdata != sta->sdata)
+                       continue;