From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f177.google.com (mail-pg1-f177.google.com [209.85.215.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9989F446821 for ; Fri, 31 Jul 2026 16:06:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.177 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785514017; cv=none; b=IyHsB9rtjrWXXWfIvEsdUTeXLh2JJLVL8YhelGdaAyUwa1ODY5PC44aoRsApb/Xc3X32KipprR/oDby54w1BX0GTUl1cjskEbhgOynIN9y5tnC7tx8f4cec+xSpCAzHYcFlPOR0wgQmHP0YppfXqXB4rG9Lq/jmDnumtkn7o8xw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785514017; c=relaxed/simple; bh=YJBvBZWvNxs18TqfUfA1hwadJeuDBfwNuagruUWmko0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=GhhFGr9b1Nw0xi8mmlEcEu9HiHQIaG1DFcyOUr4qFD96f9/gysI+lRvzrILf5ntUKG5i1V8O4zUNJusBc8rdJYkIkeMDHr2wsu2OUIVPshcwXvq4ExdnvpZjgtzHe+Rp1zA4oOVlabt69Cp45EaV7xvtscj3DrcfHG8Rb/20DWk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=LKmEUAN7; arc=none smtp.client-ip=209.85.215.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LKmEUAN7" Received: by mail-pg1-f177.google.com with SMTP id 41be03b00d2f7-ca132e9c54aso94047a12.3 for ; Fri, 31 Jul 2026 09:06:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785514015; x=1786118815; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=V+7zRa7zBwj/Mp97JNRaCsdKi8S7AHJm5zHHTJ/ZjEk=; b=LKmEUAN7WM2pOPs0/S9s2K6fX1k1hDzorSiPG/YYGoI3fwUAUxz/XaRHSRbpD9uFye A723mjHIUrRd5BoBaw7E2qKB7WFGV0IsswW7JJbzogxmD62eXeo1AVEj4481MywMyySW XaH2RoL2AhGcruek92M6oAgMq+urZ1J/uYZqkpXq8bnbgl94OMY2HlnkQ7akCX4vezYN ajl5eUYqI1b+UlqwqVThx51jIyX2Yrji7gwa7FGpIl0S/tghqDK1N9WlF7v7uFz+1fbJ hqJLOWE/3Ni5B5gYBeiWV6p4QKctm8Pt7ezUBM96dHQ2uAFGbPFLFebww/J2Bprq1ndw 8/rQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785514015; x=1786118815; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=V+7zRa7zBwj/Mp97JNRaCsdKi8S7AHJm5zHHTJ/ZjEk=; b=B5SZkh0evU07doTMGiJugsRiMMYnxmvLdfUHOadnYmuZSvHCx+QXQNtBzd3/6E0TSH /65bpstTdMTNicKJXvlLR5nwqVO5ZE1ly1ADYmmtfr8okAqS8MfkJrHvYKwQTuoSdnUZ fomRObGV3I8Dy33hPCj8unrxP8jClBYAv3IHyhqQIntn63rwLCyr0TkQ9veuZXQSnuJx JV6kGLBqEae2g6sGMORV5gPDTJUdZ3FIhTwOldPgtT0n0mcEXPl/Hy4jwvefTgqF0vdY Kxr9GG3E5U3sOIn4aGEynO+84cdSq9QScjSxFSiRZ9xuR+/HiEMdCJd31zfS3rYWPmyt bT/A== X-Forwarded-Encrypted: i=1; AHgh+RrTiua9UHIYLhpF2kTE1qZtl0GV8xsEhT2gPyKfrQoU1oyQiOifSl6npd1meg6u2NAneKTWKzBmC/emo9c=@vger.kernel.org X-Gm-Message-State: AOJu0YysZxHsH694VbfErO6Hbvi2OygrhG+0mLwM+b3fTBmcBwVZHUKb 7UdVMdcQDigFPBK5jOVNAHSsKveygs06hjpB0uxwahzYO4RCcDu6xnes X-Gm-Gg: AR+sD109nzg//6j0iDyvGTriahp+wQnL53nni4s3XmNmQkovGBFL7aS3b0loKAfZMkd qKEoMGZ1gfSBpYeiM6eJSfhe7FJ+pbFCE57xKNJ4RE8IyyWM645kU3SYVxKjPkSuK5woiskCmQK El0DqCERWI7zb/PjXuWHm/NwfQT6CI5TABKkycP8FG10r36o4UHg0enQeM1oGllWel4Dk5wmCrG Yr9ubnPtm+qbJMbSRvb7GbXAYEh0/DZTRlImrSgnQmmRcR/gJG+ePFY5Ds77E9U2qbm+IeoYZox X0GYfXL34lpTh51f/QByY6xm+B0I+dMVjjdAMpbFlytU7tm1dQ7efqjboN+5Y1J2F+xBCuw/W+g /gs1g3WpeRV9gDe80MUCkPSLWuwif+aykumLP4LB6vgK+EChJPuCsJm1J8sQc7ZYLVxadCVBF/X 0hgDo3I5weF8DPDYtRYne0lOE2SzewO7MkbaU1MdCihEXVjeJmeU2LOhCyVTY3dLrl+rvkk6h/D WqwRbte X-Received: by 2002:a17:90b:5408:b0:38e:c140:2a0b with SMTP id 98e67ed59e1d1-38fbc4c8183mr624796a91.3.1785514014834; Fri, 31 Jul 2026 09:06:54 -0700 (PDT) Received: from Potato.tail66a299.ts.net ([171.76.83.76]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3153dd9c93dsm7881425eec.8.2026.07.31.09.06.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 31 Jul 2026 09:06:54 -0700 (PDT) From: Shivesh 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 Subject: [PATCH v4 1/8] wifi: brcmfmac: flowring: replace O(N) blocked-ring scan with atomic counter Date: Fri, 31 Jul 2026 16:06:18 +0000 Message-ID: <20260731160646.3812-2-chanelshivesh@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260731160646.3812-1-chanelshivesh@gmail.com> References: <20260731160646.3812-1-chanelshivesh@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit brcmf_flowring_block() previously determined whether any sibling ring was already blocked by walking all nrofrings entries under block_lock. This O(N) scan is both slow and incomplete: a ring that transitions from RING_OPEN to RING_CLOSING while blocked causes the unblock path to skip the decrement (because ring->status is no longer RING_OPEN), leaking the implicit "someone is blocked" state and permanently stopping the netif queue for that interface. Replace the per-call O(N) walk with a per-interface atomic counter if_blocked_cnt[BRCMF_MAX_IFS]. Introduce a per-ring boolean counted_in_blocked that records whether the ring has been added to the counter, so the matching decrement is always applied regardless of the ring status at unblock time. The decision to stop or wake the netif queue is now a simple atomic_read() check, still performed under block_lock to prevent races between concurrent brcmf_flowring_block() callers. Signed-off-by: Shivesh --- .../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 + #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