From: Shivesh <chanelshivesh@gmail.com>
To: arend.vanspriel@broadcom.com
Cc: linux-wireless@vger.kernel.org, brcm80211@lists.linux.dev,
brcm80211-dev-list.pdl@broadcom.com,
linux-kernel@vger.kernel.org, Shivesh <shivesh@example.com>
Subject: [PATCH 1/8] wifi: brcmfmac: flowring: replace O(N) loop with atomic counter
Date: Thu, 30 Jul 2026 16:03:49 +0000 [thread overview]
Message-ID: <20260730160425.11933-1-shivesh@example.com> (raw)
Optimizes flowring deletion by tracking counter updates with a flag
instead of an O(N) spinlock walk, fixing a teardown leak.
Signed-off-by: Shivesh <shivesh@example.com>
---
.../broadcom/brcm80211/brcmfmac/flowring.c | 65 ++++++++++++++-----
.../broadcom/brcm80211/brcmfmac/flowring.h | 18 +++++
2 files changed, 66 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
index 35cbcea0abc9..b9c518939f50 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
@@ -182,10 +182,8 @@ static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid,
struct brcmf_bus *bus_if;
struct brcmf_pub *drvr;
struct brcmf_if *ifp;
- bool currently_blocked;
- int i;
- u8 ifidx;
unsigned long flags;
+ u8 ifidx;
spin_lock_irqsave(&flow->block_lock, flags);
@@ -194,23 +192,54 @@ static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid,
spin_unlock_irqrestore(&flow->block_lock, flags);
return;
}
- ifidx = brcmf_flowring_ifidx_get(flow, flowid);
- currently_blocked = false;
- for (i = 0; i < flow->nrofrings; i++) {
- if ((flow->rings[i]) && (i != flowid)) {
- ring = flow->rings[i];
- if ((ring->status == RING_OPEN) &&
- (brcmf_flowring_ifidx_get(flow, i) == ifidx)) {
- if (ring->blocked) {
- currently_blocked = true;
- break;
- }
- }
+ ifidx = brcmf_flowring_ifidx_get(flow, flowid);
+ ring->blocked = blocked;
+
+ /*
+ * Maintain the per-interface blocked-ring counter.
+ *
+ * We use ring->counted_in_blocked rather than checking
+ * ring->status here. A ring that became blocked while
+ * RING_OPEN has already been counted (counted_in_blocked=true).
+ * By the time we unblock it during teardown its status may have
+ * advanced to RING_CLOSING, so testing RING_OPEN would wrongly
+ * skip the atomic_dec and permanently leak the counter, leaving
+ * the netif queue stopped forever.
+ *
+ * Rule:
+ * block transition (unblocked→blocked): count only if RING_OPEN,
+ * set counted_in_blocked.
+ * unblock transition (blocked→unblocked): decrement only if we
+ * previously counted it,
+ * clear counted_in_blocked.
+ */
+ if (blocked) {
+ if (ring->status == RING_OPEN) {
+ atomic_inc(&flow->if_blocked_cnt[ifidx]);
+ ring->counted_in_blocked = true;
}
+ } else {
+ if (ring->counted_in_blocked) {
+ atomic_dec(&flow->if_blocked_cnt[ifidx]);
+ ring->counted_in_blocked = false;
+ }
+ }
+
+ /*
+ * Only propagate a netif queue-stop/wake when the interface
+ * transitions between fully-clear and at-least-one-blocked.
+ * Reading the atomic is safe here: we hold block_lock, so no
+ * concurrent brcmf_flowring_block() call can race the update
+ * we just made above.
+ */
+ if (blocked && atomic_read(&flow->if_blocked_cnt[ifidx]) != 1) {
+ /* Another ring was already blocked; no new queue-stop needed. */
+ spin_unlock_irqrestore(&flow->block_lock, flags);
+ return;
}
- flow->rings[flowid]->blocked = blocked;
- if (currently_blocked) {
+ if (!blocked && atomic_read(&flow->if_blocked_cnt[ifidx]) != 0) {
+ /* More rings still blocked; do not wake the queue yet. */
spin_unlock_irqrestore(&flow->block_lock, flags);
return;
}
@@ -367,6 +396,8 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings)
spin_lock_init(&flow->block_lock);
for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++)
flow->addr_mode[i] = ADDR_INDIRECT;
+ for (i = 0; i < ARRAY_SIZE(flow->if_blocked_cnt); i++)
+ atomic_set(&flow->if_blocked_cnt[i], 0);
for (i = 0; i < ARRAY_SIZE(flow->hash); i++)
flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
index f3d511f9a3c9..afdea8b3f8aa 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
@@ -5,6 +5,8 @@
#ifndef BRCMFMAC_FLOWRING_H
#define BRCMFMAC_FLOWRING_H
+#include <linux/atomic.h>
+
#define BRCMF_FLOWRING_HASHSIZE 512 /* has to be 2^x */
#define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF
@@ -26,6 +28,16 @@ enum ring_status {
struct brcmf_flowring_ring {
u16 hash_id;
bool blocked;
+ /*
+ * True when this ring has been counted in the per-interface
+ * if_blocked_cnt[]. Set to true whenever the ring transitions
+ * unblocked→blocked while RING_OPEN; cleared on the matching
+ * blocked→unblocked transition. Needed so that a ring that
+ * becomes blocked while RING_OPEN and is later moved to
+ * RING_CLOSING still correctly decrements the counter at
+ * teardown, even though its status is no longer RING_OPEN.
+ */
+ bool counted_in_blocked;
enum ring_status status;
struct sk_buff_head skblist;
};
@@ -40,6 +52,12 @@ struct brcmf_flowring {
struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE];
spinlock_t block_lock;
enum proto_addr_mode addr_mode[BRCMF_MAX_IFS];
+ /* Per-interface count of currently blocked open rings.
+ * Maintained atomically so brcmf_flowring_block() can check
+ * whether any sibling ring is already blocked in O(1) without
+ * holding block_lock across an O(nrofrings) walk.
+ */
+ atomic_t if_blocked_cnt[BRCMF_MAX_IFS];
u16 nrofrings;
bool tdls_active;
struct brcmf_flowring_tdls_entry *tdls_entry;
--
2.53.0
next reply other threads:[~2026-07-30 16:04 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-30 16:03 Shivesh [this message]
2026-07-30 16:03 ` [PATCH 2/8] wifi: brcmfmac: sdio: coalesce host locks in rx path Shivesh
2026-07-30 16:03 ` [PATCH 3/8] wifi: brcmfmac: core: populate radiotap header with RSSI Shivesh
2026-07-30 16:03 ` [PATCH 4/8] wifi: brcmfmac: cfg80211: implement PMKID_V2 and fix delay busy-wait Shivesh
2026-07-30 16:03 ` [PATCH 5/8] wifi: brcmfmac: msgbuf: tune thresholds and optimize sleep latency Shivesh
2026-07-30 16:03 ` [PATCH 6/8] wifi: brcmfmac: pcie: optimize latency and irq teardown Shivesh
2026-07-30 16:03 ` [PATCH 7/8] wifi: brcmfmac: fwsignal: safe no-op on duplicate MAC add Shivesh
2026-07-30 16:03 ` [PATCH 8/8] wifi: brcmsmac: ampdu: clarify standard compliance on QoS change Shivesh
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260730160425.11933-1-shivesh@example.com \
--to=chanelshivesh@gmail.com \
--cc=arend.vanspriel@broadcom.com \
--cc=brcm80211-dev-list.pdl@broadcom.com \
--cc=brcm80211@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-wireless@vger.kernel.org \
--cc=shivesh@example.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox