Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [PATCH ath-next] wifi: ath12k: add QMI capability negotiation for dynamic memory mode
From: Aaradhana Sahu @ 2026-06-18  8:52 UTC (permalink / raw)
  To: Rameshkumar Sundaram, ath12k; +Cc: linux-wireless
In-Reply-To: <7f3f0f10-3493-49b7-ac6d-cc5efca24746@oss.qualcomm.com>



On 6/18/2026 12:18 PM, Rameshkumar Sundaram wrote:
> On 6/1/2026 8:48 AM, Aaradhana Sahu wrote:
>> On AHB platforms, firmware operates in two modes: fixed-memory mode where
>> firmware uses hardcoded addresses for memory regions such as BDF and does
>> not request HOST_DDR memory from the host, and dynamic-memory mode where
>> firmware expects the host to provide memory addresses including HOST_DDR
>> after the Q6 read-only region and relies on host allocation for all memory
>> types.
>>
>> Introduce QMI capability negotiation to support both modes. Add a new QMI
>> PHY capability flag dynamic_ddr_support which is advertised by firmware to
>> indicate it supports dynamic memory mode. When the host detects this
>> capability, set the dynamic_mem_support flag in the host capability message
>> to signal the host is ready to provide dynamic memory allocation. This
>> triggers firmware to send the HOST_DDR memory request and use the
>> host-provided address.
>>
>> For backward compatibility, if firmware doesn't advertise
>> dynamic_ddr_support, the firmware continues to operate in fixed-memory mode
>> where firmware uses predefined addresses.
>>
>> Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1
>>
>> Signed-off-by: Aaradhana Sahu <aaradhana.sahu@oss.qualcomm.com>
>> ---
>>   drivers/net/wireless/ath/ath12k/qmi.c | 50 +++++++++++++++++++++++++--
>>   drivers/net/wireless/ath/ath12k/qmi.h | 10 ++++--
>>   2 files changed, 54 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c
>> index fd762b5d7bb5..85406d6e6da1 100644
>> --- a/drivers/net/wireless/ath/ath12k/qmi.c
>> +++ b/drivers/net/wireless/ath/ath12k/qmi.c
>> @@ -506,6 +506,24 @@ static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = {
>>           .offset        = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01,
>>                          feature_list),
>>       },
>> +    {
>> +        .data_type    = QMI_OPT_FLAG,
>> +        .elem_len    = 1,
>> +        .elem_size    = sizeof(u8),
>> +        .array_type    = NO_ARRAY,
>> +        .tlv_type    = 0x33,
>> +        .offset        = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01,
>> +                       dynamic_mem_support_valid),
>> +    },
>> +    {
>> +        .data_type    = QMI_UNSIGNED_1_BYTE,
>> +        .elem_len    = 1,
>> +        .elem_size    = sizeof(u8),
>> +        .array_type    = NO_ARRAY,
>> +        .tlv_type    = 0x33,
>> +        .offset        = offsetof(struct qmi_wlanfw_host_cap_req_msg_v01,
>> +                       dynamic_mem_support),
>> +    },
>>       {
>>           .data_type    = QMI_EOTI,
>>           .array_type    = NO_ARRAY,
>> @@ -602,6 +620,24 @@ static const struct qmi_elem_info qmi_wlanfw_phy_cap_resp_msg_v01_ei[] = {
>>           .offset         = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01,
>>                          single_chip_mlo_support),
>>       },
>> +    {
>> +        .data_type      = QMI_OPT_FLAG,
>> +        .elem_len       = 1,
>> +        .elem_size      = sizeof(u8),
>> +        .array_type     = NO_ARRAY,
>> +        .tlv_type       = 0x17,
>> +        .offset         = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01,
>> +                       dynamic_ddr_support_valid),
>> +    },
>> +    {
>> +        .data_type      = QMI_UNSIGNED_1_BYTE,
>> +        .elem_len       = 1,
>> +        .elem_size      = sizeof(u8),
>> +        .array_type     = NO_ARRAY,
>> +        .tlv_type       = 0x17,
>> +        .offset         = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01,
>> +                       dynamic_ddr_support),
>> +    },
>>       {
>>           .data_type    = QMI_EOTI,
>>           .array_type    = NO_ARRAY,
>> @@ -2248,6 +2284,11 @@ int ath12k_qmi_host_cap_send(struct ath12k_base *ab)
>>       if (ret < 0)
>>           goto out;
>>   +    if (ab->qmi.dynamic_ddr_support) {
>> +        req.dynamic_mem_support_valid = 1;
>> +        req.dynamic_mem_support = 1;
>> +    }
>> +
>>       ret = qmi_txn_init(&ab->qmi.handle, &txn,
>>                  qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp);
>>       if (ret < 0)
>> @@ -2319,11 +2360,14 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab)
>>         ab->qmi.num_radios = resp.num_phy;
>>   +    if (resp.dynamic_ddr_support_valid)
>> +        ab->qmi.dynamic_ddr_support = resp.dynamic_ddr_support;
>> +
>>       ath12k_dbg(ab, ATH12K_DBG_QMI,
>> -           "phy capability resp valid %d single_chip_mlo_support %d valid %d num_phy %d valid %d board_id %d\n",
>> +           "phy capability resp valid %d single_chip_mlo_support %d valid %d num_phy %d valid %d board_id %d dynamic_ddr_valid %d dynamic_ddr_support %d\n",
>>              resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support,
>> -           resp.num_phy_valid, resp.num_phy,
>> -           resp.board_id_valid, resp.board_id);
>> +           resp.num_phy_valid, resp.num_phy, resp.board_id_valid, resp.board_id,
>> +           resp.dynamic_ddr_support_valid, resp.dynamic_ddr_support);
>>         return;
>>   diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h
>> index 2a63e214eb42..dbde76e5a78d 100644
>> --- a/drivers/net/wireless/ath/ath12k/qmi.h
>> +++ b/drivers/net/wireless/ath/ath12k/qmi.h
>> @@ -156,9 +156,10 @@ struct ath12k_qmi {
>>       struct m3_mem_region aux_uc_mem;
>>       unsigned int service_ins_id;
>>       struct dev_mem_info dev_mem[ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01];
>> +    u8 dynamic_ddr_support;
>>   };
>>   -#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN        261
>> +#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN        265
>>   #define QMI_WLANFW_HOST_CAP_REQ_V01            0x0034
>>   #define QMI_WLANFW_HOST_CAP_RESP_MSG_V01_MAX_LEN    7
>>   #define QMI_WLFW_HOST_CAP_RESP_V01            0x0034
>> @@ -258,7 +259,8 @@ struct qmi_wlanfw_host_cap_req_msg_v01 {
>>       struct wlfw_host_mlo_chip_info_s_v01 mlo_chip_info[QMI_WLFW_MAX_NUM_MLO_CHIPS_V01];
>>       u8 feature_list_valid;
>>       u64 feature_list;
>> -
>> +    u8 dynamic_mem_support_valid;
>> +    u8 dynamic_mem_support;
>>   };
>>     struct qmi_wlanfw_host_cap_resp_msg_v01 {
>> @@ -267,7 +269,7 @@ struct qmi_wlanfw_host_cap_resp_msg_v01 {
>>     #define QMI_WLANFW_PHY_CAP_REQ_MSG_V01_MAX_LEN        0
>>   #define QMI_WLANFW_PHY_CAP_REQ_V01            0x0057
>> -#define QMI_WLANFW_PHY_CAP_RESP_MSG_V01_MAX_LEN        18
>> +#define QMI_WLANFW_PHY_CAP_RESP_MSG_V01_MAX_LEN        22
> 
> this is not used anywhere, but since you're touching it should this be 26 ? response TLV 7, num_phy 4, board_id 7, existing single_chip_mlo_support 4, and new dynamic_ddr_support 4
> 
> 
> -- 
> Ramesh

I checked the driver and found there are several unused QMI_*_RESP_MSG_LEN macros.
Since this macro is not used anywhere, I think it is better to remove this
change from the current patch.
I will send a separate cleanup patch to remove all unused QMI macros.

Hope that’s fine with you.

^ permalink raw reply

* [PATCH 6.12.y v3 3/3] wifi: mt76: mt7921: fix potential deadlock in mt7921_roc_abort_sync
From: Ajrat Makhmutov @ 2026-06-18  8:14 UTC (permalink / raw)
  To: stable; +Cc: sashal, nbd, linux-wireless, Sean Wang, Quan Zhou,
	Ajrat Makhmutov
In-Reply-To: <20260618081413.17812-1-rauty@altlinux.org>

From: Sean Wang <sean.wang@mediatek.com>

commit d5059e52fd8bc624ec4255c9fa01a266513d126b upstream.

roc_abort_sync() can deadlock with roc_work(). roc_work() holds
dev->mt76.mutex, while cancel_work_sync() waits for roc_work()
to finish. If the caller already owns the same mutex, both
sides block and no progress is possible.

This deadlock can occur during station removal when
mt76_sta_state() -> mt76_sta_remove() -> mt7921_mac_sta_remove() ->
mt7921_roc_abort_sync() invokes cancel_work_sync() while
roc_work() is still running and holding dev->mt76.mutex.

This avoids the mutex deadlock and preserves exactly-once
work ownership.

Fixes: 352d966126e6 ("wifi: mt76: mt7921: fix a potential association failure upon resuming")
Co-developed-by: Quan Zhou <quan.zhou@mediatek.com>
Signed-off-by: Quan Zhou <quan.zhou@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Link: https://patch.msgid.link/20260126180013.8167-1-sean.wang@kernel.org
Signed-off-by: Felix Fietkau <nbd@nbd.name>
[Ajrat: keep del_timer_sync() instead of timer_delete_sync() -- the
 timer API rename is not present in 6.12.y. ]
Signed-off-by: Ajrat Makhmutov <rauty@altlinux.org>
---
v2: drop redundant "cherry picked from" trailer; tag the 6.12.y
    adaptation note as [Ajrat] and add backporter Signed-off-by. No code change.
v3: add cover letter with upstream/stable references; mark target 6.12.y.

 drivers/net/wireless/mediatek/mt76/mt7921/main.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index f2fffca868b51..99561094640f1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -365,12 +365,15 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev)
 {
 	struct mt792x_phy *phy = &dev->phy;
 
+	if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+		return;
+
 	del_timer_sync(&phy->roc_timer);
-	cancel_work_sync(&phy->roc_work);
-	if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
-		ieee80211_iterate_interfaces(mt76_hw(dev),
-					     IEEE80211_IFACE_ITER_RESUME_ALL,
-					     mt7921_roc_iter, (void *)phy);
+	cancel_work(&phy->roc_work);
+
+	ieee80211_iterate_interfaces(mt76_hw(dev),
+				     IEEE80211_IFACE_ITER_RESUME_ALL,
+				     mt7921_roc_iter, (void *)phy);
 }
 EXPORT_SYMBOL_GPL(mt7921_roc_abort_sync);
 
-- 
2.50.1


^ permalink raw reply related

* [PATCH 6.12.y v3 2/3] wifi: mt76: mt7921: fix a potential scan no APs
From: Ajrat Makhmutov @ 2026-06-18  8:14 UTC (permalink / raw)
  To: stable
  Cc: sashal, nbd, linux-wireless, Quan Zhou, Sean Wang, David Ruth,
	Ajrat Makhmutov
In-Reply-To: <20260618081413.17812-1-rauty@altlinux.org>

From: Quan Zhou <quan.zhou@mediatek.com>

commit 5ed54896b6bd444223092cab361b0785932119ab upstream.

In multi-channel scenarios, the granted channel must be aborted before
station remove. Otherwise, the firmware will be put into a wrong state,
resulting in have chance to make subsequence scan no APs.
With this patch, the granted channel will be always aborted before
station remove.

Signed-off-by: Quan Zhou <quan.zhou@mediatek.com>
Reviewed-by: Sean Wang <sean.wang@mediatek.com>
Tested-by: David Ruth <druth@chromium.org>
Reviewed-by: David Ruth <druth@chromium.org>
Link: https://patch.msgid.link/1ac1ae779db86d4012199a24ea2ca74050ed4af6.1721300411.git.quan.zhou@mediatek.com
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Ajrat Makhmutov <rauty@altlinux.org>
---
v2: drop redundant "cherry picked from" trailer; add backporter
    Signed-off-by. No code change.
v3: add cover letter with upstream/stable references; mark target 6.12.y.

 drivers/net/wireless/mediatek/mt76/mt7921/main.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index a93ae4e44f16a..f2fffca868b51 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -368,9 +368,9 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev)
 	del_timer_sync(&phy->roc_timer);
 	cancel_work_sync(&phy->roc_work);
 	if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
-		ieee80211_iterate_active_interfaces(mt76_hw(dev),
-						    IEEE80211_IFACE_ITER_RESUME_ALL,
-						    mt7921_roc_iter, (void *)phy);
+		ieee80211_iterate_interfaces(mt76_hw(dev),
+					     IEEE80211_IFACE_ITER_RESUME_ALL,
+					     mt7921_roc_iter, (void *)phy);
 }
 EXPORT_SYMBOL_GPL(mt7921_roc_abort_sync);
 
@@ -881,6 +881,7 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
 	struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
 	struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
 
+	mt7921_roc_abort_sync(dev);
 	mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->deflink.wcid);
 	mt76_connac_pm_wake(&dev->mphy, &dev->pm);
 
-- 
2.50.1


^ permalink raw reply related

* [PATCH 6.12.y v3 1/3] wifi: mt76: mt7921: avoid undesired changes of the preset regulatory domain
From: Ajrat Makhmutov @ 2026-06-18  8:14 UTC (permalink / raw)
  To: stable
  Cc: sashal, nbd, linux-wireless, Leon Yen, Ming Yen Hsieh, David Ruth,
	Ajrat Makhmutov
In-Reply-To: <20260618081413.17812-1-rauty@altlinux.org>

From: Leon Yen <leon.yen@mediatek.com>

commit 2425dc7beaadc39c2636f97f8bdc22dc3cf88149 upstream.

Some countries have strict RF restrictions where changing the regulatory
domain dynamically based on the connected AP is not acceptable.
This patch disables Beacon country IE hinting when a valid country code
is set from usersland (e.g., by system using iw or CRDA).

Signed-off-by: Leon Yen <leon.yen@mediatek.com>
Signed-off-by: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
Tested-by: David Ruth <druth@chromium.org>
Link: https://patch.msgid.link/20240412085357.13756-1-mingyen.hsieh@mediatek.com
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Ajrat Makhmutov <rauty@altlinux.org>
---
v2: drop redundant "cherry picked from" trailer; add backporter
    Signed-off-by. No code change.
v3: add cover letter with upstream/stable references; mark target 6.12.y.

 drivers/net/wireless/mediatek/mt76/mt7921/init.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
index 4bd533c4ba9a1..276dfb9c26e0d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
@@ -137,6 +137,13 @@ mt7921_regd_notifier(struct wiphy *wiphy,
 	dev->mt76.region = request->dfs_region;
 	dev->country_ie_env = request->country_ie_env;
 
+	if (request->initiator == NL80211_REGDOM_SET_BY_USER) {
+		if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
+			wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
+		else
+			wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
+	}
+
 	if (pm->suspended)
 		return;
 
-- 
2.50.1


^ permalink raw reply related

* [PATCH 6.12.y v3 0/3] mt7921e: backport three mt76 fixes to 6.12.y
From: Ajrat Makhmutov @ 2026-06-18  8:14 UTC (permalink / raw)
  To: stable; +Cc: sashal, nbd, linux-wireless
In-Reply-To: <20260617130826.1667503-1-rauty@altlinux.org>

This series backports three mt76/mt7921 fixes to 6.12.y. All three are in
mainline; none carried Cc: stable, so they were not auto-selected for
6.12.y.

Per your earlier reply, resending as a three-patch series:
https://lore.kernel.org/all/20260610-stable-reply-0014@kernel.org/

d5059e52 is already in the v6.18.y and v7.0.y stable trees:
https://github.com/gregkh/linux/commit/91e77840bf13de3add125060cf8b32ca24a52c8c
https://github.com/gregkh/linux/commit/35180c772f5e11e2fa4d80d3dfd50906cb6d9646

The three must go together. 5ed54896 adds mt7921_roc_abort_sync() on the
sta-remove path, which runs under dev->mt76.mutex (taken by
mt76_sta_remove()); d5059e52 then turns cancel_work_sync() inside
roc_abort_sync() into cancel_work() to avoid a self-deadlock with
roc_work(). Applying 5ed54896 alone queues a reachable deadlock.

6.12.y note: d5059e52 keeps del_timer_sync() -- the timer_delete_sync()
rename is not in 6.12.y. The other two cherry-pick cleanly.

Tested on MT7921e: Acer Aspire 5 A517-52, ASUS VivoBook S14,
Lenovo IdeaPad Slim 5 14AHP9.

Link to v2:
https://lore.kernel.org/all/20260617130826.1667503-1-rauty@altlinux.org/

v3:
  * subject prefix now carries target branch (6.12.y)
  * cover letter added with stable backport status of d5059e52
  * no code changes

^ permalink raw reply

* Re: [RESEND PATCH 2/2] wifi: ath12k: enable threaded NAPI when DP IRQ affinity is unavailable
From: Baochen Qiang @ 2026-06-18  7:11 UTC (permalink / raw)
  To: Hangtian Zhu, tglx, jjohnson; +Cc: linux-wireless, ath12k, linux-kernel
In-Reply-To: <20260519011627.713068-3-hangtian.zhu@oss.qualcomm.com>



On 5/19/2026 9:16 AM, Hangtian Zhu wrote:
> Determine threaded NAPI policy from runtime IRQ capability of the DP MSI
> IRQ.
> 
> If irq_can_set_affinity() reports that affinity cannot be set, enable
> threaded NAPI for DP interrupt groups so datapath processing is not
> constrained by a single-CPU softirq context.
> 
> On RB3Gen2, where IRQ affinity is unavailable in the effective IRQ path,
> EHT160 UDP downlink throughput improved from 802 Mbps to 2.58 Gbps after
> enabling threaded NAPI.
> 
> Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0.c2-00074-QCACOLSWPL_V1_TO_SILICONZ-1
> 
> Signed-off-by: Hangtian Zhu <hangtian.zhu@oss.qualcomm.com>

Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>


^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath12k: add QMI capability negotiation for dynamic memory mode
From: Rameshkumar Sundaram @ 2026-06-18  6:48 UTC (permalink / raw)
  To: Aaradhana Sahu, ath12k; +Cc: linux-wireless
In-Reply-To: <20260601031850.3715354-1-aaradhana.sahu@oss.qualcomm.com>

On 6/1/2026 8:48 AM, Aaradhana Sahu wrote:
> On AHB platforms, firmware operates in two modes: fixed-memory mode where
> firmware uses hardcoded addresses for memory regions such as BDF and does
> not request HOST_DDR memory from the host, and dynamic-memory mode where
> firmware expects the host to provide memory addresses including HOST_DDR
> after the Q6 read-only region and relies on host allocation for all memory
> types.
> 
> Introduce QMI capability negotiation to support both modes. Add a new QMI
> PHY capability flag dynamic_ddr_support which is advertised by firmware to
> indicate it supports dynamic memory mode. When the host detects this
> capability, set the dynamic_mem_support flag in the host capability message
> to signal the host is ready to provide dynamic memory allocation. This
> triggers firmware to send the HOST_DDR memory request and use the
> host-provided address.
> 
> For backward compatibility, if firmware doesn't advertise
> dynamic_ddr_support, the firmware continues to operate in fixed-memory mode
> where firmware uses predefined addresses.
> 
> Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1
> 
> Signed-off-by: Aaradhana Sahu <aaradhana.sahu@oss.qualcomm.com>
> ---
>   drivers/net/wireless/ath/ath12k/qmi.c | 50 +++++++++++++++++++++++++--
>   drivers/net/wireless/ath/ath12k/qmi.h | 10 ++++--
>   2 files changed, 54 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c
> index fd762b5d7bb5..85406d6e6da1 100644
> --- a/drivers/net/wireless/ath/ath12k/qmi.c
> +++ b/drivers/net/wireless/ath/ath12k/qmi.c
> @@ -506,6 +506,24 @@ static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = {
>   		.offset		= offsetof(struct qmi_wlanfw_host_cap_req_msg_v01,
>   					   feature_list),
>   	},
> +	{
> +		.data_type	= QMI_OPT_FLAG,
> +		.elem_len	= 1,
> +		.elem_size	= sizeof(u8),
> +		.array_type	= NO_ARRAY,
> +		.tlv_type	= 0x33,
> +		.offset		= offsetof(struct qmi_wlanfw_host_cap_req_msg_v01,
> +					   dynamic_mem_support_valid),
> +	},
> +	{
> +		.data_type	= QMI_UNSIGNED_1_BYTE,
> +		.elem_len	= 1,
> +		.elem_size	= sizeof(u8),
> +		.array_type	= NO_ARRAY,
> +		.tlv_type	= 0x33,
> +		.offset		= offsetof(struct qmi_wlanfw_host_cap_req_msg_v01,
> +					   dynamic_mem_support),
> +	},
>   	{
>   		.data_type	= QMI_EOTI,
>   		.array_type	= NO_ARRAY,
> @@ -602,6 +620,24 @@ static const struct qmi_elem_info qmi_wlanfw_phy_cap_resp_msg_v01_ei[] = {
>   		.offset         = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01,
>   					   single_chip_mlo_support),
>   	},
> +	{
> +		.data_type      = QMI_OPT_FLAG,
> +		.elem_len       = 1,
> +		.elem_size      = sizeof(u8),
> +		.array_type     = NO_ARRAY,
> +		.tlv_type       = 0x17,
> +		.offset         = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01,
> +					   dynamic_ddr_support_valid),
> +	},
> +	{
> +		.data_type      = QMI_UNSIGNED_1_BYTE,
> +		.elem_len       = 1,
> +		.elem_size      = sizeof(u8),
> +		.array_type     = NO_ARRAY,
> +		.tlv_type       = 0x17,
> +		.offset         = offsetof(struct qmi_wlanfw_phy_cap_resp_msg_v01,
> +					   dynamic_ddr_support),
> +	},
>   	{
>   		.data_type	= QMI_EOTI,
>   		.array_type	= NO_ARRAY,
> @@ -2248,6 +2284,11 @@ int ath12k_qmi_host_cap_send(struct ath12k_base *ab)
>   	if (ret < 0)
>   		goto out;
>   
> +	if (ab->qmi.dynamic_ddr_support) {
> +		req.dynamic_mem_support_valid = 1;
> +		req.dynamic_mem_support = 1;
> +	}
> +
>   	ret = qmi_txn_init(&ab->qmi.handle, &txn,
>   			   qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp);
>   	if (ret < 0)
> @@ -2319,11 +2360,14 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab)
>   
>   	ab->qmi.num_radios = resp.num_phy;
>   
> +	if (resp.dynamic_ddr_support_valid)
> +		ab->qmi.dynamic_ddr_support = resp.dynamic_ddr_support;
> +
>   	ath12k_dbg(ab, ATH12K_DBG_QMI,
> -		   "phy capability resp valid %d single_chip_mlo_support %d valid %d num_phy %d valid %d board_id %d\n",
> +		   "phy capability resp valid %d single_chip_mlo_support %d valid %d num_phy %d valid %d board_id %d dynamic_ddr_valid %d dynamic_ddr_support %d\n",
>   		   resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support,
> -		   resp.num_phy_valid, resp.num_phy,
> -		   resp.board_id_valid, resp.board_id);
> +		   resp.num_phy_valid, resp.num_phy, resp.board_id_valid, resp.board_id,
> +		   resp.dynamic_ddr_support_valid, resp.dynamic_ddr_support);
>   
>   	return;
>   
> diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h
> index 2a63e214eb42..dbde76e5a78d 100644
> --- a/drivers/net/wireless/ath/ath12k/qmi.h
> +++ b/drivers/net/wireless/ath/ath12k/qmi.h
> @@ -156,9 +156,10 @@ struct ath12k_qmi {
>   	struct m3_mem_region aux_uc_mem;
>   	unsigned int service_ins_id;
>   	struct dev_mem_info dev_mem[ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01];
> +	u8 dynamic_ddr_support;
>   };
>   
> -#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN		261
> +#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN		265
>   #define QMI_WLANFW_HOST_CAP_REQ_V01			0x0034
>   #define QMI_WLANFW_HOST_CAP_RESP_MSG_V01_MAX_LEN	7
>   #define QMI_WLFW_HOST_CAP_RESP_V01			0x0034
> @@ -258,7 +259,8 @@ struct qmi_wlanfw_host_cap_req_msg_v01 {
>   	struct wlfw_host_mlo_chip_info_s_v01 mlo_chip_info[QMI_WLFW_MAX_NUM_MLO_CHIPS_V01];
>   	u8 feature_list_valid;
>   	u64 feature_list;
> -
> +	u8 dynamic_mem_support_valid;
> +	u8 dynamic_mem_support;
>   };
>   
>   struct qmi_wlanfw_host_cap_resp_msg_v01 {
> @@ -267,7 +269,7 @@ struct qmi_wlanfw_host_cap_resp_msg_v01 {
>   
>   #define QMI_WLANFW_PHY_CAP_REQ_MSG_V01_MAX_LEN		0
>   #define QMI_WLANFW_PHY_CAP_REQ_V01			0x0057
> -#define QMI_WLANFW_PHY_CAP_RESP_MSG_V01_MAX_LEN		18
> +#define QMI_WLANFW_PHY_CAP_RESP_MSG_V01_MAX_LEN		22

this is not used anywhere, but since you're touching it should this be 
26 ? response TLV 7, num_phy 4, board_id 7, existing 
single_chip_mlo_support 4, and new dynamic_ddr_support 4


--
Ramesh

^ permalink raw reply

* Re: [PATCH v1 1/2] overflow: Allow to sum a few arguments at once
From: Andy Shevchenko @ 2026-06-18  6:39 UTC (permalink / raw)
  To: David Laight
  Cc: Johannes Berg, linux-hardening, linux-kernel, linux-wireless,
	Kees Cook, Gustavo A. R. Silva
In-Reply-To: <20260617223056.754bfcb8@pumpkin>

On Wed, Jun 17, 2026 at 10:30:56PM +0100, David Laight wrote:
> On Wed, 17 Jun 2026 14:56:09 +0200
> Johannes Berg <johannes@sipsolutions.net> wrote:
> > On Wed, 2026-06-17 at 13:12 +0200, Andy Shevchenko wrote:
> > > Convert size_add() to take variadic argument, so we can simplify users
> > > with using a macro only once.  
> > 
> > > +#define __size_add3(addend1, addend2, addend3, addend4, ...)			\
> > > +	__size_add(__size_add2(addend1,  addend2, addend3), addend4)
> > > +#define __size_add4(addend1, addend2, addend3, addend4, addend5, ...)		\
> > > +	__size_add(__size_add3(addend1,  addend2, addend3, addend4), addend5)  
> > 
> > I guess it's not going to really matter, but it would generate fewer
> > calls to have something more like
> > 
> > #define __size_add3(a1, a2, a3, a4) \
> > 	size_add(size_add(a1, a2), size_add(a3, a4))
> > #define __size_add4(a1, a2, a3, a4, a5) \
> > 	size_add(size_add(a1, a2), size_add(a3, a4, a5))
> > 
> > as a binary tree, rather than only cutting one off every time. Not sure
> > that results in hugely different code though - maybe fewer overflow
> > checks?

Good question. I'm also thinking that one-by-one may expand in too much of
preprocessor code (haven't checked myself).

> The binary tree stands a chance of executing less slowly because the leaf
> adds can be executed in parallel.
> Excluding the saturation checks (wtf is it called size_add() not
> saturating_add() ?) (a + b) + (c + d) will usually execute faster than
> ((a + b) + c) + d because the (a + b) and (c + d) can execute at the
> same time; unfortunately gcc will always generate the latter.

I'm confused. "unfortunately... the latter"? You meant "the former"?

> > Although your version make it really completely equivalent to the
> > nl80211.c code, clearly it doesn't matter if all the values are "good",
> > and I believe the overflow behaviour means it doesn't matter for the
> > overflow case either?

Indeed. Whenever the value is saturated, the rest is just matter of sequential
unlikely branches taken.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v9] PCI: Add device-specific reset for Qualcomm devices
From: Jose Ignacio Tornos Martinez @ 2026-06-18  6:33 UTC (permalink / raw)
  To: mani
  Cc: alex, ath11k, ath12k, bhelgaas, jjohnson, jtornosm, linux-kernel,
	linux-pci, linux-wireless, mhi
In-Reply-To: <n3maiuu5cctivlal4fozysz37ir6ecdfws5u2xxw2neclsfdfj@nsvwvlh7b4j5>

Hi Mani,

Let me clarify the exact scenario and where the reset is necessary:

* For the commented WiFi devices (WCN6855/WCN7850):

Standard VFIO passthrough flow (this works fine):
  1. Unbind native driver (ath11k/ath12k/MHI)
  2. Bind vfio-pci driver
  3. Assign device to VM
  4. VM boots, loads its own driver → device works perfectly
  5. VM shuts down cleanly → device can be reassigned → works fine

The problem occurs with unclean VM termination:
  1. VM crashes or is force-terminated
  2. VFIO tries to reset the device before reassignment
  3. Without a working PCI reset method, reset fails
  4. Device stuck in undefined state → cannot be reassigned to another VM
  
     Unbinding the driver again doesn't help because the device hardware
     itself is in a bad state. From hypervisor:
     $ lspci -vvv -s 0000:03:00.0
        03:00.0 Network controller: Qualcomm Technologies, Inc (rev ff) (prog-if ff)
            !!! Unknown header type 7f
     And a full host power-cycle is necessary to recover.
     
* For the commented modem devices (SDX62/SDX65): 

Even worse because it fails during the first VM boot without proper reset
capability, standard VFIO passthrough flow:
  1. Unbind native driver (MHI)
  2. Bind vfio-pci driver
  3. Assign device to VM
  4. VM boots, loads its own driver and crashes:
     [   24.024165] mhi mhi0: Device failed to enter MHI Ready
     [   24.024168] mhi mhi0: MHI did not enter READY state
     
     Unbind/rebind attempts fail:
     [  352.643601] mhi mhi0: Requested to power ON
     [  352.643611] mhi mhi0: Power on setup success
     [  373.442954] mhi mhi0: Device failed to clear MHI Reset
     [  373.442970] mhi mhi0: MHI did not enter READY state
     And requires a full host power cycle to recover,
     even outside of VFIO scenarios.

* MHI Host driver's remove callback may handle clean software state
teardown, but it doesn't provide a PCI reset capability that VFIO can
invoke. VFIO needs a reset method registered in the PCI reset hierarchy
(device_specific, pm, flr, bus, etc.). VFIO invokes this reset both during
initial device binding (before the VM starts) and when reassigning the
device between VMs - without a working reset method, the device cannot
reach a clean state for initialization.



I hope this clarifies the scenario better. Please let me know if I can
provide more information or run any specific tests to help investigate this
further.

Thanks

Best regards
José Ignacio


^ permalink raw reply

* Re: [PATCH] wifi: mt76: mt7925: support new WoW pattern TLV
From: kernel test robot @ 2026-06-18  6:00 UTC (permalink / raw)
  To: Sean Wang, Felix Fietkau, Lorenzo Bianconi
  Cc: oe-kbuild-all, linux-wireless, linux-mediatek, Sean Wang,
	Stella Liu
In-Reply-To: <20260615212137.477893-1-sean.wang@kernel.org>

Hi Sean,

kernel test robot noticed the following build warnings:

[auto build test WARNING on wireless-next/main]
[also build test WARNING on wireless/main linus/master v7.1 next-20260616]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Sean-Wang/wifi-mt76-mt7925-support-new-WoW-pattern-TLV/20260616-052424
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
patch link:    https://lore.kernel.org/r/20260615212137.477893-1-sean.wang%40kernel.org
patch subject: [PATCH] wifi: mt76: mt7925: support new WoW pattern TLV
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260618/202606181323.heBYmqMQ-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260618/202606181323.heBYmqMQ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606181323.heBYmqMQ-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/net/wireless/mediatek/mt76/mt7925/mcu.c:15:13: warning: 'mt7925_mcu_wow_pattern_old_tlv' defined but not used [-Wunused-function]
      15 | static bool mt7925_mcu_wow_pattern_old_tlv(struct mt76_dev *dev)
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


vim +/mt7925_mcu_wow_pattern_old_tlv +15 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c

    14	
  > 15	static bool mt7925_mcu_wow_pattern_old_tlv(struct mt76_dev *dev)
    16	{
    17		const char *fw_version = dev->hw->wiphy->fw_version;
    18		const char *build_date = strrchr(fw_version, '-');
    19	
    20		if (!is_mt7925(dev))
    21			return false;
    22	
    23		if (!build_date)
    24			return false;
    25	
    26		build_date++;
    27	
    28		return strncmp(build_date, MT7925_WOW_PATTERN_NEW_FW_DATE,
    29			       strlen(MT7925_WOW_PATTERN_NEW_FW_DATE)) < 0;
    30	}
    31	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* RE: [PATCH] wifi: rtw89: regd: fix ww domain blocking on 6GHz
From: Zong-Zhe Yang @ 2026-06-18  3:45 UTC (permalink / raw)
  To: Matthew Leach, Ping-Ke Shih
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
	kernel@collabora.com
In-Reply-To: <20260617-rtw89-6ghz-regd-fixes-v1-1-33d744a07d16@collabora.com>

Matthew Leach <matthew.leach@collabora.com> wrote:
> 
> The current code keeps a separate instance for the ww reg domain outside
> the static map, and treats rtw89_regd_get_index() returning
> RTW89_REGD_MAX_COUNTRY_NUM as the index for the ww domain. This
> conflates the "not found" sentinel with a real index and causes the

We ensure regd_map size <= RTW89_REGD_MAX_COUNTRY_NUM.
And we treat "not found" as WW-like case.

> block_* bitmap lookups to skip ww entirely, explicitly blocking 6GHz on
> the ww domain rather than deferring to the kernel regulatory policy.

In fact, we meant to block 6GHz on WW for now.
And I didn't see wireless-regdb enables 6GHz on WW either.

> 
> Fold the standalone rtw89_ww_regd into rtw89_regd_map[0] and drop the
> special case in rtw89_regd_get_index() that returned
> RTW89_REGD_MAX_COUNTRY_NUM for ww. rtw89_regd_find_reg_by_name()
> now
> returns NULL on miss, and its callers either translate that into the
> RTW89_REGD_MAX_COUNTRY_NUM sentinel
> (rtw89_regd_get_index_by_name()) or
> fall back to the ww entry explicitly (rtw89_regd_notifier_apply()). With

A big problem here:
Note that regd_map will be generated/updated based on certification document
provided by our RF team, but it won't include WW. (no so-called WW certification)

There are two kinds of chipsets:
1. regd_map is loaded from built-in array
2. regd_map is loaded from fw element

For case 2, regd_map[0] won't stand for WW.
So, your fallback won't work correctly.

> ww living at a real index, the block-list checks apply and 6GHz is no
> longer unconditionally blocked for ww.
> 
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
> ---
>  drivers/net/wireless/realtek/rtw89/regd.c | 28 ++++++++++++++++++----------
>  1 file changed, 18 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/net/wireless/realtek/rtw89/regd.c
> b/drivers/net/wireless/realtek/rtw89/regd.c
> index 28466cb35ea2..698b8b7f6129 100644
> --- a/drivers/net/wireless/realtek/rtw89/regd.c
> +++ b/drivers/net/wireless/realtek/rtw89/regd.c
> @@ -21,10 +21,8 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct
> regulatory_request *request
> 
>  static_assert(BITS_PER_TYPE(unsigned long) >=
> NUM_OF_RTW89_REGD_FUNC);
> 
> -static const struct rtw89_regd rtw89_ww_regd =
> -       COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0);
> -
>  static const struct rtw89_regd rtw89_regd_map[] = {
> +       COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW,
> 0x0),
>         COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO,
> RTW89_FCC, 0x0),
>         COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0),
>         COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0),
> @@ -316,12 +314,13 @@ static const struct rtw89_regd
> *rtw89_regd_find_reg_by_name(struct rtw89_dev *rt
>                         return &regd_ctrl->map[i];
>         }
> 
> -       return &rtw89_ww_regd;
> +       return NULL;
>  }
> 
>  static bool rtw89_regd_is_ww(const struct rtw89_regd *regd)
>  {
> -       return regd == &rtw89_ww_regd;
> +       /* Index 0 in the static map contains the WW domain entry. */
> +       return regd == &rtw89_regd_map[0];
>  }
> 
>  static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct
> rtw89_regd *regd)
> @@ -331,9 +330,6 @@ static u8 rtw89_regd_get_index(struct rtw89_dev
> *rtwdev, const struct rtw89_regd
> 
>         BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) >
> RTW89_REGD_MAX_COUNTRY_NUM);
> 
> -       if (rtw89_regd_is_ww(regd))
> -               return RTW89_REGD_MAX_COUNTRY_NUM;
> -
>         return regd - regd_ctrl->map;
>  }
> 
> @@ -342,6 +338,10 @@ static u8 rtw89_regd_get_index_by_name(struct
> rtw89_dev *rtwdev, const char *alp
>         const struct rtw89_regd *regd;
> 
>         regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2);
> +
> +       if (!regd)
> +               return RTW89_REGD_MAX_COUNTRY_NUM;
> +
>         return rtw89_regd_get_index(rtwdev, regd);
>  }
> 
> @@ -721,7 +721,7 @@ int rtw89_regd_init_hint(struct rtw89_dev *rtwdev)
>                 return -EINVAL;
> 
>         chip_regd = rtw89_regd_find_reg_by_name(rtwdev,
> rtwdev->efuse.country_code);
> -       if (!rtw89_regd_is_ww(chip_regd)) {
> +       if (chip_regd && !rtw89_regd_is_ww(chip_regd)) {
>                 rtwdev->regulatory.regd = chip_regd;
>                 rtwdev->regulatory.programmed = true;
> 
> @@ -859,7 +859,15 @@ static void rtw89_regd_notifier_apply(struct
> rtw89_dev *rtwdev,
>                                       struct wiphy *wiphy,
>                                       struct regulatory_request
> *request)
>  {
> -       rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev,
> request->alpha2);
> +       const struct rtw89_regd *regd =
> rtw89_regd_find_reg_by_name(rtwdev, request->alpha2);
> +
> +       if (!regd) {
> +               /* Fallback to WW domain if name not found. */
> +               regd = &rtw89_regd_map[0];
> +       }
> +
> +       rtwdev->regulatory.regd = regd;
> +
>         /* This notification might be set from the system of distros,
>          * and it does not expect the regulatory will be modified by
>          * connecting to an AP (i.e. country ie).
> 
> ---
> base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
> change-id: 20260616-rtw89-6ghz-regd-fixes-d8f816880bd0
> 
> Best regards,
> --
> Matt
> 


^ permalink raw reply

* Re: [PATCH v3 3/3] wifi: ath6kl: fix OOB read from firmware num_msg in TX complete handler
From: Jeff Johnson @ 2026-06-18  2:26 UTC (permalink / raw)
  To: Tristan Madani, Johannes Berg
  Cc: linux-wireless, linux-kernel, Tristan Madani
In-Reply-To: <20260421135009.348084-4-tristmd@gmail.com>

On 4/21/2026 6:50 AM, Tristan Madani wrote:
> From: Tristan Madani <tristan@talencesecurity.com>
> 
> The firmware-controlled num_msg field (u8, 0-255) drives the loop in
> ath6kl_wmi_tx_complete_event_rx() without validation against the buffer
> length. This allows out-of-bounds reads of up to 1020 bytes past the
> WMI event buffer when the firmware sends an inflated num_msg.
> 
> Add a check that the buffer is large enough to hold num_msg entries.
> 
> Fixes: bdcd81707973 ("Add ath6kl cleaned up driver")
> Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
> ---
> Changes in v3:
>   - Regenerated from wireless-next with proper git format-patch to
>     produce valid index hashes (v2 had post-processed index lines).
> 
> Changes in v2:
>   - No code changes from v1.
> 
>  drivers/net/wireless/ath/ath6kl/wmi.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
> index 1cafbac2938fe..f56722c5ef5f1 100644
> --- a/drivers/net/wireless/ath/ath6kl/wmi.c
> +++ b/drivers/net/wireless/ath/ath6kl/wmi.c
> @@ -484,6 +484,12 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
>  
>  	evt = (struct wmi_tx_complete_event *) datap;
>  
> +	if (len < sizeof(*evt) ||
> +	    len < sizeof(*evt) + evt->num_msg * sizeof(struct tx_complete_msg_v1)) {
> +		ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d for %u msgs\n",
> +			   len, evt->num_msg);

In the case where the first test is true, the logging of evt->num_msg will
still overread the buffer.

I think the logic would be more clear if it follows the pattern in the 2/3
patch, first validate the fixed portion of the struct is available, and then
separately validate the variable portion of the struct is available:

	if (len < sizeof(*evt) {
		ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d\n",
			   len);
		return -EINVAL;
	}
		
	if (len < sizeof(*evt) + evt->num_msg * sizeof(struct tx_complete_msg_v1)) {
		ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d for %u msgs\n",
			   len, evt->num_msg);
		return -EINVAL;
	}

>  	ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n",
>  		   evt->num_msg, evt->msg_len, evt->msg_type);
>  


^ permalink raw reply

* Re: [PATCH] wifi: ath6kl: fix use-after-free in aggr_reset_state()
From: Jeff Johnson @ 2026-06-18  1:30 UTC (permalink / raw)
  To: Daniel Hodges, linux-wireless
  Cc: tglx, mingo, joe, vthiagar, rmani, jouni, linux-kernel, stable
In-Reply-To: <d5429e89-0eb8-46bd-b143-95fc5adefa2d@oss.qualcomm.com>

On 6/17/2026 6:26 PM, Jeff Johnson wrote:
> On 2/6/2026 10:52 AM, Daniel Hodges wrote:
>> The aggr_reset_state() function uses timer_delete() (non-synchronous)
>> for the aggregation timer before proceeding to delete TID state and
>> before the structure is freed by callers like aggr_module_destroy().
>>
>> If the timer callback (aggr_timeout) is executing when aggr_reset_state()
>> is called, the callback will continue to access aggr_conn fields like
>> rx_tid[] and stat[] which may be freed immediately after by
>> kfree(aggr_info->aggr_conn) in aggr_module_destroy().
>>
>> Additionally, the timer callback can re-arm itself via mod_timer() while
>> aggr_reset_state() is running, creating a more complex race condition.
>>
>> Use timer_delete_sync() instead to ensure any running timer callback
>> has completed before returning.
>>
>> Fixes: bdcd81707973 ("Add ath6kl cleaned up driver")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Daniel Hodges <git@danielhodges.dev>
>> ---
>>  drivers/net/wireless/ath/ath6kl/txrx.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
>> index c3b06b515c4f..25ff5dec221c 100644
>> --- a/drivers/net/wireless/ath/ath6kl/txrx.c
>> +++ b/drivers/net/wireless/ath/ath6kl/txrx.c
>> @@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)
>>  		return;
>>  
>>  	if (aggr_conn->timer_scheduled) {
>> -		timer_delete(&aggr_conn->timer);
>> +		timer_delete_sync(&aggr_conn->timer);
> 
> My review agent claims this still doesn't fix the UAF since aggr_timeout() can
> call mod_timer() to rearm itself and hence the timer can fire again.
> Instead it suggests timer_shutdown_sync() should be used since that prevents
> any rearm from taking effect.
> 
> But I'm not familiar with this driver so I don't know if there are reasons to
> not use timer_shutdown_sync(), i.e. if the timer will be reused again then
> timer_setup() will need to be called again.

Interesting enough, another iteration of the same agent says:
**The fix is correct.** `timer_delete_sync()` loops until the timer is both
not-running and not-pending — it handles the re-arm case because after the
callback calls `mod_timer()`, the sync loop picks that up and cancels it.

Gotta love our new AI-driven workflows...

/jeff

^ permalink raw reply

* Re: [PATCH] wifi: ath6kl: fix use-after-free in aggr_reset_state()
From: Jeff Johnson @ 2026-06-18  1:26 UTC (permalink / raw)
  To: Daniel Hodges, linux-wireless
  Cc: tglx, mingo, joe, vthiagar, rmani, jouni, linux-kernel, stable
In-Reply-To: <20260206185207.30098-1-git@danielhodges.dev>

On 2/6/2026 10:52 AM, Daniel Hodges wrote:
> The aggr_reset_state() function uses timer_delete() (non-synchronous)
> for the aggregation timer before proceeding to delete TID state and
> before the structure is freed by callers like aggr_module_destroy().
> 
> If the timer callback (aggr_timeout) is executing when aggr_reset_state()
> is called, the callback will continue to access aggr_conn fields like
> rx_tid[] and stat[] which may be freed immediately after by
> kfree(aggr_info->aggr_conn) in aggr_module_destroy().
> 
> Additionally, the timer callback can re-arm itself via mod_timer() while
> aggr_reset_state() is running, creating a more complex race condition.
> 
> Use timer_delete_sync() instead to ensure any running timer callback
> has completed before returning.
> 
> Fixes: bdcd81707973 ("Add ath6kl cleaned up driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Daniel Hodges <git@danielhodges.dev>
> ---
>  drivers/net/wireless/ath/ath6kl/txrx.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
> index c3b06b515c4f..25ff5dec221c 100644
> --- a/drivers/net/wireless/ath/ath6kl/txrx.c
> +++ b/drivers/net/wireless/ath/ath6kl/txrx.c
> @@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)
>  		return;
>  
>  	if (aggr_conn->timer_scheduled) {
> -		timer_delete(&aggr_conn->timer);
> +		timer_delete_sync(&aggr_conn->timer);

My review agent claims this still doesn't fix the UAF since aggr_timeout() can
call mod_timer() to rearm itself and hence the timer can fire again.
Instead it suggests timer_shutdown_sync() should be used since that prevents
any rearm from taking effect.

But I'm not familiar with this driver so I don't know if there are reasons to
not use timer_shutdown_sync(), i.e. if the timer will be reused again then
timer_setup() will need to be called again.

>  		aggr_conn->timer_scheduled = false;
>  	}
>  


^ permalink raw reply

* [PATCH ath-next] ath9k: eeprom: alias vpdTableI onto vpdTableL to shrink stack frame
From: Rosen Penev @ 2026-06-17 23:41 UTC (permalink / raw)
  To: linux-wireless
  Cc: Toke Høiland-Jørgensen, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt, open list,
	open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b

vpdTableL, vpdTableR, and vpdTableI are never live simultaneously.
vpdTableL and vpdTableR are consumed during the frequency-interpolation
step that writes vpdTableI; after the if/else they are never read
again.  Reuse vpdTableL for the interpolated result (what was
vpdTableI), reducing the stack frame by one 256-byte array.

The read-via-write in the else branch is safe: ath9k_hw_interpolate()
receives vpdTableL[i][j] by value as a function argument before the
return value is written back to vpdTableL[i][j].

Stack frame size change (x86_64, clang):
  before: 0x440 (1088 B)
  after:  0x330 (816 B)

Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 drivers/net/wireless/ath/ath9k/eeprom.c | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index df58dc02e104..272491ae9f29 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -464,8 +464,6 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
 		[AR5416_MAX_PWR_RANGE_IN_HALF_DB];
 	static u8 vpdTableR[AR5416_NUM_PD_GAINS]
 		[AR5416_MAX_PWR_RANGE_IN_HALF_DB];
-	static u8 vpdTableI[AR5416_NUM_PD_GAINS]
-		[AR5416_MAX_PWR_RANGE_IN_HALF_DB];
 
 	u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR;
 	u8 minPwrT4[AR5416_NUM_PD_GAINS];
@@ -509,7 +507,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
 						data_9287[idxL].pwrPdg[i],
 						data_9287[idxL].vpdPdg[i],
 						intercepts,
-						vpdTableI[i]);
+						vpdTableL[i]);
 			}
 		} else if (eeprom_4k) {
 			for (i = 0; i < numXpdGains; i++) {
@@ -519,7 +517,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
 						data_4k[idxL].pwrPdg[i],
 						data_4k[idxL].vpdPdg[i],
 						intercepts,
-						vpdTableI[i]);
+						vpdTableL[i]);
 			}
 		} else {
 			for (i = 0; i < numXpdGains; i++) {
@@ -529,7 +527,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
 						data_def[idxL].pwrPdg[i],
 						data_def[idxL].vpdPdg[i],
 						intercepts,
-						vpdTableI[i]);
+						vpdTableL[i]);
 			}
 		}
 	} else {
@@ -568,7 +566,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
 						vpdTableR[i]);
 
 			for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) {
-				vpdTableI[i][j] =
+				vpdTableL[i][j] =
 					(u8)(ath9k_hw_interpolate((u16)
 					     FREQ2FBIN(centers.
 						       synth_center,
@@ -605,11 +603,11 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
 					(minPwrT4[i] / 2)) -
 				       tPdGainOverlap + 1 + minDelta);
 		}
-		vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]);
+		vpdStep = (int16_t)(vpdTableL[i][1] - vpdTableL[i][0]);
 		vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
 
 		while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
-			tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep);
+			tmpVal = (int16_t)(vpdTableL[i][0] + ss * vpdStep);
 			pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal);
 			ss++;
 		}
@@ -621,17 +619,17 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
 			tgtIndex : sizeCurrVpdTable;
 
 		while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
-			pPDADCValues[k++] = vpdTableI[i][ss++];
+			pPDADCValues[k++] = vpdTableL[i][ss++];
 		}
 
-		vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] -
-				    vpdTableI[i][sizeCurrVpdTable - 2]);
+		vpdStep = (int16_t)(vpdTableL[i][sizeCurrVpdTable - 1] -
+				    vpdTableL[i][sizeCurrVpdTable - 2]);
 		vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
 
 		if (tgtIndex >= maxIndex) {
 			while ((ss <= tgtIndex) &&
 			       (k < (AR5416_NUM_PDADC_VALUES - 1))) {
-				tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] +
+				tmpVal = (int16_t)((vpdTableL[i][sizeCurrVpdTable - 1] +
 						    (ss - maxIndex + 1) * vpdStep));
 				pPDADCValues[k++] = (u8)((tmpVal > 255) ?
 							 255 : tmpVal);
-- 
2.54.0


^ permalink raw reply related

* [PATCHv2 ath-next] wifi: ath5k: ahb: use devm for ioremap
From: Rosen Penev @ 2026-06-17 23:10 UTC (permalink / raw)
  To: linux-wireless; +Cc: Jiri Slaby, Nick Kossifidis, Luis Chamberlain, open list

Simplifies the code by quite a bit in probe.

Also allows removing a goto and returning directly.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 v2: fix typo and remove error message
 drivers/net/wireless/ath/ath5k/ahb.c | 33 ++++++----------------------
 1 file changed, 7 insertions(+), 26 deletions(-)

diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c
index cb3e891ee1bd..8d28e02c3418 100644
--- a/drivers/net/wireless/ath/ath5k/ahb.c
+++ b/drivers/net/wireless/ath/ath5k/ahb.c
@@ -87,7 +87,6 @@ static int ath_ahb_probe(struct platform_device *pdev)
 	struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
 	struct ath5k_hw *ah;
 	struct ieee80211_hw *hw;
-	struct resource *res;
 	void __iomem *mem;
 	int irq;
 	int ret = 0;
@@ -95,35 +94,21 @@ static int ath_ahb_probe(struct platform_device *pdev)
 
 	if (!dev_get_platdata(&pdev->dev)) {
 		dev_err(&pdev->dev, "no platform data specified\n");
-		ret = -EINVAL;
-		goto err_out;
+		return -EINVAL;
 	}
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (res == NULL) {
-		dev_err(&pdev->dev, "no memory resource found\n");
-		ret = -ENXIO;
-		goto err_out;
-	}
-
-	mem = ioremap(res->start, resource_size(res));
-	if (mem == NULL) {
-		dev_err(&pdev->dev, "ioremap failed\n");
-		ret = -ENOMEM;
-		goto err_out;
-	}
+	mem = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(mem))
+		return PTR_ERR(mem);
 
 	irq = platform_get_irq(pdev, 0);
-	if (irq < 0) {
-		ret = irq;
-		goto err_iounmap;
-	}
+	if (irq < 0)
+		return irq;
 
 	hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops);
 	if (hw == NULL) {
 		dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
-		ret = -ENOMEM;
-		goto err_iounmap;
+		return -ENOMEM;
 	}
 
 	ah = hw->priv;
@@ -179,9 +164,6 @@ static int ath_ahb_probe(struct platform_device *pdev)
 
  err_free_hw:
 	ieee80211_free_hw(hw);
- err_iounmap:
-        iounmap(mem);
- err_out:
 	return ret;
 }
 
@@ -213,7 +195,6 @@ static void ath_ahb_remove(struct platform_device *pdev)
 	}
 
 	ath5k_deinit_ah(ah);
-	iounmap(ah->iobase);
 	ieee80211_free_hw(hw);
 }
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH 0/4] bcma/ssb/b43: BCM4352 AC-PHY wip
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri

This series introduce the initial support for BCM4352, an AC class
chip, this is just a gentle introduction before the bulk of edits, 
because in reality i already have AC support working up to RX signal 
and now debugging TX.
From the latest dmesg grepped from my router, this patch series allow
the user to reach to the "Bus registered" message plus the correct init
of the DMA that took me two weeks to discover.

[   18.280000] bcma-pci-bridge 0000:01:00.0: enabling device (0000 -> 0002)
[   18.280000] bcma-pci-bridge 0000:01:00.0: bus0: Found chip with id 0x4352, rev 0x03 and package 0x01
[   18.290000] bcma-pci-bridge 0000:01:00.0: bus0: Core 0 found: ChipCommon (manuf 0x4BF, id 0x800, rev 0x2B, class 0x0)
[   18.300000] bcma-pci-bridge 0000:01:00.0: bus0: Core 1 found: IEEE 802.11 (manuf 0x4BF, id 0x812, rev 0x2A, class 0x0)
[   18.320000] bcma-pci-bridge 0000:01:00.0: bus0: Core 2 found: ARM CR4 (manuf 0x4BF, id 0x83E, rev 0x02, class 0x0)
[   18.330000] bcma-pci-bridge 0000:01:00.0: bus0: Core 3 found: PCIe Gen2 (manuf 0x4BF, id 0x83C, rev 0x01, class 0x0)
[   18.340000] bcma-pci-bridge 0000:01:00.0: bus0: Core 4 found: USB 2.0 Device (manuf 0x4BF, id 0x81A, rev 0x11, class 0x0)
[   18.380000] bcma: bcma: SPROM rev 11 parsed: board=0x0668 rev=4947 ccode=0x0000 txchain=0x3 rxchain=0x3 antswitch=0 subband5gver=4
[   18.390000] bcma: bcma: SPROM r11 chain0 rxgains 2g={0,0,0} 5gl={3,6,1} 5gm={7,15,1} 5gh={7,15,1}
[   18.400000] bcma: bcma: SPROM r11 chain1 rxgains 2g={0,0,0} 5gl={3,6,1} 5gm={7,15,1} 5gh={7,15,1}
[   18.410000] bcma: bcma: SPROM r11 chain2 rxgains 2g={0,0,0} 5gl={3,6,1} 5gm={7,15,1} 5gh={7,15,1}
[   18.420000] bcma-pci-bridge 0000:01:00.0: bus0: Bus registered
[   19.340000] b43-phy0: Broadcom 4352 WLAN found (core revision 42)
[   19.340000] b43-phy0: Found PHY: Analog 12, Type 11 (AC), Revision 1
[   19.350000] b43-phy0: Found Radio: Manuf 0x17F, ID 0x2069, Revision 4, Version 0
[   44.250000] b43-phy0: DEBUG pre-PSM_RUN: MACCTL=0x00020504 IRQ_REASON=0x00000000 SHM[UCODEREV]=0x0000
[   44.260000] b43-phy0: DEBUG post-PSM_RUN: MACCTL=0x00020502 IRQ_REASON=0x00000000
[   44.260000] b43-phy0: Loading firmware version 784.2 (2012-08-15 21:43:22)
[   44.270000] b43-phy0: phy-ac: rxgain_init applied (2 cores, UNII-1 5gl, sprom rev 11)
[   44.280000] b43-phy0: phy-ac: rxgain core0: triso=6 elnagain=3 trelnabyp=1 (gainctx=0x14)
[   44.290000] b43-phy0: phy-ac: rxgain core1: triso=6 elnagain=3 trelnabyp=1 (gainctx=0x14)
[   44.500000] b43-phy0: DEBUG dma_setup RX DMA64: base=0x020c0000 alo=0x020c0000 ahi=0x80000000 addrext=0x0 translation=0x80000000 in_low=0 ctl=0x0000084d
[   44.720000] b43 bcma0:1 phy0-ap0: entered allmulticast mode
[   44.730000] b43 bcma0:1 phy0-ap0: entered promiscuous mode

The two patches generated with the help of claude are marked as such.
The other two are minimal and did not require assistance from claude.

Assisted-by: claude:claude-Opus-4.8
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
Alessio Ferri (4):
      bcma: host_pci: add BCM4352 device ID 0x43b3
      bcma: pmu: program max resource mask on BCM4352/BCM4360
      ssb: bcma: add SPROM revision 11 extraction
      b43: align DMA64 descriptor ring to 64K on AC cores

 drivers/bcma/driver_chipcommon_pmu.c    |   8 +++
 drivers/bcma/host_pci.c                 |   1 +
 drivers/bcma/sprom.c                    | 100 +++++++++++++++++++++++++++++++-
 drivers/net/wireless/broadcom/b43/dma.c |  20 +++++--
 include/linux/ssb/ssb.h                 |  31 ++++++++++
 include/linux/ssb/ssb_regs.h            |  44 ++++++++++++++
 6 files changed, 199 insertions(+), 5 deletions(-)
---
base-commit: e771677c937da5808f7b6c1f0e4a97ec1a84f8a8
change-id: 20260617-b43-add-4352-wip-f3c3a3d159d8

Best regards,
-- 
Alessio Ferri <alessio.ferri.3012@gmail.com>


^ permalink raw reply

* [PATCH 2/4] bcma: pmu: program max resource mask on BCM4352/BCM4360
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri
In-Reply-To: <20260617-b43-add-4352-wip-v1-0-c81323496720@gmail.com>

From: Alessio Ferri <alessio.ferri.3012@gmail.com>

bcma_pmu_resources_init() has no case for the BCM4352/BCM4360 family.
The vendor instead set the max mask for chip rev > 2 with CS bit 5 clear.
Match the vendor behaviour.

Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
 drivers/bcma/driver_chipcommon_pmu.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
index 263ef6fa1d0f..cdd5f7d668ba 100644
--- a/drivers/bcma/driver_chipcommon_pmu.c
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -188,6 +188,14 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc)
 			  BCMA_RES_4314_WL_CORE_READY;
 		max_msk = 0x3FFFFFFF;
 		break;
+	case BCMA_CHIP_ID_BCM4352:
+	case BCMA_CHIP_ID_BCM4360:
+		/*
+		 * Vendor programs the max mask here for chip rev > 2 with ChipStatus bit 5 clear
+		 */
+		if (bus->chipinfo.rev > 2 && !(cc->status & BIT(5)))
+			max_msk = 0x1ff;
+		break;
 	default:
 		bcma_debug(bus, "PMU resource config unknown or not needed for device 0x%04X\n",
 			   bus->chipinfo.id);

-- 
2.54.0


^ permalink raw reply related

* [PATCH 4/4] b43: align DMA64 descriptor ring to 64K on AC cores
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri
In-Reply-To: <20260617-b43-add-4352-wip-v1-0-c81323496720@gmail.com>

From: Alessio Ferri <alessio.ferri.3012@gmail.com>

The AC d11 core (core_rev 40/42, BCM4352) exposes a 16-bit DMA descriptor
pointer (wrap mask 0xffff, not the 0x1fff b43 assumes). With an 8K-aligned
ring the engine wraps the pointer outside the ring and raises I_DE on RX.
Allocate the 64-bit ring with 64K size on these cores so the base is
64K-aligned and (base & 0xffff) == 0; dma_alloc_coherent() aligns to the
allocation size. ring_mem_size widens to u32 to hold 0x10000. N-PHY cores
report 0x1fff and keep the 8K allocation.

Assisted-by: claude:claude-Opus-4.8
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
 drivers/net/wireless/broadcom/b43/dma.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/broadcom/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c
index 05da6987a845..cb308080312b 100644
--- a/drivers/net/wireless/broadcom/b43/dma.c
+++ b/drivers/net/wireless/broadcom/b43/dma.c
@@ -404,6 +404,18 @@ static inline
 	}
 }
 
