* [PATCH v2] wifi: mt76: mt7921: add regulatory wiphy self manager support
From: JB Tsai @ 2026-06-09 6:50 UTC (permalink / raw)
To: nbd, lorenzo
Cc: linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang, Quan.Zhou,
Ryder.Lee, Leon.Yen, litien.chang, Charlie-cy.Wu, jb.tsai
From: Charlie-cy Wu <Charlie-cy.Wu@mediatek.com>
Introduce regulatory wiphy self-managed mode support for MT7921,
allowing the driver to manage its own regulatory domain independently
from the kernel's regulatory framework.
Signed-off-by: Charlie-cy Wu <Charlie-cy.Wu@mediatek.com>
---
v2: fix regd.c build warning
---
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 +
.../net/wireless/mediatek/mt76/mt7921/mcu.c | 3 +
.../net/wireless/mediatek/mt76/mt7921/regd.c | 209 ++++++++++++++++--
.../net/wireless/mediatek/mt76/mt7921/regd.h | 55 ++++-
4 files changed, 245 insertions(+), 23 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index ed5c441748d8..c10a2c4e7ee2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1363,6 +1363,7 @@ enum {
MCU_CE_CMD_FWLOG_2_HOST = 0xc5,
MCU_CE_CMD_GET_WTBL = 0xcd,
MCU_CE_CMD_GET_TXPWR = 0xd0,
+ MCU_CE_CMD_SET_REGD_CH = 0xd1,
};
enum {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 25b9437250f7..2e0769d18f87 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -1403,6 +1403,9 @@ int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
/* submit all clc config */
for (i = 0; i < ARRAY_SIZE(phy->clc); i++) {
+ if (i == MT792x_CLC_REGD)
+ continue;
+
ret = __mt7921_mcu_set_clc(dev, alpha2, env_cap,
phy->clc[i], i);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
index f122e418d825..d29b3b0113f2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
@@ -10,6 +10,15 @@ static bool mt7921_disable_clc;
module_param_named(disable_clc, mt7921_disable_clc, bool, 0644);
MODULE_PARM_DESC(disable_clc, "disable CLC support");
+static struct ieee80211_regdomain mt7921_regd_ww = {
+ .n_reg_rules = 1,
+ .alpha2 = "00",
+ .reg_rules = {
+ /* IEEE 802.11b/g, channels 1..11 */
+ REG_RULE(2412 - 10, 2462 + 10, 40, 6, 20, 0),
+ }
+};
+
bool mt7921_regd_clc_supported(struct mt792x_dev *dev)
{
if (mt7921_disable_clc ||
@@ -33,6 +42,9 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
np = mt76_find_power_limits_node(mdev);
sband = wiphy->bands[NL80211_BAND_5GHZ];
+ if (!sband)
+ return;
+
band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
@@ -71,35 +83,36 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
}
}
-int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
- enum environment_cap country_ie_env)
+static int mt7921_mcu_apply_regd(struct mt792x_dev *dev, u8 *alpha2,
+ enum environment_cap env)
{
- struct mt76_dev *mdev = &dev->mt76;
- struct ieee80211_hw *hw = mdev->hw;
+ struct ieee80211_hw *hw = mt76_hw(dev);
struct wiphy *wiphy = hw->wiphy;
- int ret = 0;
-
- dev->regd_in_progress = true;
-
- mt792x_mutex_acquire(dev);
- if (!dev->regd_change)
- goto err;
+ int ret;
- ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env);
+ ret = mt7921_mcu_set_clc(dev, alpha2, env);
if (ret < 0)
- goto err;
+ return ret;
mt7921_regd_channel_update(wiphy, dev);
ret = mt76_connac_mcu_set_channel_domain(hw->priv);
if (ret < 0)
- goto err;
+ return ret;
- ret = mt7921_set_tx_sar_pwr(hw, NULL);
- if (ret < 0)
- goto err;
+ return mt7921_set_tx_sar_pwr(hw, NULL);
+}
-err:
+int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
+ enum environment_cap country_ie_env)
+{
+ int ret = 0;
+
+ dev->regd_in_progress = true;
+
+ mt792x_mutex_acquire(dev);
+ if (dev->regd_change)
+ ret = mt7921_mcu_apply_regd(dev, alpha2, country_ie_env);
mt792x_mutex_release(dev);
dev->regd_change = false;
dev->regd_in_progress = false;
@@ -142,10 +155,160 @@ void mt7921_regd_notifier(struct wiphy *wiphy,
if (pm->suspended)
return;
+ if (MT7921_REGD_SUPPORTED(&dev->phy)) {
+ mt7921_regd_update(&dev->phy, req->alpha2);
+
+ return;
+ }
+
mt7921_mcu_regd_update(dev, req->alpha2,
req->country_ie_env);
}
+static struct sk_buff *
+mt7921_regd_query_regdb(struct mt792x_phy *phy, char *alpha2)
+{
+ struct wiphy *wiphy = phy->mt76->hw->wiphy;
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt7921_clc *clc = phy->clc[MT792x_CLC_REGD];
+ struct mt7921_regd_query_req *req;
+ struct mt7921_regd_cc *regd_cc;
+ struct sk_buff *ret_skb = NULL;
+ u8 *pos, *last_pos;
+ int ret = 0;
+
+ if (!clc)
+ return NULL;
+
+ pos = clc->data;
+ last_pos = pos + le32_to_cpu(clc->len) - sizeof(struct mt7921_clc);
+ while (pos < last_pos) {
+ u32 req_len = 0;
+ u32 rules_len = 0;
+ u32 sign_len = 4;
+
+ if (pos + sizeof(*regd_cc) > last_pos)
+ break;
+
+ regd_cc = (struct mt7921_regd_cc *)pos;
+ rules_len = sizeof(struct mt7921_regd_rule_header) +
+ sizeof(struct mt7921_regd_rule) *
+ le32_to_cpu(regd_cc->n_reg_rules);
+
+ if (pos + sizeof(*regd_cc) + rules_len + sign_len > last_pos)
+ break;
+
+ pos += sizeof(*regd_cc) + rules_len + sign_len;
+ if (memcmp(regd_cc->alpha2, alpha2, 2))
+ continue;
+
+ req_len = sizeof(*req) + rules_len + sign_len;
+ req = devm_kmalloc(dev->mt76.dev, req_len, GFP_KERNEL);
+
+ if (!req)
+ return NULL;
+
+ req->ver = regd_cc->ver;
+ req->sign_type = regd_cc->sign_type;
+ req->size = cpu_to_le32(rules_len + sign_len);
+ req->n_reg_rules = regd_cc->n_reg_rules;
+
+ memcpy(req->alpha2, regd_cc->alpha2, 2);
+ memcpy(req->data, regd_cc->data, rules_len + sign_len);
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_CE_CMD(SET_REGD_CH),
+ req, req_len, true, &ret_skb);
+
+ devm_kfree(dev->mt76.dev, req);
+
+ return ret < 0 ? NULL : ret_skb;
+ }
+
+ return NULL;
+}
+
+int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2)
+{
+ struct wiphy *wiphy = phy->mt76->hw->wiphy;
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt7921_regd_rule *mt7921_rule;
+ struct mt76_dev *mdev = &dev->mt76;
+ struct ieee80211_regdomain *regd;
+ struct ieee80211_reg_rule *rule;
+ struct mt7921_regd_rule_ev *ev;
+ int i, num_of_rules = 0;
+ struct sk_buff *skb;
+ int ret = 0;
+
+ if (dev->hw_full_reset)
+ return 0;
+
+ if (!MT7921_REGD_SUPPORTED(phy))
+ return -EOPNOTSUPP;
+
+ mt792x_mutex_acquire(dev);
+ skb = mt7921_regd_query_regdb(phy, alpha2);
+ mt792x_mutex_release(dev);
+
+ if (!skb)
+ return -EINVAL;
+
+ ev = (struct mt7921_regd_rule_ev *)(skb->data + 4);
+ num_of_rules = le32_to_cpu(ev->n_reg_rules);
+
+ if (!num_of_rules ||
+ WARN_ON_ONCE(num_of_rules > NL80211_MAX_SUPP_REG_RULES)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL);
+ if (!regd) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < num_of_rules; i++) {
+ mt7921_rule = &ev->reg_rule[i];
+ rule = ®d->reg_rules[i];
+
+ rule->freq_range.start_freq_khz =
+ MHZ_TO_KHZ(mt7921_rule->start_freq);
+ rule->freq_range.end_freq_khz =
+ MHZ_TO_KHZ(mt7921_rule->end_freq);
+ rule->freq_range.max_bandwidth_khz =
+ MHZ_TO_KHZ(mt7921_rule->max_bw);
+ /* not used by fw */
+ rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
+ rule->power_rule.max_eirp = DBM_TO_MBM(22);
+ rule->flags = mt7921_rule->flags;
+ }
+
+ regd->n_reg_rules = num_of_rules;
+ regd->dfs_region = ev->dfs_region;
+
+ memcpy(regd->alpha2, alpha2, 2);
+ memcpy(mdev->alpha2, alpha2, 2);
+
+ dev->regd_change = true;
+ mt7921_mcu_regd_update(dev, alpha2, ENVIRON_ANY);
+
+ ret = regulatory_set_wiphy_regd(wiphy, regd);
+
+ kfree(regd);
+err:
+ dev_kfree_skb(skb);
+
+ if (ret < 0)
+ return regulatory_set_wiphy_regd(wiphy, &mt7921_regd_ww);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mt7921_regd_update);
+
static bool
mt7921_regd_is_valid_alpha2(const char *alpha2)
{
@@ -183,7 +346,9 @@ int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2)
if (!memcmp(alpha2, mdev->alpha2, 2))
return 0;
- if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
+ if (MT7921_REGD_SUPPORTED(phy))
+ return mt7921_regd_update(phy, alpha2);
+ else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
return regulatory_hint(wiphy, alpha2);
else
return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
@@ -197,7 +362,11 @@ int mt7921_regd_init(struct mt792x_phy *phy)
struct mt792x_dev *dev = mt792x_hw_dev(hw);
struct mt76_dev *mdev = &dev->mt76;
- if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
+ if (MT7921_REGD_SUPPORTED(phy)) {
+ wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED |
+ REGULATORY_DISABLE_BEACON_HINTS;
+ return mt7921_regd_update(phy, "00");
+ } else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE |
REGULATORY_DISABLE_BEACON_HINTS;
else
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
index 571f31629e9e..c1e94cd4c958 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
@@ -4,9 +4,57 @@
#ifndef __MT7921_REGD_H
#define __MT7921_REGD_H
-struct mt792x_dev;
-struct wiphy;
-struct regulatory_request;
+#include "mt7921.h"
+
+struct mt7921_regd_rule_header {
+ u8 alpha2[2];
+ u8 dfs_region;
+ u8 rsv[13];
+};
+
+struct mt7921_regd_rule {
+ u32 start_freq;
+ u32 end_freq;
+ u32 max_bw;
+ u32 eirp;
+ u32 flags;
+ u8 rsv[12];
+};
+
+struct mt7921_regd_cc {
+ u8 alpha2[2];
+ u8 ver;
+ u8 rsv;
+ __le32 n_reg_rules;
+ u8 sign_type;
+ u8 rsv1[7];
+ u8 data[];
+};
+
+struct mt7921_regd_rule_ev {
+ __le16 tag;
+ __le16 len;
+ __le32 n_reg_rules;
+ u8 dfs_region;
+ u8 rsv[15];
+ struct mt7921_regd_rule reg_rule[];
+};
+
+struct mt7921_regd_query_req {
+ u8 ver;
+ u8 sign_type;
+ u8 rsv1[2];
+ __le32 size;
+ u8 alpha2[2];
+ u8 rsv2[2];
+ __le32 n_reg_rules;
+ u8 rsv3[64];
+ u8 data[];
+};
+
+#define MT7921_REGD_SUPPORTED(phy) \
+ (((phy)->chip_cap & MT792x_CHIP_CAP_REGD_EN) && \
+ (phy)->clc[MT792x_CLC_REGD])
int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
enum environment_cap country_ie_env);
@@ -15,5 +63,6 @@ void mt7921_regd_notifier(struct wiphy *wiphy,
bool mt7921_regd_clc_supported(struct mt792x_dev *dev);
int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2);
int mt7921_regd_init(struct mt792x_phy *phy);
+int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2);
#endif
--
2.18.0
^ permalink raw reply related
* Re: Unable to unsubscribe from linux-wireless@vger.kernel.org
From: Sedat Dilek @ 2026-06-09 7:22 UTC (permalink / raw)
To: Konstantin Ryabitsev; +Cc: postmaster, linux-wireless
In-Reply-To: <20260608-gigantic-vicugna-of-karma-d3d26e@lemur>
On Mon, Jun 8, 2026 at 3:44 PM Konstantin Ryabitsev
<konstantin@linuxfoundation.org> wrote:
>
> On Mon, Jun 08, 2026 at 10:22:04AM +0200, Sedat Dilek wrote:
> > Hi Konstantin et all,
> >
> > Can you please check or help me find the right person?
> >
> > I registered linux-wireless ML via <sedat.dilek@googlemail.com> email
> > (email confirmed in monthly bounce-email from ML).
> > In 2005 short xxx@gmail.com was not allowed these days for users from Germany.
> > This restriction changed some years later.
> >
> > All my unsubscription emails are sent as <sedat.dilek@gmail.com> and fail.
> > I still get emails from linux-wireless ML.
> >
> > So, how can I unsubscribe successfully?
> > Any hints/help much appreciated.
>
> I've unsubscribed you.
>
> -K
Thank you Konstantin.
-sed@-
^ permalink raw reply
* Re: [PATCH net] wifi: mac80211: fix memory leak in ieee80211_register_hw()
From: Dawei Feng @ 2026-06-09 7:40 UTC (permalink / raw)
To: jeff.johnson
Cc: dawei.feng, jianhao.xu, johannes, linux-kernel, linux-wireless,
stable, zilin
In-Reply-To: <dffefec6-14e0-4a87-85dd-97d328fedb50@oss.qualcomm.com>
Hi Jeff,
Thanks for your time and the review.
On Mon, Jun 8, 2026 at 10:24 AM, Jeff Johnson wrote:
> > Fixes: 09b4a4faf9d0 ("mac80211: introduce capability flags for VHT EXT NSS support")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
>
> why is this SOB here?
>
> > Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
>
> this is the posted author of the patch, and this patch hasn't been posted
> previously, so it is unclear why there is an additional S-o-b
Zilin is the discoverer of this bug. We are in the same research group,
and he actively participated in reviewing this patch.
To better align with the kernel submission guidelines, I will add a
"Co-developed-by:" tag in the v2 patch for Zilin to properly reflect his
contributions. Would this be acceptable?
> I'm wondering if it would be more logical to have another label at the same
> place, i.e. fail_band, since it is illogical to goto fail_wiphy_register when
> you aren't performing the wiphy_register function
Thanks for the suggestion. I will add a new label fail_band and send out
the v2 patch soon.
Thanks,
Dawei
^ permalink raw reply
* [PATCH v4 0/8] Support for block device NVMEM providers
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski, Konrad Dybcio
On embedded devices, it is common for factory provisioning to store
device-specific information, such as Ethernet or WiFi MAC addresses,
in a dedicated area of an eMMC partition. This avoids the need for
and additional EEPROM/OTP and leverages the persistence of eMMC.
One example is the Arduino UNO-Q, where the WiFi MAC address and the
Bluetooth Device address are stored in the eMMC Boot1 partition.
Until now, accessing this information required a custom bootloader
to read the data and inject it into the Device Tree before handing
control over to the kernel. This approach is fragile and leads to
device-specific workarounds.
Rather than adding a new NVMEM provider specifically to the eMMC
subsystem, the new support operates at the block layer, allowing any
block device to behave like other non-volatile memories such as EEPROM
or OTP.
This series builds on earlier work by Daniel Golle that enables block
devices to act as NVMEM providers:
https://lore.kernel.org/all/6061aa4201030b9bb2f8d03ef32a564fdb786ed1.1709667858.git.daniel@makrotopia.org/
It also introduces an NVMEM layout description for the Arduino UNO-Q,
allowing device-specific data stored in the eMMC Boot1 partition to
be accessed in a standard way.
WiFi and Ethernet already support retrieving MAC addresses from NVMEM.
Bluetooth requires similar support, which is also addressed.
Note that this is currently limited to MMC-backed block devices, as
only the MMC core associates a firmware node with the block device
(add_disk_fwnode). This can be easily extended in the future to
support additional block drivers.
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
Changes in v4:
- Fix squash issue (dts commit incorrectly squashed) (Konrad)
- Use devres for nvmem resources (Bartosz)
- use __free() destructor helper when possible (Bartosz)
- Fix value return checking for bdev_file_open_by_dev
- Link to v3: https://lore.kernel.org/r/20260608-block-as-nvmem-v3-0-82681f50aa35@oss.qualcomm.com
Changes in v3:
- Fixed missing 'fixed-partitions' compatible in partition (Rob)
- Fixed clashing nvmem cells, document calibration along mac (Sashiko)
- Remove workaround to handle dangling nvmem references after
unregistering, this is a generic nvmem framework issue handled
in Bartosz's series:
https://lore.kernel.org/all/20260429-nvmem-unbind-v3-0-2a694f95395b@oss.qualcomm.com/
- Validate mac (is_valid_ether_addr) before copying to output buffer
- Link to v2: https://lore.kernel.org/r/20260507-block-as-nvmem-v2-0-bf17edd5134e@oss.qualcomm.com
Changes in v2:
- Fix example nvmem-layout cells to use compatible = "mac-base"
- Squash WiFi MAC and Bluetooth BD address consumer patches into the nvmem layout patch
- Fix possible use-after-free in blk-nvmem: bnv (nvmem priv) linked to nvmem lifetime
- Simplify nvmem-cell-names from items: - const: to plain const:
- Factor out common NVMEM EUI-48 retrieval logic
- Reorder changes
- Link to v1: https://lore.kernel.org/r/20260428-block-as-nvmem-v1-0-6ad23e75190a@oss.qualcomm.com
---
Daniel Golle (1):
block: implement NVMEM provider
Loic Poulain (7):
dt-bindings: mmc: Document support for nvmem-layout
dt-bindings: net: wireless: qcom,ath10k: Document NVMEM cells
dt-bindings: bluetooth: qcom: Add NVMEM BD address cell
net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup
Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval
Bluetooth: qca: Set NVMEM BD address quirks when address is invalid
arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses
.../devicetree/bindings/mmc/mmc-card.yaml | 29 ++++++
.../net/bluetooth/qcom,bluetooth-common.yaml | 9 ++
.../bindings/net/wireless/qcom,ath10k.yaml | 16 +++
arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts | 39 +++++++
block/Kconfig | 9 ++
block/Makefile | 1 +
block/blk-nvmem.c | 114 +++++++++++++++++++++
drivers/bluetooth/btqca.c | 5 +-
include/linux/of_net.h | 7 ++
include/net/bluetooth/hci.h | 18 ++++
net/bluetooth/hci_sync.c | 39 ++++++-
net/core/of_net.c | 49 ++++++---
12 files changed, 321 insertions(+), 14 deletions(-)
---
base-commit: 47c4835fc0fed583d01d90387b67633950eba2b2
change-id: 20260428-block-as-nvmem-4b308e8bda9a
Best regards,
--
Loic Poulain <loic.poulain@oss.qualcomm.com>
^ permalink raw reply
* [PATCH v4 1/8] dt-bindings: mmc: Document support for nvmem-layout
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
Add support for an nvmem-layout subnode under an eMMC hardware
partition. This allows the partition to be exposed as an NVMEM
provider and its internal layout to be described. For example,
an eMMC boot partition can be used to store device-specific
information such as a WiFi MAC address.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
.../devicetree/bindings/mmc/mmc-card.yaml | 29 ++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/mmc-card.yaml b/Documentation/devicetree/bindings/mmc/mmc-card.yaml
index a61d6c96df759102f9c1fbfd548b026a77921cae..ca907ad73095925b234b119948f94ae81e698c86 100644
--- a/Documentation/devicetree/bindings/mmc/mmc-card.yaml
+++ b/Documentation/devicetree/bindings/mmc/mmc-card.yaml
@@ -40,6 +40,9 @@ patternProperties:
contains:
const: fixed-partitions
+ nvmem-layout:
+ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml
+
required:
- compatible
- reg
@@ -86,6 +89,32 @@ examples:
read-only;
};
};
+
+ partitions-boot2 {
+ compatible = "fixed-partitions";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ nvmem-layout {
+ compatible = "fixed-layout";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ mac-addr@4400 {
+ compatible = "mac-base";
+ reg = <0x4400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+
+ bd-addr@5400 {
+ compatible = "mac-base";
+ reg = <0x5400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+ };
+ };
};
};
--
2.34.1
^ permalink raw reply related
* [PATCH v4 2/8] dt-bindings: net: wireless: qcom,ath10k: Document NVMEM cells
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
Document the NVMEM cells supported by the ath10k driver, the
mac-address, pre-calibration data, and calibration data.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
.../devicetree/bindings/net/wireless/qcom,ath10k.yaml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
index c21d66c7cd558ab792524be9afec8b79272d1c87..7391df5e7071e626af4c64b9919d48c41ac09f1e 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml
@@ -92,6 +92,22 @@ properties:
ieee80211-freq-limit: true
+ nvmem-cells:
+ minItems: 1
+ maxItems: 3
+ description: |
+ References to nvmem cells for MAC address and/or calibration data.
+ Supported cell names are mac-address, calibration, and pre-calibration.
+
+ nvmem-cell-names:
+ minItems: 1
+ maxItems: 3
+ items:
+ enum:
+ - mac-address
+ - calibration
+ - pre-calibration
+
qcom,calibration-data:
$ref: /schemas/types.yaml#/definitions/uint8-array
description:
--
2.34.1
^ permalink raw reply related
* [PATCH v4 3/8] dt-bindings: bluetooth: qcom: Add NVMEM BD address cell
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
Add support for an NVMEM cell provider for "local-bd-address",
allowing the Bluetooth stack to retrieve controller's BD address
from non-volatile storage such as an EEPROM or an eMMC partition.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
.../devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml
index c8e9c55c1afb4c8e05ba2dae41ce2db4194b4a0f..7cb28f30c9af032082f23311f2fc89a32f266f17 100644
--- a/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml
@@ -22,4 +22,13 @@ properties:
description:
boot firmware is incorrectly passing the address in big-endian order
+ nvmem-cells:
+ maxItems: 1
+ description:
+ Nvmem data cell that contains a 6 byte BD address with the most
+ significant byte first (big-endian).
+
+ nvmem-cell-names:
+ const: local-bd-address
+
additionalProperties: true
--
2.34.1
^ permalink raw reply related
* [PATCH v4 4/8] block: implement NVMEM provider
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
From: Daniel Golle <daniel@makrotopia.org>
On embedded devices using an eMMC it is common that one or more partitions
on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
data. Allow referencing the partition in device tree for the kernel and
Wi-Fi drivers accessing it via the NVMEM layer.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
block/Kconfig | 9 +++++
block/Makefile | 1 +
block/blk-nvmem.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 124 insertions(+)
diff --git a/block/Kconfig b/block/Kconfig
index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
by falling back to the kernel crypto API when inline
encryption hardware is not present.
+config BLK_NVMEM
+ bool "Block device NVMEM provider"
+ depends on OF
+ depends on NVMEM
+ help
+ Allow block devices (or partitions) to act as NVMEM providers,
+ typically used with eMMC to store MAC addresses or Wi-Fi
+ calibration data on embedded devices.
+
source "block/partitions/Kconfig"
config BLK_PM
diff --git a/block/Makefile b/block/Makefile
index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
blk-crypto-sysfs.o
obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
+obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
new file mode 100644
index 0000000000000000000000000000000000000000..a6e62fa98675ee9bcb9c7035a611b5a573ab9091
--- /dev/null
+++ b/block/blk-nvmem.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * block device NVMEM provider
+ *
+ * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * Useful on devices using a partition on an eMMC for MAC addresses or
+ * Wi-Fi calibration EEPROM data.
+ */
+
+#include <linux/file.h>
+#include <linux/nvmem-provider.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/pagemap.h>
+#include <linux/property.h>
+
+#include "blk.h"
+
+static int blk_nvmem_reg_read(void *priv, unsigned int from,
+ void *val, size_t bytes)
+{
+ blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
+ dev_t devt = (dev_t)(uintptr_t)priv;
+ size_t bytes_left = bytes;
+ loff_t pos = from;
+ int ret = 0;
+
+ struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
+ if (IS_ERR(bdev_file))
+ return PTR_ERR(bdev_file);
+
+ while (bytes_left) {
+ pgoff_t f_index = pos >> PAGE_SHIFT;
+ struct folio *folio;
+ size_t folio_off;
+ size_t to_read;
+
+ folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
+ if (IS_ERR(folio)) {
+ ret = PTR_ERR(folio);
+ break;
+ }
+
+ folio_off = offset_in_folio(folio, pos);
+ to_read = min(bytes_left, folio_size(folio) - folio_off);
+ memcpy_from_folio(val, folio, folio_off, to_read);
+ pos += to_read;
+ bytes_left -= to_read;
+ val += to_read;
+ folio_put(folio);
+ }
+
+ return ret;
+}
+
+static int blk_nvmem_register(struct device *dev)
+{
+ struct block_device *bdev = dev_to_bdev(dev);
+ struct nvmem_config config = {};
+
+ /* skip devices which do not have a device tree node */
+ if (!dev_of_node(dev))
+ return 0;
+
+ /* skip devices without an nvmem layout defined */
+ struct device_node *child __free(device_node) =
+ of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
+ if (!child)
+ return 0;
+
+ /*
+ * skip block device too large to be represented as NVMEM devices,
+ * the NVMEM reg_read callback uses an unsigned int offset
+ */
+ if (bdev_nr_bytes(bdev) > UINT_MAX) {
+ dev_warn(dev, "block device too large to be an NVMEM provider\n");
+ return -ENODEV;
+ }
+
+ config.id = NVMEM_DEVID_NONE;
+ config.dev = dev;
+ config.name = dev_name(dev);
+ config.owner = THIS_MODULE;
+ config.priv = (void *)(uintptr_t)dev->devt;
+ config.reg_read = blk_nvmem_reg_read;
+ config.size = bdev_nr_bytes(bdev);
+ config.word_size = 1;
+ config.stride = 1;
+ config.read_only = true;
+ config.root_only = true;
+ config.ignore_wp = true;
+ config.of_node = to_of_node(dev->fwnode);
+
+ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
+}
+
+static struct class_interface blk_nvmem_bus_interface __refdata = {
+ .class = &block_class,
+ .add_dev = &blk_nvmem_register,
+};
+
+static int __init blk_nvmem_init(void)
+{
+ int ret;
+
+ ret = class_interface_register(&blk_nvmem_bus_interface);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+device_initcall(blk_nvmem_init);
--
2.34.1
^ permalink raw reply related
* [PATCH v4 6/8] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
Some devices store the Bluetooth BD address in non-volatile
memory, which can be accessed through the NVMEM framework.
Similar to Ethernet or WiFi MAC addresses, add support for
reading the BD address from a 'local-bd-address' NVMEM cell.
As with the device-tree provided BD address, add a quirk to
indicate whether a device or platform should attempt to read
the address from NVMEM when no valid in-chip address is present.
Also add a quirk to indicate if the address is stored in
big-endian byte order.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
include/net/bluetooth/hci.h | 18 ++++++++++++++++++
net/bluetooth/hci_sync.c | 39 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 572b1c620c5d653a1fe10b26c1b0ba33e8f4968f..7686466d1109253b0d75edeb5f6a99fb98ce4cc6 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -164,6 +164,24 @@ enum {
*/
HCI_QUIRK_BDADDR_PROPERTY_BROKEN,
+ /* When this quirk is set, the public Bluetooth address
+ * initially reported by HCI Read BD Address command
+ * is considered invalid. The public BD Address can be
+ * retrieved via a 'local-bd-address' NVMEM cell.
+ *
+ * This quirk can be set before hci_register_dev is called or
+ * during the hdev->setup vendor callback.
+ */
+ HCI_QUIRK_USE_BDADDR_NVMEM,
+
+ /* When this quirk is set, the Bluetooth Device Address provided by
+ * the 'local-bd-address' NVMEM is stored in big-endian order.
+ *
+ * This quirk can be set before hci_register_dev is called or
+ * during the hdev->setup vendor callback.
+ */
+ HCI_QUIRK_BDADDR_NVMEM_BE,
+
/* When this quirk is set, the duplicate filtering during
* scanning is based on Bluetooth devices addresses. To allow
* RSSI based updates, restart scanning if needed.
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index fd3aacdea512a37c22b9a2be90c89ddca4b4d99f..589ccdfa26c1281d6eb979370523fff0d7920302 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -7,6 +7,7 @@
*/
#include <linux/property.h>
+#include <linux/of_net.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -3588,6 +3589,37 @@ int hci_powered_update_sync(struct hci_dev *hdev)
return 0;
}
+/**
+ * hci_dev_get_bd_addr_from_nvmem - Get the Bluetooth Device Address
+ * (BD_ADDR) for a HCI device from
+ * an NVMEM cell.
+ * @hdev: The HCI device
+ *
+ * Search for 'local-bd-address' NVMEM cell in the device firmware node.
+ *
+ * All-zero BD addresses are rejected (unprovisioned).
+ */
+static int hci_dev_get_bd_addr_from_nvmem(struct hci_dev *hdev)
+{
+ struct device_node *np = dev_of_node(hdev->dev.parent);
+ u8 ba[sizeof(bdaddr_t)];
+ int err;
+
+ if (!np)
+ return -ENODEV;
+
+ err = of_get_nvmem_eui48(np, "local-bd-address", ba);
+ if (err)
+ return err;
+
+ if (hci_test_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE))
+ baswap(&hdev->public_addr, (bdaddr_t *)ba);
+ else
+ bacpy(&hdev->public_addr, (bdaddr_t *)ba);
+
+ return 0;
+}
+
/**
* hci_dev_get_bd_addr_from_property - Get the Bluetooth Device Address
* (BD_ADDR) for a HCI device from
@@ -5042,12 +5074,17 @@ static int hci_dev_setup_sync(struct hci_dev *hdev)
* its setup callback.
*/
invalid_bdaddr = hci_test_quirk(hdev, HCI_QUIRK_INVALID_BDADDR) ||
- hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+ hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY) ||
+ hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
if (!ret) {
if (hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY) &&
!bacmp(&hdev->public_addr, BDADDR_ANY))
hci_dev_get_bd_addr_from_property(hdev);
+ if (hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM) &&
+ !bacmp(&hdev->public_addr, BDADDR_ANY))
+ hci_dev_get_bd_addr_from_nvmem(hdev);
+
if (invalid_bdaddr && bacmp(&hdev->public_addr, BDADDR_ANY) &&
hdev->set_bdaddr) {
ret = hdev->set_bdaddr(hdev, &hdev->public_addr);
--
2.34.1
^ permalink raw reply related
* [PATCH v4 7/8] Bluetooth: qca: Set NVMEM BD address quirks when address is invalid
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
When the controller BD address is invalid (zero or default),
set the NVMEM quirks to allow retrieving the address from a
'local-bd-address' NVMEM cell. The BD address is often stored
alongside the WiFi MAC address in big-endian format, so also
set the big-endian quirk.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/bluetooth/btqca.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index dda76365726f0bfe0e80e05fe04859fa4f0592e1..df33eacfd29fa680f393f90215150743e6001d5b 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -721,8 +721,11 @@ static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *co
}
bda = (struct hci_rp_read_bd_addr *)skb->data;
- if (!bacmp(&bda->bdaddr, &config->bdaddr))
+ if (!bacmp(&bda->bdaddr, &config->bdaddr)) {
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
+ hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
+ }
kfree_skb(skb);
--
2.34.1
^ permalink raw reply related
* [PATCH v4 8/8] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Konrad Dybcio, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
On Arduino Uno-Q, the eMMC boot1 partition is factory provisioned
with device-specific information such as the WiFi MAC address
and the Bluetooth BD address. This partition can serve as an
alternative to additional non-volatile memory, such as a
dedicated EEPROM.
The eMMC boot partitions are typically good candidates, as they
are relatively small, read-only by default (and can be enforced
as hardware read-only), and are not affected by board reflashing
procedures, which generally target the eMMC user or GP partitions.
Describe the corresponding nvmem-layout for the WiFi and Bluetooth
addresses, and point the WiFi and Bluetooth nodes to the appropriate
NVMEM cells to retrieve them.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts | 39 ++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
index bf088fa9807f040f0c8f405f9111b01790b09377..128c7a7e76b5b089044745f5d6407d6391055fc2 100644
--- a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
+++ b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
@@ -409,7 +409,40 @@ &sdhc_1 {
no-sdio;
no-sd;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
status = "okay";
+
+ card@0 {
+ compatible = "mmc-card";
+ reg = <0>;
+
+ partitions-boot1 {
+ compatible = "fixed-partitions";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ nvmem-layout {
+ compatible = "fixed-layout";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ wifi_mac_addr: mac-addr@4400 {
+ compatible = "mac-base";
+ reg = <0x4400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+
+ bd_addr: bd-addr@5400 {
+ compatible = "mac-base";
+ reg = <0x5400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+ };
+ };
+ };
};
&spi5 {
@@ -512,6 +545,9 @@ bluetooth {
vddch0-supply = <&pm4125_l22>;
enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>;
max-speed = <3000000>;
+
+ nvmem-cells = <&bd_addr 0>;
+ nvmem-cell-names = "local-bd-address";
};
};
@@ -557,6 +593,9 @@ &wifi {
qcom,ath10k-calibration-variant = "ArduinoImola";
firmware-name = "qcm2290";
+ nvmem-cells = <&wifi_mac_addr 0>;
+ nvmem-cell-names = "mac-address";
+
status = "okay";
};
--
2.34.1
^ permalink raw reply related
* [PATCH v4 5/8] net: of_net: Add of_get_nvmem_eui48() helper for EUI-48 lookup
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
Factor out the common NVMEM EUI-48 retrieval logic from
of_get_mac_address_nvmem() into a new of_get_nvmem_eui48() helper that
accepts the NVMEM cell name as a parameter. This allows other subsystems
(e.g. Bluetooth) to reuse the same lookup-validate-copy pattern with a
different cell name, without duplicating code.
of_get_mac_address_nvmem() is updated to call of_get_nvmem_eui48() with
"mac-address", preserving its existing behavior.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
include/linux/of_net.h | 7 +++++++
net/core/of_net.c | 49 +++++++++++++++++++++++++++++++++++++------------
2 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/include/linux/of_net.h b/include/linux/of_net.h
index d88715a0b3a52f87af23d47791bea3baf5be5200..7854ba555d9a55f3d020a37fe00a27ae52e0e5dc 100644
--- a/include/linux/of_net.h
+++ b/include/linux/of_net.h
@@ -15,6 +15,7 @@ struct net_device;
extern int of_get_phy_mode(struct device_node *np, phy_interface_t *interface);
extern int of_get_mac_address(struct device_node *np, u8 *mac);
extern int of_get_mac_address_nvmem(struct device_node *np, u8 *mac);
+int of_get_nvmem_eui48(struct device_node *np, const char *cell_name, u8 *addr);
int of_get_ethdev_address(struct device_node *np, struct net_device *dev);
extern struct net_device *of_find_net_device_by_node(struct device_node *np);
#else
@@ -34,6 +35,12 @@ static inline int of_get_mac_address_nvmem(struct device_node *np, u8 *mac)
return -ENODEV;
}
+static inline int of_get_nvmem_eui48(struct device_node *np,
+ const char *cell_name, u8 *addr)
+{
+ return -ENODEV;
+}
+
static inline int of_get_ethdev_address(struct device_node *np, struct net_device *dev)
{
return -ENODEV;
diff --git a/net/core/of_net.c b/net/core/of_net.c
index 93ea425b9248a23f4f95a336e9cdbf0053248e32..75341c186123e949fbe21f1e51fce3ac74d4f56b 100644
--- a/net/core/of_net.c
+++ b/net/core/of_net.c
@@ -61,9 +61,7 @@ static int of_get_mac_addr(struct device_node *np, const char *name, u8 *addr)
int of_get_mac_address_nvmem(struct device_node *np, u8 *addr)
{
struct platform_device *pdev = of_find_device_by_node(np);
- struct nvmem_cell *cell;
- const void *mac;
- size_t len;
+ u8 mac[ETH_ALEN];
int ret;
/* Try lookup by device first, there might be a nvmem_cell_lookup
@@ -75,27 +73,54 @@ int of_get_mac_address_nvmem(struct device_node *np, u8 *addr)
return ret;
}
- cell = of_nvmem_cell_get(np, "mac-address");
+ ret = of_get_nvmem_eui48(np, "mac-address", mac);
+ if (ret)
+ return ret;
+
+ if (!is_valid_ether_addr(mac))
+ return -EINVAL;
+
+ ether_addr_copy(addr, mac);
+ return 0;
+}
+EXPORT_SYMBOL(of_get_mac_address_nvmem);
+
+/**
+ * of_get_nvmem_eui48 - Read a 6-byte EUI-48 address from a named NVMEM cell.
+ * @np: Device node to look up the NVMEM cell from.
+ * @cell_name: Name of the NVMEM cell (e.g. "mac-address", "local-bd-address").
+ * @addr: Output buffer for the 6-byte address.
+ *
+ * Reads the named NVMEM cell and validates that it contains a non-zero 6-byte
+ * address. Returns 0 on success, negative errno on failure.
+ */
+int of_get_nvmem_eui48(struct device_node *np, const char *cell_name, u8 *addr)
+{
+ struct nvmem_cell *cell;
+ const void *eui48;
+ size_t len;
+
+ cell = of_nvmem_cell_get(np, cell_name);
if (IS_ERR(cell))
return PTR_ERR(cell);
- mac = nvmem_cell_read(cell, &len);
+ eui48 = nvmem_cell_read(cell, &len);
nvmem_cell_put(cell);
- if (IS_ERR(mac))
- return PTR_ERR(mac);
+ if (IS_ERR(eui48))
+ return PTR_ERR(eui48);
- if (len != ETH_ALEN || !is_valid_ether_addr(mac)) {
- kfree(mac);
+ if (len != ETH_ALEN || !memchr_inv(eui48, 0, ETH_ALEN)) {
+ kfree(eui48);
return -EINVAL;
}
- memcpy(addr, mac, ETH_ALEN);
- kfree(mac);
+ memcpy(addr, eui48, ETH_ALEN);
+ kfree(eui48);
return 0;
}
-EXPORT_SYMBOL(of_get_mac_address_nvmem);
+EXPORT_SYMBOL_GPL(of_get_nvmem_eui48);
/**
* of_get_mac_address()
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v4 4/8] block: implement NVMEM provider
From: Bartosz Golaszewski @ 2026-06-09 8:52 UTC (permalink / raw)
To: Loic Poulain
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
In-Reply-To: <20260609-block-as-nvmem-v4-4-45712e6b22c6@oss.qualcomm.com>
On Tue, 9 Jun 2026 09:52:29 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> From: Daniel Golle <daniel@makrotopia.org>
>
> On embedded devices using an eMMC it is common that one or more partitions
> on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
> data. Allow referencing the partition in device tree for the kernel and
> Wi-Fi drivers accessing it via the NVMEM layer.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
> block/Kconfig | 9 +++++
> block/Makefile | 1 +
> block/blk-nvmem.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 124 insertions(+)
>
> diff --git a/block/Kconfig b/block/Kconfig
> index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
> --- a/block/Kconfig
> +++ b/block/Kconfig
> @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
> by falling back to the kernel crypto API when inline
> encryption hardware is not present.
>
> +config BLK_NVMEM
> + bool "Block device NVMEM provider"
> + depends on OF
> + depends on NVMEM
> + help
> + Allow block devices (or partitions) to act as NVMEM providers,
> + typically used with eMMC to store MAC addresses or Wi-Fi
> + calibration data on embedded devices.
> +
> source "block/partitions/Kconfig"
>
> config BLK_PM
> diff --git a/block/Makefile b/block/Makefile
> index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
> --- a/block/Makefile
> +++ b/block/Makefile
> @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
> blk-crypto-sysfs.o
> obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
> obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
> +obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
> diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..a6e62fa98675ee9bcb9c7035a611b5a573ab9091
> --- /dev/null
> +++ b/block/blk-nvmem.c
> @@ -0,0 +1,114 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * block device NVMEM provider
> + *
> + * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + * Useful on devices using a partition on an eMMC for MAC addresses or
> + * Wi-Fi calibration EEPROM data.
> + */
> +
> +#include <linux/file.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/pagemap.h>
> +#include <linux/property.h>
> +
> +#include "blk.h"
> +
> +static int blk_nvmem_reg_read(void *priv, unsigned int from,
> + void *val, size_t bytes)
> +{
> + blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> + dev_t devt = (dev_t)(uintptr_t)priv;
> + size_t bytes_left = bytes;
> + loff_t pos = from;
> + int ret = 0;
> +
> + struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
> + if (IS_ERR(bdev_file))
> + return PTR_ERR(bdev_file);
> +
> + while (bytes_left) {
> + pgoff_t f_index = pos >> PAGE_SHIFT;
> + struct folio *folio;
> + size_t folio_off;
> + size_t to_read;
> +
> + folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
> + if (IS_ERR(folio)) {
> + ret = PTR_ERR(folio);
> + break;
> + }
> +
> + folio_off = offset_in_folio(folio, pos);
> + to_read = min(bytes_left, folio_size(folio) - folio_off);
> + memcpy_from_folio(val, folio, folio_off, to_read);
> + pos += to_read;
> + bytes_left -= to_read;
> + val += to_read;
> + folio_put(folio);
> + }
> +
> + return ret;
> +}
> +
> +static int blk_nvmem_register(struct device *dev)
> +{
> + struct block_device *bdev = dev_to_bdev(dev);
> + struct nvmem_config config = {};
> +
> + /* skip devices which do not have a device tree node */
> + if (!dev_of_node(dev))
> + return 0;
> +
> + /* skip devices without an nvmem layout defined */
> + struct device_node *child __free(device_node) =
> + of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
> + if (!child)
> + return 0;
> +
> + /*
> + * skip block device too large to be represented as NVMEM devices,
> + * the NVMEM reg_read callback uses an unsigned int offset
> + */
> + if (bdev_nr_bytes(bdev) > UINT_MAX) {
> + dev_warn(dev, "block device too large to be an NVMEM provider\n");
> + return -ENODEV;
Wait, I must have suggested -ENODEV here on too little coffee. This callback
is called from device_add(), not when the device is bound so it's not the same
thing as returning -ENODEV from probe().
On the other hand, we don't want to not provide the block device just because
someone added a DT property on one that's too big. I'd say: warn, but return 0.
Does it make sense?
> + }
> +
> + config.id = NVMEM_DEVID_NONE;
> + config.dev = dev;
> + config.name = dev_name(dev);
> + config.owner = THIS_MODULE;
> + config.priv = (void *)(uintptr_t)dev->devt;
> + config.reg_read = blk_nvmem_reg_read;
> + config.size = bdev_nr_bytes(bdev);
> + config.word_size = 1;
> + config.stride = 1;
> + config.read_only = true;
> + config.root_only = true;
> + config.ignore_wp = true;
> + config.of_node = to_of_node(dev->fwnode);
> +
> + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
And that was a wrong suggestion on my part too because I was under the
impression that we're in the probe() path, not device_add(). You can't use
devres here as the device at this point is not yet bound and may never be.
Which leads me to the second point: this is not the moment to add the nvmem
provider. This should happen at or after probe(). Once nvmem_register()
returns, you have a visible nvmem resource but nothing backing it in the block
layer.
Either do this in block core when registering a new device or schedule
a notifier here for the BUS_NOTIFY_BOUND_DRIVER event and do it in the notifier
callback.
Sorry, I should have paid more attention, I forgot how the class interface
works.
> +}
> +
> +static struct class_interface blk_nvmem_bus_interface __refdata = {
> + .class = &block_class,
> + .add_dev = &blk_nvmem_register,
> +};
> +
> +static int __init blk_nvmem_init(void)
> +{
> + int ret;
> +
> + ret = class_interface_register(&blk_nvmem_bus_interface);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +device_initcall(blk_nvmem_init);
>
> --
> 2.34.1
>
>
Bart
^ permalink raw reply
* [ath-current] wifi: ath11k: fix NULL pointer dereference in ath11k_hal_srng_access_begin
From: Gaole Zhang @ 2026-06-09 9:06 UTC (permalink / raw)
To: ath11k, linux-wireless; +Cc: gaole.zhang, miaoqing.pan, hangtian.zhu
In ATH11K_QMI_EVENT_FW_READY, ATH11K_FLAG_REGISTERED is set
unconditionally even when ath11k_core_qmi_firmware_ready() fails.
This leaves the driver in an inconsistent state where
initialization is considered complete although the firmware ready
handling did not finish successfully. During the subsequent SSR,
the driver enters the restart path based on this incorrect state
and dereferences uninitialized srng members, resulting in a NULL
pointer dereference.
Call trace:
ath11k_hal_srng_access_begin+0xc/0x60 [ath11k] (P)
ath11k_ce_cleanup_pipes+0x17c/0x180 [ath11k]
ath11k_core_restart+0x40/0x168 [ath11k]
Fix this by:
- skipping firmware_ready if ATH11K_FLAG_REGISTERED is already set
- setting ATH11K_FLAG_REGISTERED only when firmware_ready succeeds
- setting ATH11K_FLAG_QMI_FAIL and aborting the FW_READY handling
on error
Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.2.0.c2-00204-QCAMSLSWPLZ-1
Fixes: 6fe62a8cec51c ("wifi: ath11k: Add cold boot calibration support on WCN6750")
Signed-off-by: Gaole Zhang <gaole.zhang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath11k/qmi.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index feebbc30f3df..a304feca7042 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -3295,9 +3295,14 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work)
clear_bit(ATH11K_FLAG_CRASH_FLUSH,
&ab->dev_flags);
clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags);
- ath11k_core_qmi_firmware_ready(ab);
- set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags);
-
+ if (!test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) {
+ ret = ath11k_core_qmi_firmware_ready(ab);
+ if (ret) {
+ set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags);
+ break;
+ }
+ set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags);
+ }
break;
case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE:
break;
base-commit: dc14686f27df6454b13b16ad1c9203ab3e9b0375
--
2.34.1
^ permalink raw reply related
* Re: [PATCH] wifi: rtlwifi: rtl8723be: Remove unnecessary irq save/restore in hw_init()
From: William Hansen-Baird @ 2026-06-09 9:21 UTC (permalink / raw)
To: Ping-Ke Shih, William Hansen-Baird
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <8b562a921c7944819e4eea862b1b53b3@realtek.com>
Thanks for the review. I'll fix the commit message and send a v2 with your ack included.
^ permalink raw reply
* [PATCH] wifi: ath11k: fix refcount leak in ath11k_ahb_fw_resources_init()
From: Wentao Liang @ 2026-06-09 9:25 UTC (permalink / raw)
To: jjohnson; +Cc: linux-wireless, ath11k, linux-kernel, Wentao Liang, stable
of_get_child_by_name() returns a node pointer with refcount
incremented, but the error path when ath11k_ahb_setup_msa_resources()
fails does not release it. Add the missing of_node_put() to avoid
leaking the reference.
Cc: stable@vger.kernel.org
Fixes: 095cb947490c ("wifi: ath11k: allow missing memory-regions")
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
---
drivers/net/wireless/ath/ath11k/ahb.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index 08d3a0c8f105..8a08275db40a 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -996,6 +996,7 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab)
ret = ath11k_ahb_setup_msa_resources(ab);
if (ret) {
ath11k_err(ab, "failed to setup msa resources\n");
+ of_node_put(node);
return ret;
}
--
2.34.1
^ permalink raw reply related
* [PATCH] wifi: mac80211_hwsim: fix destroy-on-close UAF in netlink handlers
From: Dominik 'Disconnect3d' Czarnota @ 2026-06-09 9:32 UTC (permalink / raw)
To: Johannes Berg
Cc: Jukka Rissanen, linux-wireless, linux-kernel,
Dominik 'Disconnect3d' Czarnota
mac80211_hwsim is a developer testing driver for simulated 802.11
radios and is not used for normal wireless LAN operation.
Its generic-netlink handlers can look up a radio by MAC address and then
continue using the returned hwsim data after the rhashtable lookup has
completed. A destroy_on_close netlink socket can concurrently remove that
radio from the global table and unregister/free the ieee80211_hw, leaving
the handler with stale hwsim_data, wdev or PMSR request pointers. This
can lead to a use-after-free.
Make address lookup take an active radio reference under hwsim_radio_lock.
Drop that reference at the end of each netlink handler that uses the
lookup helper. During radio deletion, drop the initial reference and wait
for active handlers to finish before unregistering and freeing the hw.
Fixes: e9ed49bf4c2c ("mac80211-hwsim: Add support for HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE")
Assisted-by: Codex:gpt-5.5-cyber-preview
Signed-off-by: Dominik 'Disconnect3d' Czarnota <dominik.czarnota@trailofbits.com>
---
The issue was reproduced on a KASAN kernel with a PMSR-based netlink
race reproducer. The report showed a slab-use-after-free in
cfg80211_pmsr_report() from hwsim_pmsr_report_nl(). I can share the
reproducer and full QEMU log if useful.
drivers/net/wireless/virtual/mac80211_hwsim.c | 37 ++++++++++++++++++++++++---
1 file changed, 34 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 1fcf5d0d2e13f..0cc9d06b98041 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -15,6 +15,7 @@
*/
#include <linux/list.h>
+#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <net/dst.h>
@@ -31,6 +32,7 @@
#include <net/genetlink.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
+#include <linux/refcount.h>
#include <linux/rhashtable.h>
#include <linux/nospec.h>
#include <linux/virtio.h>
@@ -688,6 +690,8 @@ struct mac80211_hwsim_data {
int channels, idx;
bool use_chanctx;
bool destroy_on_close;
+ refcount_t ref;
+ struct completion ref_done;
u32 portid;
char alpha2[2];
const struct ieee80211_regdomain *regd;
@@ -795,7 +799,22 @@ struct hwsim_radiotap_ack_hdr {
static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr)
{
- return rhashtable_lookup_fast(&hwsim_radios_rht, addr, hwsim_rht_params);
+ struct mac80211_hwsim_data *data;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ data = rhashtable_lookup_fast(&hwsim_radios_rht, addr,
+ hwsim_rht_params);
+ if (data && !refcount_inc_not_zero(&data->ref))
+ data = NULL;
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ return data;
+}
+
+static void hwsim_data_put(struct mac80211_hwsim_data *data)
+{
+ if (refcount_dec_and_test(&data->ref))
+ complete(&data->ref_done);
}
/* MAC80211_HWSIM netlink family */
@@ -4124,6 +4143,7 @@ static int hwsim_pmsr_report_nl(struct sk_buff *msg, struct genl_info *info)
data->pmsr_request_wdev = NULL;
mutex_unlock(&data->mutex);
+ hwsim_data_put(data);
return err;
}
@@ -5620,6 +5640,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
data = hw->priv;
data->hw = hw;
+ refcount_set(&data->ref, 1);
+ init_completion(&data->ref_done);
data->dev = device_create(&hwsim_class, NULL, 0, hw, "hwsim%d", idx);
if (IS_ERR(data->dev)) {
@@ -6127,6 +6149,9 @@ static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data,
const char *hwname,
struct genl_info *info)
{
+ hwsim_data_put(data);
+ wait_for_completion(&data->ref_done);
+
hwsim_mcast_del_radio(data->idx, hwname, info);
debugfs_remove_recursive(data->debugfs);
ieee80211_unregister_hw(data->hw);
@@ -6239,7 +6264,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
{
struct ieee80211_hdr *hdr;
- struct mac80211_hwsim_data *data2;
+ struct mac80211_hwsim_data *data2 = NULL;
struct ieee80211_tx_info *txi;
struct hwsim_tx_rate *tx_attempts;
u64 ret_skb_cookie;
@@ -6326,8 +6351,11 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
txi->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
ieee80211_tx_status_irqsafe(data2->hw, skb);
+ hwsim_data_put(data2);
return 0;
out:
+ if (data2)
+ hwsim_data_put(data2);
return -EINVAL;
}
@@ -6335,7 +6363,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
struct genl_info *info)
{
- struct mac80211_hwsim_data *data2;
+ struct mac80211_hwsim_data *data2 = NULL;
struct ieee80211_rx_status rx_status;
struct ieee80211_hdr *hdr;
const u8 *dst;
@@ -6439,10 +6467,13 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
mac80211_hwsim_rx(data2, &rx_status, skb);
+ hwsim_data_put(data2);
return 0;
err:
pr_debug("mac80211_hwsim: error occurred in %s\n", __func__);
out:
+ if (data2)
+ hwsim_data_put(data2);
dev_kfree_skb(skb);
return -EINVAL;
}
^ permalink raw reply related
* Re: [PATCH v2] wifi: mt76: mt76u: use a threaded NAPI for the RX path
From: Lorenzo Bianconi @ 2026-06-09 9:45 UTC (permalink / raw)
To: Filip Bakreski; +Cc: nbd, ryder.lee, shayne.chen, sean.wang, linux-wireless
In-Reply-To: <20260609003224.132191-1-phial@phiality.com>
[-- Attachment #1: Type: text/plain, Size: 5719 bytes --]
> The USB RX path delivers frames to the stack via mt76_rx_complete() with
> a NULL napi pointer, taking the netif_receive_skb_list() path, so it never
> benefits from GRO -- unlike the DMA-based mt76 drivers, which pass a real
> napi and use napi_gro_receive(). For bulk TCP traffic this is costly, as
> every segment traverses the stack individually.
>
> Service the MT_RXQ_MAIN queue from a threaded NAPI, reusing mt76_dev's
> existing napi_dev and napi[] rather than adding new fields. The URB
> completion handler schedules the napi; its poll drains the URBs, builds
> the skbs, resubmits and delivers them through napi_gro_receive(). The MCU
> queue stays on the existing RX worker. This enables GRO and moves RX
> processing into its own kernel thread, parallelising the datapath.
>
> On mt7921u at HE-MCS 11 (2x2, 80 MHz; fast.com, multiple streams) this
> averages ~588 Mbit/s, versus ~424 Mbit/s when the same napi is instead
> driven manually from the RX worker, and ~380 Mbit/s for the unmodified
> driver.
>
> Suggested-by: Lorenzo Bianconi <lorenzo@kernel.org>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Filip Bakreski <phial@phiality.com>
> ---
> v2:
> - Service MT_RXQ_MAIN from a threaded NAPI instead of a NAPI driven
> manually from the RX worker; on mt7921u the threaded variant measured
> ~39% faster (~588 vs ~424 Mbit/s, fast.com) (Lorenzo Bianconi).
> - Reuse mt76_dev's existing napi_dev/napi[] instead of adding new fields
> to struct mt76_usb (Lorenzo Bianconi).
>
> v1: https://lore.kernel.org/linux-wireless/20260608044109.31730-1-phial@phiality.com/
Hi Filip,
I guess the patch is fine, just a couple of nits inline.
Regards,
Lorenzo
>
> drivers/net/wireless/mediatek/mt76/usb.c | 56 +++++++++++++++++++++---
> 1 file changed, 49 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
> index d9638a9b7..aef8f855f 100644
> --- a/drivers/net/wireless/mediatek/mt76/usb.c
> +++ b/drivers/net/wireless/mediatek/mt76/usb.c
> @@ -580,7 +580,10 @@ static void mt76u_complete_rx(struct urb *urb)
>
> q->head = (q->head + 1) % q->ndesc;
> q->queued++;
> - mt76_worker_schedule(&dev->usb.rx_worker);
nit: new-line here.
> + if (q == &dev->q_rx[MT_RXQ_MAIN])
> + napi_schedule(&dev->napi[MT_RXQ_MAIN]);
> + else
> + mt76_worker_schedule(&dev->usb.rx_worker);
> out:
> spin_unlock_irqrestore(&q->lock, flags);
> }
> @@ -618,11 +621,23 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
> }
> mt76u_submit_rx_buf(dev, qid, urb);
> }
> - if (qid == MT_RXQ_MAIN) {
> - local_bh_disable();
> - mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
> - local_bh_enable();
> - }
> +}
> +
> +/* Threaded NAPI poll for the MAIN RX queue: drain URBs, build skbs, resubmit,
> + * then deliver through napi_gro_receive() and let napi_complete() flush GRO.
> + */
> +static int mt76u_napi_poll(struct napi_struct *napi, int budget)
> +{
> + struct mt76_dev *dev = mt76_priv(napi->dev);
> +
> + rcu_read_lock();
> + mt76u_process_rx_queue(dev, &dev->q_rx[MT_RXQ_MAIN]);
> + mt76_rx_poll_complete(dev, MT_RXQ_MAIN, napi);
> + rcu_read_unlock();
> +
> + napi_complete(napi);
> +
> + return 0;
> }
>
> static void mt76u_rx_worker(struct mt76_worker *w)
> @@ -632,8 +647,12 @@ static void mt76u_rx_worker(struct mt76_worker *w)
> int i;
>
> rcu_read_lock();
> - mt76_for_each_q_rx(dev, i)
> + mt76_for_each_q_rx(dev, i) {
> + /* MT_RXQ_MAIN is serviced by the threaded NAPI poll */
> + if (i == MT_RXQ_MAIN)
> + continue;
nit: new-line here.
> mt76u_process_rx_queue(dev, &dev->q_rx[i]);
> + }
> rcu_read_unlock();
> }
>
> @@ -723,6 +742,8 @@ void mt76u_stop_rx(struct mt76_dev *dev)
> int i;
>
> mt76_worker_disable(&dev->usb.rx_worker);
> + if (dev->napi_dev)
> + napi_disable(&dev->napi[MT_RXQ_MAIN]);
>
> mt76_for_each_q_rx(dev, i) {
> struct mt76_queue *q = &dev->q_rx[i];
> @@ -751,6 +772,8 @@ int mt76u_resume_rx(struct mt76_dev *dev)
> }
>
> mt76_worker_enable(&dev->usb.rx_worker);
> + if (dev->napi_dev)
> + napi_enable(&dev->napi[MT_RXQ_MAIN]);
>
> return 0;
> }
> @@ -1051,6 +1074,13 @@ void mt76u_queues_deinit(struct mt76_dev *dev)
> mt76u_stop_rx(dev);
> mt76u_stop_tx(dev);
>
> + /* mt76u_stop_rx() (above) already napi_disable()d the MAIN queue */
> + if (dev->napi_dev) {
> + netif_napi_del(&dev->napi[MT_RXQ_MAIN]);
> + free_netdev(dev->napi_dev);
> + dev->napi_dev = NULL;
> + }
> +
> mt76u_free_rx(dev);
> mt76u_free_tx(dev);
> }
> @@ -1115,6 +1145,18 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
> sched_set_fifo_low(usb->rx_worker.task);
> sched_set_fifo_low(usb->status_worker.task);
>
> + /* threaded NAPI on a dummy netdev (reusing mt76_dev's napi_dev/napi[])
> + * services the MAIN RX queue and gives the RX path GRO
> + */
> + dev->napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *));
> + if (!dev->napi_dev)
> + return -ENOMEM;
nit: new-line here.
> + *(struct mt76_dev **)netdev_priv(dev->napi_dev) = dev;
To make the code more readable, I guess you can define priv pointer similar to mt76_dma_init().
> + strscpy(dev->napi_dev->name, "mt76u-rx", sizeof(dev->napi_dev->name));
> + dev->napi_dev->threaded = 1;
> + netif_napi_add(dev->napi_dev, &dev->napi[MT_RXQ_MAIN], mt76u_napi_poll);
> + napi_enable(&dev->napi[MT_RXQ_MAIN]);
> +
> return 0;
> }
> EXPORT_SYMBOL_GPL(__mt76u_init);
>
> base-commit: 5f6099446d1ddb888e36cdf93b6a0551f05c1267
> --
> 2.54.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH rtw-next v2] wifi: rtlwifi: rtl8723be: Remove unnecessary irq save/restore in hw_init()
From: William Hansen-Baird @ 2026-06-09 9:53 UTC (permalink / raw)
To: pkshih; +Cc: linux-wireless, linux-kernel, William Hansen-Baird
In-Reply-To: <20260608135345.2526325-3-williamhb+k@fastmail.com>
rtl8723be hw_init() calls local_save_flags(flags) followed by
local_irq_enable(). Later, local_irq_restore(flags) is called.
This causes warnings from Lockdep on boot and modprobe,
as local_irq_restore(flags) should only be called while irqs are disabled.
The warning was introduced to detect this class of bug in [1].
With testing I found that all paths which call hw_init() have irqs
already enabled for rtl8723be.
Furthermore, the calls were originally added for the rtl8192ce
in commit f78bccd79ba3 ("rtlwifi: rtl8192ce: Fix too long disable of IRQs")
before later being added to most other rtlwifi drivers.
Commit d3feae41a347 ("rtlwifi: Update power-save routines for 062814 driver")
then replaces the call to spin_lock_irqsave() before hw_init(),
and thus the codepath which caused irqs to be disabled in hw_init and
prompted the original commit has been removed.
The same irq save/restore pattern is also present in the hw_init() of
rtl8192ce, rtl8723ae, rtl8188ee, rtl8192se and rtl8192cu,
however I don't have the hardware to test them,
so I did not include them in my changes.
Tested on a Razer Blade 14 2017.
Example of output from Lockdep prior to fix:
raw_local_irq_restore() called with IRQs enabled
...
Call Trace:
<TASK>
rtl8723be_hw_init+0x5992/0x7220 [rtl8723be]
? static_obj+0x61/0xa0
rtl_pci_start+0x222/0x5c0 [rtl_pci]
rtl_op_start+0x128/0x1a0 [rtlwifi]
? __kasan_check_read+0x11/0x20
drv_start+0x16c/0x550 [mac80211]
...
irq event stamp: 887679
hardirqs last enabled at (887689): [<ffffffff96511170>] __up_console_sem+0x90/0xa0
hardirqs last disabled at (887698): [<ffffffff96511155>] __up_console_sem+0x75/0xa0
softirqs last enabled at (887670): [<ffffffff962f4675>] __irq_exit_rcu+0x175/0x2f0
softirqs last disabled at (887649): [<ffffffff962f4675>] __irq_exit_rcu+0x175/0x2f0
---[ end trace 0000000000000000 ]---
[1] https://lore.kernel.org/all/20210111153707.10071-1-mark.rutland@arm.com/
Signed-off-by: William Hansen-Baird <william.hansen.baird@gmail.com>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
---
Changes since v1:
- Remove timestamps from dmesg output
- Use [1] footnote style for link
- Fix subject prefix to rtw-next
drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
index e1f811218894..bf7b5a32adaa 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c
@@ -1333,11 +1333,6 @@ int rtl8723be_hw_init(struct ieee80211_hw *hw)
bool rtstatus = true;
int err;
u8 tmp_u1b;
- unsigned long flags;
-
- /* reenable interrupts to not interfere with other devices */
- local_save_flags(flags);
- local_irq_enable();
rtlhal->fw_ready = false;
rtlpriv->rtlhal.being_init_adapter = true;
@@ -1443,7 +1438,6 @@ int rtl8723be_hw_init(struct ieee80211_hw *hw)
rtl8723be_dm_init(hw);
exit:
- local_irq_restore(flags);
rtlpriv->rtlhal.being_init_adapter = false;
return err;
}
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v4 4/8] block: implement NVMEM provider
From: Loic Poulain @ 2026-06-09 10:13 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Srinivas Kandagatla,
Andrew Lunn, Heiner Kallweit, Russell King, Saravana Kannan
In-Reply-To: <CAMRc=MfuiPMkSm=-G6VEJpqKhos0TD_pf0ScBGqfwLHT0uk8yQ@mail.gmail.com>
Hi bartosz,
On Tue, Jun 9, 2026 at 10:52 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
>
> On Tue, 9 Jun 2026 09:52:29 +0200, Loic Poulain
> <loic.poulain@oss.qualcomm.com> said:
> > From: Daniel Golle <daniel@makrotopia.org>
> >
> > On embedded devices using an eMMC it is common that one or more partitions
> > on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
> > data. Allow referencing the partition in device tree for the kernel and
> > Wi-Fi drivers accessing it via the NVMEM layer.
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > ---
> > block/Kconfig | 9 +++++
> > block/Makefile | 1 +
> > block/blk-nvmem.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 124 insertions(+)
> >
> > diff --git a/block/Kconfig b/block/Kconfig
> > index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
> > --- a/block/Kconfig
> > +++ b/block/Kconfig
> > @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
> > by falling back to the kernel crypto API when inline
> > encryption hardware is not present.
> >
> > +config BLK_NVMEM
> > + bool "Block device NVMEM provider"
> > + depends on OF
> > + depends on NVMEM
> > + help
> > + Allow block devices (or partitions) to act as NVMEM providers,
> > + typically used with eMMC to store MAC addresses or Wi-Fi
> > + calibration data on embedded devices.
> > +
> > source "block/partitions/Kconfig"
> >
> > config BLK_PM
> > diff --git a/block/Makefile b/block/Makefile
> > index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
> > --- a/block/Makefile
> > +++ b/block/Makefile
> > @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
> > blk-crypto-sysfs.o
> > obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
> > obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
> > +obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
> > diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..a6e62fa98675ee9bcb9c7035a611b5a573ab9091
> > --- /dev/null
> > +++ b/block/blk-nvmem.c
> > @@ -0,0 +1,114 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * block device NVMEM provider
> > + *
> > + * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
> > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > + *
> > + * Useful on devices using a partition on an eMMC for MAC addresses or
> > + * Wi-Fi calibration EEPROM data.
> > + */
> > +
> > +#include <linux/file.h>
> > +#include <linux/nvmem-provider.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/pagemap.h>
> > +#include <linux/property.h>
> > +
> > +#include "blk.h"
> > +
> > +static int blk_nvmem_reg_read(void *priv, unsigned int from,
> > + void *val, size_t bytes)
> > +{
> > + blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> > + dev_t devt = (dev_t)(uintptr_t)priv;
> > + size_t bytes_left = bytes;
> > + loff_t pos = from;
> > + int ret = 0;
> > +
> > + struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
> > + if (IS_ERR(bdev_file))
> > + return PTR_ERR(bdev_file);
> > +
> > + while (bytes_left) {
> > + pgoff_t f_index = pos >> PAGE_SHIFT;
> > + struct folio *folio;
> > + size_t folio_off;
> > + size_t to_read;
> > +
> > + folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
> > + if (IS_ERR(folio)) {
> > + ret = PTR_ERR(folio);
> > + break;
> > + }
> > +
> > + folio_off = offset_in_folio(folio, pos);
> > + to_read = min(bytes_left, folio_size(folio) - folio_off);
> > + memcpy_from_folio(val, folio, folio_off, to_read);
> > + pos += to_read;
> > + bytes_left -= to_read;
> > + val += to_read;
> > + folio_put(folio);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int blk_nvmem_register(struct device *dev)
> > +{
> > + struct block_device *bdev = dev_to_bdev(dev);
> > + struct nvmem_config config = {};
> > +
> > + /* skip devices which do not have a device tree node */
> > + if (!dev_of_node(dev))
> > + return 0;
> > +
> > + /* skip devices without an nvmem layout defined */
> > + struct device_node *child __free(device_node) =
> > + of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
> > + if (!child)
> > + return 0;
> > +
> > + /*
> > + * skip block device too large to be represented as NVMEM devices,
> > + * the NVMEM reg_read callback uses an unsigned int offset
> > + */
> > + if (bdev_nr_bytes(bdev) > UINT_MAX) {
> > + dev_warn(dev, "block device too large to be an NVMEM provider\n");
> > + return -ENODEV;
>
> Wait, I must have suggested -ENODEV here on too little coffee. This callback
> is called from device_add(), not when the device is bound so it's not the same
> thing as returning -ENODEV from probe().
>
> On the other hand, we don't want to not provide the block device just because
> someone added a DT property on one that's too big. I'd say: warn, but return 0.
> Does it make sense?
It’s still technically an error in the sense that we cannot provide
the required nvmem feature. However, it only becomes a real issue if a
consumer actually attempts to use it.
Also, the block device should still be added, since the return code
from add_dev is not checked. In any case, I’m fine with either
approach, as long as we emit a warning message.
>
> > + }
> > +
> > + config.id = NVMEM_DEVID_NONE;
> > + config.dev = dev;
> > + config.name = dev_name(dev);
> > + config.owner = THIS_MODULE;
> > + config.priv = (void *)(uintptr_t)dev->devt;
> > + config.reg_read = blk_nvmem_reg_read;
> > + config.size = bdev_nr_bytes(bdev);
> > + config.word_size = 1;
> > + config.stride = 1;
> > + config.read_only = true;
> > + config.root_only = true;
> > + config.ignore_wp = true;
> > + config.of_node = to_of_node(dev->fwnode);
> > +
> > + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
>
> And that was a wrong suggestion on my part too because I was under the
> impression that we're in the probe() path, not device_add(). You can't use
> devres here as the device at this point is not yet bound and may never be.
So I understand The bd_device is purely a class device with no bus, no driver.
For driverless devices, devres_release_all() is called explicitly
within device_del() .
>
> Which leads me to the second point: this is not the moment to add the nvmem
> provider. This should happen at or after probe(). Once nvmem_register()
> returns, you have a visible nvmem resource but nothing backing it in the block
> layer.
There is a short window during which a read attempt will 'properly'
fail, but this does seem somewhat fragile indeed.
> Either do this in block core when registering a new device or schedule
> a notifier here for the BUS_NOTIFY_BOUND_DRIVER event and do it in the notifier
> callback.
So in the end, it seems that the simpler and more robust approach is
probably to move away from the class_interface driver and instead
register/unregister the nvmem directly in add_disk/del_gendisk.
If that's ok I will move to this approach in the next version.
Regards,
Loic
^ permalink raw reply
* pull request: mt76-next 2026-06-09
From: Felix Fietkau @ 2026-06-09 10:50 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
Hi,
Here's my mt76 pull request for 7.2
- Felix
The following changes since commit b224d18b1e5d1cddfc67f63f41d80023b2ec8889:
wifi: mac80211: bound S1G TIM PVB walk to the TIM element (2026-06-08 08:59:03 +0200)
are available in the Git repository at:
https://github.com/nbd168/wireless tags/mt76-next-2026-06-09
for you to fetch changes up to 50a7f9f9d48eb50c0e95bef53358acb5af5cb3c6:
wifi: mt76: Drop unneeded mt76_register_debugfs_fops() return checks (2026-06-09 10:34:02 +0000)
----------------------------------------------------------------
mt76 patches for 7.2
- fixes
- mt792x broken usb transport detection
- mt7921 regd improvements
- mt7927 support
----------------------------------------------------------------
Arjan van de Ven (1):
wifi: mt76: mt7921/mt7925: fix NULL dereference in CSA beacon
Aviel Zohar (2):
wifi: mt76: mt7925: validate skb length in testmode query
wifi: mt76: mt7915: validate skb length in txpower SKU query
Bjoern A. Zeeb (1):
wifi: mt76: fix argument to ieee80211_is_first_frag()
Devin Wittmayer (2):
wifi: mt76: mt7925: add Netgear A8500 USB device ID
wifi: mt76: mt7921: assert sniffer on chanctx change
Dylan Eskew (2):
wifi: mt76: mt7996: reduce phy work in set_coverage
wifi: mt76: mt7996: limit work in set_bitrate_mask
ElXreno (2):
wifi: mt76: route TDLS-peer frames as 3-addr non-DS in HW encap
wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer
Felix Fietkau (2):
wifi: mt76: mt7996: fix out-of-bounds array access during hardware restart
wifi: mt76: mt7996: add missing max_remain_on_channel_duration
Hongling Zeng (1):
wifi: mt76: mt7921: fix resource leak in probe error path
Ingyu Jang (1):
wifi: mt76: Drop unneeded mt76_register_debugfs_fops() return checks
JB Tsai (5):
wifi: mt76: mt7921: refactor regulatory domain handling to regd.[ch]
wifi: mt76: mt7921: refactor CLC support check flow
wifi: mt76: mt7921: refactor regulatory notifier flow
wifi: mt76: mt7921: add auto regdomain switch support
wifi: mt76: mt7921: disable auto regd changes after user set
Javier Tia (9):
wifi: mt76: mt7925: fix stale pointer comparisons in change_vif_links
wifi: mt76: mt7925: add 320MHz bandwidth to bss_rlm_tlv
wifi: mt76: mt7925: handle 320MHz bandwidth in RXV and TXS
wifi: mt76: mt7925: populate EHT 320MHz MCS map in sta_rec
wifi: mt76: mt7925: advertise EHT 320MHz capabilities for 6GHz band
wifi: mt76: mt7925: add MT7927 chip ID helpers
wifi: mt76: mt7925: add MT7927 firmware paths
wifi: mt76: mt7925: use irq_map for chip-specific interrupt handling
wifi: mt76: mt7925: disable ASPM and runtime PM for MT7927
Jiajia Liu (2):
wifi: mt76: add wcid publish check in mt76_sta_add
wifi: mt76: transform aspm_conf for pci_disable_link_state
Johan Hovold (5):
wifi: mt76: drop redundant device reference
wifi: mt76x0u: drop redundant device reference
wifi: mt76x2u: drop redundant device reference
wifi: mt76: mt792xu: drop redundant device reference
wifi: mt7601u: drop redundant device reference
Lorenzo Bianconi (5):
wifi: mt76: mt7996: Fix NULL pointer dereference in mt7996_init_tx_queues()
wifi: mt76: mt7996: Fix possible token leak in mt7996_tx_prepare_skb()
wifi: mt76: mt7996: Fix possible NULL pointer dereference in mt7996_mac_write_txwi_80211()
wifi: mt76: mt7996: fix reading zeroed info->control.flags after mt76_tx_status_skb_add()
wifi: mt76: mt7996: remove redundant pdev->bus check in probe
Myeonghun Pak (1):
wifi: mt76: mt7925: clean up DMA on probe failure
Rajat Gupta (1):
wifi: mt76: use kfree_rcu for offchannel link in mt76_put_vif_phy_link
Rosen Penev (1):
wifi: mt76: fix of_get_mac_address error handling
Ryder Lee (5):
wifi: mt76: mt7996: disable UNI_BSS_INFO_PROTECT_INFO for mt7996
wifi: mt76: mt7915: fix potential tx_retries underflow
wifi: mt76: mt7921: fix potential tx_retries underflow
wifi: mt76: mt7925: fix potential tx_retries underflow
wifi: mt76: mt7996: fix potential tx_retries underflow
Sean Wang (22):
wifi: mt76: connac: replace is_mt7925() with is_connac3()
wifi: mt76: mt7925: use link-specific removal for non-MLD STA
wifi: mt76: connac: tolerate inactive BSS deactivation
wifi: mt76: mt792x: add MT7927 WFSYS reset support
wifi: mt76: mt792x: factor out common DMA queue allocation
wifi: mt76: mt7925: switch DMA init to common mt792x queue helpers
wifi: mt76: mt792x: add MT7927-specific PCIe DMA support
wifi: mt76: mt7925: sync MT7927 BSS band assignment
wifi: mt76: mt7925: add MBMC event handling
wifi: mt76: mt792x: enable CNM ops for MT7927
wifi: mt76: mt7925: add MT7927 PCIe support
wifi: mt76: mt7925: add MT7927 USB support
wifi: mt76: mt7925: keep TX BA state in the primary WCID
wifi: mt76: mt7925: pass WCID explicitly to mt7925_mcu_sta_ba()
wifi: mt76: mt7925: program BA state on active links
wifi: mt76: mt792x: skip MLD header rewrite for 802.3 encap TX
wifi: mt76: mt7921u: add MT7902 USB support
wifi: mt76: connac: use a helper to cache txpower_cur
wifi: mt76: connac: factor out rate power limit calculation
wifi: mt76: mt792x: report txpower for the requested vif link
wifi: mt76: mt792x: add common USB transport reset helpers
wifi: mt76: mt7921u: escalate broken USB transport to device reset
Zenm Chen (1):
wifi: mt76: mt76x2u: Add support for ELECOM WDC-867SU3S
drivers/net/wireless/mediatek/mt76/channel.c | 2 +-
drivers/net/wireless/mediatek/mt76/eeprom.c | 2 +-
drivers/net/wireless/mediatek/mt76/mac80211.c | 19 ++++--
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c | 2 -
drivers/net/wireless/mediatek/mt76/mt7615/init.c | 1 -
drivers/net/wireless/mediatek/mt76/mt7615/usb.c | 3 -
drivers/net/wireless/mediatek/mt76/mt76_connac.h | 21 +++++-
drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 13 +++-
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 49 +++++++++++---
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 29 ++++++++-
drivers/net/wireless/mediatek/mt76/mt76x0/usb.c | 3 -
drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 5 +-
drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c | 3 +-
drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 10 +--
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 3 +
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 8 +++
drivers/net/wireless/mediatek/mt76/mt7921/Makefile | 2 +-
drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c | 2 -
drivers/net/wireless/mediatek/mt76/mt7921/init.c | 98 +---------------------------
drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 12 +++-
drivers/net/wireless/mediatek/mt76/mt7921/main.c | 29 +++++++--
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 12 ++--
drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 1 -
drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 20 ++++--
drivers/net/wireless/mediatek/mt76/mt7921/regd.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt7921/regd.h | 19 ++++++
drivers/net/wireless/mediatek/mt76/mt7921/usb.c | 11 +++-
drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c | 2 -
drivers/net/wireless/mediatek/mt76/mt7925/init.c | 12 +++-
drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 29 ++++++---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 101 ++++++++++++++++++++++++-----
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 116 ++++++++++++++++++++++++++++-----
drivers/net/wireless/mediatek/mt76/mt7925/mcu.h | 3 +-
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 29 ++++++++-
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 151 +++++++++++++++++++++++++++++++------------
drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c | 12 +++-
drivers/net/wireless/mediatek/mt76/mt7925/testmode.c | 5 ++
drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 22 ++++++-
drivers/net/wireless/mediatek/mt76/mt792x.h | 33 ++++++++++
drivers/net/wireless/mediatek/mt76/mt792x_core.c | 56 +++++++++++++++-
drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++------
drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 23 +++++++
drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 79 ++++++++++++++++++++++-
drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c | 6 +-
drivers/net/wireless/mediatek/mt76/mt7996/dma.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 51 ++++++---------
drivers/net/wireless/mediatek/mt76/mt7996/main.c | 29 +++++----
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 36 +++++++----
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 22 ++++++-
drivers/net/wireless/mediatek/mt76/mt7996/pci.c | 2 +-
drivers/net/wireless/mediatek/mt76/pci.c | 8 ++-
drivers/net/wireless/mediatek/mt76/tx.c | 2 +-
drivers/net/wireless/mediatek/mt76/usb.c | 2 +
drivers/net/wireless/mediatek/mt7601u/usb.c | 3 -
55 files changed, 1270 insertions(+), 350 deletions(-)
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.c
create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.h
^ permalink raw reply
* [PATCH v3] wifi: mt76: mt76u: use a threaded NAPI for the RX path
From: Filip Bakreski @ 2026-06-09 10:53 UTC (permalink / raw)
To: nbd, lorenzo, ryder.lee; +Cc: shayne.chen, sean.wang, linux-wireless
The USB RX path delivers frames to the stack via mt76_rx_complete() with
a NULL napi pointer, taking the netif_receive_skb_list() path, so it never
benefits from GRO -- unlike the DMA-based mt76 drivers, which pass a real
napi and use napi_gro_receive(). For bulk TCP traffic this is costly, as
every segment traverses the stack individually.
Service the MT_RXQ_MAIN queue from a threaded NAPI, reusing mt76_dev's
existing napi_dev and napi[] rather than adding new fields. The URB
completion handler schedules the napi; its poll drains the URBs, builds
the skbs, resubmits and delivers them through napi_gro_receive(). The MCU
queue stays on the existing RX worker. This enables GRO and moves RX
processing into its own kernel thread, parallelising the datapath.
On mt7921u at HE-MCS 11 (2x2, 80 MHz; fast.com, multiple streams) this
averages ~588 Mbit/s, versus ~424 Mbit/s when the same napi is instead
driven manually from the RX worker, and ~380 Mbit/s for the unmodified
driver.
Suggested-by: Lorenzo Bianconi <lorenzo@kernel.org>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Filip Bakreski <phial@phiality.com>
---
v3:
- Address review nits: add blank lines for readability and use a priv
pointer for netdev_priv() like mt76_dma_init() (Lorenzo Bianconi).
v2: https://lore.kernel.org/linux-wireless/20260609003224.132191-1-phial@phiality.com/
v1: https://lore.kernel.org/linux-wireless/20260608044109.31730-1-phial@phiality.com/
drivers/net/wireless/mediatek/mt76/usb.c | 61 +++++++++++++++++++++---
1 file changed, 54 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index d9638a9b7..77a8e35b1 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -580,7 +580,11 @@ static void mt76u_complete_rx(struct urb *urb)
q->head = (q->head + 1) % q->ndesc;
q->queued++;
- mt76_worker_schedule(&dev->usb.rx_worker);
+
+ if (q == &dev->q_rx[MT_RXQ_MAIN])
+ napi_schedule(&dev->napi[MT_RXQ_MAIN]);
+ else
+ mt76_worker_schedule(&dev->usb.rx_worker);
out:
spin_unlock_irqrestore(&q->lock, flags);
}
@@ -618,11 +622,23 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
}
mt76u_submit_rx_buf(dev, qid, urb);
}
- if (qid == MT_RXQ_MAIN) {
- local_bh_disable();
- mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
- local_bh_enable();
- }
+}
+
+/* Threaded NAPI poll for the MAIN RX queue: drain URBs, build skbs, resubmit,
+ * then deliver through napi_gro_receive() and let napi_complete() flush GRO.
+ */
+static int mt76u_napi_poll(struct napi_struct *napi, int budget)
+{
+ struct mt76_dev *dev = mt76_priv(napi->dev);
+
+ rcu_read_lock();
+ mt76u_process_rx_queue(dev, &dev->q_rx[MT_RXQ_MAIN]);
+ mt76_rx_poll_complete(dev, MT_RXQ_MAIN, napi);
+ rcu_read_unlock();
+
+ napi_complete(napi);
+
+ return 0;
}
static void mt76u_rx_worker(struct mt76_worker *w)
@@ -632,8 +648,13 @@ static void mt76u_rx_worker(struct mt76_worker *w)
int i;
rcu_read_lock();
- mt76_for_each_q_rx(dev, i)
+ mt76_for_each_q_rx(dev, i) {
+ /* MT_RXQ_MAIN is serviced by the threaded NAPI poll */
+ if (i == MT_RXQ_MAIN)
+ continue;
+
mt76u_process_rx_queue(dev, &dev->q_rx[i]);
+ }
rcu_read_unlock();
}
@@ -723,6 +744,8 @@ void mt76u_stop_rx(struct mt76_dev *dev)
int i;
mt76_worker_disable(&dev->usb.rx_worker);
+ if (dev->napi_dev)
+ napi_disable(&dev->napi[MT_RXQ_MAIN]);
mt76_for_each_q_rx(dev, i) {
struct mt76_queue *q = &dev->q_rx[i];
@@ -751,6 +774,8 @@ int mt76u_resume_rx(struct mt76_dev *dev)
}
mt76_worker_enable(&dev->usb.rx_worker);
+ if (dev->napi_dev)
+ napi_enable(&dev->napi[MT_RXQ_MAIN]);
return 0;
}
@@ -1051,6 +1076,13 @@ void mt76u_queues_deinit(struct mt76_dev *dev)
mt76u_stop_rx(dev);
mt76u_stop_tx(dev);
+ /* mt76u_stop_rx() (above) already napi_disable()d the MAIN queue */
+ if (dev->napi_dev) {
+ netif_napi_del(&dev->napi[MT_RXQ_MAIN]);
+ free_netdev(dev->napi_dev);
+ dev->napi_dev = NULL;
+ }
+
mt76u_free_rx(dev);
mt76u_free_tx(dev);
}
@@ -1078,6 +1110,7 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
{
struct usb_device *udev = interface_to_usbdev(intf);
struct mt76_usb *usb = &dev->usb;
+ struct mt76_dev **priv;
int err;
INIT_WORK(&usb->stat_work, mt76u_tx_status_data);
@@ -1115,6 +1148,20 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
sched_set_fifo_low(usb->rx_worker.task);
sched_set_fifo_low(usb->status_worker.task);
+ /* threaded NAPI on a dummy netdev (reusing mt76_dev's napi_dev/napi[])
+ * services the MAIN RX queue and gives the RX path GRO
+ */
+ dev->napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *));
+ if (!dev->napi_dev)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev->napi_dev);
+ *priv = dev;
+ strscpy(dev->napi_dev->name, "mt76u-rx", sizeof(dev->napi_dev->name));
+ dev->napi_dev->threaded = 1;
+ netif_napi_add(dev->napi_dev, &dev->napi[MT_RXQ_MAIN], mt76u_napi_poll);
+ napi_enable(&dev->napi[MT_RXQ_MAIN]);
+
return 0;
}
EXPORT_SYMBOL_GPL(__mt76u_init);
base-commit: 5f6099446d1ddb888e36cdf93b6a0551f05c1267
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v2] wifi: mt76: mt76u: use a threaded NAPI for the RX path
From: Phiality @ 2026-06-09 10:57 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: nbd, ryder.lee, shayne.chen, sean.wang, linux-wireless
In-Reply-To: <aifg0WOxWD6x7no4@lore-desk>
> I guess the patch is fine, just a couple of nits inline.
Patch updated in v3 to address the nits,
https://lore.kernel.org/linux-wireless/20260609105301.196302-1-phial@phiality.com/
Cheers,
Filip
^ permalink raw reply
* Re: [PATCH] wifi: ath11k: fix refcount leak in ath11k_ahb_fw_resources_init()
From: Rameshkumar Sundaram @ 2026-06-09 11:00 UTC (permalink / raw)
To: Wentao Liang, jjohnson; +Cc: linux-wireless, ath11k, linux-kernel, stable
In-Reply-To: <20260609092528.220547-1-vulab@iscas.ac.cn>
On 6/9/2026 2:55 PM, Wentao Liang wrote:
> of_get_child_by_name() returns a node pointer with refcount
> incremented, but the error path when ath11k_ahb_setup_msa_resources()
> fails does not release it. Add the missing of_node_put() to avoid
> leaking the reference.
>
> Cc: stable@vger.kernel.org
> Fixes: 095cb947490c ("wifi: ath11k: allow missing memory-regions")
> Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v4 4/8] block: implement NVMEM provider
From: Bartosz Golaszewski @ 2026-06-09 11:05 UTC (permalink / raw)
To: Loic Poulain
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Srinivas Kandagatla,
Andrew Lunn, Heiner Kallweit, Russell King, Saravana Kannan,
Bartosz Golaszewski
In-Reply-To: <CAFEp6-1syMQsvuQ+dLU39bnDeL5Ok7vK1mA7CS0v1m7cjhyMQw@mail.gmail.com>
On Tue, 9 Jun 2026 12:13:08 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> Hi bartosz,
>
...
>>
>> On the other hand, we don't want to not provide the block device just because
>> someone added a DT property on one that's too big. I'd say: warn, but return 0.
>> Does it make sense?
>
> It’s still technically an error in the sense that we cannot provide
> the required nvmem feature. However, it only becomes a real issue if a
> consumer actually attempts to use it.
> Also, the block device should still be added, since the return code
> from add_dev is not checked. In any case, I’m fine with either
> approach, as long as we emit a warning message.
>
We don't check the return value now (why?) but if we ever do, the safer option
is to return 0 IMO.
>>
>> > + }
>> > +
>> > + config.id = NVMEM_DEVID_NONE;
>> > + config.dev = dev;
>> > + config.name = dev_name(dev);
>> > + config.owner = THIS_MODULE;
>> > + config.priv = (void *)(uintptr_t)dev->devt;
>> > + config.reg_read = blk_nvmem_reg_read;
>> > + config.size = bdev_nr_bytes(bdev);
>> > + config.word_size = 1;
>> > + config.stride = 1;
>> > + config.read_only = true;
>> > + config.root_only = true;
>> > + config.ignore_wp = true;
>> > + config.of_node = to_of_node(dev->fwnode);
>> > +
>> > + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
>>
>> And that was a wrong suggestion on my part too because I was under the
>> impression that we're in the probe() path, not device_add(). You can't use
>> devres here as the device at this point is not yet bound and may never be.
>
> So I understand The bd_device is purely a class device with no bus, no driver.
> For driverless devices, devres_release_all() is called explicitly
> within device_del() .
>
Right.
>>
>> Which leads me to the second point: this is not the moment to add the nvmem
>> provider. This should happen at or after probe(). Once nvmem_register()
>> returns, you have a visible nvmem resource but nothing backing it in the block
>> layer.
>
> There is a short window during which a read attempt will 'properly'
> fail, but this does seem somewhat fragile indeed.
>
>> Either do this in block core when registering a new device or schedule
>> a notifier here for the BUS_NOTIFY_BOUND_DRIVER event and do it in the notifier
>> callback.
>
> So in the end, it seems that the simpler and more robust approach is
> probably to move away from the class_interface driver and instead
> register/unregister the nvmem directly in add_disk/del_gendisk.
> If that's ok I will move to this approach in the next version.
>
Yes, I think it's the right approach.
Bart
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox