From: Gokul Sivakumar <gokulkumar.sivakumar@infineon.com>
To: Shivesh <chanelshivesh@gmail.com>
Cc: <arend.vanspriel@broadcom.com>, <linux-wireless@vger.kernel.org>,
<brcm80211@lists.linux.dev>,
<brcm80211-dev-list.pdl@broadcom.com>,
<linux-kernel@vger.kernel.org>,
<wlan-kernel-dev-list@infineon.com>
Subject: Re: [PATCH v4 4/8] wifi: brcmfmac: cfg80211: implement PMKID_V2 and fix brcmf_delay busy-wait
Date: Wed, 5 Aug 2026 13:49:40 +0000 [thread overview]
Message-ID: <anM_dGEcPO1kvxL9@ISCN5CG54722ST.infineon.com> (raw)
In-Reply-To: <20260731160646.3812-5-chanelshivesh@gmail.com>
On 07/31, Shivesh wrote:
> Two independent fixes:
>
> 1. PMKID_V2 implementation
> Firmware revision 12 introduced a versioned PMKID list (V2) with
> FILS-specific fields: raw PMK material, SSID, and fils_cache_id.
> The set/del/flush callbacks all contained "TODO: implement PMKID_V2"
> placeholders and fell through to the V1 path, breaking FILS
> fast-roaming on devices with V2-capable firmware.
>
> Add brcmf_pmksa_v2_op() which maintains a shadow brcmf_pmk_list_v2_le
> in cfg80211_info and pushes the full updated list to firmware via the
> pmkid_info iovar on every set/del/flush. The shadow counter is kept
> in list->length between calls; the wire-format byte-length is
> computed only at send time to avoid corrupting the shadow on the next
> call. Dispatch V2 between the existing V3 and V1 paths.
>
> 2. brcmf_delay() busy-wait
> When ms < (1000/HZ), brcmf_delay() called mdelay(), which is a
> CPU busy-wait loop. Since the function is always called in a
> sleepable context (it falls back to msleep() for larger values),
> the busy-wait is unnecessary and wastes CPU cycles. Replace with
> usleep_range() for values <= 20ms.
>
> Signed-off-by: Shivesh <chanelshivesh@gmail.com>
> ---
> .../broadcom/brcm80211/brcmfmac/cfg80211.c | 123 ++++++++++++++++--
> .../broadcom/brcm80211/brcmfmac/cfg80211.h | 4 +-
> 2 files changed, 115 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
> index 0b55d445895f..2375c2f9d97a 100644
> --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
> +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
> @@ -3989,12 +3989,10 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
>
> static __always_inline void brcmf_delay(u32 ms)
> {
> - if (ms < 1000 / HZ) {
> - cond_resched();
> - mdelay(ms);
> - } else {
> + if (ms <= 20)
> + usleep_range(ms * 1000, ms * 1000 + 1000);
> + else
> msleep(ms);
> - }
> }
>
> static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
> @@ -4364,6 +4362,109 @@ brcmf_pmksa_v3_op(struct brcmf_if *ifp, struct cfg80211_pmksa *pmksa,
> return ret;
> }
>
> +/**
> + * brcmf_pmksa_v2_op - update firmware PMKSA cache using the V2 list interface.
> + *
> + * V2 firmware (revision 12) uses a versioned flat list structure
> + * (brcmf_pmk_list_v2_le) rather than the per-entry operation model of V3.
> + * Each entry carries FILS-specific fields (raw PMK material, SSID, and
> + * fils_cache_id) in addition to the basic BSSID + PMKID pair, enabling
> + * FILS fast-roaming on devices that do not support V3.
> + *
> + * @cfg: driver config structure holding the shadow V2 PMKSA list
> + * @ifp: interface pointer
> + * @pmksa: the PMKSA to add/remove, or NULL for a flush
> + * @alive: true = add (set time_left to no-expiry), false = remove/flush
> + */
> +static s32
> +brcmf_pmksa_v2_op(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
> + struct cfg80211_pmksa *pmksa, bool alive)
> +{
> + struct brcmf_pub *drvr = cfg->pub;
> + struct brcmf_pmk_list_v2_le *list = &cfg->pmk_list_v2;
> + struct brcmf_pmksa_v2 *pmk = list->pmk;
> + u32 npmk = le16_to_cpu(list->length);
> + u32 i;
> +
> + /* npmk here stores the count of valid entries, repurposing the
> + * length field of the shadow list as a counter. We convert to
> + * the wire format (byte length) when sending to firmware.
> + */
> + if (!pmksa) {
> + /* Flush: zero the shadow list and push an empty V2 list. */
> + memset(list, 0, sizeof(*list));
> + goto send;
> + }
> +
> + if (alive) {
> + /* Set: search for existing BSSID match first. */
> + for (i = 0; i < npmk; i++)
> + if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
> + break;
> +
> + if (i >= BRCMF_MAXPMKID) {
> + bphy_err(drvr, "V2 PMKSA cache full (%d entries)\n",
> + npmk);
> + return -EINVAL;
> + }
> +
> + memset(&pmk[i], 0, sizeof(pmk[i]));
> + pmk[i].length = cpu_to_le16(sizeof(struct brcmf_pmksa_v2));
> + if (pmksa->bssid)
> + memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
> + if (pmksa->pmkid)
> + memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
> + if (pmksa->pmk && pmksa->pmk_len &&
> + pmksa->pmk_len <= WLAN_PMK_LEN_SUITE_B_192) {
> + memcpy(pmk[i].pmk, pmksa->pmk, pmksa->pmk_len);
> + pmk[i].pmk_len = cpu_to_le16(pmksa->pmk_len);
> + }
> + if (pmksa->ssid && pmksa->ssid_len) {
> + memcpy(pmk[i].ssid.SSID, pmksa->ssid, pmksa->ssid_len);
> + pmk[i].ssid.SSID_len = pmksa->ssid_len;
> + }
> + if (pmksa->fils_cache_id)
The struct cfg80211_pmksa defined in include/net/cfg80211.h kernel header does not
have this struct member "fils_cache_id", it does have a member "cache_id" though.
I doubt whether this patchset compiles successfully. It should be "pmksa->cache_id"
not "pmksa->fils_cache_id".
> + pmk[i].fils_cache_id = *pmksa->fils_cache_id;
Assuming, the member name is fixed in RHS. Here 2-bytes needs to be copied as per
endinannes, not just 1-byte, because "pmksa->cache_id" is a pointer for 2-bytes
and the LHS variable "pmk[i].fils_cache_id" is also two-bytes.
And RHS is a pointer, it is worth doing a NULL check before dereferencing for copy.
> + if (i == npmk)
> + npmk++;
> + } else {
> + /* Delete: find by BSSID and compact the list. */
> + for (i = 0; i < npmk; i++)
It is better to add a bounds check for "nmpk" with BRCMF_MAXPMKID at the beginning
of the funciton, since it is being directly used in loop condition inside the
function.
> + if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
> + break;
> +
> + if (i >= npmk) {
> + bphy_err(drvr, "V2 PMKSA entry not found\n");
> + return -EINVAL;
> + }
> +
> + for (; i < npmk - 1; i++)
> + memcpy(&pmk[i], &pmk[i + 1], sizeof(pmk[i]));
> + memset(&pmk[npmk - 1], 0, sizeof(pmk[npmk - 1]));
> + npmk--;
> + }
> +
> + /* Write the updated entry count back to shadow BEFORE we overwrite
> + * list->length with the wire-format byte length at send:. If we
> + * don't do this here, the next call will read a byte-length back
> + * as an entry count and silently corrupt the list.
> + */
> + list->length = cpu_to_le16(npmk);
As mentioned in the comment at the beginning of the function, here first the
"list->length" member is stored with function entry counter "npmk"...
> +send:
> + /* Build the wire-format byte length and send the full list to firmware.
> + * Read npmk back from the shadow (handles the flush path where npmk=0).
> + */
> + npmk = le16_to_cpu(list->length);
> + list->version = cpu_to_le16(BRCMF_PMKSA_VER_2);
> + list->length = cpu_to_le16(offsetof(struct brcmf_pmk_list_v2_le, pmk) +
> + npmk * sizeof(struct brcmf_pmksa_v2));
and then set with wire-format byte length before calling brcmf_fil_iovar_data_set().
> + return brcmf_fil_iovar_data_set(ifp, "pmkid_info", list, sizeof(*list));
But after setting the IOVAR, "list->length" member is never restored back with the
the entry count. So in all the subsequent function calls, it reads that byte length
value back as the function entry count, corrupting the shadow list. This is exactly
the issue that a above mentioned comment claims it is addressing, but actually not.
It would be way more simpler to use "list->length" only as a wire-format byte length,
and have a dedicated entry counter variable outside the function scope.
> +}
> +
> +
> static __used s32
> brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
> {
> @@ -4402,8 +4503,8 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
>
> if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
> return brcmf_pmksa_v3_op(ifp, pmksa, true);
> -
> - /* TODO: implement PMKID_V2 */
> + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V2))
> + return brcmf_pmksa_v2_op(cfg, ifp, pmksa, true);
>
> npmk = le32_to_cpu(cfg->pmk_list.npmk);
> for (i = 0; i < npmk; i++)
> @@ -4446,8 +4547,8 @@ brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
>
> if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
> return brcmf_pmksa_v3_op(ifp, pmksa, false);
> -
> - /* TODO: implement PMKID_V2 */
> + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V2))
> + return brcmf_pmksa_v2_op(cfg, ifp, pmksa, false);
>
> npmk = le32_to_cpu(cfg->pmk_list.npmk);
> for (i = 0; i < npmk; i++)
> @@ -4487,8 +4588,8 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
>
> if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
> return brcmf_pmksa_v3_op(ifp, NULL, false);
> -
> - /* TODO: implement PMKID_V2 */
> + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V2))
> + return brcmf_pmksa_v2_op(cfg, ifp, NULL, false);
>
The new PMKID V2 flow is trying to follow the existing PMKID V3 flow which currently
has an incorrect assumption about the Firwmare vendor support. These vendor specific
PMKID V3 and V2 operations are exercised here based on the BRCM_FEAT override flags.
But the BRCMF_FEAT_PMKID_{V3/V2} flags are infact currently set in driver only based
on "wlc_ver" IOVAR major, minor numbers queried from firmware irrespective of FWVID.
It would be more appropriate to set these BRCMF_FEAT_PMKID_{V3/v2} flags only for the
respective Firmware vendors based on the FWVID. Else, there are more chances for these
firmware "wlc_ver" major minors numbers to overlap across different firmware vendors,
leading to incorrect BRCMF_FEAT flag overrides. And driver would end up doing invalid
operations on a firmware from a different vendor.
Regards,
Gokul
next prev parent reply other threads:[~2026-08-05 13:49 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-31 16:06 [PATCH v4 0/8] wifi: brcm80211: performance and stability fixes Shivesh
2026-07-31 16:06 ` [PATCH v4 1/8] wifi: brcmfmac: flowring: replace O(N) blocked-ring scan with atomic counter Shivesh
2026-07-31 16:06 ` [PATCH v4 2/8] wifi: brcmfmac: sdio: coalesce sdio_claim_host calls in rxglom path Shivesh
2026-07-31 16:06 ` [PATCH v4 3/8] wifi: brcmfmac: core: fix missing headroom check and populate radiotap RSSI Shivesh
2026-07-31 16:06 ` [PATCH v4 4/8] wifi: brcmfmac: cfg80211: implement PMKID_V2 and fix brcmf_delay busy-wait Shivesh
2026-08-05 13:49 ` Gokul Sivakumar [this message]
2026-07-31 16:06 ` [PATCH v4 5/8] wifi: brcmfmac: msgbuf: fix TX stall and tune buffer/threshold constants Shivesh
2026-07-31 16:06 ` [PATCH v4 6/8] wifi: brcmfmac: pcie: replace msleep polling with usleep_range and backoff Shivesh
2026-07-31 16:06 ` [PATCH v4 7/8] wifi: brcmfmac: fwsignal: document safe no-op for duplicate MAC handle ADD Shivesh
2026-07-31 16:06 ` [PATCH v4 8/8] wifi: brcmsmac: ampdu: document IEEE 802.11n TID requirement Shivesh
2026-08-02 10:15 ` [PATCH v4 0/8] wifi: brcm80211: performance and stability fixes Arend van Spriel
2026-08-02 10:25 ` Arend van Spriel
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=anM_dGEcPO1kvxL9@ISCN5CG54722ST.infineon.com \
--to=gokulkumar.sivakumar@infineon.com \
--cc=arend.vanspriel@broadcom.com \
--cc=brcm80211-dev-list.pdl@broadcom.com \
--cc=brcm80211@lists.linux.dev \
--cc=chanelshivesh@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-wireless@vger.kernel.org \
--cc=wlan-kernel-dev-list@infineon.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.