+static u32 b43_dma64_ringmem_size(struct b43_dmaring *ring)
+{
+	/* AC d11 cores (core_rev 40/42) expose a 16-bit descriptor pointer
+	 * (mask 0xffff) and need a 64K-aligned ring; dma_alloc_coherent()
+	 * aligns to the (power-of-2) allocation size. Smaller cores use the
+	 * 0x1fff mask and are fine with 8K.
+	 */
+	if (ring->dev->dev->core_rev == 40 || ring->dev->dev->core_rev == 42)
+		return 0x10000;
+	return B43_DMA64_RINGMEMSIZE;
+}
+
 static int alloc_ringmemory(struct b43_dmaring *ring)
 {
 	/* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
@@ -415,8 +427,8 @@ static int alloc_ringmemory(struct b43_dmaring *ring)
 	 * B43_DMA64_RXSTATDPTR. Let's just use 8K buffers even if we don't use
 	 * more than 256 slots for ring.
 	 */
-	u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
-				B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE;
+	u32 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
+			b43_dma64_ringmem_size(ring) : B43_DMA32_RINGMEMSIZE;
 
 	ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev,
 					    ring_mem_size, &(ring->dmabase),
@@ -429,8 +441,8 @@ static int alloc_ringmemory(struct b43_dmaring *ring)
 
 static void free_ringmemory(struct b43_dmaring *ring)
 {
-	u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
-				B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE;
+	u32 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
+			b43_dma64_ringmem_size(ring) : B43_DMA32_RINGMEMSIZE;
 	dma_free_coherent(ring->dev->dev->dma_dev, ring_mem_size,
 			  ring->descbase, ring->dmabase);
 }

-- 
2.54.0


^ permalink raw reply related

* [PATCH 3/4] ssb: bcma: add SPROM revision 11 extraction
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri
In-Reply-To: <20260617-b43-add-4352-wip-v1-0-c81323496720@gmail.com>

From: Alessio Ferri <alessio.ferri.3012@gmail.com>

SPROM revision 11 is needed with the BCM4352/BCM4360 family. Extend
struct ssb_sprom with the rev-11 fields actually parsed (per-band rxgains,
per-chain maxp/pa power info, 80/160 MHz MCS power offsets, pdoffset40ma)
and add bcma_sprom_extract_r11(), dispatched from bcma_sprom_get() when the
SPROM revision word reads as 11. bcma_sprom_get() already probes the rev-11
buffer size, and bcma_sprom_valid() populates bus->sprom.revision before
the dispatch. Revisions <= 10 keep the extract_r8 path.

Header fields (IL0MAC/ANTAVAIL/TXRXC/CCODE) reuse the rev-8 masks/shifts;
only the absolute offsets moved. CCODE uses 0x96, since rev-8 0x92 collides
with the middle word of the rev-11 IL0MAC block.

Assisted-by: Claude:claude-Opus-4.8
Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
 drivers/bcma/sprom.c         | 100 ++++++++++++++++++++++++++++++++++++++++++-
 include/linux/ssb/ssb.h      |  31 ++++++++++++++
 include/linux/ssb/ssb_regs.h |  44 +++++++++++++++++++
 3 files changed, 174 insertions(+), 1 deletion(-)

diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index e668ad7963fc..514c8149dbe7 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -474,6 +474,101 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
 	     SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
 }
 
