* RE: [PATCH] wifi: rtw88: Add NULL check for chip->edcca_th
From: Ping-Ke Shih @ 2026-04-14 2:03 UTC (permalink / raw)
To: Panagiotis Petrakopoulos
Cc: linux-wireless@vger.kernel.org, goainwo@gmail.com,
Bitterblue Smith
In-Reply-To: <20260413100249.28618-1-npetrakopoulos2003@gmail.com>
+ Bitterblue
Panagiotis Petrakopoulos <npetrakopoulos2003@gmail.com> wrote:
> It was recently reported that rtw_fw_adaptivity_result
> in fw.c dereferences rtwdev->chip->edcca_th without
> a null check. The issue appears to be that devices
> with the 8821CE chip don't define edcca_th in their
> chip info. As a result, when rtw_fw_adaptivity_result
> tries to dereference it, the kernel triggers an oops.
>
> Add a NULL check for edcca_th before dereferencing
> it in rtw_fw_adaptivity_result() in fw.c and
> rtw_phy_set_edcca_th() in phy.c.
>
> Tested on a 8822CE chip which defines edcca_th, so
> this issue is not present on it, but it still uses
> this driver and I can verify there are no regressions.
>
> Reported-by: Oleksandr Havrylov <goainwo@gmail.com>
> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221286
> Link:
> https://lore.kernel.org/linux-wireless/CALdGYqQriS7mP0vj_rm_xvisfzFVh0hbpy+---48r6bodZO7tg@mail.gm
> ail.com/
> Signed-off-by: Panagiotis Petrakopoulos <npetrakopoulos2003@gmail.com>
> ---
> drivers/net/wireless/realtek/rtw88/fw.c | 3 +++
> drivers/net/wireless/realtek/rtw88/phy.c | 3 +++
> 2 files changed, 6 insertions(+)
>
> diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
> index 48207052e3f8..c4819ef6d54d 100644
> --- a/drivers/net/wireless/realtek/rtw88/fw.c
> +++ b/drivers/net/wireless/realtek/rtw88/fw.c
> @@ -284,6 +284,9 @@ static void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload,
> result->density, result->igi, result->l2h_th_init, result->l2h,
> result->h2l, result->option);
>
> + if (!edcca_th)
> + return;
> +
As Bitterblue analysis, this might be a garbage, so I'd return at the entry
of this function.
> rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Reg Setting: L2H %x H2L %x\n",
> rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr,
> edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask),
> diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
> index e2ac5c6fd500..c10eb28e54ad 100644
> --- a/drivers/net/wireless/realtek/rtw88/phy.c
> +++ b/drivers/net/wireless/realtek/rtw88/phy.c
> @@ -161,6 +161,9 @@ void rtw_phy_set_edcca_th(struct rtw_dev *rtwdev, u8 l2h, u8 h2l)
> {
> const struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th;
>
> + if (!edcca_th)
> + return;
> +
The callers of rtw_phy_set_edcca_th() are RTL8822B and RTL8822C, which both
define rtwdev->chip->edcca_th. How can edcca_th be NULL?
> rtw_write32_mask(rtwdev,
> edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr,
> edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask,
> --
> 2.53.0
^ permalink raw reply
* RE: [PATCH wireless-next] wifi: mac80211: add __packed to union members of struct ieee80211_rx_status
From: Ping-Ke Shih @ 2026-04-14 0:55 UTC (permalink / raw)
To: johannes@sipsolutions.net, linux-wireless@vger.kernel.org
In-Reply-To: <20260411072509.1556635-1-pkshih@realtek.com>
Ping-Ke Shih <pkshih@realtek.com> wrote:
> The arm-linux-gnueabi-gcc compiler, align the field followed by union
> members, causing size of struct ieee80211_rx_status over skb->cb
> (48 bytes). By investigation, the union member starts at offset 32,
> and the offset of next field rate_idx is 36 instead of expected 33, and
> the total size is (unexpected) 52.
>
> When compiling rtw88 driver, it throws:
>
> In file included from /work/linux-src/linux-stable/include/linux/string.h:386,
> from /work/linux-src/linux-stable/include/linux/bitmap.h:13,
> from /work/linux-src/linux-stable/include/linux/cpumask.h:11,
> from /work/linux-src/linux-stable/include/linux/smp.h:13,
> from /work/linux-src/linux-stable/include/linux/lockdep.h:14,
> from /work/linux-src/linux-stable/include/linux/mutex.h:17,
> from /work/linux-src/linux-stable/include/linux/kernfs.h:11,
> from /work/linux-src/linux-stable/include/linux/sysfs.h:16,
> from /work/linux-src/linux-stable/include/linux/kobject.h:20,
> from /work/linux-src/linux-stable/include/linux/dmi.h:6,
> from pci.c:5:
> In function 'fortify_memcpy_chk',
> inlined from 'rtw_pci_rx_napi.constprop' at pci.c:1095:4:
> /work/linux-src/linux-stable/include/linux/fortify-string.h:569:25: warning: call to
> '__write_overflow_field' declared with attribute warning: detected write beyond size of field (1st
> parameter); maybe use struct_group()? [-Wattribute-warning]
> 569 | __write_overflow_field(p_size_field, size);
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> After this patch, the size of struct ieee80211_rx_status is 48.
>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
> ---
> Because of size assertion of rtw88's efuse map [1], I found
> arm-linux-gnueabi-gcc compiler throws this warning, but x86 gcc is absolutely
> silent and expected without this patch.
>
> [1]
> https://lore.kernel.org/linux-wireless/7c65e315-5a2e-455e-87ee-8fc6d60ed807@gmail.com/T/#m43fdf8a1
> c2b8cff92c1ef50faab7993522647162
I'd note that discussion thread [2] of original kernel test robot.
Arnd pointed out the cause is CONFIG_AEABI is not set, and he said
nobody should be using ARM OABI any more.
Maybe, we can ignore the CPU and skip this patch.
[2] https://lore.kernel.org/lkml/6b6310b8-2b0d-4390-992e-5ccd81cef2e0@app.fastmail.com/
^ permalink raw reply
* [Question] mt7925: CSI (Channel State Information) support in STA firmware?
From: Geramy Loveless @ 2026-04-14 0:47 UTC (permalink / raw)
To: deren.wu
Cc: chank.chen, Bo.Jiao, ryder.lee, shayne.chen, sean.wang,
mingyen.hsieh, nbd, lorenzo, linux-wireless
Hi Deren, Chank,
I'm working on enabling CSI extraction on the MT7925 (Filogic 360,
PCIe) running as a station. I've built the full kernel-side
infrastructure (vendor netlink, ring buffer, debugfs, event handlers)
and have been testing against the stock firmware:
WIFI_RAM_CODE_MT7925_1_1.bin
Version: t-neptune-main-mt7925-2249 (build 2025-12-10)
I found the connac3 CSI patch for mt7996 in the MediaTek SDK feeds:
0095-mtk-mt76-mt7996-Add-connac3-csi-feature.patch
(by Chank Chen, Jan 2024)
which defines:
MCU_UNI_CMD_CSI_CTRL = 0x4A
MCU_UNI_EVENT_CSI_REPORT = 0x4A
Command tags:
UNI_CMD_CSI_STOP = 0
UNI_CMD_CSI_START = 1
UNI_CMD_CSI_SET_FRAME_TYPE = 2
...
I've been sending MCU_WM_UNI_CMD(CSI_CTRL) with tag=1 (CSI_START) to
the MT7925 firmware. The command returns success (ret=0) but the
firmware never generates MCU_UNI_EVENT_CSI_REPORT (eid=0x4A) events.
All commands return success but none produce CSI I/Q data events.
My questions:
1. Does the MT7925 STA firmware (CE variant) have the CSI reporting
handler compiled in? Or is CSI only available in AP firmware
builds (mt7996, mt7981)?
2. If CSI is not in the current firmware, is there a firmware build
or roadmap that would add MCU_UNI_CMD_CSI_CTRL support for the
MT7925 in STA mode?
3. Is there any alternative path to obtain per-subcarrier channel
estimates on the MT7925? (e.g., via ICAP, WiFi Spectrum mode,
or PHY register reads)
The kernel-side driver patches are ready and tested. The full
infrastructure (NL80211 vendor commands, TLV event parsing, ring
buffer) will work immediately once the firmware starts delivering
CSI events.
Hardware: ASUS ROG Strix Halo (Ryzen AI Max+ 395)
WiFi: MT7925 PCIe [14c3:7925]
Kernel: 7.0-rc7 with mt76 from mainline + CSI patches
Firmware: WIFI_RAM_CODE_MT7925_1_1.bin v2249
Thanks for any guidance.
Geramy Loveless
^ permalink raw reply
* Firmware for reverse engineering b43?
From: Joshua Peisach @ 2026-04-13 12:44 UTC (permalink / raw)
To: b43-dev, linux-wireless
Hi all,
As I've been getting into kernel development, I found the b43 driver
as a suitable place for me to work in, given its status as orphan and
my access to it having an old iMac.
When it comes to figuring out what code to write and how to implement
functions, I see that there has been a mix of answers across the driver.
Lots of functions refer to specs RE'd from the 4.x firmware[1], but
my own install uses 5.x firmware. Additionally, the 6.x firmware is
available.
For my reverse engineering process, I opened the firmware in Ghidra
and basically try to map the functionality to the driver, which appears
to be what was done with the 4.x specs?
But, as I compare to the newer firmware, some functions have been moved
or replaced. And sometimes, the newer firmware functions contradict the
RE'd 4.x specs, or have extra steps in its process.
Take b43_nphy_perical, or as known in the v4 firmware,
wlc_phy_perical_nphy. I got to this function because wlc_nphy_init calls
it. But actually, the RE'd version says init calls
wlc_phy_perical_nphy_run, which the firmware says is not the case
(it goes through wlc_phy_perical_nphy first, which has its own
conditions).
So, which is the best source of truth(s) for this driver? The v4.x
specs, the v5 firmware, or the v6 firmware? Which one should be used,
and which has a higher priority level over the other?
Keep in mind the v4 specs came before the release of Ghidra; now that
it exists, we can refer to it instead of manually probing functionality.
I've even seen Ghidra show different instructions than the 4.x spec.
Thanks,
-Josh
[1]: https://bcm-v4.sipsolutions.net
^ permalink raw reply
* [PATCH ath-next] wifi: ath12k: Remove unused HAL_RX_REO_QUEUE_INFO macros
From: Jeff Johnson @ 2026-04-13 23:58 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, linux-kernel, Jeff Johnson
Many of the HAL_RX_REO_QUEUE_INFO macros are unused, so remove them.
No functional change. Compile Tested only.
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
---
This issue was highlighted by AI review which noted that the
HAL_RX_REO_QUEUE_INFO2_MSDU_COUNT macro was missing GENMASK().
However, rather than fix an unused macro, it seems "better" to just
remove it (along with all other unused HAL_RX_REO_QUEUE_INFO macros).
---
drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h | 26 ------------------------
1 file changed, 26 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h
index e1ab47b44433..822de3a4abc8 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h
@@ -2100,41 +2100,15 @@ enum hal_rx_reo_queue_pn_size {
#define HAL_RX_REO_QUEUE_INFO0_VLD BIT(0)
#define HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER GENMASK(2, 1)
-#define HAL_RX_REO_QUEUE_INFO0_DIS_DUP_DETECTION BIT(3)
-#define HAL_RX_REO_QUEUE_INFO0_SOFT_REORDER_EN BIT(4)
#define HAL_RX_REO_QUEUE_INFO0_AC GENMASK(6, 5)
-#define HAL_RX_REO_QUEUE_INFO0_BAR BIT(7)
#define HAL_RX_REO_QUEUE_INFO0_RETRY BIT(8)
-#define HAL_RX_REO_QUEUE_INFO0_CHECK_2K_MODE BIT(9)
-#define HAL_RX_REO_QUEUE_INFO0_OOR_MODE BIT(10)
#define HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE GENMASK(20, 11)
#define HAL_RX_REO_QUEUE_INFO0_PN_CHECK BIT(21)
-#define HAL_RX_REO_QUEUE_INFO0_EVEN_PN BIT(22)
-#define HAL_RX_REO_QUEUE_INFO0_UNEVEN_PN BIT(23)
-#define HAL_RX_REO_QUEUE_INFO0_PN_HANDLE_ENABLE BIT(24)
#define HAL_RX_REO_QUEUE_INFO0_PN_SIZE GENMASK(26, 25)
#define HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG BIT(27)
#define HAL_RX_REO_QUEUE_INFO1_SVLD BIT(0)
#define HAL_RX_REO_QUEUE_INFO1_SSN GENMASK(12, 1)
-#define HAL_RX_REO_QUEUE_INFO1_CURRENT_IDX GENMASK(22, 13)
-#define HAL_RX_REO_QUEUE_INFO1_SEQ_2K_ERR BIT(23)
-#define HAL_RX_REO_QUEUE_INFO1_PN_ERR BIT(24)
-#define HAL_RX_REO_QUEUE_INFO1_PN_VALID BIT(31)
-
-#define HAL_RX_REO_QUEUE_INFO2_MPDU_COUNT GENMASK(6, 0)
-#define HAL_RX_REO_QUEUE_INFO2_MSDU_COUNT (31, 7)
-
-#define HAL_RX_REO_QUEUE_INFO3_TIMEOUT_COUNT GENMASK(9, 4)
-#define HAL_RX_REO_QUEUE_INFO3_FWD_DUE_TO_BAR_CNT GENMASK(15, 10)
-#define HAL_RX_REO_QUEUE_INFO3_DUPLICATE_COUNT GENMASK(31, 16)
-
-#define HAL_RX_REO_QUEUE_INFO4_FRAME_IN_ORD_COUNT GENMASK(23, 0)
-#define HAL_RX_REO_QUEUE_INFO4_BAR_RECVD_COUNT GENMASK(31, 24)
-
-#define HAL_RX_REO_QUEUE_INFO5_LATE_RX_MPDU_COUNT GENMASK(11, 0)
-#define HAL_RX_REO_QUEUE_INFO5_WINDOW_JUMP_2K GENMASK(15, 12)
-#define HAL_RX_REO_QUEUE_INFO5_HOLE_COUNT GENMASK(31, 16)
struct hal_rx_reo_queue {
struct hal_desc_header desc_hdr;
---
base-commit: 15551ababf6d4e857f2101366a0c3eaa86dd822c
change-id: 20260404-hal_rx_reo_queue_info2_msdu_count-0e2eb6da0461
^ permalink raw reply related
* Re: [patch 15/38] ptp: ptp_vmclock: Replace get_cycles() usage
From: Arnd Bergmann @ 2026-04-13 19:30 UTC (permalink / raw)
To: David Woodhouse, Thomas Gleixner, LKML
Cc: x86, Baolu Lu, iommu, Michael Grzeschik, Netdev, linux-wireless,
Herbert Xu, linux-crypto, Vlastimil Babka (SUSE), linux-mm,
Bernie Thompson, linux-fbdev, Theodore Ts'o, linux-ext4,
Andrew Morton, Uladzislau Rezki (Sony), Marco Elver,
Dmitry Vyukov, kasan-dev, Andrey Ryabinin, Thomas Sailer,
linux-hams, Jason A . Donenfeld, Richard Henderson, linux-alpha,
Russell King, linux-arm-kernel, Catalin Marinas, Huacai Chen,
loongarch, Geert Uytterhoeven, linux-m68k, Dinh Nguyen,
Jonas Bonn, linux-openrisc@vger.kernel.org, Helge Deller,
linux-parisc, Michael Ellerman, linuxppc-dev, Paul Walmsley,
linux-riscv, Heiko Carstens, linux-s390, David S . Miller,
sparclinux
In-Reply-To: <7a48b636cb3146f4f7134c6d4fe42070ac2edb43.camel@infradead.org>
On Mon, Apr 13, 2026, at 17:33, David Woodhouse wrote:
> On Fri, 2026-04-10 at 14:19 +0200, Thomas Gleixner wrote:
>
> ... depend on TSC_RELIABLE¹, since if the guest doesn't believe that it
> is, then the guest shouldn't be trying to use it as the basis for
> precise timing.
>
> ¹ (Or... one of the other zoo of TSC flags for the gradually reducing
> brokenness over the years...)
It looks like this is sufficiently handled in the caller:
static int vmclock_get_crosststamp(struct vmclock_state *st,
struct ptp_system_timestamp *sts,
struct system_counterval_t *system_counter,
struct timespec64 *tspec)
{
....
#ifdef CONFIG_X86
/*
* We'd expect the hypervisor to know this and to report the clock
* status as VMCLOCK_STATUS_UNRELIABLE. But be paranoid.
*/
if (check_tsc_unstable())
return -EINVAL;
#endif
With 486 and ELAN out of the way, Winchip6 seems to be the only
one without X86_FEATURE_TSC, so I think the next logical step would
be to turn off Winchip6 as well and remove all X86_FEATURE_TSC
and CONFIG_X86_TSC checks.
Arnd
^ permalink raw reply
* [PATCH] wifi: iwlwifi: mld: bail out from TX when firmware is dead
From: Aaron Esau @ 2026-04-13 18:43 UTC (permalink / raw)
To: linux-wireless; +Cc: miriam.rachel.korenblit, stable, Aaron Esau
The drain loop in iwl_mld_tx_from_txq() keeps dequeuing and processing
packets while stop_full is not set and mac80211 has buffered frames.
When firmware crashes, STATUS_FW_ERROR may not be set before softirq TX
paths enter this loop. Each iteration performs expensive GSO segmentation
via iwl_tx_tso_segment() -> skb_gso_segment() while holding the netdev
TX queue spinlock (HARD_TX_LOCK from __dev_queue_xmit), blocking all
other CPUs attempting to transmit.
The existing backpressure mechanism (stop_full, set via iwl_txq_stop()
when the hardware ring crosses its high water mark) is insufficient:
iwl_trans_pcie_tx() silently absorbs frames into its overflow_q and
returns success even when the ring is full and firmware is not consuming
descriptors. This delays the stop_full signal long enough for the loop
to hold the TX lock for tens of seconds, triggering soft lockups across
multiple CPUs that cascade into SLUB freelist corruption during the
subsequent driver reprobe.
Add a test_bit(STATUS_FW_ERROR) check to the drain loop so that it
exits promptly when firmware is no longer alive.
Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
Cc: stable@vger.kernel.org
Signed-off-by: Aaron Esau <aaron1esau@gmail.com>
---
Found while investigating a complete system freeze on a Lenovo ThinkPad
(Intel Core Ultra 7 155H, Intel Wi-Fi 7 BE200) running kernel 6.19.11.
The firmware LMAC crashed (NMI_INTERRUPT_UNKNOWN, both LMACs halted at
0xd0), the drain loop held HARD_TX_LOCK for 26+ seconds, 7 CPUs soft
locked, and the subsequent reprobe corrupted SLUB freelist pointers
(non-canonical address 0x54c991d5bf7e0cce in __kmalloc), killing Xorg,
systemd --user, and requiring a hard power off.
CachyOS/linux-cachyos#673 reports the same crash signature on BE200
with kernel 6.18+, confirming the pattern. The documented workaround
(ethtool -K <iface> tso off gso off) works because it eliminates the
per-packet skb_gso_segment() cost, allowing the ring to fill and
stop_full to trip before the watchdog fires.
drivers/net/wireless/intel/iwlwifi/mld/tx.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
index e3fb4fc4f..5e8a63e24 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
@@ -989,6 +989,8 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
rcu_read_lock();
do {
while (likely(!mld_txq->status.stop_full) &&
+ !test_bit(STATUS_FW_ERROR,
+ &mld->trans->status) &&
(skb = ieee80211_tx_dequeue(mld->hw, txq)))
iwl_mld_tx_skb(mld, skb, txq);
} while (atomic_dec_return(&mld_txq->tx_request));
--
2.49.0
^ permalink raw reply related
* [PATCH ath-next v2 5/5] wifi: ath12k: add thermal cooling device support
From: Maharaja Kennadyrajan @ 2026-04-13 15:38 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
In-Reply-To: <20260413153840.1969931-1-maharaja.kennadyrajan@oss.qualcomm.com>
Add thermal cooling device support to control the temperature by throttling data
transmission. Throttling is performed by suspending data TX queues according to
a configured duty-cycle off percentage. The thermal cooling device allows users
to configure the duty-cycle off percentage and operate the device with the
selected value.
User configuration updates a single duty-cycle off percentage, which is applied
uniformly by the host and treated as only one temperature level. This value
remains in effect until updated again by the user. All other thermal throttling
parameters continue to use their default firmware provided values.
Reject invalid duty-cycle off percentage values that fall outside the supported
range. Register a cooling device to allow the thermal framework to query and set
the current throttle state, report the maximum supported state, and keep the
host state in sync with successful firmware updates. A throttle state of zero
restores the default firmware thermal configuration.
Command to set the duty-cycle off percent:
echo 40 > /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/cur_state
Command to read duty-cycle off percent:
cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/cur_state
Command to read the maximum duty-cycle off percent:
cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/max_state
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/mac.c | 1 +
drivers/net/wireless/ath/ath12k/thermal.c | 107 +++++++++++++++++++++-
drivers/net/wireless/ath/ath12k/thermal.h | 14 +++
3 files changed, 121 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 2a221ee83cad..4718802cec50 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -14821,6 +14821,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
init_completion(&ar->completed_11d_scan);
init_completion(&ar->regd_update_completed);
init_completion(&ar->thermal.wmi_sync);
+ mutex_init(&ar->thermal.lock);
ar->thermal.temperature = 0;
ar->thermal.hwmon_dev = NULL;
diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c
index 6f70c11c1098..97fc49c40ac1 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.c
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -76,6 +76,73 @@ void ath12k_thermal_init_configs(struct ath12k *ar)
ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0];
}
+static int
+ath12k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = ATH12K_THERMAL_THROTTLE_MAX;
+
+ return 0;
+}
+
+static int
+ath12k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct ath12k *ar = cdev->devdata;
+
+ mutex_lock(&ar->thermal.lock);
+ *state = ar->thermal.throttle_state;
+ mutex_unlock(&ar->thermal.lock);
+
+ return 0;
+}
+
+int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state)
+{
+ struct ath12k_wmi_thermal_mitigation_arg param = {};
+ struct ath12k_wmi_tt_level_config_param cfg = {};
+ int ret;
+
+ param.num_levels = 1;
+ cfg.dcoffpercent = throttle_state;
+ param.levelconf = &cfg;
+
+ ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m);
+ if (ret)
+ ath12k_warn(ar->ab, "failed to send thermal mitigation cmd: %d\n",
+ ret);
+
+ return ret;
+}
+
+static int
+ath12k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
+ unsigned long throttle_state)
+{
+ struct ath12k *ar = cdev->devdata;
+
+ if (throttle_state > ATH12K_THERMAL_THROTTLE_MAX)
+ return -EINVAL;
+
+ scoped_guard(mutex, &ar->thermal.lock) {
+ if (ar->thermal.throttle_state == throttle_state)
+ return 0;
+ ar->thermal.throttle_state = throttle_state;
+ }
+
+ if (throttle_state == 0)
+ return ath12k_thermal_throttling_config_default(ar);
+
+ return ath12k_thermal_set_throttling(ar, throttle_state);
+}
+
+static const struct thermal_cooling_device_ops ath12k_thermal_ops = {
+ .get_max_state = ath12k_thermal_get_max_throttle_state,
+ .get_cur_state = ath12k_thermal_get_cur_throttle_state,
+ .set_cur_state = ath12k_thermal_set_cur_throttle_state,
+};
+
static ssize_t ath12k_thermal_temp_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -132,6 +199,7 @@ ATTRIBUTE_GROUPS(ath12k_hwmon);
static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
{
+ char pdev_name[20];
struct ath12k *ar;
int ret;
@@ -139,6 +207,28 @@ static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
if (!ar)
return 0;
+ ar->thermal.cdev =
+ thermal_cooling_device_register("ath12k_thermal", ar,
+ &ath12k_thermal_ops);
+ if (IS_ERR(ar->thermal.cdev)) {
+ ret = PTR_ERR(ar->thermal.cdev);
+ ar->thermal.cdev = NULL;
+ ath12k_err(ar->ab, "failed to register cooling device: %d\n",
+ ret);
+ return ret;
+ }
+
+ scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u",
+ ar->hw_link_id);
+
+ ret = sysfs_create_link(&ar->ah->hw->wiphy->dev.kobj,
+ &ar->thermal.cdev->device.kobj, pdev_name);
+ if (ret) {
+ ath12k_err(ab, "failed to create cooling device symlink: %d\n",
+ ret);
+ goto unregister_cdev;
+ }
+
ar->thermal.hwmon_dev =
hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
"ath12k_hwmon", ar,
@@ -148,14 +238,22 @@ static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
ar->thermal.hwmon_dev = NULL;
ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
ret);
- return ret;
+ goto remove_sysfs;
}
return 0;
+
+remove_sysfs:
+ sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name);
+unregister_cdev:
+ thermal_cooling_device_unregister(ar->thermal.cdev);
+ ar->thermal.cdev = NULL;
+ return ret;
}
static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i)
{
+ char pdev_name[20];
struct ath12k *ar;
ar = ab->pdevs[i].ar;
@@ -164,6 +262,13 @@ static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i)
hwmon_device_unregister(ar->thermal.hwmon_dev);
ar->thermal.hwmon_dev = NULL;
+
+ scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u",
+ ar->hw_link_id);
+ sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name);
+
+ thermal_cooling_device_unregister(ar->thermal.cdev);
+ ar->thermal.cdev = NULL;
}
int ath12k_thermal_register(struct ath12k_base *ab)
diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h
index 33231bb3683c..30e7b0880e05 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.h
+++ b/drivers/net/wireless/ath/ath12k/thermal.h
@@ -7,9 +7,12 @@
#ifndef _ATH12K_THERMAL_
#define _ATH12K_THERMAL_
+#include <linux/mutex.h>
+
#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
#define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100
+#define ATH12K_THERMAL_THROTTLE_MAX 100
enum ath12k_thermal_cfg_idx {
/* Internal Power Amplifier Device */
@@ -26,6 +29,10 @@ struct ath12k_thermal {
int temperature;
struct device *hwmon_dev;
const struct ath12k_wmi_tt_level_config_param *tt_level_configs;
+ struct thermal_cooling_device *cdev;
+ /* Serialize thermal operations and hwmon reads */
+ struct mutex lock;
+ u32 throttle_state;
};
#if IS_REACHABLE(CONFIG_THERMAL)
@@ -34,6 +41,7 @@ void ath12k_thermal_unregister(struct ath12k_base *ab);
void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature);
int ath12k_thermal_throttling_config_default(struct ath12k *ar);
void ath12k_thermal_init_configs(struct ath12k *ar);
+int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state);
#else
static inline int ath12k_thermal_register(struct ath12k_base *ab)
{
@@ -57,5 +65,11 @@ static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar)
static inline void ath12k_thermal_init_configs(struct ath12k *ar)
{
}
+
+static inline int ath12k_thermal_set_throttling(struct ath12k *ar,
+ u32 throttle_state)
+{
+ return 0;
+}
#endif
#endif /* _ATH12K_THERMAL_ */
--
2.34.1
^ permalink raw reply related
* [PATCH ath-next v2 4/5] wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup
From: Maharaja Kennadyrajan @ 2026-04-13 15:38 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
In-Reply-To: <20260413153840.1969931-1-maharaja.kennadyrajan@oss.qualcomm.com>
A later change adds thermal cooling device sysfs under the wiphy device kobject.
With the current teardown order, MAC/wiphy are unregistered before per-device
cleanup, so any subsequent thermal sysfs removal would run after the wiphy kobject
is gone. That ordering is asymmetric with setup and would risk kernfs issues during
removal.
This change also adjusts the position of ath12k_mac_mlo_teardown(). Previously
it ran before per-device cleanup/MAC unregister. MLO teardown issues WMI to teardown
multi-link state and is part of the MAC teardown sequence. Placing it alongside
MAC unregister (after per-device cleanup) preserves setup/teardown symmetry and
avoids racing with remaining netdev/wiphy state.
Reorder hw_group_stop() so per-device cleanup (including thermal/hwmon sysfs
removal) runs while the wiphy still exists. After per-device cleanup completes,
unregister the MAC (dropping wiphys), run ath12k_mac_mlo_teardown(), and finally
destroy the MAC. This mirrors the setup sequence and keeps sysfs cleanup safe
when introduced in a later patch.
To keep start/stop symmetry, add ath12k_core_device_setup() to encapsulate the
per-device bring-up steps (pdev create, IRQ enable, rfkill config) that were
previously open-coded in hw_group_start(). Use this helper in hw_group_start()
to match the existing per-device cleanup helper used by hw_group_stop().
Note that set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags) is now executed outside
the ab->core_lock critical section. The core_lock has not provided protection for
the REGISTERED flag, readers do not rely on core_lock for this bit, and the flag
is only toggled in the serialized group start/stop path using atomic bitops.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/core.c | 50 +++++++++++++++-----------
1 file changed, 29 insertions(+), 21 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index e8137144fd1f..1a9866061f82 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1006,6 +1006,27 @@ static void ath12k_core_device_cleanup(struct ath12k_base *ab)
mutex_unlock(&ab->core_lock);
}
+static int ath12k_core_device_setup(struct ath12k_base *ab)
+{
+ int ret;
+
+ guard(mutex)(&ab->core_lock);
+
+ ret = ath12k_core_pdev_create(ab);
+ if (ret) {
+ ath12k_err(ab, "failed to create pdev core %d\n", ret);
+ return ret;
+ }
+
+ ath12k_hif_irq_enable(ab);
+
+ ret = ath12k_core_rfkill_config(ab);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+
+ return 0;
+}
+
static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
{
struct ath12k_base *ab;
@@ -1015,10 +1036,6 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
clear_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags);
- ath12k_mac_unregister(ag);
-
- ath12k_mac_mlo_teardown(ag);
-
for (i = ag->num_devices - 1; i >= 0; i--) {
ab = ag->ab[i];
if (!ab)
@@ -1029,6 +1046,12 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
ath12k_core_device_cleanup(ab);
}
+ /* Unregister MAC (drops wiphys) only after per-device cleanup */
+ ath12k_mac_unregister(ag);
+
+ /* Teardown MLO state after MAC unregister for symmetry */
+ ath12k_mac_mlo_teardown(ag);
+
ath12k_mac_destroy(ag);
}
@@ -1165,26 +1188,11 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag)
if (!ab)
continue;
- mutex_lock(&ab->core_lock);
-
set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags);
- ret = ath12k_core_pdev_create(ab);
- if (ret) {
- ath12k_err(ab, "failed to create pdev core %d\n", ret);
- mutex_unlock(&ab->core_lock);
- goto err;
- }
-
- ath12k_hif_irq_enable(ab);
-
- ret = ath12k_core_rfkill_config(ab);
- if (ret && ret != -EOPNOTSUPP) {
- mutex_unlock(&ab->core_lock);
+ ret = ath12k_core_device_setup(ab);
+ if (ret)
goto err;
- }
-
- mutex_unlock(&ab->core_lock);
}
return 0;
--
2.34.1
^ permalink raw reply related
* [PATCH ath-next v2 3/5] wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup
From: Maharaja Kennadyrajan @ 2026-04-13 15:38 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
In-Reply-To: <20260413153840.1969931-1-maharaja.kennadyrajan@oss.qualcomm.com>
Both the error path in thermal registration and the normal thermal unregister
path performed the same hwmon device unregistration and pointer cleanup.
Consolidate this logic into a single helper to reduce code duplication and ensure
consistent cleanup across all paths. Add a helper to set up the hwmon registration
during thermal registration to keep symmetry with thermal cleanup.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/thermal.c | 83 +++++++++++++----------
1 file changed, 47 insertions(+), 36 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c
index 4f76622e8117..6f70c11c1098 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.c
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -130,59 +130,70 @@ static struct attribute *ath12k_hwmon_attrs[] = {
};
ATTRIBUTE_GROUPS(ath12k_hwmon);
-int ath12k_thermal_register(struct ath12k_base *ab)
+static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
+{
+ struct ath12k *ar;
+ int ret;
+
+ ar = ab->pdevs[i].ar;
+ if (!ar)
+ return 0;
+
+ ar->thermal.hwmon_dev =
+ hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
+ "ath12k_hwmon", ar,
+ ath12k_hwmon_groups);
+ if (IS_ERR(ar->thermal.hwmon_dev)) {
+ ret = PTR_ERR(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+ ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i)
{
struct ath12k *ar;
- int i, j, ret;
+
+ ar = ab->pdevs[i].ar;
+ if (!ar)
+ return;
+
+ hwmon_device_unregister(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+}
+
+int ath12k_thermal_register(struct ath12k_base *ab)
+{
+ int i, ret;
if (!IS_REACHABLE(CONFIG_HWMON))
return 0;
for (i = 0; i < ab->num_radios; i++) {
- ar = ab->pdevs[i].ar;
- if (!ar)
- continue;
-
- ar->thermal.hwmon_dev =
- hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
- "ath12k_hwmon", ar,
- ath12k_hwmon_groups);
- if (IS_ERR(ar->thermal.hwmon_dev)) {
- ret = PTR_ERR(ar->thermal.hwmon_dev);
- ar->thermal.hwmon_dev = NULL;
- ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
- ret);
- for (j = i - 1; j >= 0; j--) {
- ar = ab->pdevs[j].ar;
- if (!ar)
- continue;
-
- hwmon_device_unregister(ar->thermal.hwmon_dev);
- ar->thermal.hwmon_dev = NULL;
- }
- return ret;
- }
+ ret = ath12k_thermal_setup_radio(ab, i);
+ if (ret)
+ goto out;
}
return 0;
+out:
+ for (i--; i >= 0; i--)
+ ath12k_thermal_cleanup_radio(ab, i);
+
+ return ret;
}
void ath12k_thermal_unregister(struct ath12k_base *ab)
{
- struct ath12k *ar;
int i;
if (!IS_REACHABLE(CONFIG_HWMON))
return;
- for (i = 0; i < ab->num_radios; i++) {
- ar = ab->pdevs[i].ar;
- if (!ar)
- continue;
-
- if (ar->thermal.hwmon_dev) {
- hwmon_device_unregister(ar->thermal.hwmon_dev);
- ar->thermal.hwmon_dev = NULL;
- }
- }
+ for (i = 0; i < ab->num_radios; i++)
+ ath12k_thermal_cleanup_radio(ab, i);
}
--
2.34.1
^ permalink raw reply related
* [PATCH ath-next v2 2/5] wifi: ath12k: configure firmware thermal throttling via WMI
From: Maharaja Kennadyrajan @ 2026-04-13 15:38 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
In-Reply-To: <20260413153840.1969931-1-maharaja.kennadyrajan@oss.qualcomm.com>
Ath12k firmware supports thermal-throttling but requires the host to explicitly
program throttle levels and mitigation actions via WMI. Without this configuration,
firmware-driven thermal mitigation remains inactive or relies on platform-specific
defaults.
Add host-side support to build and send thermal-throttle configuration using
WMI_THERM_THROT_SET_CONF_CMDID during MAC radio start, ensuring thermal parameters
are programmed before data traffic begins.
Maintain per-radio storage for thermal throttle levels and provide conservative
default level tables for Internal Power Amplifier Device (IPA) and External Power
Amplifier Device or External Front End Module (XFEM) targets. The appropriate
default table is selected based on firmware-advertised service bits, allowing the
host to align with target thermal mitigation capabilities. If the WMI TLV service
WMI_TLV_SERVICE_IS_TARGET_IPA bit is set, then host selects the thermal throttle
values from IPA index from the table and selects values from XFEM index from the
table if this WMI TLV service bit is not set.
Build and send the thermal throttle configuration request with either 4 or
5 levels depending on firmware capability, and populate optional fields
(pout reduction and tx chain mask) only when the corresponding service bits
are advertised.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/mac.c | 8 +++
drivers/net/wireless/ath/ath12k/thermal.c | 64 +++++++++++++++++++++
drivers/net/wireless/ath/ath12k/thermal.h | 21 +++++++
drivers/net/wireless/ath/ath12k/wmi.c | 69 +++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/wmi.h | 42 ++++++++++++++
5 files changed, 204 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index fbdfe6424fd7..2a221ee83cad 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -9673,6 +9673,12 @@ static int ath12k_mac_start(struct ath12k *ar)
}
}
+ ret = ath12k_thermal_throttling_config_default(ar);
+ if (ret) {
+ ath12k_err(ab, "failed to set thermal throttle: %d\n", ret);
+ goto err;
+ }
+
rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx],
&ab->pdevs[ar->pdev_idx]);
@@ -14461,6 +14467,8 @@ static int ath12k_mac_setup_register(struct ath12k *ar,
ar->rssi_info.temp_offset = 0;
ar->rssi_info.noise_floor = ar->rssi_info.min_nf_dbm + ar->rssi_info.temp_offset;
+ ath12k_thermal_init_configs(ar);
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c
index a764d2112a3c..4f76622e8117 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.c
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -12,6 +12,70 @@
#include "core.h"
#include "debug.h"
+static const struct ath12k_wmi_tt_level_config_param
+tt_level_configs[ATH12K_TT_CFG_IDX_MAX][ENHANCED_THERMAL_LEVELS] = {
+ [ATH12K_TT_CFG_IDX_IPA] = {
+ [0] = { .tmplwm = -100, .tmphwm = 115, .dcoffpercent = 0,
+ .pout_reduction_db = 0 },
+ [1] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 0,
+ .pout_reduction_db = 12 },
+ [2] = { .tmplwm = 115, .tmphwm = 125, .dcoffpercent = 50,
+ .pout_reduction_db = 12 },
+ [3] = { .tmplwm = 120, .tmphwm = 130, .dcoffpercent = 90,
+ .pout_reduction_db = 12 },
+ [4] = { .tmplwm = 125, .tmphwm = 130, .dcoffpercent = 100,
+ .pout_reduction_db = 12 },
+ },
+ [ATH12K_TT_CFG_IDX_XFEM] = {
+ [0] = { .tmplwm = -100, .tmphwm = 105, .dcoffpercent = 0,
+ .pout_reduction_db = 0 },
+ [1] = { .tmplwm = 100, .tmphwm = 110, .dcoffpercent = 0,
+ .pout_reduction_db = 0 },
+ [2] = { .tmplwm = 105, .tmphwm = 115, .dcoffpercent = 50,
+ .pout_reduction_db = 0 },
+ [3] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 90,
+ .pout_reduction_db = 0 },
+ [4] = { .tmplwm = 115, .tmphwm = 120, .dcoffpercent = 100,
+ .pout_reduction_db = 0 },
+ },
+};
+
+static enum ath12k_thermal_cfg_idx ath12k_thermal_cfg_index(struct ath12k *ar)
+{
+ if (test_bit(WMI_TLV_SERVICE_IS_TARGET_IPA, ar->ab->wmi_ab.svc_map))
+ return ATH12K_TT_CFG_IDX_IPA;
+
+ return ATH12K_TT_CFG_IDX_XFEM;
+}
+
+int ath12k_thermal_throttling_config_default(struct ath12k *ar)
+{
+ struct ath12k_wmi_thermal_mitigation_arg param = {};
+ int ret;
+
+ if (test_bit(WMI_TLV_SERVICE_THERM_THROT_5_LEVELS, ar->ab->wmi_ab.svc_map))
+ param.num_levels = ENHANCED_THERMAL_LEVELS;
+ else
+ param.num_levels = THERMAL_LEVELS;
+
+ param.levelconf = ar->thermal.tt_level_configs;
+
+ ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "failed to send thermal mitigation cmd for default config: %d\n",
+ ret);
+ return ret;
+}
+
+void ath12k_thermal_init_configs(struct ath12k *ar)
+{
+ enum ath12k_thermal_cfg_idx cfg_idx;
+
+ cfg_idx = ath12k_thermal_cfg_index(ar);
+ ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0];
+}
+
static ssize_t ath12k_thermal_temp_show(struct device *dev,
struct device_attribute *attr,
char *buf)
diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h
index 9d84056188e1..33231bb3683c 100644
--- a/drivers/net/wireless/ath/ath12k/thermal.h
+++ b/drivers/net/wireless/ath/ath12k/thermal.h
@@ -9,18 +9,31 @@
#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
+#define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100
+
+enum ath12k_thermal_cfg_idx {
+ /* Internal Power Amplifier Device */
+ ATH12K_TT_CFG_IDX_IPA,
+ /* External Power Amplifier Device or External Front End Module */
+ ATH12K_TT_CFG_IDX_XFEM,
+ ATH12K_TT_CFG_IDX_MAX,
+};
+
struct ath12k_thermal {
struct completion wmi_sync;
/* temperature value in Celsius degree protected by data_lock. */
int temperature;
struct device *hwmon_dev;
+ const struct ath12k_wmi_tt_level_config_param *tt_level_configs;
};
#if IS_REACHABLE(CONFIG_THERMAL)
int ath12k_thermal_register(struct ath12k_base *ab);
void ath12k_thermal_unregister(struct ath12k_base *ab);
void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature);
+int ath12k_thermal_throttling_config_default(struct ath12k *ar);
+void ath12k_thermal_init_configs(struct ath12k *ar);
#else
static inline int ath12k_thermal_register(struct ath12k_base *ab)
{
@@ -36,5 +49,13 @@ static inline void ath12k_thermal_event_temperature(struct ath12k *ar,
{
}
+static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar)
+{
+ return 0;
+}
+
+static inline void ath12k_thermal_init_configs(struct ath12k *ar)
+{
+}
#endif
#endif /* _ATH12K_THERMAL_ */
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 6b24911bdb7c..05ebb0a203a1 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -3380,6 +3380,75 @@ int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar,
return ret;
}
+int
+ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar,
+ struct ath12k_wmi_thermal_mitigation_arg *arg)
+{
+ struct ath12k_wmi_therm_throt_level_config_param *lvl_conf;
+ struct ath12k_wmi_therm_throt_config_request_cmd *cmd;
+ struct ath12k_wmi_pdev *wmi = ar->wmi;
+ struct wmi_tlv *tlv;
+ struct sk_buff *skb;
+ int i, ret, len;
+
+ len = sizeof(*cmd) + TLV_HDR_SIZE + (arg->num_levels * sizeof(*lvl_conf));
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct ath12k_wmi_therm_throt_config_request_cmd *)skb->data;
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_CONFIG_REQUEST,
+ sizeof(*cmd));
+ cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id);
+ cmd->enable = cpu_to_le32(1);
+ cmd->dc = cpu_to_le32(100);
+ cmd->dc_per_event = cpu_to_le32(0xffffffff);
+ cmd->therm_throt_levels = cpu_to_le32(arg->num_levels);
+
+ tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd));
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT,
+ arg->num_levels * sizeof(*lvl_conf));
+
+ lvl_conf = (struct ath12k_wmi_therm_throt_level_config_param *)tlv->value;
+
+ for (i = 0; i < arg->num_levels; i++) {
+ lvl_conf->tlv_header =
+ ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO,
+ sizeof(*lvl_conf));
+
+ lvl_conf->temp_lwm = a_cpu_to_sle32(arg->levelconf[i].tmplwm);
+ lvl_conf->temp_hwm = a_cpu_to_sle32(arg->levelconf[i].tmphwm);
+ lvl_conf->dc_off_percent = cpu_to_le32(arg->levelconf[i].dcoffpercent);
+
+ if (test_bit(WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION,
+ ar->ab->wmi_ab.svc_map))
+ lvl_conf->pout_reduction_25db =
+ cpu_to_le32(arg->levelconf[i].pout_reduction_db);
+
+ if (test_bit(WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK,
+ ar->ab->wmi_ab.svc_map))
+ lvl_conf->tx_chain_mask = cpu_to_le32(ar->cfg_tx_chainmask);
+
+ lvl_conf->duty_cycle = cpu_to_le32(ATH12K_THERMAL_DEFAULT_DUTY_CYCLE);
+ lvl_conf++;
+ }
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "WMI vdev set thermal throt pdev_id %u enable dc 100 dc_per_event 0xffffffff levels %d\n",
+ ar->pdev->pdev_id, arg->num_levels);
+
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_THERM_THROT_SET_CONF_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to send WMI_THERM_THROT_SET_CONF cmd: %d\n",
+ ret);
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
+}
+
int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar,
struct wmi_11d_scan_start_arg *arg)
{
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index 1853a6f9cf27..66d8c40fa52b 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -2274,6 +2274,10 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_WMSK_COMPACTION_RX_TLVS = 361,
WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365,
+ WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION = 410,
+ WMI_TLV_SERVICE_IS_TARGET_IPA = 425,
+ WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK = 426,
+ WMI_TLV_SERVICE_THERM_THROT_5_LEVELS = 429,
WMI_TLV_SERVICE_ETH_OFFLOAD = 461,
WMI_MAX_EXT2_SERVICE,
@@ -4128,6 +4132,42 @@ struct wmi_therm_throt_stats_event {
__le32 therm_throt_levels;
} __packed;
+#define THERMAL_LEVELS 4
+#define ENHANCED_THERMAL_LEVELS 5
+
+struct ath12k_wmi_tt_level_config_param {
+ s32 tmplwm;
+ s32 tmphwm;
+ u32 dcoffpercent;
+ u32 pout_reduction_db;
+};
+
+struct ath12k_wmi_therm_throt_config_request_cmd {
+ __le32 tlv_header;
+ __le32 pdev_id;
+ __le32 enable;
+ __le32 dc;
+ /* After how many duty cycles the firmware sends stats to host */
+ __le32 dc_per_event;
+ __le32 therm_throt_levels;
+} __packed;
+
+struct ath12k_wmi_therm_throt_level_config_param {
+ __le32 tlv_header;
+ a_sle32 temp_lwm;
+ a_sle32 temp_hwm;
+ __le32 dc_off_percent;
+ __le32 prio;
+ __le32 pout_reduction_25db;
+ __le32 tx_chain_mask;
+ __le32 duty_cycle;
+} __packed;
+
+struct ath12k_wmi_thermal_mitigation_arg {
+ int num_levels;
+ const struct ath12k_wmi_tt_level_config_param *levelconf;
+};
+
struct ath12k_wmi_init_country_arg {
union {
u16 country_code;
@@ -6522,6 +6562,8 @@ __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len);
int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar,
enum wmi_halphy_ctrl_path_stats_id tpc_stats_type);
void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar);
+int ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar,
+ struct ath12k_wmi_thermal_mitigation_arg *arg);
static inline u32
ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param)
--
2.34.1
^ permalink raw reply related
* [PATCH ath-next v2 1/5] wifi: ath12k: handle thermal throttle stats WMI event
From: Maharaja Kennadyrajan @ 2026-04-13 15:38 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
In-Reply-To: <20260413153840.1969931-1-maharaja.kennadyrajan@oss.qualcomm.com>
Add handling for WMI_THERM_THROT_STATS_EVENTID by defining the
wmi_therm_throt_stats_event TLV and parsing pdev_id, temperature and
throttle level.
The firmware can emit this event periodically, including when the
throttle level is 0.
Log the received thermal throttle stats to get the current temperature level,
temperature and thermal throttling levels.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/wmi.c | 39 +++++++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/wmi.h | 8 ++++++
2 files changed, 47 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 484fdd3b1b7f..6b24911bdb7c 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -8762,6 +8762,42 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
rcu_read_unlock();
}
+static void ath12k_wmi_thermal_throt_stats_event(struct ath12k_base *ab,
+ struct sk_buff *skb)
+{
+ const struct wmi_therm_throt_stats_event *ev;
+ struct ath12k *ar;
+ const void **tb;
+
+ tb = ath12k_wmi_tlv_parse(ab, skb);
+ if (IS_ERR(tb)) {
+ ath12k_err(ab, "failed to parse thermal throttling stats tlv: %ld\n",
+ PTR_ERR(tb));
+ return;
+ }
+
+ ev = tb[WMI_TAG_THERM_THROT_STATS_EVENT];
+ if (!ev) {
+ ath12k_err(ab, "failed to fetch thermal throt stats ev\n");
+ return;
+ }
+
+ rcu_read_lock();
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev->pdev_id));
+ if (!ar) {
+ ath12k_warn(ab, "received thermal_throt_stats in invalid pdev %u\n",
+ le32_to_cpu(ev->pdev_id));
+ rcu_read_unlock();
+ return;
+ }
+ rcu_read_unlock();
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "thermal stats ev level %u pdev_id %u temp %u throt_levels %u\n",
+ le32_to_cpu(ev->level), le32_to_cpu(ev->pdev_id),
+ le32_to_cpu(ev->temp), le32_to_cpu(ev->therm_throt_levels));
+}
+
static void ath12k_fils_discovery_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
@@ -9811,6 +9847,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_PDEV_TEMPERATURE_EVENTID:
ath12k_wmi_pdev_temperature_event(ab, skb);
break;
+ case WMI_THERM_THROT_STATS_EVENTID:
+ ath12k_wmi_thermal_throt_stats_event(ab, skb);
+ break;
case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID:
ath12k_wmi_pdev_dma_ring_buf_release_event(ab, skb);
break;
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index 4a34b2ca99ea..1853a6f9cf27 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -870,6 +870,7 @@ enum wmi_tlv_event_id {
WMI_READ_DATA_FROM_FLASH_EVENTID,
WMI_REPORT_RX_AGGR_FAILURE_EVENTID,
WMI_PKGID_EVENTID,
+ WMI_THERM_THROT_STATS_EVENTID,
WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO),
WMI_UPLOADH_EVENTID,
WMI_CAPTUREH_EVENTID,
@@ -4120,6 +4121,13 @@ enum set_init_cc_flags {
ALPHA_IS_SET,
};
+struct wmi_therm_throt_stats_event {
+ __le32 pdev_id;
+ __le32 temp;
+ __le32 level;
+ __le32 therm_throt_levels;
+} __packed;
+
struct ath12k_wmi_init_country_arg {
union {
u16 country_code;
--
2.34.1
^ permalink raw reply related
* [PATCH ath-next v2 0/5] wifi: ath12k: thermal throttling and cooling device support
From: Maharaja Kennadyrajan @ 2026-04-13 15:38 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan
Patch 1 handles the firmware stats event so we can track the current
temperature and throttle level per pdev without spamming logs.
Patch 2 enables thermal throttling at bring-up and programs default level
tables to firmware via WMI_THERM_THROT_SET_CONF_CMDID; the driver picks
IPA/XFEM defaults based on the firmware WMI service bitmap, supports 4 or 5
levels as advertised, and only fills optional fields (pout reduction,
tx chain mask) when the corresponding WMI service bits are present.
Patch 3 refactors per-radio thermal hwmon cleanup to reduce code duplication and
ensure consistent cleanup across thermal register and unregister paths.
Patch 4 reorders the group teardown logic symmetric for safe thermal sysfs cleanup.
Patch 5 exposes a thermal cooling device per radio so the kernel thermal
framework or userspace can set the TX duty-cycle off percentage; writes
are validated against the throttling state range and host state is kept in
sync with successful firmware updates.
Examples:
echo 40 > /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_stat
cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/cur_state
cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device/max_state
v2: Addressed Jeff's comment by rebased on latest TOT with wmi tb - alloc and free
interface change.
Maharaja Kennadyrajan (5):
wifi: ath12k: handle thermal throttle stats WMI event
wifi: ath12k: configure firmware thermal throttling via WMI
wifi: ath12k: refactor per-radio thermal hwmon setup and cleanup
wifi: ath12k: reorder group start/stop for safe thermal sysfs cleanup
wifi: ath12k: add thermal cooling device support
drivers/net/wireless/ath/ath12k/core.c | 50 +++--
drivers/net/wireless/ath/ath12k/mac.c | 9 +
drivers/net/wireless/ath/ath12k/thermal.c | 252 ++++++++++++++++++----
drivers/net/wireless/ath/ath12k/thermal.h | 35 +++
drivers/net/wireless/ath/ath12k/wmi.c | 108 ++++++++++
drivers/net/wireless/ath/ath12k/wmi.h | 50 +++++
6 files changed, 447 insertions(+), 57 deletions(-)
base-commit: 9a4f673eb08d2a7713b258d671b4a45f2a6e68b7
--
2.34.1
^ permalink raw reply
* Re: [patch 15/38] ptp: ptp_vmclock: Replace get_cycles() usage
From: David Woodhouse @ 2026-04-13 15:33 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Arnd Bergmann, x86, Lu Baolu, iommu, Michael Grzeschik, netdev,
linux-wireless, Herbert Xu, linux-crypto, Vlastimil Babka,
linux-mm, Bernie Thompson, linux-fbdev, Theodore Tso, linux-ext4,
Andrew Morton, Uladzislau Rezki, Marco Elver, Dmitry Vyukov,
kasan-dev, Andrey Ryabinin, Thomas Sailer, linux-hams,
Jason A. Donenfeld, Richard Henderson, linux-alpha, Russell King,
linux-arm-kernel, Catalin Marinas, Huacai Chen, loongarch,
Geert Uytterhoeven, linux-m68k, Dinh Nguyen, Jonas Bonn,
linux-openrisc, Helge Deller, linux-parisc, Michael Ellerman,
linuxppc-dev, Paul Walmsley, linux-riscv, Heiko Carstens,
linux-s390, David S. Miller, sparclinux
In-Reply-To: <20260410120318.592237447@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 994 bytes --]
On Fri, 2026-04-10 at 14:19 +0200, Thomas Gleixner wrote:
> get_cycles() is not really well defined and similar to other usaage of the
> underlying hardware CPU counters the PTP vmclock should use an explicit
> interface as well.
>
> Implement ptp_vmclock_read_cpu_counter() in arm64 and x86 and simplify the
> Kconfig selection while at it.
>
> No functional change.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: David Woodhouse <dwmw2@infradead.org>
Acked-by: David Woodhouse <dwmw@amazon.co.uk>
Although I might follow up with a change to make this...
> +static inline u64 ptp_vmclock_read_cpu_counter(void)
> +{
> + return cpu_feature_enabled(X86_FEATURE_TSC) ? rdtsc() : 0;
> +}
> +
... depend on TSC_RELIABLE¹, since if the guest doesn't believe that it
is, then the guest shouldn't be trying to use it as the basis for
precise timing.
¹ (Or... one of the other zoo of TSC flags for the gradually reducing
brokenness over the years...)
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [PATCH ath-next 1/5] wifi: ath12k: handle thermal throttle stats WMI event
From: Maharaja Kennadyrajan @ 2026-04-13 15:29 UTC (permalink / raw)
To: Jeff Johnson, ath12k; +Cc: linux-wireless
In-Reply-To: <bb391f8d-e385-42b6-adec-171c21c67246@oss.qualcomm.com>
On 10-04-2026 10:54 pm, Jeff Johnson wrote:
> On 3/31/2026 7:24 AM, Maharaja Kennadyrajan wrote:
>> +static void ath12k_wmi_thermal_throt_stats_event(struct ath12k_base *ab,
>> + struct sk_buff *skb)
>> +{
>> + const struct wmi_therm_throt_stats_event *ev;
>> + struct ath12k *ar;
>> +
>> + const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
> please rebase on current ath-main, this interface changed with:
> https://msgid.link/20260407095426.3285574-1-nico.escande@gmail.com
sure, I will rebase on latest TOT with new interface and send the next
version.
> /jeff
^ permalink raw reply
* Re: [patch 10/38] arcnet: Remove function timing code
From: David Woodhouse @ 2026-04-13 15:29 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Michael Grzeschik, netdev, Arnd Bergmann, x86, Lu Baolu, iommu,
linux-wireless, Herbert Xu, linux-crypto, Vlastimil Babka,
linux-mm, Bernie Thompson, linux-fbdev, Theodore Tso, linux-ext4,
Andrew Morton, Uladzislau Rezki, Marco Elver, Dmitry Vyukov,
kasan-dev, Andrey Ryabinin, Thomas Sailer, linux-hams,
Jason A. Donenfeld, Richard Henderson, linux-alpha, Russell King,
linux-arm-kernel, Catalin Marinas, Huacai Chen, loongarch,
Geert Uytterhoeven, linux-m68k, Dinh Nguyen, Jonas Bonn,
linux-openrisc, Helge Deller, linux-parisc, Michael Ellerman,
linuxppc-dev, Paul Walmsley, linux-riscv, Heiko Carstens,
linux-s390, David S. Miller, sparclinux
In-Reply-To: <20260410120318.253872322@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 778 bytes --]
On Fri, 2026-04-10 at 14:19 +0200, Thomas Gleixner wrote:
> ARCNET is a museums piece and the function timing can be done with
> ftrace. Remove the cruft.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: Michael Grzeschik <m.grzeschik@pengutronix.de>
> Cc: netdev@vger.kernel.org
> ---
> drivers/net/arcnet/arc-rimi.c | 4 ++--
> drivers/net/arcnet/arcdevice.h | 20 +-------------------
> drivers/net/arcnet/com20020.c | 6 ++----
> drivers/net/arcnet/com90io.c | 6 ++----
> drivers/net/arcnet/com90xx.c | 4 ++--
> 5 files changed, 9 insertions(+), 31 deletions(-)
Acked-by: David Woodhouse <dwmw2@infradead.org>
By coincidence, I took the last of my ARCNET cards to the tip just this
morning...
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [patch 17/38] ext4: Replace get_cycles() usage with ktime_get()
From: Arnd Bergmann @ 2026-04-13 14:46 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Theodore Ts'o, linux-ext4, x86, Baolu Lu, iommu,
Michael Grzeschik, Netdev, linux-wireless, Herbert Xu,
linux-crypto, Vlastimil Babka (SUSE), linux-mm, David Woodhouse,
Bernie Thompson, linux-fbdev, Andrew Morton,
Uladzislau Rezki (Sony), Marco Elver, Dmitry Vyukov, kasan-dev,
Andrey Ryabinin, Thomas Sailer, linux-hams, Jason A . Donenfeld,
Richard Henderson, linux-alpha, Russell King, linux-arm-kernel,
Catalin Marinas, Huacai Chen, loongarch, Geert Uytterhoeven,
linux-m68k, Dinh Nguyen, Jonas Bonn,
linux-openrisc@vger.kernel.org, Helge Deller, linux-parisc,
Michael Ellerman, linuxppc-dev, Paul Walmsley, linux-riscv,
Heiko Carstens, linux-s390, David S . Miller, sparclinux
In-Reply-To: <20260410120318.727211419@kernel.org>
On Fri, Apr 10, 2026, at 14:19, Thomas Gleixner wrote:
> get_cycles() is not guaranteed to be functional on all systems/platforms
> and the values returned are unitless and not easy to map to something
> useful.
>
> Use ktime_get() instead, which provides nanosecond timestamps and is
> functional everywhere.
>
> This is part of a larger effort to limit get_cycles() usage to low level
> architecture code.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: "Theodore Ts'o" <tytso@mit.edu>
> Cc: linux-ext4@vger.kernel.org
I think this is technically an ABI chance, since the time
difference gets exported through procfs, but the new version
is clearly the right thing to do since it replaces a hardware
specific value with a portable one.
Arnd
^ permalink raw reply
* Re: [patch 38/38] treewide: Remove asm/timex.h includes from generic code
From: Arnd Bergmann @ 2026-04-13 14:45 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: x86, Baolu Lu, iommu, Michael Grzeschik, Netdev, linux-wireless,
Herbert Xu, linux-crypto, Vlastimil Babka (SUSE), linux-mm,
David Woodhouse, Bernie Thompson, linux-fbdev, Theodore Ts'o,
linux-ext4, Andrew Morton, Uladzislau Rezki (Sony), Marco Elver,
Dmitry Vyukov, kasan-dev, Andrey Ryabinin, Thomas Sailer,
linux-hams, Jason A . Donenfeld, Richard Henderson, linux-alpha,
Russell King, linux-arm-kernel, Catalin Marinas, Huacai Chen,
loongarch, Geert Uytterhoeven, linux-m68k, Dinh Nguyen,
Jonas Bonn, linux-openrisc@vger.kernel.org, Helge Deller,
linux-parisc, Michael Ellerman, linuxppc-dev, Paul Walmsley,
linux-riscv, Heiko Carstens, linux-s390, David S . Miller,
sparclinux
In-Reply-To: <20260410120320.163559629@kernel.org>
On Fri, Apr 10, 2026, at 14:21, Thomas Gleixner wrote:
> asm/timex.h does not provide any functionality for non-architecture code
> anymore.
>
> Remove the asm-generic fallback and all references in include and source
> files along with the random_get_entropy() #ifdeffery in timex.h.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> ---
> include/asm-generic/Kbuild | 1 -
> include/asm-generic/timex.h | 15 ---------------
> include/linux/random.h | 3 +++
> include/linux/timex.h | 26 --------------------------
Acked-by: Arnd Bergmann <arnd@arndb.de>
^ permalink raw reply
* Re: [patch 32/38] powerpc/spufs: Use mftb() directly
From: Arnd Bergmann @ 2026-04-13 14:43 UTC (permalink / raw)
To: Thomas Gleixner, LKML
Cc: Michael Ellerman, linuxppc-dev, x86, Baolu Lu, iommu,
Michael Grzeschik, Netdev, linux-wireless, Herbert Xu,
linux-crypto, Vlastimil Babka (SUSE), linux-mm, David Woodhouse,
Bernie Thompson, linux-fbdev, Theodore Ts'o, linux-ext4,
Andrew Morton, Uladzislau Rezki (Sony), Marco Elver,
Dmitry Vyukov, kasan-dev, Andrey Ryabinin, Thomas Sailer,
linux-hams, Jason A . Donenfeld, Richard Henderson, linux-alpha,
Russell King, linux-arm-kernel, Catalin Marinas, Huacai Chen,
loongarch, Geert Uytterhoeven, linux-m68k, Dinh Nguyen,
Jonas Bonn, linux-openrisc@vger.kernel.org, Helge Deller,
linux-parisc, Paul Walmsley, linux-riscv, Heiko Carstens,
linux-s390, David S . Miller, sparclinux
In-Reply-To: <20260410120319.723429844@kernel.org>
On Fri, Apr 10, 2026, at 14:21, Thomas Gleixner wrote:
> There is no reason to indirect via get_cycles(), which is about to be
> removed.
>
> Use mftb() directly.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> Cc: linuxppc-dev@lists.ozlabs.org
Acked-by: Arnd Bergmann <arnd@arndb.de>
^ permalink raw reply
* Re: [patch 14/38] slub: Use prandom instead of get_cycles()
From: Vlastimil Babka (SUSE) @ 2026-04-13 13:45 UTC (permalink / raw)
To: hu.shengming, harry
Cc: tglx, linux-kernel, linux-mm, arnd, x86, baolu.lu, iommu,
m.grzeschik, netdev, linux-wireless, herbert, linux-crypto, dwmw2,
bernie, linux-fbdev, tytso, linux-ext4, akpm, urezki, elver,
dvyukov, kasan-dev, ryabinin.a.a, t.sailer, linux-hams, Jason,
richard.henderson, linux-alpha, linux, linux-arm-kernel,
catalin.marinas, chenhuacai, loongarch, geert, linux-m68k,
dinguyen, jonas, linux-openrisc, deller, linux-parisc, mpe,
linuxppc-dev, pjw, linux-riscv, hca, linux-s390, davem,
sparclinux, hao.li, cl, rientjes, roman.gushchin
In-Reply-To: <20260413210252672ZfdcegJLJtyvlYdFAUBlr@zte.com.cn>
On 4/13/26 15:02, hu.shengming@zte.com.cn wrote:
> Harry wrote:
>> [Resending after fixing broken email headers]
>>
>> On Fri, Apr 10, 2026 at 02:19:37PM +0200, Thomas Gleixner wrote:
>> > The decision whether to scan remote nodes is based on a 'random' number
>> > retrieved via get_cycles(). get_cycles() is about to be removed.
>> >
>> > There is already prandom state in the code, so use that instead.
>> >
>> > Signed-off-by: Thomas Gleixner <tglx@kernel.org>
>> > Cc: Vlastimil Babka <vbabka@kernel.org>
>> > Cc: linux-mm@kvack.org
>> > ---
>>
>> Acked-by: Harry Yoo (Oracle) <harry@kernel.org>
>>
>> Is this for this merge window?
I'd say it's not intended for 7.1 as it's not in -next and v1 was posted
just before the merge window.
>> This may conflict with upcoming changes on freelist shuffling [1]
>> (not queued for slab/for-next yet though), but it should be easy to
>> resolve.
Indeed, it's a simple conflict.
>
> Hi Harry,
>
> Would you like me to wait for this patch to land linux-next and then
> rebase and send v6 on top?
Just send it now based same as previously so we can finish the reviews, and
we'll deal with it after rc1.
^ permalink raw reply
* Re: [PATCH] rtw88: add TX power limit support to 114 and 130 channels
From: Thadeu Lima de Souza Cascardo @ 2026-04-13 13:12 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: Kalle Valo, Yan-Hsuan Chuang, linux-wireless@vger.kernel.org,
kernel-dev@igalia.com
In-Reply-To: <f347beb33eb142cba384bbe9378a061c@realtek.com>
On Mon, Apr 13, 2026 at 05:56:17AM +0000, Ping-Ke Shih wrote:
> Thadeu Lima de Souza Cascardo <cascardo@igalia.com> wrote:
> > On Fri, Apr 10, 2026 at 03:56:11AM +0000, Ping-Ke Shih wrote:
> > > Thadeu Lima de Souza Cascardo <cascardo@igalia.com> wrote:
> > > > Though 114 and 130 are not usual channels, they are found in the wild with
> > > > setups using 5350MHz as the center frequency of a 80MHz setup.
> > >
> > > What did the AP setup? channel 114 160MHz?
> > > I wonder why rtw88 can select a not usual channel 114 80MHz.
> > >
> > > Please share your environment setup.
> > >
> >
> > This is a Mikrotik that uses channel 130 at 80MHz.
>
> I'm surprised that an AP can work on this not usual channel/bandwidth.
> Can you change the setting to usual channel/bandwidth? We'd avoid using
> this unsupported channel/bandwidth by [1].
>
It seems to be "well-known" that some APs do it and it has caused other
issues in other drivers. But it works just fine with a lot of other
drivers. So, I would rather make rtw88 work with that setting, even if it
falls back to 40MHz, for example.
Let me check what happens when using the proposed patch and I will report
back to you.
> >
> > > >
> > > > rtw88 supports that, but issues a WARNING because it cannot find the TX
> > > > power limit for those channels.
> > >
> > > Actually, rtw88 hardware can't support that, so we are working on patch
> > > to avoid selecting unusual channels. Can it work properly with
> > > the AP after this patch?
> > >
> >
> > It does work just fine even without the patch. The only issue is the
> > WARNING that is triggered.
> >
>
> As internal discussion, hardware doesn't work on channel 130 80M,
> which means connection might be well, but it can't yield expected performance.
> More, the power limit is not really verified on ch130 80M, so we wonder
> that the signal might not in expectation.
>
The rationale behing my patch was exactly to try to set the power limit to
the lesser one of the adjacent channels. I am not sure that the expected
performance was not achieved. How would you suggest that I verify?
Thanks a lot.
Cascardo.
> By above reasons, we'd avoid using channel 114 and 130.
>
> [1] https://lore.kernel.org/linux-wireless/20260413053601.13037-1-pkshih@realtek.com/T/#u
>
>
^ permalink raw reply
* Re: [patch 14/38] slub: Use prandom instead of get_cycles()
From: hu.shengming @ 2026-04-13 13:02 UTC (permalink / raw)
To: harry
Cc: tglx, linux-kernel, vbabka, linux-mm, arnd, x86, baolu.lu, iommu,
m.grzeschik, netdev, linux-wireless, herbert, linux-crypto, dwmw2,
bernie, linux-fbdev, tytso, linux-ext4, akpm, urezki, elver,
dvyukov, kasan-dev, ryabinin.a.a, t.sailer, linux-hams, Jason,
richard.henderson, linux-alpha, linux, linux-arm-kernel,
catalin.marinas, chenhuacai, loongarch, geert, linux-m68k,
dinguyen, jonas, linux-openrisc, deller, linux-parisc, mpe,
linuxppc-dev, pjw, linux-riscv, hca, linux-s390, davem,
sparclinux, hao.li, cl, rientjes, roman.gushchin
In-Reply-To: <adyyNeVTkXQlnh_2@hyeyoo>
Harry wrote:
> [Resending after fixing broken email headers]
>
> On Fri, Apr 10, 2026 at 02:19:37PM +0200, Thomas Gleixner wrote:
> > The decision whether to scan remote nodes is based on a 'random' number
> > retrieved via get_cycles(). get_cycles() is about to be removed.
> >
> > There is already prandom state in the code, so use that instead.
> >
> > Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> > Cc: Vlastimil Babka <vbabka@kernel.org>
> > Cc: linux-mm@kvack.org
> > ---
>
> Acked-by: Harry Yoo (Oracle) <harry@kernel.org>
>
> Is this for this merge window?
>
> This may conflict with upcoming changes on freelist shuffling [1]
> (not queued for slab/for-next yet though), but it should be easy to
> resolve.
>
Hi Harry,
Would you like me to wait for this patch to land linux-next and then
rebase and send v6 on top?
Thanks,
--
With Best Regards,
Shengming
> [Cc'ing Shengming and SLAB ALLOCATOR folks]
> [1] https://lore.kernel.org/linux-mm/20260409204352095kKWVYKtZImN59ybO6iRNj@zte.com.cn
>
> --
> Cheers,
> Harry / Hyeonggon
>
> > mm/slub.c | 37 +++++++++++++++++++++++--------------
> > 1 file changed, 23 insertions(+), 14 deletions(-)
> >
> > --- a/mm/slub.c
> > +++ b/mm/slub.c
> > @@ -3302,6 +3302,25 @@ static inline struct slab *alloc_slab_pa
> > return slab;
> > }
> >
> > +#if defined(CONFIG_SLAB_FREELIST_RANDOM) || defined(CONFIG_NUMA)
> > +static DEFINE_PER_CPU(struct rnd_state, slab_rnd_state);
> > +
> > +static unsigned int slab_get_prandom_state(unsigned int limit)
> > +{
> > + struct rnd_state *state;
> > + unsigned int res;
> > +
> > + /*
> > + * An interrupt or NMI handler might interrupt and change
> > + * the state in the middle, but that's safe.
> > + */
> > + state = &get_cpu_var(slab_rnd_state);
> > + res = prandom_u32_state(state) % limit;
> > + put_cpu_var(slab_rnd_state);
> > + return res;
> > +}
> > +#endif
> > +
> > #ifdef CONFIG_SLAB_FREELIST_RANDOM
> > /* Pre-initialize the random sequence cache */
> > static int init_cache_random_seq(struct kmem_cache *s)
> > @@ -3365,8 +3384,6 @@ static void *next_freelist_entry(struct
> > return (char *)start + idx;
> > }
> >
> > -static DEFINE_PER_CPU(struct rnd_state, slab_rnd_state);
> > -
> > /* Shuffle the single linked freelist based on a random pre-computed sequence */
> > static bool shuffle_freelist(struct kmem_cache *s, struct slab *slab,
> > bool allow_spin)
> > @@ -3383,15 +3400,7 @@ static bool shuffle_freelist(struct kmem
> > if (allow_spin) {
> > pos = get_random_u32_below(freelist_count);
> > } else {
> > - struct rnd_state *state;
> > -
> > - /*
> > - * An interrupt or NMI handler might interrupt and change
> > - * the state in the middle, but that's safe.
> > - */
> > - state = &get_cpu_var(slab_rnd_state);
> > - pos = prandom_u32_state(state) % freelist_count;
> > - put_cpu_var(slab_rnd_state);
> > + pos = slab_get_prandom_state(freelist_count);
> > }
> >
> > page_limit = slab->objects * s->size;
> > @@ -3882,7 +3891,7 @@ static void *get_from_any_partial(struct
> > * with available objects.
> > */
> > if (!s->remote_node_defrag_ratio ||
> > - get_cycles() % 1024 > s->remote_node_defrag_ratio)
> > + slab_get_prandom_state(1024) > s->remote_node_defrag_ratio)
> > return NULL;
> >
> > do {
> > @@ -7102,7 +7111,7 @@ static unsigned int
> >
> > /* see get_from_any_partial() for the defrag ratio description */
> > if (!s->remote_node_defrag_ratio ||
> > - get_cycles() % 1024 > s->remote_node_defrag_ratio)
> > + slab_get_prandom_state(1024) > s->remote_node_defrag_ratio)
> > return 0;
> >
> > do {
> > @@ -8421,7 +8430,7 @@ void __init kmem_cache_init_late(void)
> > flushwq = alloc_workqueue("slub_flushwq", WQ_MEM_RECLAIM | WQ_PERCPU,
> > 0);
> > WARN_ON(!flushwq);
> > -#ifdef CONFIG_SLAB_FREELIST_RANDOM
> > +#if defined(CONFIG_SLAB_FREELIST_RANDOM) || defined(CONFIG_NUMA)
> > prandom_init_once(&slab_rnd_state);
> > #endif
> > }
> >
> >
^ permalink raw reply
* Re: [PATCH] wifi: rtw88: Add NULL check for chip->edcca_th
From: LB F @ 2026-04-13 12:35 UTC (permalink / raw)
To: Panagiotis Petrakopoulos; +Cc: pkshih, linux-wireless
In-Reply-To: <20260413100249.28618-1-npetrakopoulos2003@gmail.com>
Hi Panagiotis,
Thank you for your interest and for looking into this issue!
I appreciate the effort, but as I'm not a developer, I would prefer to
wait for a review and approval from the maintainer (Ping-Ke Shih)
before testing or applying any patch — to make sure everything stays
consistent with the official direction.
Ping-Ke, could you please take a look at this patch when you have a
chance? If you approve the approach, I'll be happy to test it and
provide a Tested-by.
Best regards, Oleksandr Havrylov
^ permalink raw reply
* [PATCH v2 1/1] wifi: cfg80211: reject duplicate wiphy cipher suite entries
From: Ren Wei @ 2026-04-13 12:30 UTC (permalink / raw)
To: linux-wireless, johannes
Cc: linville, kilroyd, yifanwucs, tomapufckgml, yuantan098, bird,
xuyuqiabc, n05ec
From: Yuqi Xu <xuyuqiabc@gmail.com>
Duplicate entries in wiphy->cipher_suites do not describe any
additional capability, but cfg80211 currently accepts them and leaves
individual consumers to deal with them.
One such consumer is the WEXT compatibility code, which appends a WEP
key length for each WEP cipher entry it sees. Repeated WEP entries can
therefore overflow the fixed iw_range::encoding_size array returned by
SIOCGIWRANGE.
Reject duplicate cipher suite entries in wiphy_register() instead.
This keeps the cipher suite invariant in one place and makes malformed
wiphy descriptions fail early with -EINVAL, rather than relying on a
single cfg80211 user to handle duplicates correctly.
Cc: stable@kernel.org
Fixes: 2ab658f9ce21 ("cfg80211: set WE encoding size based on available ciphers")
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Co-developed-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
Suggested-by: Xin Liu <bird@lzu.edu.cn>
Signed-off-by: Yuqi Xu <xuyuqiabc@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
---
changes in v2:
- reject duplicate wiphy->cipher_suites entries in wiphy_register()
- leave net/wireless/wext-compat.c unchanged
- return -EINVAL without WARN_ON() on duplicate entries
- v2 link: https://lore.kernel.org/all/32271ec25eae3d23aae2450ab864f37e9d475e24.1774627789.git.xuyuqiabc@gmail.com/
net/wireless/core.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 28ca4290ca99..59d93f142fc2 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -731,6 +731,24 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
return ret;
}
+static bool wiphy_cipher_suites_valid(const struct wiphy *wiphy)
+{
+ int i, j;
+
+ if (wiphy->n_cipher_suites && !wiphy->cipher_suites)
+ return false;
+
+ for (i = 0; i < wiphy->n_cipher_suites; i++) {
+ for (j = 0; j < i; j++) {
+ if (wiphy->cipher_suites[i] ==
+ wiphy->cipher_suites[j])
+ return false;
+ }
+ }
+
+ return true;
+}
+
int wiphy_register(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
@@ -863,6 +881,9 @@ int wiphy_register(struct wiphy *wiphy)
if (res)
return res;
+ if (!wiphy_cipher_suites_valid(wiphy))
+ return -EINVAL;
+
/* sanity check supported bands/channels */
for (band = 0; band < NUM_NL80211_BANDS; band++) {
const struct ieee80211_sband_iftype_data *iftd;
--
2.52.0
^ permalink raw reply related
* [PATCH] wifi: rtw88: Add NULL check for chip->edcca_th
From: Panagiotis Petrakopoulos @ 2026-04-13 10:02 UTC (permalink / raw)
To: pkshih; +Cc: linux-wireless, goainwo, Panagiotis Petrakopoulos
It was recently reported that rtw_fw_adaptivity_result
in fw.c dereferences rtwdev->chip->edcca_th without
a null check. The issue appears to be that devices
with the 8821CE chip don't define edcca_th in their
chip info. As a result, when rtw_fw_adaptivity_result
tries to dereference it, the kernel triggers an oops.
Add a NULL check for edcca_th before dereferencing
it in rtw_fw_adaptivity_result() in fw.c and
rtw_phy_set_edcca_th() in phy.c.
Tested on a 8822CE chip which defines edcca_th, so
this issue is not present on it, but it still uses
this driver and I can verify there are no regressions.
Reported-by: Oleksandr Havrylov <goainwo@gmail.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221286
Link: https://lore.kernel.org/linux-wireless/CALdGYqQriS7mP0vj_rm_xvisfzFVh0hbpy+---48r6bodZO7tg@mail.gmail.com/
Signed-off-by: Panagiotis Petrakopoulos <npetrakopoulos2003@gmail.com>
---
drivers/net/wireless/realtek/rtw88/fw.c | 3 +++
drivers/net/wireless/realtek/rtw88/phy.c | 3 +++
2 files changed, 6 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
index 48207052e3f8..c4819ef6d54d 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.c
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -284,6 +284,9 @@ static void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload,
result->density, result->igi, result->l2h_th_init, result->l2h,
result->h2l, result->option);
+ if (!edcca_th)
+ return;
+
rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Reg Setting: L2H %x H2L %x\n",
rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr,
edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask),
diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c
index e2ac5c6fd500..c10eb28e54ad 100644
--- a/drivers/net/wireless/realtek/rtw88/phy.c
+++ b/drivers/net/wireless/realtek/rtw88/phy.c
@@ -161,6 +161,9 @@ void rtw_phy_set_edcca_th(struct rtw_dev *rtwdev, u8 l2h, u8 h2l)
{
const struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th;
+ if (!edcca_th)
+ return;
+
rtw_write32_mask(rtwdev,
edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr,
edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask,
--
2.53.0
^ permalink raw reply related
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