+static void bcma_sprom_unpack_rxgains(struct ssb_sprom_rxgains *out,
+				      int chain, u8 packed)
+{
+	out->trelnabyp[chain] = (packed >> 7) & 0x01;
+	out->triso[chain]     = (packed >> 3) & 0x0f;
+	out->elnagain[chain]  =  packed       & 0x07;
+}
+
+static void bcma_sprom_extract_r11(struct bcma_bus *bus, const u16 *sprom)
+{
+	static const u16 pwr_info_offset[] = {
+		SSB_SPROM11_PWR_INFO_CORE0,
+		SSB_SPROM11_PWR_INFO_CORE1,
+		SSB_SPROM11_PWR_INFO_CORE2,
+	};
+	u16 v;
+	int i, j;
+
+	BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) >
+		     ARRAY_SIZE(bus->sprom.core_pwr_info));
+
+	/* Header: rev-11 offsets, rev-8 masks/shifts. */
+	for (i = 0; i < 3; i++) {
+		v = sprom[SPOFF(SSB_SPROM11_IL0MAC) + i];
+		*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
+	}
+
+	SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
+	SPEX(board_type, SSB_SPROM1_SPID, ~0, 0);
+
+	SPEX(country_code, SSB_SPROM11_CCODE, ~0, 0);
+	SPEX(ant_available_a, SSB_SPROM11_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
+	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
+	SPEX(ant_available_bg, SSB_SPROM11_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
+	     SSB_SPROM8_ANTAVAIL_BG_SHIFT);
+	SPEX(txchain, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
+	     SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
+	SPEX(rxchain, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
+	     SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
+	SPEX(antswitch, SSB_SPROM11_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
+	     SSB_SPROM8_TXRXC_SWITCH_SHIFT);
+	SPEX(subband5gver, SSB_SPROM11_SUBBAND5GVER, 0x00ff, 0);
+
+	/* Per-chain power info. */
+	for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
+		struct ssb_sprom_core_pwr_info *p = &bus->sprom.core_pwr_info[i];
+		u16 base = pwr_info_offset[i];
+
+		p->maxp2ga = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP2GA)] & 0xff;
+
+		for (j = 0; j < 3; j++)
+			p->pa2ga[j] = sprom[SPOFF(base + SSB_SPROM11_PWR_PA2GA) + j];
+
+		v = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP5GA)];
+		p->maxp5ga[0] = v & 0xff;
+		p->maxp5ga[1] = (v >> 8) & 0xff;
+		v = sprom[SPOFF(base + SSB_SPROM11_PWR_MAXP5GA) + 1];
+		p->maxp5ga[2] = v & 0xff;
+		p->maxp5ga[3] = (v >> 8) & 0xff;
+
+		for (j = 0; j < 12; j++)
+			p->pa5ga[j] = sprom[SPOFF(base + SSB_SPROM11_PWR_PA5GA) + j];
+
+		v = sprom[SPOFF(base + SSB_SPROM11_PWR_RXGAINS0)];
+		bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gm, i,  v       & 0xff);
+		bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gh, i, (v >> 8) & 0xff);
+		v = sprom[SPOFF(base + SSB_SPROM11_PWR_RXGAINS1)];
+		bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_2g,  i,  v       & 0xff);
+		bcma_sprom_unpack_rxgains(&bus->sprom.rxgains_5gl, i, (v >> 8) & 0xff);
+	}
+
+	for (i = 0; i < 3; i++)
+		bus->sprom.pdoffset40ma[i] =
+			sprom[SPOFF(SSB_SPROM11_PDOFFSET40MA) + i];
+
+	SPEX(cckbw202gpo,           SSB_SPROM11_CCKBW202GPO,           ~0, 0);
+	SPEX(cckbw20ul2gpo,         SSB_SPROM11_CCKBW20UL2GPO,         ~0, 0);
+	SPEX32(mcsbw202gpo,         SSB_SPROM11_MCSBW202GPO,           ~0, 0);
+	SPEX32(mcsbw402gpo,         SSB_SPROM11_MCSBW402GPO,           ~0, 0);
+	SPEX(dot11agofdmhrbw202gpo, SSB_SPROM11_DOT11AGOFDMHRBW202GPO, ~0, 0);
+	SPEX(ofdmlrbw202gpo,        SSB_SPROM11_OFDMLRBW202GPO,        ~0, 0);
+	SPEX32(mcsbw205glpo,        SSB_SPROM11_MCSBW205GLPO,          ~0, 0);
+	SPEX32(mcsbw405glpo,        SSB_SPROM11_MCSBW405GLPO,          ~0, 0);
+	SPEX32(mcsbw805glpo,        SSB_SPROM11_MCSBW805GLPO,          ~0, 0);
+	SPEX32(mcsbw1605glpo,       SSB_SPROM11_MCSBW1605GLPO,         ~0, 0);
+	SPEX32(mcsbw205gmpo,        SSB_SPROM11_MCSBW205GMPO,          ~0, 0);
+	SPEX32(mcsbw405gmpo,        SSB_SPROM11_MCSBW405GMPO,          ~0, 0);
+	SPEX32(mcsbw805gmpo,        SSB_SPROM11_MCSBW805GMPO,          ~0, 0);
+	SPEX32(mcsbw1605gmpo,       SSB_SPROM11_MCSBW1605GMPO,         ~0, 0);
+	SPEX32(mcsbw205ghpo,        SSB_SPROM11_MCSBW205GHPO,          ~0, 0);
+	SPEX32(mcsbw405ghpo,        SSB_SPROM11_MCSBW405GHPO,          ~0, 0);
+	SPEX32(mcsbw805ghpo,        SSB_SPROM11_MCSBW805GHPO,          ~0, 0);
+	SPEX32(mcsbw1605ghpo,       SSB_SPROM11_MCSBW1605GHPO,         ~0, 0);
+}
+
 /*
  * Indicates the presence of external SPROM.
  */
@@ -640,7 +735,10 @@ int bcma_sprom_get(struct bcma_bus *bus)
 		bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
 		err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
 	} else {
-		bcma_sprom_extract_r8(bus, sprom);
+		if (bus->sprom.revision == 11)
+			bcma_sprom_extract_r11(bus, sprom);
+		else
+			bcma_sprom_extract_r8(bus, sprom);
 		kfree(sprom);
 	}
 
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index e1fb11e0f12c..9d4a13c4eca3 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -23,6 +23,19 @@ struct ssb_sprom_core_pwr_info {
 	u8 itssi_2g, itssi_5g;
 	u8 maxpwr_2g, maxpwr_5gl, maxpwr_5g, maxpwr_5gh;
 	u16 pa_2g[4], pa_5gl[4], pa_5g[4], pa_5gh[4];
+
+	/* SROM rev 11 */
+	u8 maxp2ga;
+	u8 maxp5ga[4];
+	u16 pa2ga[3];
+	u16 pa5ga[12];
+};
+
+/* Per-band rx gain context, one value per RF chain. SROM rev 11. */
+struct ssb_sprom_rxgains {
+	u8 elnagain[3];
+	u8 triso[3];
+	u8 trelnabyp[3];
 };
 
 struct ssb_sprom {
@@ -192,6 +205,24 @@ struct ssb_sprom {
 	u16 legofdm40duppo;
 	u8 sar2g;
 	u8 sar5g;
+
+	/* SROM rev 11. Populated only by the rev 11 extractor; zero on
+	 * rev <= 10 boards.
+	 */
+	struct ssb_sprom_rxgains rxgains_2g;
+	struct ssb_sprom_rxgains rxgains_5gl;
+	struct ssb_sprom_rxgains rxgains_5gm;
+	struct ssb_sprom_rxgains rxgains_5gh;
+	u8 subband5gver;
+	u16 dot11agofdmhrbw202gpo;
+	u16 ofdmlrbw202gpo;
+	u32 mcsbw805glpo;
+	u32 mcsbw805gmpo;
+	u32 mcsbw805ghpo;
+	u32 mcsbw1605glpo;
+	u32 mcsbw1605gmpo;
+	u32 mcsbw1605ghpo;
+	u16 pdoffset40ma[3];
 };
 
 /* Information about the PCB the circuitry is soldered on. */
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index 210f46494065..9fedf5dcdccd 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -606,6 +606,50 @@
 #define SSB_SPROM8_BW40PO		0x0196
 #define SSB_SPROM8_BWDUPPO		0x0198
 
+/* SROM revision 11. Only the header fields whose absolute offset moved
+ * are redefined; the bitfield masks/shifts are reused from rev 8.
+ */
+#define SSB_SPROM11_IL0MAC		0x0090	/* 6 byte MAC address */
+#define SSB_SPROM11_CCODE		0x0096	/* 2 byte country code */
+#define SSB_SPROM11_ANTAVAIL		0x00A0
+#define SSB_SPROM11_TXRXC		0x00A8
+#define SSB_SPROM11_PDOFFSET40MA	0x00CA	/* 3 x u16, one per chain */
+#define SSB_SPROM11_SUBBAND5GVER	0x00D6
+
+/* Per-chain power info blocks. Stride 0x28 bytes; field layout given by
+ * SSB_SPROM11_PWR_* below.
+ */
+#define SSB_SPROM11_PWR_INFO_CORE0	0x00D8
+#define SSB_SPROM11_PWR_INFO_CORE1	0x0100
+#define SSB_SPROM11_PWR_INFO_CORE2	0x0128
+
+#define SSB_SPROM11_PWR_MAXP2GA		0x0000	/* u8 in low byte */
+#define SSB_SPROM11_PWR_PA2GA		0x0002	/* 3 x u16 */
+#define SSB_SPROM11_PWR_RXGAINS0	0x0008	/* 5gm (lo) / 5gh (hi) */
+#define SSB_SPROM11_PWR_RXGAINS1	0x000A	/* 2g  (lo) / 5gl (hi) */
+#define SSB_SPROM11_PWR_MAXP5GA		0x000C	/* 2 x u16 -> 4 x u8 */
+#define SSB_SPROM11_PWR_PA5GA		0x0010	/* 12 x u16 */
+
+/* Power-per-rate region. */
+#define SSB_SPROM11_CCKBW202GPO		0x0150
+#define SSB_SPROM11_CCKBW20UL2GPO	0x0152
+#define SSB_SPROM11_MCSBW202GPO		0x0154
+#define SSB_SPROM11_MCSBW402GPO		0x0158
+#define SSB_SPROM11_DOT11AGOFDMHRBW202GPO 0x015C
+#define SSB_SPROM11_OFDMLRBW202GPO	0x015E
+#define SSB_SPROM11_MCSBW205GLPO	0x0160
+#define SSB_SPROM11_MCSBW405GLPO	0x0164
+#define SSB_SPROM11_MCSBW805GLPO	0x0168
+#define SSB_SPROM11_MCSBW1605GLPO	0x016C
+#define SSB_SPROM11_MCSBW205GMPO	0x0170
+#define SSB_SPROM11_MCSBW405GMPO	0x0174
+#define SSB_SPROM11_MCSBW805GMPO	0x0178
+#define SSB_SPROM11_MCSBW1605GMPO	0x017C
+#define SSB_SPROM11_MCSBW205GHPO	0x0180
+#define SSB_SPROM11_MCSBW405GHPO	0x0184
+#define SSB_SPROM11_MCSBW805GHPO	0x0188
+#define SSB_SPROM11_MCSBW1605GHPO	0x018C
+
 /* Values for boardflags_lo read from SPROM */
 #define SSB_BFL_BTCOEXIST		0x0001	/* implements Bluetooth coexistance */
 #define SSB_BFL_PACTRL			0x0002	/* GPIO 9 controlling the PA */

-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/4] bcma: host_pci: add BCM4352 device ID 0x43b3
From: Alessio Ferri @ 2026-06-17 21:42 UTC (permalink / raw)
  To: Rafał Miłecki, Michael Buesch
  Cc: linux-wireless, linux-kernel, b43-dev, Alessio Ferri,
	Alessio Ferri
In-Reply-To: <20260617-b43-add-4352-wip-v1-0-c81323496720@gmail.com>

From: Alessio Ferri <alessio.ferri.3012@gmail.com>

The BCM4352 is an AC PHY. It is missing from bcma_pci_bridge_tbl[],
so bcma-pci-bridge does not bind. Add the ID.

The chip identifies as BCM4352 (chip rev 3, PHY type AC); b43 with AC-PHY
support handles it once bcma reaches the D11 core.

Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
---
 drivers/bcma/host_pci.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 3dc2985063f1..f6cf722a3798 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -298,6 +298,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b1) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43b3) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) },	/* 0xa8db, BCM43217 (sic!) */
 	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43228) },	/* 0xa8dc */

-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v1 1/2] overflow: Allow to sum a few arguments at once
From: David Laight @ 2026-06-17 21:30 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Andy Shevchenko, linux-hardening, linux-kernel, linux-wireless,
	Kees Cook, Gustavo A. R. Silva
In-Reply-To: <1e656f5798a9f2f36daa00aba60d2196b2456335.camel@sipsolutions.net>

On Wed, 17 Jun 2026 14:56:09 +0200
Johannes Berg <johannes@sipsolutions.net> wrote:

> On Wed, 2026-06-17 at 13:12 +0200, Andy Shevchenko wrote:
> > Convert size_add() to take variadic argument, so we can simplify users
> > with using a macro only once.  
> 
> > +#define __size_add3(addend1, addend2, addend3, addend4, ...)			\
> > +	__size_add(__size_add2(addend1,  addend2, addend3), addend4)
> > +#define __size_add4(addend1, addend2, addend3, addend4, addend5, ...)		\
> > +	__size_add(__size_add3(addend1,  addend2, addend3, addend4), addend5)  
> 
> I guess it's not going to really matter, but it would generate fewer
> calls to have something more like
> 
> #define __size_add3(a1, a2, a3, a4) \
> 	size_add(size_add(a1, a2), size_add(a3, a4))
> #define __size_add4(a1, a2, a3, a4, a5) \
> 	size_add(size_add(a1, a2), size_add(a3, a4, a5))
> 
> as a binary tree, rather than only cutting one off every time. Not sure
> that results in hugely different code though - maybe fewer overflow
> checks?

The binary tree stands a chance of executing less slowly because the leaf
adds can be executed in parallel.
Excluding the saturation checks (wtf is it called size_add() not
saturating_add() ?) (a + b) + (c + d) will usually execute faster than
((a + b) + c) + d because the (a + b) and (c + d) can execute at the
same time; unfortunately gcc will always generate the latter.

	David

> 
> Although your version make it really completely equivalent to the
> nl80211.c code, clearly it doesn't matter if all the values are "good",
> and I believe the overflow behaviour means it doesn't matter for the
> overflow case either?
> 
> johannes
> 


^ permalink raw reply

* Re: [PATCH v9] PCI: Add device-specific reset for Qualcomm devices
From: Manivannan Sadhasivam @ 2026-06-17 16:55 UTC (permalink / raw)
  To: Jose Ignacio Tornos Martinez
  Cc: alex, ath11k, ath12k, bhelgaas, jjohnson, linux-kernel, linux-pci,
	linux-wireless, mhi
In-Reply-To: <20260617154709.186286-1-jtornosm@redhat.com>

On Wed, Jun 17, 2026 at 05:47:04PM +0200, Jose Ignacio Tornos Martinez wrote:
> Hi Mani,
> 
> Thank you for the internal clarification and sharing this information.
> 
> I understand the behavior is firmware error recovery, not a proper reset.
> However, these devices are widely used, and the inability to use them in VMs
> is a significant problem. Could we explore options to achieve safe VFIO
> operation?
> 
>   1. Are there ANY alternative reset mechanisms besides D3cold? For example:
>      - Device-specific registers or commands?
>      - MHI bus-level operations?
>      - Firmware commands that could trigger proper reset?
> 
>      If such mechanisms exist, I'm willing to implement whatever is needed.
> 
>   2. If firmware error recovery is the only option available on platforms
>      without _PR3, could we add software steps to make it VFIO-safe?
>      For example, before/after the D3hot transition:
>      - Explicit MHI state teardown?
>      - Firmware commands to clear sensitive device state?
>      - Additional verification or cleanup steps?
> 
>   3. The practical challenge is that _PR3 support is not available on most
>      platforms where these devices need to be deployed (desktops, servers).
>      Additionally, the general d3cold reset method has limitations and
>      remains unimplemented due to the concerns raised earlier (ACPI
>      portability, bridge issues, runtime PM complications).
> 
>      If D3cold is the only proper reset but requires _PR3, and no alternative
>      mechanisms exist, could we consider accepting the firmware error recovery
>      behavior as a last resort - clearly documented as a platform-specific
>      workaround?
> 
>      Currently these devices have no reset capability on most platforms,
>      making them completely unusable for VFIO. Even an imperfect reset is
>      significantly better than no reset at all.
> 
> My goal is ensuring these devices can be safely reassigned between VMs.
> I'm open to implementing any of the above approaches - or others you might
> suggest.
> 

Can you share the exact steps that you tried for passthrough? I'm curious to see
whether you unbinded the MHI host/WLAN driver from the device or not. For the
modem devices, the MHI Host driver's (drivers/bus/mhi/host/pci_generic.c) remove
callback should've quiesced the device and moved the MHI state to RESET if the
driver was unbinded before binding the device with vfio-pci.

I certainly feel that the MHI/WLAN driver should be able to reset the device
during unbind. But I'm not sure if that reset will affect only the firmware
state or the device's config state also. This is something I need to
investigate.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply

* Re: [PATCH v9] PCI: Add device-specific reset for Qualcomm devices
From: Jose Ignacio Tornos Martinez @ 2026-06-17 15:47 UTC (permalink / raw)
  To: mani
  Cc: alex, ath11k, ath12k, bhelgaas, jjohnson, jtornosm, linux-kernel,
	linux-pci, linux-wireless, mhi
In-Reply-To: <6nivb5fncfd5dwqkzlxwhtgbsiqvifazcbgpsgukp44iib45ke@65qpwgrvtkgn>

Hi Mani,

Thank you for the internal clarification and sharing this information.

I understand the behavior is firmware error recovery, not a proper reset.
However, these devices are widely used, and the inability to use them in VMs
is a significant problem. Could we explore options to achieve safe VFIO
operation?

  1. Are there ANY alternative reset mechanisms besides D3cold? For example:
     - Device-specific registers or commands?
     - MHI bus-level operations?
     - Firmware commands that could trigger proper reset?

     If such mechanisms exist, I'm willing to implement whatever is needed.

  2. If firmware error recovery is the only option available on platforms
     without _PR3, could we add software steps to make it VFIO-safe?
     For example, before/after the D3hot transition:
     - Explicit MHI state teardown?
     - Firmware commands to clear sensitive device state?
     - Additional verification or cleanup steps?

  3. The practical challenge is that _PR3 support is not available on most
     platforms where these devices need to be deployed (desktops, servers).
     Additionally, the general d3cold reset method has limitations and
     remains unimplemented due to the concerns raised earlier (ACPI
     portability, bridge issues, runtime PM complications).

     If D3cold is the only proper reset but requires _PR3, and no alternative
     mechanisms exist, could we consider accepting the firmware error recovery
     behavior as a last resort - clearly documented as a platform-specific
     workaround?

     Currently these devices have no reset capability on most platforms,
     making them completely unusable for VFIO. Even an imperfect reset is
     significantly better than no reset at all.

My goal is ensuring these devices can be safely reassigned between VMs.
I'm open to implementing any of the above approaches - or others you might
suggest.

Thank you

Best regards
José Ignacio


^ permalink raw reply

* Re: [PATCH v9] PCI: Add device-specific reset for Qualcomm devices
From: Manivannan Sadhasivam @ 2026-06-17 14:47 UTC (permalink / raw)
  To: Jose Ignacio Tornos Martinez
  Cc: bhelgaas, alex, jjohnson, linux-pci, linux-wireless, ath11k,
	ath12k, mhi, linux-kernel
In-Reply-To: <20260612142638.1243895-1-jtornosm@redhat.com>

On Fri, Jun 12, 2026 at 04:26:38PM +0200, Jose Ignacio Tornos Martinez wrote:
> Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
> lack working reset methods for VFIO passthrough scenarios. These devices
> have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
> broken bus reset.
> 
> The problem manifests in VFIO passthrough scenarios:
> 
> - WCN6855 WiFi card (17cb:1103): Normal VM operation works fine, including
>   clean shutdown/reboot. However, when the VM terminates uncleanly
>   (crash, force-off), VFIO attempts to reset the device before it can
>   be assigned to another VM. Without a working reset method, the device
>   remains in an undefined state, preventing reuse.
> 
> - WCN7850 WiFi card (17cb:1107): Same behavior as WCN6855.
> 
> - SDX62/SDX65 5G modems (17cb:0308): Never successfully initialize even
>   on first VM assignment without proper reset capability.
> 
> Add device-specific reset entries for these Qualcomm devices using D3hot
> power cycling. Testing shows that despite advertising NoSoftRst+, D3hot
> transition provides sufficient reset for VFIO reuse, particularly after
> unexpected VM termination. While not a complete reset (BARs preserved),
> it provides the only viable reset mechanism for these devices.
> 

I checked internally within Qcom and I was told that these PCIe devices retain
the context during D3Hot to D0 transition and that's why they advertise
No_Soft_Reset.

The partial reset behavior you are seeing might be due to firmware handling the
transition as an error state. All these devices use MHI bus and when the MHI
state doesn't match the PCIe device state, then the firmware will treat it as an
error and will try to torn down resources. This could be the reason why you are
seeing partial reset.

Nevertheless, these devices do not support any form of Soft Reset and only way
to reset them would be by doing D3Cold. But that depends on platform support
though. So it would be inaccurate/wrong to assume that these devices support
Soft Reset during D3Hot to D0 transition.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox