Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [PATCH 3/4] dt-bindings: bus: add brcm,bcm6362-wlan
From: Krzysztof Kozlowski @ 2026-05-30 11:50 UTC (permalink / raw)
  To: Alessio Ferri
  Cc: Rafał Miłecki, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Florian Fainelli, linux-kernel,
	linux-wireless, devicetree
In-Reply-To: <20260529-add-bcm6362-wlan-v1-3-722242777f58@gmail.com>

On Fri, May 29, 2026 at 02:06:01AM +0200, Alessio Ferri wrote:
> Document the binding for the SHIM bridge that gates the on-chip
> 2.4 GHz WLAN block of the Broadcom BCM6362 SoC. The bridge owns the
> SHIM peephole, a single clock for the macro, and two resets (the
> SHIM macro itself and its ubus side). It is also a bus: it carries
> one brcm,bus-axi child describing the bcma backplane behind the
> SHIM, with a standard interrupt-map routing the d11 core's IRQ to
> the SoC interrupt controller.
> 
> Assisted-by: Claude:claude-4.8-opus
> Signed-off-by: Alessio Ferri <alessio.ferri@mythread.it>
> ---
>  .../devicetree/bindings/bus/brcm,bcm6362-wlan.yaml | 106 +++++++++++++++++++++
>  1 file changed, 106 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/bus/brcm,bcm6362-wlan.yaml b/Documentation/devicetree/bindings/bus/brcm,bcm6362-wlan.yaml
> new file mode 100644
> index 000000000000..c8d49ccdd2c1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/bus/brcm,bcm6362-wlan.yaml
> @@ -0,0 +1,106 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/bus/brcm,bcm6362-wlan.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Broadcom BCM6362 on-chip WLAN SHIM bridge
> +
> +maintainers:
> +  - Alessio Ferri <alessio.ferri@mythread.it>
> +
> +description: |
> +  The BCM6362 SoC integrates a 2.4 GHz Broadcom WLAN block whose
> +  register backplane uses the Broadcom AMBA (bcma) architecture. The
> +  backplane is gated by a small SHIM bridge that holds the WLAN macro
> +  in reset and disables its clocks until released by software. CFE
> +  does not release this block, so software bring-up is required
> +  before bcma can enumerate the backplane.
> +
> +  This binding describes the SHIM bridge node. The SHIM driver brings

Do not describe binding. Do not describe driver.
Describe hardware.

> +  the macro up and then populates the brcm,bus-axi child node, which
> +  describes the bcma backplane behind the SHIM and is bound by the
> +  bcma-host-soc driver. The SoC-specific configuration (big-endian
> +  accessors, SHIM-attached topology, SHIM Control register peephole
> +  pointer) is delivered to bcma via platform_data injected at
> +  populate time, so the brcm,bus-axi child stays SoC-agnostic.

How is it relevant?

> +
> +properties:
> +  compatible:
> +    const: brcm,bcm6362-wlan
> +
> +  reg:
> +    maxItems: 1
> +    description: SHIM peephole registers.

What is SHIM?

> +
> +  reg-names:
> +    items:
> +      - const: shim
> +
> +  clocks:
> +    maxItems: 1
> +
> +  resets:
> +    items:
> +      - description: SHIM macro reset
> +      - description: SHIM ubus reset
> +
> +  reset-names:
> +    items:
> +      - const: shim
> +      - const: shim-ubus
> +
> +  '#address-cells':
> +    const: 1
> +
> +  '#size-cells':
> +    const: 1
> +
> +  ranges: true
> +
> +patternProperties:
> +  "^axi@[0-9a-f]+$":

Use consistent quotes.

> +    type: object
> +    description: The bcma AXI backplane behind the SHIM.
> +    $ref: /schemas/types.yaml#

Need proper ref. You could easily check instead of sending Claude slop -
is there any binding with above syntax?

You don't get subnodes for buses for devices not being the actual
buses.

Best regards,
Krzysztof


^ permalink raw reply

* [PATCH] wifi: ath11k: fix potential buffer underflow in ath11k_hal_rx_msdu_list_get()
From: Dmitry Morgun @ 2026-05-30 11:42 UTC (permalink / raw)
  To: Jeff Johnson
  Cc: Dmitry Morgun, linux-wireless, ath11k, linux-kernel, lvc-project,
	stable

When the first entry in msdu_details has a zero buffer address,
the code accesses msdu_details[i - 1] with i == 0, causing a
buffer underflow.

Fix similarly to ath12k_wifi7_hal_rx_msdu_list_get() by adding
a separate check for i == 0 before the main condition to prevent
the out-of-bounds access.

Found by Linux Verification Center (linuxtesting.org) with SVACE.

Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Signed-off-by: Dmitry Morgun <d.morgun@ispras.ru>
---
 drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 2a413e3a0..c9f520c2a 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -4565,6 +4565,9 @@ static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar,
 	msdu_details = &msdu_link->msdu_link[0];
 
 	for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) {
+		if (!i && FIELD_GET(BUFFER_ADDR_INFO0_ADDR,
+				    msdu_details[i].buf_addr_info.info0) == 0)
+			break;
 		if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR,
 			      msdu_details[i].buf_addr_info.info0) == 0) {
 			msdu_desc_info = &msdu_details[i - 1].rx_msdu_info;
-- 
2.34.1


^ permalink raw reply related

* Re: [patch V2 04/25] pps: Convert to ktime_get_snapshot_id()
From: Rodolfo Giometti @ 2026-05-30 11:08 UTC (permalink / raw)
  To: Thomas Gleixner, LKML
  Cc: David Woodhouse, Miroslav Lichvar, John Stultz, Stephen Boyd,
	Anna-Maria Behnsen, Frederic Weisbecker, thomas.weissschuh,
	Arthur Kiyanovski, Vincent Donnefort, Marc Zyngier, Oliver Upton,
	kvmarm, Oliver Upton, Richard Cochran, netdev, Takashi Iwai,
	Miri Korenblit, Johannes Berg, Jacob Keller, Tony Nguyen,
	Saeed Mahameed, Peter Hilber, Michael S. Tsirkin, virtualization,
	linux-wireless, linux-sound, David Woodhouse, Vadim Fedorenko
In-Reply-To: <20260529195557.123410250@kernel.org>

On 29/05/2026 22:00, Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@kernel.org>
> 
> ktime_get_snapshot() resolves to ktime_get_snapshot_id(CLOCK_REALTIME).
> 
> Make it obvious in the code and convert the readout to use the
> snapshot::systime and monoraw fields instead of snapshot::real and raw,
> which aregoing away.
> 
> Similar to the PPS generators, avoid the more expensive snapshot when
> CONFIG_NTP_PPS is disabled.
> 
> No functional change intended.
> 
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Tested-by: Arthur Kiyanovski <akiyano@amazon.com>
> Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
> Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
> ---
>   include/linux/pps_kernel.h |   10 ++++++----
>   1 file changed, 6 insertions(+), 4 deletions(-)
> --- a/include/linux/pps_kernel.h
> +++ b/include/linux/pps_kernel.h
> @@ -99,12 +99,14 @@ static inline void timespec_to_pps_ktime
>   
>   static inline void pps_get_ts(struct pps_event_time *ts)
>   {
> +#ifdef CONFIG_NTP_PPS
>   	struct system_time_snapshot snap;
>   
> -	ktime_get_snapshot(&snap);
> -	ts->ts_real = ktime_to_timespec64(snap.real);
> -#ifdef CONFIG_NTP_PPS
> -	ts->ts_raw = ktime_to_timespec64(snap.raw);
> +	ktime_get_snapshot_id(CLOCK_REALTIME, &snap);
> +	ts->ts_real = ktime_to_timespec64(snap.systime);
> +	ts->ts_raw = ktime_to_timespec64(snap.monoraw);
> +#else
> +	ktime_get_real_ts64(&ts->ts_real);

	/*
	 * Prevent kernel stack information disclosure if the
	 * structure is later copied to userspace.
	 */
	ts->ts_raw = (struct timespec64){0, 0};

>   #endif
>   }
>   
> 

Ciao,

Rodolfo

-- 
GNU/Linux Solutions                  e-mail: giometti@enneenne.com
Linux Device Driver                          giometti@linux.it
Embedded Systems                     phone:  +39 349 2432127
UNIX programming

^ permalink raw reply

* Re: [patch V2 03/25] pps: generators: Use ktime_get_real_ts64() instead of ktime_get_snapshot()
From: Rodolfo Giometti @ 2026-05-30 11:04 UTC (permalink / raw)
  To: Thomas Gleixner, LKML
  Cc: David Woodhouse, Miroslav Lichvar, John Stultz, Stephen Boyd,
	Anna-Maria Behnsen, Frederic Weisbecker, thomas.weissschuh,
	Arthur Kiyanovski, Vincent Donnefort, Marc Zyngier, Oliver Upton,
	kvmarm, Oliver Upton, Richard Cochran, netdev, Takashi Iwai,
	Miri Korenblit, Johannes Berg, Jacob Keller, Tony Nguyen,
	Saeed Mahameed, Peter Hilber, Michael S. Tsirkin, virtualization,
	linux-wireless, linux-sound, David Woodhouse, Vadim Fedorenko
In-Reply-To: <20260529195557.074439049@kernel.org>

On 29/05/2026 21:59, Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@kernel.org>
> 
> There is no reason to use the more complex ktime_get_snapshot() for
> retrieving CLOCK_REALTIME.
> 
> Just use ktime_get_real_ts64(), which avoids the extra timespec64
> conversion as a bonus.
> 
> No functional change intended.
> 
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Tested-by: Arthur Kiyanovski <akiyano@amazon.com>
> Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
> Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>

Acked-by: Rodolfo Giometti <giometti@enneenne.com>

> ---
>   drivers/pps/generators/pps_gen-dummy.c |    6 +-----
>   drivers/pps/generators/pps_gen_tio.c   |    6 +-----
>   2 files changed, 2 insertions(+), 10 deletions(-)
> --- a/drivers/pps/generators/pps_gen-dummy.c
> +++ b/drivers/pps/generators/pps_gen-dummy.c
> @@ -39,11 +39,7 @@ static void pps_gen_ktimer_event(struct
>   static int pps_gen_dummy_get_time(struct pps_gen_device *pps_gen,
>   					struct timespec64 *time)
>   {
> -	struct system_time_snapshot snap;
> -
> -	ktime_get_snapshot(&snap);
> -	*time = ktime_to_timespec64(snap.real);
> -
> +	ktime_get_real_ts64(time);
>   	return 0;
>   }
>   
> --- a/drivers/pps/generators/pps_gen_

From: Thomas Gleixner <tglx@kernel.org>

There is no reason to use the more complex ktime_get_snapshot() for
retrieving CLOCK_REALTIME.

Just use ktime_get_real_ts64(), which avoids the extra timespec64
conversion as a bonus.

No functional change intended.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: Arthur Kiyanovski <akiyano@amazon.com>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
---
  drivers/pps/generators/pps_gen-dummy.c |    6 +-----
  drivers/pps/generators/pps_gen_tio.c   |    6 +-----
  2 files changed, 2 insertions(+), 10 deletions(-)
--- a/drivers/pps/generators/pps_gen-dummy.c
+++ b/drivers/pps/generators/pps_gen-dummy.c
@@ -39,11 +39,7 @@ static void pps_gen_ktimer_event(struct
  static int pps_gen_dummy_get_time(struct pps_gen_device *pps_gen,
  					struct timespec64 *time)
  {
-	struct system_time_snapshot snap;
-
-	ktime_get_snapshot(&snap);
-	*time = ktime_to_timespec64(snap.real);
-
+	ktime_get_real_ts64(time);
  	return 0;
  }

--- a/drivers/pps/generators/pps_gen_tio.c
+++ b/drivers/pps/generators/pps_gen_tio.c
@@ -189,11 +189,7 @@ static int pps_tio_gen_enable(struct pps
  static int pps_tio_get_time(struct pps_gen_device *pps_gen,
  			    struct timespec64 *time)
  {
-	struct system_time_snapshot snap;
-
-	ktime_get_snapshot(&snap);
-	*time = ktime_to_timespec64(snap.real);
-
+	ktime_get_real_ts64(time);
  	return 0;
  }


tio.c
> +++ b/drivers/pps/generators/pps_gen_tio.c
> @@ -189,11 +189,7 @@ static int pps_tio_gen_enable(struct pps
>   static int pps_tio_get_time(struct pps_gen_device *pps_gen,
>   			    struct timespec64 *time)
>   {
> -	struct system_time_snapshot snap;
> -
> -	ktime_get_snapshot(&snap);
> -	*time = ktime_to_timespec64(snap.real);
> -
> +	ktime_get_real_ts64(time);
>   	return 0;
>   }
>   
> 


-- 
GNU/Linux Solutions                  e-mail: giometti@enneenne.com
Linux Device Driver                          giometti@linux.it
Embedded Systems                     phone:  +39 349 2432127
UNIX programming

^ permalink raw reply

* [wireless-next:main] BUILD SUCCESS e7d6bd24e883bf7c328d73c99bf6bcde19bf5e61
From: kernel test robot @ 2026-05-30  3:27 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: Johannes Berg, linux-wireless

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
branch HEAD: e7d6bd24e883bf7c328d73c99bf6bcde19bf5e61  Merge tag 'wireless-next-2026-05-28' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next

elapsed time: 1631m

configs tested: 135
configs skipped: 78

The following configs have been built successfully.
More configs may be tested in the coming days.

tested configs:
alpha                             allnoconfig    gcc-15.2.0
alpha                            allyesconfig    gcc-15.2.0
alpha                               defconfig    gcc-15.2.0
arc                               allnoconfig    gcc-15.2.0
arc                                 defconfig    gcc-15.2.0
arc                   randconfig-001-20260530    gcc-14.3.0
arc                   randconfig-002-20260530    gcc-14.3.0
arm                               allnoconfig    clang-23
arm                               allnoconfig    gcc-15.2.0
arm                              allyesconfig    gcc-15.2.0
arm                                 defconfig    gcc-15.2.0
arm                   randconfig-001-20260530    gcc-13.4.0
arm                   randconfig-001-20260530    gcc-14.3.0
arm                   randconfig-002-20260530    gcc-11.5.0
arm                   randconfig-002-20260530    gcc-14.3.0
arm                   randconfig-003-20260530    clang-23
arm                   randconfig-003-20260530    gcc-14.3.0
arm                   randconfig-004-20260530    clang-23
arm                   randconfig-004-20260530    gcc-14.3.0
arm                             rpc_defconfig    clang-18
arm64                            allmodconfig    clang-19
arm64                             allnoconfig    gcc-15.2.0
arm64                               defconfig    gcc-15.2.0
csky                              allnoconfig    gcc-15.2.0
csky                                defconfig    gcc-15.2.0
hexagon                          allmodconfig    gcc-15.2.0
hexagon                           allnoconfig    gcc-15.2.0
hexagon                             defconfig    gcc-15.2.0
hexagon               randconfig-001-20260530    clang-23
hexagon               randconfig-002-20260530    clang-23
i386                             allmodconfig    clang-20
i386                              allnoconfig    gcc-14
i386                              allnoconfig    gcc-15.2.0
i386                             allyesconfig    clang-20
i386                                defconfig    gcc-15.2.0
i386                  randconfig-001-20260530    clang-20
i386                  randconfig-002-20260530    clang-20
i386                  randconfig-003-20260530    clang-20
i386                  randconfig-004-20260530    clang-20
i386                  randconfig-005-20260530    clang-20
i386                  randconfig-006-20260530    clang-20
i386                  randconfig-007-20260530    clang-20
loongarch                         allnoconfig    gcc-15.2.0
loongarch                           defconfig    clang-19
loongarch             randconfig-001-20260530    clang-23
loongarch             randconfig-002-20260530    clang-23
m68k                              allnoconfig    gcc-15.2.0
m68k                                defconfig    clang-19
microblaze                        allnoconfig    gcc-15.2.0
microblaze                          defconfig    clang-19
mips                             allmodconfig    gcc-15.2.0
mips                              allnoconfig    gcc-15.2.0
nios2                            allmodconfig    clang-23
nios2                             allnoconfig    clang-23
nios2                               defconfig    clang-19
nios2                 randconfig-001-20260530    clang-23
nios2                 randconfig-002-20260530    clang-23
openrisc                         allmodconfig    clang-23
openrisc                          allnoconfig    clang-23
parisc                           allmodconfig    gcc-15.2.0
parisc                            allnoconfig    clang-23
parisc                           allyesconfig    clang-19
parisc                randconfig-001-20260530    gcc-8.5.0
parisc                randconfig-002-20260530    gcc-8.5.0
parisc64                            defconfig    clang-19
powerpc                          allmodconfig    gcc-15.2.0
powerpc                           allnoconfig    clang-23
powerpc               randconfig-001-20260530    gcc-8.5.0
powerpc               randconfig-002-20260530    gcc-8.5.0
powerpc64             randconfig-001-20260530    gcc-8.5.0
powerpc64             randconfig-002-20260530    gcc-8.5.0
riscv                             allnoconfig    clang-23
riscv                 randconfig-001-20260530    gcc-12.5.0
riscv                 randconfig-002-20260530    gcc-12.5.0
s390                             allmodconfig    clang-19
s390                              allnoconfig    clang-23
s390                             allyesconfig    gcc-15.2.0
s390                  randconfig-001-20260530    gcc-12.5.0
s390                  randconfig-002-20260530    gcc-12.5.0
sh                               allmodconfig    gcc-15.2.0
sh                                allnoconfig    clang-23
sh                               allyesconfig    clang-19
sh                                  defconfig    gcc-14
sh                    randconfig-001-20260530    gcc-12.5.0
sh                    randconfig-002-20260530    gcc-12.5.0
sparc                             allnoconfig    clang-23
sparc                 randconfig-001-20260530    gcc-9.5.0
sparc                 randconfig-002-20260530    gcc-9.5.0
sparc64                          allmodconfig    clang-23
sparc64                             defconfig    gcc-14
sparc64               randconfig-001-20260530    gcc-9.5.0
sparc64               randconfig-002-20260530    gcc-9.5.0
um                               allmodconfig    clang-19
um                                allnoconfig    clang-23
um                               allyesconfig    gcc-15.2.0
um                                  defconfig    gcc-14
um                             i386_defconfig    gcc-14
um                    randconfig-001-20260530    gcc-9.5.0
um                    randconfig-002-20260530    gcc-9.5.0
um                           x86_64_defconfig    gcc-14
x86_64                           allmodconfig    clang-20
x86_64                            allnoconfig    clang-20
x86_64                            allnoconfig    clang-23
x86_64                           allyesconfig    clang-20
x86_64      buildonly-randconfig-001-20260530    gcc-14
x86_64      buildonly-randconfig-002-20260530    gcc-14
x86_64      buildonly-randconfig-003-20260530    gcc-14
x86_64      buildonly-randconfig-004-20260530    gcc-14
x86_64      buildonly-randconfig-005-20260530    gcc-14
x86_64      buildonly-randconfig-006-20260530    gcc-14
x86_64                              defconfig    gcc-14
x86_64                                  kexec    clang-20
x86_64                randconfig-011-20260530    gcc-14
x86_64                randconfig-012-20260530    gcc-14
x86_64                randconfig-013-20260530    gcc-14
x86_64                randconfig-014-20260530    gcc-14
x86_64                randconfig-015-20260530    gcc-14
x86_64                randconfig-016-20260530    gcc-14
x86_64                randconfig-071-20260530    gcc-14
x86_64                randconfig-072-20260530    gcc-14
x86_64                randconfig-073-20260530    gcc-14
x86_64                randconfig-074-20260530    gcc-14
x86_64                randconfig-075-20260530    gcc-14
x86_64                randconfig-076-20260530    gcc-14
x86_64                               rhel-9.4    clang-20
x86_64                           rhel-9.4-bpf    gcc-14
x86_64                          rhel-9.4-func    clang-20
x86_64                    rhel-9.4-kselftests    clang-20
x86_64                         rhel-9.4-kunit    gcc-14
x86_64                           rhel-9.4-ltp    gcc-14
x86_64                          rhel-9.4-rust    clang-20
xtensa                            allnoconfig    clang-23
xtensa                           allyesconfig    clang-23
xtensa                randconfig-001-20260530    gcc-9.5.0
xtensa                randconfig-002-20260530    gcc-9.5.0

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

^ permalink raw reply

* [PATCH v2] wifi: mt76: mt7915: add thermal zone device registration
From: Ryan Leung @ 2026-05-30  3:13 UTC (permalink / raw)
  To: linux-wireless
  Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
	Sean Wang, Ryan Leung

Register the mt7915 phy as a thermal zone sensor using
devm_thermal_of_zone_register() so that device tree thermal-zones
nodes can reference the WiFi chip as a temperature source. This
allows the kernel thermal governor to control external cooling
devices such as PWM fans based on WiFi chip temperature.

Registration is non-fatal: -ENODEV is returned when no
thermal-sensors DT property references this device, which is the
expected case on platforms without a thermal zone configured.

Signed-off-by: Ryan Leung <untilscour@protonmail.com>
---

v2:
- Use phy->mt76->band_idx instead of hardcoded 0 as sensor ID

---
 .../net/wireless/mediatek/mt76/mt7915/init.c  | 30 +++++++++++++++++++
 .../wireless/mediatek/mt76/mt7915/mt7915.h    |  1 +
 2 files changed, 31 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index 250c2d2479b0..6568d7b6bc0a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -177,6 +177,25 @@ static const struct thermal_cooling_device_ops mt7915_thermal_ops = {
 	.set_cur_state = mt7915_thermal_set_cur_throttle_state,
 };
 
+static int mt7915_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+	struct mt7915_phy *phy = thermal_zone_device_priv(tz);
+	int val;
+
+	mutex_lock(&phy->dev->mt76.mutex);
+	val = mt7915_mcu_get_temperature(phy);
+	mutex_unlock(&phy->dev->mt76.mutex);
+	if (val < 0)
+		return val;
+
+	*temp = val * 1000;
+	return 0;
+}
+
+static const struct thermal_zone_device_ops mt7915_tz_ops = {
+	.get_temp = mt7915_thermal_get_temp,
+};
+
 static void mt7915_unregister_thermal(struct mt7915_phy *phy)
 {
 	struct wiphy *wiphy = phy->mt76->hw->wiphy;
@@ -213,6 +232,17 @@ static int mt7915_thermal_init(struct mt7915_phy *phy)
 	phy->throttle_temp[MT7915_CRIT_TEMP_IDX] = MT7915_CRIT_TEMP;
 	phy->throttle_temp[MT7915_MAX_TEMP_IDX] = MT7915_MAX_TEMP;
 
+	phy->tzone = devm_thermal_of_zone_register(phy->dev->mt76.dev,
+						   phy->mt76->band_idx, phy,
+						   &mt7915_tz_ops);
+	if (IS_ERR(phy->tzone)) {
+		if (PTR_ERR(phy->tzone) != -ENODEV)
+			dev_warn(phy->dev->mt76.dev,
+				 "failed to register thermal zone: %ld\n",
+				 PTR_ERR(phy->tzone));
+		phy->tzone = NULL;
+	}
+
 	if (!IS_REACHABLE(CONFIG_HWMON))
 		return 0;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index bf1d915a3ca2..92e0f9f0169c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -205,6 +205,7 @@ struct mt7915_phy {
 
 	struct ieee80211_vif *monitor_vif;
 
+	struct thermal_zone_device *tzone;
 	struct thermal_cooling_device *cdev;
 	u8 cdev_state;
 	u8 throttle_state;
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH v2 2/6] bus: mhi: Drop controller runtime PM callback indirection
From: Jeff Johnson @ 2026-05-30  0:01 UTC (permalink / raw)
  To: Krishna Chaitanya Chundru, Manivannan Sadhasivam, Jeff Hugo,
	Carl Vanderlip, Oded Gabbay, Jeff Johnson, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Loic Poulain, Sergey Ryazanov, Johannes Berg
  Cc: mhi, linux-arm-msm, linux-kernel, dri-devel, linux-wireless,
	ath11k, ath12k, netdev, mayank.rana, quic_vbadigan,
	vivek.pernamitta, Konrad Dybcio
In-Reply-To: <20260522-mhi_runtimepm-v2-2-fbebf41a82bb@oss.qualcomm.com>

On 5/22/2026 3:00 AM, Krishna Chaitanya Chundru wrote:
> The MHI controller interface exposes runtime_get and runtime_put callbacks
> to abstract runtime PM handling from the MHI core. This indirection is
> unnecessary since the MHI core can directly use the generic pm_runtime_*
> APIs, and the existing implementations are either no-ops or trivial
> wrappers around those same APIs.
> 
> Remove the runtime_get and runtime_put function pointers from struct
> mhi_controller and update all users in the MHI host stack to call the
> standard runtime PM helpers directly.
> 
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---

>  drivers/net/wireless/ath/ath11k/mhi.c | 10 ----------
>  drivers/net/wireless/ath/ath12k/mhi.c | 11 -----------

Acked-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>


^ permalink raw reply

* [PATCH v9 9/9] Add KUnit test for ieee80211_mesh_perr_size_ok
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260529230952.124754-1-masashi.honma@gmail.com>

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 net/mac80211/tests/elems.c | 124 +++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c
index b96424d5d025..4b13a5aad875 100644
--- a/net/mac80211/tests/elems.c
+++ b/net/mac80211/tests/elems.c
@@ -137,6 +137,109 @@ static const struct mesh_prep_parse_test_case {
 
 KUNIT_ARRAY_PARAM_DESC(mesh_prep_parse, mesh_prep_parse_cases, desc);
 
+static const struct mesh_perr_parse_test_case {
+	const char *desc;
+	u8 len;
+	u8 number_of_dst;
+	int ae_enabled_idx;
+	bool result;
+} mesh_perr_parse_cases[] = {
+	{
+		.desc = "shorter than header",
+		.len = 1,
+		.number_of_dst = 1,
+		.ae_enabled_idx = -1,
+		.result = false,
+	},
+	{
+		.desc = "number_of_dst is 0",
+		.len = 2,
+		.number_of_dst = 0,
+		.ae_enabled_idx = -1,
+		.result = true,
+	},
+	{
+		.desc = "number_of_dst is 20",
+		.len = 255,
+		.number_of_dst = 20,
+		.ae_enabled_idx = -1,
+		.result = false,
+	},
+	{
+		.desc = "number_of_dst is 1, non AE, short",
+		.len = 14,
+		.number_of_dst = 1,
+		.ae_enabled_idx = -1,
+		.result = false,
+	},
+	{
+		.desc = "number_of_dst is 1, non AE",
+		.len = 15,
+		.number_of_dst = 1,
+		.ae_enabled_idx = -1,
+		.result = true,
+	},
+	{
+		.desc = "number_of_dst is 1, non AE, extra short dst header",
+		.len = 25,
+		.number_of_dst = 1,
+		.ae_enabled_idx = -1,
+		.result = false,
+	},
+	{
+		.desc = "number_of_dst is 1, non AE, extra dst header",
+		.len = 26,
+		.number_of_dst = 1,
+		.ae_enabled_idx = -1,
+		.result = false,
+	},
+	{
+		.desc = "number_of_dst is 1, AE, short",
+		.len = 20,
+		.number_of_dst = 1,
+		.ae_enabled_idx = 0,
+		.result = false,
+	},
+	{
+		.desc = "number_of_dst is 1, AE",
+		.len = 21,
+		.number_of_dst = 1,
+		.ae_enabled_idx = 0,
+		.result = true,
+	},
+	{
+		.desc = "number_of_dst is 19, non AE, short",
+		.len = 2 + 13 * 19 - 1,
+		.number_of_dst = 19,
+		.ae_enabled_idx = -1,
+		.result = false,
+	},
+	{
+		.desc = "number_of_dst is 19, non AE",
+		.len = 2 + 13 * 19,
+		.number_of_dst = 19,
+		.ae_enabled_idx = -1,
+		.result = true,
+	},
+	{
+		.desc = "number_of_dst is 19, AE, short",
+		.len = 2 + 13 * 19 + 6 - 1,
+		.number_of_dst = 19,
+		.ae_enabled_idx = 18,
+		.result = false,
+	},
+	{
+		.desc = "number_of_dst is 19, AE",
+		.len = 2 + 13 * 19 + 6,
+		.number_of_dst = 19,
+		.ae_enabled_idx = 18,
+		.result = true,
+	},
+};
+
+KUNIT_ARRAY_PARAM_DESC(mesh_perr_parse, mesh_perr_parse_cases, desc);
+
+
 static void mle_defrag(struct kunit *test)
 {
 	struct ieee80211_elems_parse_params parse_params = {
@@ -247,10 +350,31 @@ static void mesh_prep_parse(struct kunit *test)
 			params->result);
 }
 
+static void mesh_perr_parse(struct kunit *test)
+{
+	const struct mesh_perr_parse_test_case *params = test->param_value;
+	u8 data[256] = {};
+	struct ieee80211_mesh_hwmp_perr *perr = (void *)data;
+
+	perr->number_of_dst = params->number_of_dst;
+	if (params->ae_enabled_idx > -1) {
+		struct ieee80211_mesh_hwmp_perr_dst *dst =
+			ieee80211_mesh_hwmp_perr_get_dst(
+				data, params->ae_enabled_idx);
+
+		dst->flags = AE_F;
+	}
+
+	KUNIT_EXPECT_EQ(test,
+			ieee80211_mesh_perr_size_ok(data, params->len),
+			params->result);
+}
+
 static struct kunit_case element_parsing_test_cases[] = {
 	KUNIT_CASE(mle_defrag),
 	KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params),
 	KUNIT_CASE_PARAM(mesh_prep_parse, mesh_prep_parse_gen_params),
+	KUNIT_CASE_PARAM(mesh_perr_parse, mesh_perr_parse_gen_params),
 	{}
 };
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v9 8/9] Add KUnit test for ieee80211_mesh_prep_size_ok
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260529230952.124754-1-masashi.honma@gmail.com>

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 net/mac80211/tests/elems.c | 53 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c
index 576ba746a526..b96424d5d025 100644
--- a/net/mac80211/tests/elems.c
+++ b/net/mac80211/tests/elems.c
@@ -97,6 +97,46 @@ static const struct mesh_preq_parse_test_case {
 
 KUNIT_ARRAY_PARAM_DESC(mesh_preq_parse, mesh_preq_parse_cases, desc);
 
+static const struct mesh_prep_parse_test_case {
+	const char *desc;
+	u8 len;
+	bool ae_enabled;
+	bool result;
+} mesh_prep_parse_cases[] = {
+	{
+		.desc = "shorter than header",
+		.len = 12,
+		.ae_enabled = false,
+		.result = false,
+	},
+	{
+		.desc = "non AE short",
+		.len = 30,
+		.ae_enabled = false,
+		.result = false,
+	},
+	{
+		.desc = "non AE",
+		.len = 31,
+		.ae_enabled = false,
+		.result = true,
+	},
+	{
+		.desc = "AE short",
+		.len = 36,
+		.ae_enabled = true,
+		.result = false,
+	},
+	{
+		.desc = "AE",
+		.len = 37,
+		.ae_enabled = true,
+		.result = true,
+	},
+};
+
+KUNIT_ARRAY_PARAM_DESC(mesh_prep_parse, mesh_prep_parse_cases, desc);
+
 static void mle_defrag(struct kunit *test)
 {
 	struct ieee80211_elems_parse_params parse_params = {
@@ -195,9 +235,22 @@ static void mesh_preq_parse(struct kunit *test)
 			params->result);
 }
 
+static void mesh_prep_parse(struct kunit *test)
+{
+	const struct mesh_prep_parse_test_case *params = test->param_value;
+	u8 data[64] = {};
+	struct ieee80211_mesh_hwmp_prep_top *top = (void *)data;
+	top->flags = params->ae_enabled ? AE_F : 0;
+
+	KUNIT_EXPECT_EQ(test,
+			ieee80211_mesh_prep_size_ok(data, params->len),
+			params->result);
+}
+
 static struct kunit_case element_parsing_test_cases[] = {
 	KUNIT_CASE(mle_defrag),
 	KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params),
+	KUNIT_CASE_PARAM(mesh_prep_parse, mesh_prep_parse_gen_params),
 	{}
 };
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v9 7/9] Add KUnit test for ieee80211_mesh_preq_size_ok
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260529230952.124754-1-masashi.honma@gmail.com>

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 net/mac80211/tests/elems.c | 105 +++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c
index 1039794a0183..576ba746a526 100644
--- a/net/mac80211/tests/elems.c
+++ b/net/mac80211/tests/elems.c
@@ -9,6 +9,94 @@
 
 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
 
+static const struct mesh_preq_parse_test_case {
+	const char *desc;
+	u8 len;
+	bool ae_enabled;
+	u8 target_count;
+	bool result;
+} mesh_preq_parse_cases[] = {
+	{
+		.desc = "shorter than header",
+		.len = 16,
+		.ae_enabled = false,
+		.target_count = 1,
+		.result = false,
+	},
+	{
+		.desc = "too short non AE, target count is not included",
+		.len = 29,
+		.ae_enabled = false,
+		.target_count = 1,
+		.result = false,
+	},
+	{
+		.desc = "too short non AE, target count is 1",
+		.len = 36,
+		.ae_enabled = false,
+		.target_count = 1,
+		.result = false,
+	},
+	{
+		.desc = "too short AE, target count is not included",
+		.len = 35,
+		.ae_enabled = true,
+		.target_count = 1,
+		.result = false,
+	},
+	{
+		.desc = "too short AE, target count is 1",
+		.len = 42,
+		.ae_enabled = true,
+		.target_count = 1,
+		.result = false,
+	},
+	{
+		.desc = "target count is zero",
+		.len = 26,
+		.ae_enabled = false,
+		.target_count = 0,
+		.result = false,
+	},
+	{
+		.desc = "target count is 21",
+		.len = 255,
+		.ae_enabled = false,
+		.target_count = 21,
+		.result = false,
+	},
+	{
+		.desc = "non AE, target count is 1",
+		.len = 37,
+		.ae_enabled = false,
+		.target_count = 1,
+		.result = true,
+	},
+	{
+		.desc = "non AE, target count is 20",
+		.len = 246,
+		.ae_enabled = false,
+		.target_count = 20,
+		.result = true,
+	},
+	{
+		.desc = "AE, target count is 1",
+		.len = 43,
+		.ae_enabled = true,
+		.target_count = 1,
+		.result = true,
+	},
+	{
+		.desc = "AE, target count is 20",
+		.len = 252,
+		.ae_enabled = true,
+		.target_count = 20,
+		.result = true,
+	},
+};
+
+KUNIT_ARRAY_PARAM_DESC(mesh_preq_parse, mesh_preq_parse_cases, desc);
+
 static void mle_defrag(struct kunit *test)
 {
 	struct ieee80211_elems_parse_params parse_params = {
@@ -91,8 +179,25 @@ static void mle_defrag(struct kunit *test)
 	kfree_skb(skb);
 }
 
+static void mesh_preq_parse(struct kunit *test)
+{
+	const struct mesh_preq_parse_test_case *params = test->param_value;
+	u8 data[64] = {};
+	struct ieee80211_mesh_hwmp_preq_top *top = (void *)data;
+	struct ieee80211_mesh_hwmp_preq_bottom *bottom;
+
+	top->flags = params->ae_enabled ? AE_F : 0;
+	bottom = ieee80211_mesh_hwmp_preq_get_bottom(data);
+	bottom->target_count = params->target_count;
+
+	KUNIT_EXPECT_EQ(test,
+			ieee80211_mesh_preq_size_ok(data, params->len),
+			params->result);
+}
+
 static struct kunit_case element_parsing_test_cases[] = {
 	KUNIT_CASE(mle_defrag),
+	KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params),
 	{}
 };
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v9 6/9] wifi: mac80211: Fix PERR frame processing
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260529230952.124754-1-masashi.honma@gmail.com>

There are no issues with the PERR processing itself; however, to maintain
consistency with the previous PREQ/PREP code modifications, I will create a
new mesh_path_parse_error_frame() function to separately implement the
frame format validation and the "not supported" check.

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 36 ++++++++++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 18 +++++++++++++++--
 net/mac80211/parse.c           |  9 +++++++--
 3 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 482ac0c6d759..7eb15834531c 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -403,4 +403,40 @@ static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen)
 	return elen == needed;
 }
 
+/* IEEE Std 802.11-2016 9.4.2.115 PERR element */
+static inline bool ieee80211_mesh_perr_size_ok(const u8 *pos, u8 elen)
+{
+	struct ieee80211_mesh_hwmp_perr *perr_elem = (void *)pos;
+	const u8 *start = pos;
+	u8 number_of_dst;
+	int needed;
+	int i;
+
+	needed = sizeof(struct ieee80211_mesh_hwmp_perr);
+
+	/* Check if the element contains number of dst */
+	if (elen < needed)
+		return false;
+
+	pos += sizeof(struct ieee80211_mesh_hwmp_perr);
+	number_of_dst = perr_elem->number_of_dst;
+
+	for (i = 0; i < number_of_dst; i++) {
+		struct ieee80211_mesh_hwmp_perr_dst *dst = (void *)pos;
+		u8 dst_len = sizeof(struct ieee80211_mesh_hwmp_perr_dst);
+
+		/* Check if the element contains flags */
+		if (elen < pos - start + dst_len)
+			return false;
+
+		dst_len += ((dst->flags & AE_F) ? ETH_ALEN : 0)
+			  /* Destination External Address */ +
+			  2 /* Reason Code */;
+		needed += dst_len;
+		pos += dst_len;
+	}
+
+	return elen == needed;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index f07e57d5568a..84903737271d 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -957,9 +957,23 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 						path_metric);
 	}
 	if (elems->perr) {
-		if (elems->perr_len != 15)
-			/* Right now we support only one destination per PERR */
+		struct ieee80211_mesh_hwmp_perr *perr_elem =
+			(struct ieee80211_mesh_hwmp_perr *)elems->perr;
+		int i;
+
+		/* Right now we support only one destination per PERR */
+		if (perr_elem->number_of_dst != 1)
 			goto free;
+
+		/* Right now we do not support AE (Address Extension) */
+		for (i = 0; i < perr_elem->number_of_dst; i++) {
+			struct ieee80211_mesh_hwmp_perr_dst *dst =
+				ieee80211_mesh_hwmp_perr_get_dst(elems->perr, i);
+
+			if (dst->flags & AE_F)
+				goto free;
+		}
+
 		hwmp_perr_frame_process(sdata, mgmt, elems->perr);
 	}
 	if (elems->rann)
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 97508b141b8c..c44e81a2f80d 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -581,8 +581,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 			}
 			break;
 		case WLAN_EID_PERR:
-			elems->perr = pos;
-			elems->perr_len = elen;
+			if (ieee80211_mesh_perr_size_ok(pos, elen)) {
+				elems->perr = pos;
+				elems->perr_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_RANN:
 			if (elen >= sizeof(struct ieee80211_rann_ie))
-- 
2.43.0


^ permalink raw reply related

* [PATCH v9 5/9] wifi: mac80211: Fix overread in PREP frame processing
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260529230952.124754-1-masashi.honma@gmail.com>

When the AF flag is enabled, hwmp_prep_frame_process() overreads orig_addr
by 2 bytes. Since this occurs within the socket buffer, it does not read
across memory boundaries and therefore poses no security risk; however, we
will fix it as a precaution.

In this fix, a new function mesh_path_parse_reply_frame() is established to
separate the implementation of frame format validation and the check for
unsupported features. This is intended to facilitate future work when
implementing the currently unsupported parts.

Assisted-by: Claude:Sonnet 4.6
Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 16 ++++++++++++++++
 net/mac80211/mesh_hwmp.c       |  4 ++--
 net/mac80211/parse.c           |  9 +++++++--
 3 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 8fbd31d9538d..482ac0c6d759 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -387,4 +387,20 @@ static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
 	return elen == needed;
 }
 
+/* IEEE Std 802.11-2016 9.4.2.114 PREP element */
+static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen)
+{
+	u8 needed;
+
+	/* Check if the element contains flags */
+	needed = sizeof(struct ieee80211_mesh_hwmp_prep_top);
+	if (elen < needed)
+		return false;
+
+	needed += (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0)
+		 /* Target External Address */ +
+		 sizeof(struct ieee80211_mesh_hwmp_prep_bottom);
+	return elen == needed;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index ef6eff52f32a..f07e57d5568a 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -947,8 +947,8 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 						path_metric);
 	}
 	if (elems->prep) {
-		if (elems->prep_len != 31)
-			/* Right now we support no AE */
+		/* Right now we do not support AE (Address Extension) */
+		if (ieee80211_mesh_preq_prep_ae_enabled(elems->prep))
 			goto free;
 		path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep,
 						  MPATH_PREP);
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 3d441ff9593d..97508b141b8c 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -572,8 +572,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 			}
 			break;
 		case WLAN_EID_PREP:
-			elems->prep = pos;
-			elems->prep_len = elen;
+			if (ieee80211_mesh_prep_size_ok(pos, elen)) {
+				elems->prep = pos;
+				elems->prep_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_PERR:
 			elems->perr = pos;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v9 4/9] wifi: mac80211: Fix overread in PREQ frame processing
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260529230952.124754-1-masashi.honma@gmail.com>

When the AF flag is enabled, hwmp_preq_frame_process() overreads
target_addr by 2 bytes. Since this occurs within the socket buffer, it does
not read across memory boundaries and therefore poses no security risk;
however, we will fix it as a precaution.

In this fix, a new function mesh_path_parse_request_frame() is established
to separate the implementation of frame format validation and the check for
unsupported features. This is intended to facilitate future work when
implementing the currently unsupported parts.

Assisted-by: Claude:Sonnet 4.6
Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 29 +++++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 12 ++++++++++--
 net/mac80211/parse.c           |  9 +++++++--
 3 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index f709263c310b..8fbd31d9538d 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -358,4 +358,33 @@ ieee80211_mesh_hwmp_perr_get_rcode(const u8 *ie, u8 dst_idx)
 		(dst->flags & AE_F) ? ETH_ALEN : 0]);
 }
 
+/* IEEE Std 802.11-2016 9.4.2.113 PREQ element */
+static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
+{
+	struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+		ieee80211_mesh_hwmp_preq_get_bottom(pos);
+	u8 target_count;
+	int needed;
+
+	/* Check if the element contains flags */
+	needed = sizeof(struct ieee80211_mesh_hwmp_preq_top);
+	if (elen < needed)
+		return false;
+
+	/* Check if the element contains target_count */
+	needed += (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0)
+		 /* Originator External Address */ +
+		 sizeof(struct ieee80211_mesh_hwmp_preq_bottom);
+	if (elen < needed)
+		return false;
+
+	target_count = preq_elem_bottom->target_count;
+	/* IEEE Std 802.11-2016 Table 14-10 to 14-16 */
+	if (target_count < 1)
+		return false;
+
+	needed += target_count * sizeof(struct ieee80211_mesh_hwmp_preq_target);
+	return elen == needed;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 378338778a23..ef6eff52f32a 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -929,9 +929,17 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 		return;
 
 	if (elems->preq) {
-		if (elems->preq_len != 37)
-			/* Right now we support just 1 destination and no AE */
+		struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+			ieee80211_mesh_hwmp_preq_get_bottom(elems->preq);
+
+		/* Right now we do not support AE (Address Extension) */
+		if (ieee80211_mesh_preq_prep_ae_enabled(elems->preq))
 			goto free;
+
+		/* Right now we only support 1 target */
+		if (preq_elem_bottom->target_count != 1)
+			goto free;
+
 		path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq,
 						  MPATH_PREQ);
 		if (path_metric)
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 8b30e361b622..3d441ff9593d 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -563,8 +563,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 				elems->awake_window = (void *)pos;
 			break;
 		case WLAN_EID_PREQ:
-			elems->preq = pos;
-			elems->preq_len = elen;
+			if (ieee80211_mesh_preq_size_ok(pos, elen)) {
+				elems->preq = pos;
+				elems->preq_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_PREP:
 			elems->prep = pos;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v9 3/9] wifi: mac80211: Use struct instead of macro for PERR frame
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260529230952.124754-1-masashi.honma@gmail.com>

The existing PERR_IE_* macros access HWMP PERR frame fields via hardcoded
byte offsets. Each PERR destination entry contains an optional 6-byte AE
(Address Extension) address followed by a reason code, making offset-based
access error-prone.

Introduce typed packed C structs to represent the PERR frame layout:
  - ieee80211_mesh_hwmp_perr: top-level frame containing TTL and
    destination count
  - ieee80211_mesh_hwmp_perr_dst: per-destination entry with optional AE
    address and variable-position reason code

Add ieee80211_mesh_hwmp_perr_get_rcode() to locate the reason code in
each destination entry depending on whether the AE flag is set.

This refactoring makes the PERR processing code consistent with the
struct-based approach adopted for PREQ and PREP in preceding patches.

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 62 ++++++++++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 31 +++--------------
 2 files changed, 67 insertions(+), 26 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 4ce4e47d6d01..f709263c310b 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -71,6 +71,21 @@ struct ieee80211_mesh_hwmp_prep_bottom {
 	__le32 orig_sn;
 } __packed;
 
+struct ieee80211_mesh_hwmp_perr_dst {
+	u8 flags;
+	u8 addr[ETH_ALEN];
+	__le32 sn;
+	/* optional Destination External Address */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_perr {
+	u8 ttl;
+	u8 number_of_dst;
+	/* Destinations */
+	u8 variable[];
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
@@ -296,4 +311,51 @@ ieee80211_mesh_hwmp_prep_get_bottom(const u8 *ie)
 		ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0];
 }
 
+static inline struct ieee80211_mesh_hwmp_perr_dst *
+ieee80211_mesh_hwmp_perr_get_dst(const u8 *ie, u8 dst_idx)
+{
+	struct ieee80211_mesh_hwmp_perr *perr_ie = (void *)ie;
+	struct ieee80211_mesh_hwmp_perr_dst *dst;
+	u8 *pos = perr_ie->variable;
+	int i;
+
+	for (i = 0; i < dst_idx + 1; i++) {
+		dst = (void *)pos;
+		pos += sizeof(struct ieee80211_mesh_hwmp_perr_dst) +
+			  ((dst->flags & AE_F) ? ETH_ALEN : 0)
+			  /* Destination External Address */ +
+			  2 /* Reason Code */;
+	}
+
+	return dst;
+}
+
+static inline u8 *
+ieee80211_mesh_hwmp_perr_get_addr(const u8 *ie, u8 dst_idx)
+{
+	struct ieee80211_mesh_hwmp_perr_dst *dst =
+		ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx);
+
+	return dst->addr;
+}
+
+static inline u32
+ieee80211_mesh_hwmp_perr_get_sn(const u8 *ie, u8 dst_idx)
+{
+	struct ieee80211_mesh_hwmp_perr_dst *dst =
+		ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx);
+
+	return le32_to_cpu(dst->sn);
+}
+
+static inline u16
+ieee80211_mesh_hwmp_perr_get_rcode(const u8 *ie, u8 dst_idx)
+{
+	struct ieee80211_mesh_hwmp_perr_dst *dst =
+		ieee80211_mesh_hwmp_perr_get_dst(ie, dst_idx);
+
+	return get_unaligned_le16(&dst->variable[
+		(dst->flags & AE_F) ? ETH_ALEN : 0]);
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 39b782370df0..378338778a23 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -20,29 +20,7 @@
 
 static void mesh_queue_preq(struct mesh_path *, u8);
 
-static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
-{
-	if (ae)
-		offset += 6;
-	return get_unaligned_le32(preq_elem + offset);
-}
-
-static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
-{
-	if (ae)
-		offset += 6;
-	return get_unaligned_le16(preq_elem + offset);
-}
-
 /* HWMP IE processing macros */
-#define AE_F_SET(x)		(*x & AE_F)
-
-#define PERR_IE_TTL(x)		(*(x))
-#define PERR_IE_TARGET_FLAGS(x)	(*(x + 2))
-#define PERR_IE_TARGET_ADDR(x)	(x + 3)
-#define PERR_IE_TARGET_SN(x)	u32_field_get(x, 9, 0)
-#define PERR_IE_TARGET_RCODE(x)	u16_field_get(x, 13, 0)
-
 #define MSEC_TO_TU(x) (x*1000/1024)
 #define SN_GT(x, y) ((s32)(y - x) < 0)
 #define SN_LT(x, y) ((s32)(x - y) < 0)
@@ -774,6 +752,7 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *perr_elem)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_perr *perr_elem_s = (void *)perr_elem;
 	struct mesh_path *mpath;
 	u8 ttl;
 	const u8 *ta, *target_addr;
@@ -781,15 +760,15 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
 	u16 target_rcode;
 
 	ta = mgmt->sa;
-	ttl = PERR_IE_TTL(perr_elem);
+	ttl = perr_elem_s->ttl;
 	if (ttl <= 1) {
 		ifmsh->mshstats.dropped_frames_ttl++;
 		return;
 	}
 	ttl--;
-	target_addr = PERR_IE_TARGET_ADDR(perr_elem);
-	target_sn = PERR_IE_TARGET_SN(perr_elem);
-	target_rcode = PERR_IE_TARGET_RCODE(perr_elem);
+	target_addr = ieee80211_mesh_hwmp_perr_get_addr(perr_elem, 0);
+	target_sn = ieee80211_mesh_hwmp_perr_get_sn(perr_elem, 0);
+	target_rcode = ieee80211_mesh_hwmp_perr_get_rcode(perr_elem, 0);
 
 	rcu_read_lock();
 	mpath = mesh_path_lookup(sdata, target_addr);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v9 2/9] wifi: mac80211: Use struct instead of macro for PREP frame
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260529230952.124754-1-masashi.honma@gmail.com>

The existing PREP_IE_* macros access HWMP PREP frame fields via hardcoded
byte offsets. When the AE (Address Extension) flag is set, an additional
6 bytes appear mid-frame, making the offset arithmetic error-prone.

Introduce typed packed C structs to represent the PREP frame layout:
  - ieee80211_mesh_hwmp_prep_top: fixed fields before the optional AE
    address
  - ieee80211_mesh_hwmp_prep_bottom: fields after the optional AE address

Add ieee80211_mesh_hwmp_prep_get_bottom() to locate the bottom struct
correctly based on whether the AE flag is set.

This preparatory refactoring is needed to fix a 2-byte overread of
orig_addr in hwmp_prep_frame_process() when AE is enabled, which is
addressed in a subsequent patch.

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 27 ++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 46 ++++++++++++++++------------------
 2 files changed, 49 insertions(+), 24 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index bf4a544aed00..4ce4e47d6d01 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -53,6 +53,24 @@ struct ieee80211_mesh_hwmp_preq_bottom {
 	struct ieee80211_mesh_hwmp_preq_target targets[];
 } __packed;
 
+struct ieee80211_mesh_hwmp_prep_top {
+	u8 flags;
+	u8 hopcount;
+	u8 ttl;
+	u8 target_addr[ETH_ALEN];
+	__le32 target_sn;
+
+	/* optional Target External Address */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_prep_bottom {
+	__le32 lifetime;
+	__le32 metric;
+	u8 orig_addr[ETH_ALEN];
+	__le32 orig_sn;
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
@@ -269,4 +287,13 @@ ieee80211_mesh_hwmp_preq_get_bottom(const u8 *ie)
 		ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0];
 }
 
+static inline struct ieee80211_mesh_hwmp_prep_bottom *
+ieee80211_mesh_hwmp_prep_get_bottom(const u8 *ie)
+{
+	struct ieee80211_mesh_hwmp_prep_top *top = (void *)ie;
+
+	return (void *)&top->variable[
+		ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0];
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 1a6a22b185d9..39b782370df0 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -37,16 +37,6 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 /* HWMP IE processing macros */
 #define AE_F_SET(x)		(*x & AE_F)
 
-#define PREP_IE_FLAGS(x)	(*(x))
-#define PREP_IE_HOPCOUNT(x)	(*(x + 1))
-#define PREP_IE_TTL(x)		(*(x + 2))
-#define PREP_IE_ORIG_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21)
-#define PREP_IE_ORIG_SN(x)	u32_field_get(x, 27, AE_F_SET(x))
-#define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x))
-#define PREP_IE_METRIC(x)	u32_field_get(x, 17, AE_F_SET(x))
-#define PREP_IE_TARGET_ADDR(x)	(x + 3)
-#define PREP_IE_TARGET_SN(x)	u32_field_get(x, 9, 0)
-
 #define PERR_IE_TTL(x)		(*(x))
 #define PERR_IE_TARGET_FLAGS(x)	(*(x + 2))
 #define PERR_IE_TARGET_ADDR(x)	(x + 3)
@@ -419,11 +409,16 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
 		 * so that we can easily use a single function to gather path
 		 * information from both PREQ and PREP frames.
 		 */
-		orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie);
-		orig_sn = PREP_IE_TARGET_SN(hwmp_ie);
-		orig_lifetime = PREP_IE_LIFETIME(hwmp_ie);
-		orig_metric = PREP_IE_METRIC(hwmp_ie);
-		hopcount = PREP_IE_HOPCOUNT(hwmp_ie) + 1;
+		struct ieee80211_mesh_hwmp_prep_top *prep_elem_top =
+			(void *)hwmp_ie;
+		struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom =
+			ieee80211_mesh_hwmp_prep_get_bottom(hwmp_ie);
+
+		orig_addr = prep_elem_top->target_addr;
+		orig_sn = le32_to_cpu(prep_elem_top->target_sn);
+		orig_lifetime = le32_to_cpu(prep_elem_bottom->lifetime);
+		orig_metric = le32_to_cpu(prep_elem_bottom->metric);
+		hopcount = prep_elem_top->hopcount + 1;
 		break;
 	default:
 		rcu_read_unlock();
@@ -714,6 +709,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *prep_elem, u32 metric)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_prep_top *prep_elem_top = (void *)prep_elem;
+	struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom =
+		ieee80211_mesh_hwmp_prep_get_bottom(prep_elem);
 	struct mesh_path *mpath;
 	const u8 *target_addr, *orig_addr;
 	u8 ttl, hopcount, flags;
@@ -721,9 +719,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	u32 target_sn, orig_sn, lifetime;
 
 	mhwmp_dbg(sdata, "received PREP from %pM\n",
-		  PREP_IE_TARGET_ADDR(prep_elem));
+		  prep_elem_top->target_addr);
 
-	orig_addr = PREP_IE_ORIG_ADDR(prep_elem);
+	orig_addr = prep_elem_bottom->orig_addr;
 	if (ether_addr_equal(orig_addr, sdata->vif.addr))
 		/* destination, no forwarding required */
 		return;
@@ -731,7 +729,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	if (!ifmsh->mshcfg.dot11MeshForwarding)
 		return;
 
-	ttl = PREP_IE_TTL(prep_elem);
+	ttl = prep_elem_top->ttl;
 	if (ttl <= 1) {
 		sdata->u.mesh.mshstats.dropped_frames_ttl++;
 		return;
@@ -750,12 +748,12 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN);
 	spin_unlock_bh(&mpath->state_lock);
 	--ttl;
-	flags = PREP_IE_FLAGS(prep_elem);
-	lifetime = PREP_IE_LIFETIME(prep_elem);
-	hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1;
-	target_addr = PREP_IE_TARGET_ADDR(prep_elem);
-	target_sn = PREP_IE_TARGET_SN(prep_elem);
-	orig_sn = PREP_IE_ORIG_SN(prep_elem);
+	flags = prep_elem_top->flags;
+	lifetime = le32_to_cpu(prep_elem_bottom->lifetime);
+	hopcount = prep_elem_top->hopcount + 1;
+	target_addr = prep_elem_top->target_addr;
+	target_sn = le32_to_cpu(prep_elem_top->target_sn);
+	orig_sn = le32_to_cpu(prep_elem_bottom->orig_sn);
 
 	mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, orig_sn, 0,
 			       target_addr, target_sn, next_hop, hopcount,
-- 
2.43.0


^ permalink raw reply related

* [PATCH v9 1/9] wifi: mac80211: Use struct instead of macro for PREQ frame
From: Masashi Honma @ 2026-05-29 23:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma

The existing PREQ_IE_* macros access HWMP PREQ frame fields via hardcoded
byte offsets. When the AE (Address Extension) flag is set, an additional
6 bytes appear mid-frame, and the macros handle this with conditional
arithmetic (e.g., AE_F_SET(x) ? x + N+6 : x + N). This approach
obscures the frame layout and is prone to miscalculation.

Introduce typed packed C structs to represent the PREQ frame layout:
  - ieee80211_mesh_hwmp_preq_top: fixed fields before the optional AE
    address
  - ieee80211_mesh_hwmp_preq_bottom: fields after the optional AE address
  - ieee80211_mesh_hwmp_preq_target: per-target fields

Add ieee80211_mesh_hwmp_preq_get_bottom() to locate the bottom struct
correctly based on whether the AE flag is set.

This preparatory refactoring is needed to fix a 2-byte overread of
target_addr in hwmp_preq_frame_process() when AE is enabled, which is
addressed in a subsequent patch.

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 42 +++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 67 ++++++++++++++++------------------
 2 files changed, 74 insertions(+), 35 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 4b829bcb38b6..bf4a544aed00 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -28,12 +28,40 @@ struct ieee80211s_hdr {
 	u8 eaddr2[ETH_ALEN];
 } __packed __aligned(2);
 
+struct ieee80211_mesh_hwmp_preq_target {
+	u8 flags;
+	u8 addr[ETH_ALEN];
+	__le32 sn;
+} __packed;
+
+struct ieee80211_mesh_hwmp_preq_top {
+	u8 flags;
+	u8 hopcount;
+	u8 ttl;
+	__le32 preq_id;
+	u8 orig_addr[ETH_ALEN];
+	__le32 orig_sn;
+
+	/* optional AE, lifetime, metric, target */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_preq_bottom {
+	__le32 lifetime;
+	__le32 metric;
+	u8 target_count;
+	struct ieee80211_mesh_hwmp_preq_target targets[];
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
 #define MESH_FLAGS_AE		0x3
 #define MESH_FLAGS_PS_DEEP	0x4
 
+/* HWMP IE processing macros */
+#define AE_F			(1<<6)
+
 /**
  * enum ieee80211_preq_flags - mesh PREQ element flags
  *
@@ -227,4 +255,18 @@ enum ieee80211_root_mode_identifier {
 	IEEE80211_PROACTIVE_RANN = 4,
 };
 
+static inline bool ieee80211_mesh_preq_prep_ae_enabled(const u8 *ie)
+{
+	return ie[0] & AE_F;
+}
+
+static inline struct ieee80211_mesh_hwmp_preq_bottom *
+ieee80211_mesh_hwmp_preq_get_bottom(const u8 *ie)
+{
+	struct ieee80211_mesh_hwmp_preq_top *top = (void *)ie;
+
+	return (void *)&top->variable[
+		ieee80211_mesh_preq_prep_ae_enabled(ie) ? ETH_ALEN : 0];
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 9d89ebcce1c1..1a6a22b185d9 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -35,24 +35,11 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 }
 
 /* HWMP IE processing macros */
-#define AE_F			(1<<6)
 #define AE_F_SET(x)		(*x & AE_F)
-#define PREQ_IE_FLAGS(x)	(*(x))
-#define PREQ_IE_HOPCOUNT(x)	(*(x + 1))
-#define PREQ_IE_TTL(x)		(*(x + 2))
-#define PREQ_IE_PREQ_ID(x)	u32_field_get(x, 3, 0)
-#define PREQ_IE_ORIG_ADDR(x)	(x + 7)
-#define PREQ_IE_ORIG_SN(x)	u32_field_get(x, 13, 0)
-#define PREQ_IE_LIFETIME(x)	u32_field_get(x, 17, AE_F_SET(x))
-#define PREQ_IE_METRIC(x) 	u32_field_get(x, 21, AE_F_SET(x))
-#define PREQ_IE_TARGET_F(x)	(*(AE_F_SET(x) ? x + 32 : x + 26))
-#define PREQ_IE_TARGET_ADDR(x) 	(AE_F_SET(x) ? x + 33 : x + 27)
-#define PREQ_IE_TARGET_SN(x) 	u32_field_get(x, 33, AE_F_SET(x))
-
-
-#define PREP_IE_FLAGS(x)	PREQ_IE_FLAGS(x)
-#define PREP_IE_HOPCOUNT(x)	PREQ_IE_HOPCOUNT(x)
-#define PREP_IE_TTL(x)		PREQ_IE_TTL(x)
+
+#define PREP_IE_FLAGS(x)	(*(x))
+#define PREP_IE_HOPCOUNT(x)	(*(x + 1))
+#define PREP_IE_TTL(x)		(*(x + 2))
 #define PREP_IE_ORIG_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21)
 #define PREP_IE_ORIG_SN(x)	u32_field_get(x, 27, AE_F_SET(x))
 #define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x))
@@ -415,11 +402,16 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
 
 	switch (action) {
 	case MPATH_PREQ:
-		orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie);
-		orig_sn = PREQ_IE_ORIG_SN(hwmp_ie);
-		orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie);
-		orig_metric = PREQ_IE_METRIC(hwmp_ie);
-		hopcount = PREQ_IE_HOPCOUNT(hwmp_ie) + 1;
+		struct ieee80211_mesh_hwmp_preq_top *preq_elem_top =
+			(void *)hwmp_ie;
+		struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+			ieee80211_mesh_hwmp_preq_get_bottom(hwmp_ie);
+
+		orig_addr = preq_elem_top->orig_addr;
+		orig_sn = le32_to_cpu(preq_elem_top->orig_sn);
+		orig_lifetime = le32_to_cpu(preq_elem_bottom->lifetime);
+		orig_metric = le32_to_cpu(preq_elem_bottom->metric);
+		hopcount = preq_elem_top->hopcount + 1;
 		break;
 	case MPATH_PREP:
 		/* Originator here refers to the MP that was the target in the
@@ -579,6 +571,11 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *preq_elem, u32 orig_metric)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_preq_top *preq_elem_top = (void *)preq_elem;
+	struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+		ieee80211_mesh_hwmp_preq_get_bottom(preq_elem);
+	struct ieee80211_mesh_hwmp_preq_target *target =
+		preq_elem_bottom->targets;
 	struct mesh_path *mpath = NULL;
 	const u8 *target_addr, *orig_addr;
 	const u8 *da;
@@ -589,13 +586,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 	bool root_is_gate;
 
 	/* Update target SN, if present */
-	target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
-	orig_addr = PREQ_IE_ORIG_ADDR(preq_elem);
-	target_sn = PREQ_IE_TARGET_SN(preq_elem);
-	orig_sn = PREQ_IE_ORIG_SN(preq_elem);
-	target_flags = PREQ_IE_TARGET_F(preq_elem);
+	target_addr = target[0].addr;
+	orig_addr = preq_elem_top->orig_addr;
+	target_sn = le32_to_cpu(target[0].sn);
+	orig_sn = le32_to_cpu(preq_elem_top->orig_sn);
+	target_flags = target[0].flags;
 	/* Proactive PREQ gate announcements */
-	flags = PREQ_IE_FLAGS(preq_elem);
+	flags = preq_elem_top->flags;
 	root_is_gate = !!(flags & RANN_FLAG_IS_GATE);
 
 	mhwmp_dbg(sdata, "received PREQ from %pM\n", orig_addr);
@@ -655,7 +652,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 	}
 
 	if (reply) {
-		lifetime = PREQ_IE_LIFETIME(preq_elem);
+		lifetime = le32_to_cpu(preq_elem_bottom->lifetime);
 		ttl = ifmsh->mshcfg.element_ttl;
 		if (ttl != 0) {
 			mhwmp_dbg(sdata, "replying to the PREQ\n");
@@ -673,22 +670,22 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 		u32 preq_id;
 		u8 hopcount;
 
-		ttl = PREQ_IE_TTL(preq_elem);
-		lifetime = PREQ_IE_LIFETIME(preq_elem);
+		ttl = preq_elem_top->ttl;
+		lifetime = le32_to_cpu(preq_elem_bottom->lifetime);
 		if (ttl <= 1) {
 			ifmsh->mshstats.dropped_frames_ttl++;
 			return;
 		}
 		mhwmp_dbg(sdata, "forwarding the PREQ from %pM\n", orig_addr);
 		--ttl;
-		preq_id = PREQ_IE_PREQ_ID(preq_elem);
-		hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1;
+		preq_id = le32_to_cpu(preq_elem_top->preq_id);
+		hopcount = preq_elem_top->hopcount + 1;
 		da = (mpath && mpath->is_root) ?
 			mpath->rann_snd_addr : broadcast_addr;
 
 		if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) {
-			target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
-			target_sn = PREQ_IE_TARGET_SN(preq_elem);
+			target_addr = target[0].addr;
+			target_sn = le32_to_cpu(target[0].sn);
 		}
 
 		mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v8 6/6] wifi: mac80211: Fix PERR frame processing
From: Masashi Honma @ 2026-05-29 23:07 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <244b27052cbfd1f922132f2f3c5a16d6b25b0450.camel@sipsolutions.net>

Fixed as well.

2026年5月28日(木) 17:12 Johannes Berg <johannes@sipsolutions.net>:
>
> On Fri, 2026-05-22 at 07:58 +0900, Masashi Honma wrote:
> >
> > +/* IEEE Std 802.11-2016 9.4.2.115 PERR element */
> > +static inline bool ieee80211_mesh_perr_size_ok(const u8 *pos, u8 elen)
> > +{
> > +     struct ieee80211_mesh_hwmp_perr *perr_elem = (void *)pos;
> > +     u8 number_of_dst;
> > +     u8 needed;
> > +     const u8 *start;
> > +     int i;
> > +
> > +     start = pos;
> > +     needed = sizeof(struct ieee80211_mesh_hwmp_perr);
> > +     pos += sizeof(struct ieee80211_mesh_hwmp_perr);
> > +
> > +     /* Check if the element contains number of dst */
> > +     if (elen < needed)
> > +             return false;
> > +
> > +     number_of_dst = perr_elem->number_of_dst;
> > +     if (number_of_dst < 1 || number_of_dst > 19)
> > +             return false;
>
> Same here, though I didn't double-check this one; if we just go to 'int'
> or 'unsigned int' for 'needed', it's not necessary to even have this.
>
> > +
> > +     for (i = 0; i < number_of_dst; i++) {
> > +             struct ieee80211_mesh_hwmp_perr_dst *perr_dst =
> > +                     &perr_elem->dsts[i];
> > +             u8 dst_len;
> > +
> > +             /* Check if the element contains flags */
> > +             if (elen < pos - start + 1)
> > +                     return false;
>
> that comment seems a bit misleading. I figured out what you mean, but
> IMHO it'd be more obvious if you wrote it as
>
>         for (...) {
>                 struct _perr_dst *perr_dst;
>                 u8 dst_len;
>
>                 if (elen < pos - start + sizeof(*perr_dst))
>                         return false;
>
> > +             dst_len = sizeof(struct ieee80211_mesh_hwmp_perr_dst) +
> > +                       ((perr_dst->flags & AE_F) ? ETH_ALEN : 0)
> > +                       /* Destination External Address */ +
> > +                       2 /* Reason Code */;
> > +             needed += dst_len;
> > +             pos += dst_len;
>
> and technically that pos+= could be UB, so it should be
>
>                 if (elen < pos - start + dst_len)
>                         return false;
>                 pos += dst_len;
>
> > +             /* Right now we do not support AE (Address Extension) */
> > +             for (i = 0; i < perr_elem->number_of_dst; i++)
> > +                     if (perr_elem->dsts[i].flags & AE_F)
> > +                             goto free;
>
> This code will need to change based on what I commented on patch 3 wrt.
> the variable members, although we only really use [0] and allow for a
> single entry anyway ...
>
> Probably should have an inline that returns struct
> ieee80211_mesh_hwmp_perr_dst based on the index, and then have
> ieee80211_mesh_hwmp_perr_get_rcode() work on that pointer instead of the
> element and index.
>
>
> Thanks for sticking with this, it already looks really nice!
>
> johannes

^ permalink raw reply

* Re: [PATCH v8 4/6] wifi: mac80211: Fix overread in PREQ frame processing
From: Masashi Honma @ 2026-05-29 23:06 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <f268572dbc71a4941523c510349207afb3fbe34c.camel@sipsolutions.net>

Yes, fixed.

2026年5月28日(木) 17:04 Johannes Berg <johannes@sipsolutions.net>:
>
> On Fri, 2026-05-22 at 07:58 +0900, Masashi Honma wrote:
> >
> > +/* IEEE Std 802.11-2016 9.4.2.113 PREQ element */
> > +static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
> > +{
> > +     struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
> > +             ieee80211_mesh_hwmp_preq_get_bottom(pos);
> > +     u8 target_count;
> > +     u8 needed;
> > +
> > +     /* Check if the element contains flags */
> > +     if (elen < 1)
> > +             return false;
> > +
> > +     /* Check if the element contains target_count */
> > +     needed = sizeof(struct ieee80211_mesh_hwmp_preq_top) +
> > +              (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0)
> > +              /* Originator External Address */ +
> > +              sizeof(struct ieee80211_mesh_hwmp_preq_bottom);
> > +     if (elen < needed)
> > +             return false;
> > +
> > +     target_count = preq_elem_bottom->target_count;
> > +     if (target_count < 1 || target_count > 20)
> > +             return false;
>
> While this is correct now, I think it's perhaps too tricky ...
>
> The reason it's correct is that needed starts out with at most
> sizeof(top) + ETH_ALEN + sizeof(bottom) == 17 + 6 + 9 == 32, target
> sizeof is 11, so 20*11+32 == 252 and cannot overflow.
>
> But I think it'd be far simpler to declare "needed" simply as 'int',
> then even with target_count == 255 and whatever else happened before
> cannot overflow, the elen==needed check will promote to int and refuse a
> bad target_count implicitly instead of needing to do so explicitly to
> avoid the integer overflow.
>
> johannes

^ permalink raw reply

* Re: [PATCH v8 3/6] wifi: mac80211: Use struct instead of macro for PERR frame
From: Masashi Honma @ 2026-05-29 23:06 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <8ab957b3c31dec7be844ea9e1ac2fbb2fdcd8278.camel@sipsolutions.net>

Thank you for your reviews!

Ah sorry, I overlooked this point, so I have fixed it. I also added
KUnit, as the code was becoming complex.

2026年5月28日(木) 17:00 Johannes Berg <johannes@sipsolutions.net>:
>
> Hi,
>
> So I was just about to apply this set, but now I'm thinking about this:
>
>
> > +struct ieee80211_mesh_hwmp_perr_dst {
> > +     u8 flags;
> > +     u8 addr[ETH_ALEN];
> > +     __le32 sn;
> > +     /* optional Destination External Address */
> > +     u8 variable[];
> > +} __packed;
>
> This has a variable member, and the presence of the address in
> 'variable' depends on the flags (AE_F set there.)
>
> > +struct ieee80211_mesh_hwmp_perr {
> > +     u8 ttl;
> > +     u8 number_of_dst;
> > +     struct ieee80211_mesh_hwmp_perr_dst dsts[];
> > +} __packed;
>
> However this declares an array of these, and that doesn't really seem
> right. It effectively puts non-variable fields (e.g. dsts[1].ttl) after
> the variable field dsts[0].variable. I _think_ some compilers (versions)
> will also (rightfully) complain about this.
>
> It seems like this should just be
>
> struct ... {
>         u8 ttl;
>         u8 number_of_dst;
>
>         /* list of variably sized struct ieee80211_mesh_hwmp_perr_dst
> */
>         u8 dsts[];
> } __packed;
>
> > +static inline u16
> > +ieee80211_mesh_hwmp_perr_get_rcode(const u8 *ie, u8 dst_idx)
> > +{
> > +     struct ieee80211_mesh_hwmp_perr *perr_ie = (void *)ie;
> > +     struct ieee80211_mesh_hwmp_perr_dst *dst =
> > +             &perr_ie->dsts[dst_idx];
>
> And especially this indexing doesn't seem like it could work - you have
> to walk through all of them to see if each has the AE_F set and skip
> sizeof() + optional ETH_ALEN.
>
> > +
> > +     return get_unaligned_le16(&dst->variable[
> > +             (dst->flags & AE_F) ? ETH_ALEN : 0]);
>
> This looks like the comment above should be
>
>         /* optional Destination External Address, rcode */
>         u8 variable[];
>
> or so?
>
> > +     target_rcode = ieee80211_mesh_hwmp_perr_get_rcode(perr_elem, 0);
>
> but evidently, this doesn't really matter right now if only one
> destination entry is ever read.
>
> Still, please fix that, if only to avoid the compiler warnings I'm
> imagining will happen.
>
> johannes

^ permalink raw reply

* Re: [patch V2 00/25] timekeeping/ptp: Expand snapshot functionality
From: Jacob Keller @ 2026-05-29 20:33 UTC (permalink / raw)
  To: Thomas Gleixner, LKML
  Cc: David Woodhouse, Miroslav Lichvar, John Stultz, Stephen Boyd,
	Anna-Maria Behnsen, Frederic Weisbecker, thomas.weissschuh,
	Arthur Kiyanovski, Rodolfo Giometti, Vincent Donnefort,
	Marc Zyngier, Oliver Upton, kvmarm, Oliver Upton, Richard Cochran,
	netdev, Takashi Iwai, Miri Korenblit, Johannes Berg, Tony Nguyen,
	Saeed Mahameed, Peter Hilber, Michael S. Tsirkin, virtualization,
	linux-wireless, linux-sound, David Woodhouse, Vadim Fedorenko
In-Reply-To: <20260529193435.921555544@kernel.org>

On 5/29/2026 12:59 PM, Thomas Gleixner wrote:
> This is an update to V1 which can be found here:
> 
>    https://lore.kernel.org/lkml/20260526165826.392227559@kernel.org
> 
> PTP wants to grow new snapshot functionality, which provides not only the
> captured CLOCK* values, but also the underlying clocksource counter value.
> 
>    https://lore.kernel.org/20260515164033.6403-1-akiyano@amazon.com
> 
> There was quite some discussion in seemingly related threads how to capture
> these values and how to provide core infrastructure so that driver writers
> have something to work with
> 
>    https://lore.kernel.org/20260514225842.110706-1-hramamurthy@google.com
>    https://lore.kernel.org/20260520135207.37826-1-dwmw2@infradead.org
> 
> This series implements the timekeeping related mechanisms to:
> 
>      1) Capture CLOCK values along with the clocksource counter value for
>      	non-hardware based sampling
> 
>      2) Expanding the hardware cross time stamp mechanism to hand back the
>      	clocksource counter value, which was captured by the device, along
>      	with the related CLOCK values
> 
>      3) Adding AUX clock support to the hardware cross timestamping core
> 
>      4) Add support for derived clocksources to the snapshot mechanism (New
>      	in V2)
> 
> Changes vs. V1:
> 
>   - Fixed the ptp_ocp typo - 0-day, Jakub
> 
>   - Renamed the system_time_snapshot members sys and raw so systime and
>     monoraw to make them less ambigous.
> 
>   - Fixed the error case return values of get_device_system_crosststamp()
> 
>   - Made ktime_snapshot_id() void as there is no point for the return
>     value, which is nowhere checked and cannot be propagated.
>     system_time_snapshot::valid has to be evaluated at the call sites
>     anyway. - Jacob
> 
>   - Picked up the first patch from Davids follow up series, which extends
>     the snapshot mechanism so that derived clocksources (like kvmclock and
>     Hyper-V scaled TSC) can return the actual underlying hardware counter
>     value (TSC for the two examples).
> 
>   - Collected Reviewed/Acked/Tested-by tags
> 
> Delta patch against v1 below.
> 
> The series is based on v7.1-rc2 and also available from git:
> 
>     git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git timekeeping-ptp-extend-v2
> 
> Thanks,

The changes in v2 are great! Appreciate it.

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>

^ permalink raw reply

* [patch V2 25/25] timekeeping: Add clocksource read_snapshot() method and hw_cycles to snapshot
From: Thomas Gleixner @ 2026-05-29 20:01 UTC (permalink / raw)
  To: LKML
  Cc: David Woodhouse, Miroslav Lichvar, John Stultz, Stephen Boyd,
	Anna-Maria Behnsen, Frederic Weisbecker, thomas.weissschuh,
	Arthur Kiyanovski, Rodolfo Giometti, Vincent Donnefort,
	Marc Zyngier, Oliver Upton, kvmarm, Oliver Upton, Richard Cochran,
	netdev, Takashi Iwai, Miri Korenblit, Johannes Berg, Jacob Keller,
	Tony Nguyen, Saeed Mahameed, Peter Hilber, Michael S. Tsirkin,
	virtualization, linux-wireless, linux-sound, David Woodhouse,
	Vadim Fedorenko
In-Reply-To: <20260529193435.921555544@kernel.org>

From: David Woodhouse <dwmw@amazon.co.uk>

Add a read_snapshot() callback to struct clocksource which returns the
derived clocksource value while also providing the underlying hardware
counter reading and the related clocksource ID.

This allows ktime_get_snapshot_id() to populate new hw_cycles and hw_csid
fields in struct system_time_snapshot.

For clocksources that are derived from an underlying counter (e.g., Hyper-V
TSC page scales TSC to 10MHz, kvmclock scales TSC to 1GHz), this provides
atomic access to both the derived value needed for timekeeping
calculations, and the raw hardware counter needed by consumers like KVM's
master clock and the vmclock PTP driver.

[ tglx: Reworked it slightly ]

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Assisted-by: Kiro:claude-opus-4.6-1m
Link: https://patch.msgid.link/20260526230635.136914-1-dwmw2@infradead.org
---
 include/linux/clocksource.h |   24 ++++++++++++++++++++++++
 include/linux/timekeeping.h |    6 ++++++
 kernel/time/timekeeping.c   |   21 ++++++++++++++++++++-
 3 files changed, 50 insertions(+), 1 deletion(-)
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -32,6 +32,21 @@ struct module;
 #include <vdso/clocksource.h>
 
 /**
+ * struct clocksource_hw_snapshot - Snapshot for the underlying hardware counter of derived
+ *				    clocksources like kvmclock or Hyper-V scaled TSC
+ * @hw_cycles:		The hardware counter value
+ * @hw_csid:		Clocksource ID of the hardware counter
+ *
+ * Such clocksources must implement the read_snapshot() callback and fill in the
+ * hardware counter value, the clocksource ID of the hardware counter and derive
+ * the actual clocksource cycles from @hw_cycles to provide an atomic snapshot
+ */
+struct clocksource_hw_snapshot {
+	u64			hw_cycles;
+	enum clocksource_ids	hw_csid;
+};
+
+/**
  * struct clocksource - hardware abstraction for a free running counter
  *	Provides mostly state-free accessors to the underlying hardware.
  *	This is the structure used for system time.
@@ -72,6 +87,14 @@ struct module;
  * @flags:		Flags describing special properties
  * @base:		Hardware abstraction for clock on which a clocksource
  *			is based
+ * @read_snapshot:	Extended @read() function for clocksources such as
+ *			kvmclock or the Hyper-V scaled TSC where the actual
+ *			clocksource value for timekeeping is calculated from an
+ *			underlying hardware counter. Returns the timekeeping
+ *			relevant cycle value and stores the raw value of the
+ *			underlying counter from which it was calculated
+ *			including the clocksource ID of that counter in the
+ *			clocksource hardware snapshot.
  * @enable:		Optional function to enable the clocksource
  * @disable:		Optional function to disable the clocksource
  * @suspend:		Optional suspend function for the clocksource
@@ -113,6 +136,7 @@ struct clocksource {
 	unsigned long		flags;
 	struct clocksource_base *base;
 
+	u64			(*read_snapshot)(struct clocksource *cs, struct clocksource_hw_snapshot *chs);
 	int			(*enable)(struct clocksource *cs);
 	void			(*disable)(struct clocksource *cs);
 	void			(*suspend)(struct clocksource *cs);
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -279,18 +279,24 @@ static inline bool ktime_get_aux_ts64(cl
  * struct system_time_snapshot - Simultaneous time capture of CLOCK_MONOTONIC_RAW,
  *				 a selected CLOCK_* and the clocksource counter value
  * @cycles:		Clocksource counter value to produce the system times
+ * @hw_cycles:		For derived clocksources, the hardware counter value from
+ *			which @cycles was derived
  * @systime:		The system time of the selected CLOCK ID
  * @monoraw:		Monotonic raw system time
  * @cs_id:		Clocksource ID
+ * @hw_csid:		Clocksource ID of the underlying hardware counter for derived
+ *			clocksources which implement the read_snapshot() callback.
  * @clock_was_set_seq:	The sequence number of clock-was-set events
  * @cs_was_changed_seq:	The sequence number of clocksource change events
  * @valid:		True if the snapshot is valid
  */
 struct system_time_snapshot {
 	u64			cycles;
+	u64			hw_cycles;
 	ktime_t			systime;
 	ktime_t			monoraw;
 	enum clocksource_ids	cs_id;
+	enum clocksource_ids	hw_csid;
 	unsigned int		clock_was_set_seq;
 	u8			cs_was_changed_seq;
 	u8			valid;
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -320,6 +320,7 @@ static __always_inline u64 tk_clock_read
 
 	return clock->read(clock);
 }
+
 static inline void clocksource_disable_inline_read(void) { }
 static inline void clocksource_enable_inline_read(void) { }
 #endif
@@ -1187,6 +1188,18 @@ noinstr time64_t __ktime_get_real_second
 	return tk->xtime_sec;
 }
 
+static inline u64 tk_clock_read_snapshot(const struct tk_read_base *tkr,
+					 struct clocksource_hw_snapshot *chs)
+{
+	struct clocksource *clock = READ_ONCE(tkr->clock);
+
+	if (unlikely(clock->read_snapshot))
+		return clock->read_snapshot(clock, chs);
+
+	return clock->read(clock);
+}
+
+
 /**
  * ktime_get_snapshot_id -  Simultaneously snapshot a given clock ID with
  *			    CLOCK_MONOTONIC_RAW and the underlying
@@ -1237,14 +1250,20 @@ void ktime_get_snapshot_id(clockid_t clo
 	tk = &tkd->timekeeper;
 
 	do {
+		struct clocksource_hw_snapshot chs = { };
+
 		seq = read_seqcount_begin(&tkd->seq);
 
 		/* Aux clocks can be invalid */
 		if (!tk->clock_valid)
 			return;
 
-		now = tk_clock_read(&tk->tkr_mono);
+		now = tk_clock_read_snapshot(&tk->tkr_mono, &chs);
 		systime_snapshot->cs_id = tk->tkr_mono.clock->id;
+
+		systime_snapshot->hw_cycles = chs.hw_cycles;
+		systime_snapshot->hw_csid = chs.hw_csid;
+
 		systime_snapshot->cs_was_changed_seq = tk->cs_was_changed_seq;
 		systime_snapshot->clock_was_set_seq = tk->clock_was_set_seq;
 


^ permalink raw reply

* [patch V2 24/25] ptp: Switch to ktime_get_snapshot_id() for pre/post timestamps
From: Thomas Gleixner @ 2026-05-29 20:01 UTC (permalink / raw)
  To: LKML
  Cc: David Woodhouse, Miroslav Lichvar, John Stultz, Stephen Boyd,
	Anna-Maria Behnsen, Frederic Weisbecker, thomas.weissschuh,
	Arthur Kiyanovski, Rodolfo Giometti, Vincent Donnefort,
	Marc Zyngier, Oliver Upton, kvmarm, Oliver Upton, Richard Cochran,
	netdev, Takashi Iwai, Miri Korenblit, Johannes Berg, Jacob Keller,
	Tony Nguyen, Saeed Mahameed, Peter Hilber, Michael S. Tsirkin,
	virtualization, linux-wireless, linux-sound, David Woodhouse,
	Vadim Fedorenko
In-Reply-To: <20260529193435.921555544@kernel.org>

From: Thomas Gleixner <tglx@kernel.org>

To prepare for a new PTP IOCTL, which exposes the raw counter value along
with the requested system time snapshot, switch the pre/post time stamp
sampling over to use ktime_get_snapshot_id() and fix up all usage sites.

No functional change intended.

The ptp_vmclock conversion was simplified by David Woodhouse.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: David Woodhouse <dwmw@amazon.co.uk>
Tested-by: Arthur Kiyanovski <akiyano@amazon.com>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Acked-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 drivers/net/dsa/sja1105/sja1105_main.c |    8 ++++----
 drivers/ptp/ptp_chardev.c              |   14 +++++++++-----
 drivers/ptp/ptp_ocp.c                  |   11 ++++-------
 drivers/ptp/ptp_vmclock.c              |   23 +++++++----------------
 include/linux/ptp_clock_kernel.h       |   15 ++++++++-------
 5 files changed, 32 insertions(+), 39 deletions(-)
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -2310,10 +2310,10 @@ int sja1105_static_config_reload(struct
 		goto out;
 	}
 
-	t1 = timespec64_to_ns(&ptp_sts_before.pre_ts);
-	t2 = timespec64_to_ns(&ptp_sts_before.post_ts);
-	t3 = timespec64_to_ns(&ptp_sts_after.pre_ts);
-	t4 = timespec64_to_ns(&ptp_sts_after.post_ts);
+	t1 = ktime_to_ns(ptp_sts_before.pre_sts.systime);
+	t2 = ktime_to_ns(ptp_sts_before.post_sts.systime);
+	t3 = ktime_to_ns(ptp_sts_after.pre_sts.systime);
+	t4 = ktime_to_ns(ptp_sts_after.post_sts.systime);
 	/* Mid point, corresponds to pre-reset PTPCLKVAL */
 	t12 = t1 + (t2 - t1) / 2;
 	/* Mid point, corresponds to post-reset PTPCLKVAL, aka 0 */
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -386,15 +386,19 @@ static long ptp_sys_offset_extended(stru
 			return err;
 
 		/* Filter out disabled or unavailable clocks */
-		if (sts.pre_ts.tv_sec < 0 || sts.post_ts.tv_sec < 0)
+		if (!sts.pre_sts.valid || !sts.post_sts.valid)
 			return -EINVAL;
 
-		extoff->ts[i][0].sec = sts.pre_ts.tv_sec;
-		extoff->ts[i][0].nsec = sts.pre_ts.tv_nsec;
 		extoff->ts[i][1].sec = ts.tv_sec;
 		extoff->ts[i][1].nsec = ts.tv_nsec;
-		extoff->ts[i][2].sec = sts.post_ts.tv_sec;
-		extoff->ts[i][2].nsec = sts.post_ts.tv_nsec;
+
+		ts = ktime_to_timespec64(sts.pre_sts.systime);
+		extoff->ts[i][0].sec = ts.tv_sec;
+		extoff->ts[i][0].nsec = ts.tv_nsec;
+
+		ts = ktime_to_timespec64(sts.post_sts.systime);
+		extoff->ts[i][2].sec = ts.tv_sec;
+		extoff->ts[i][2].nsec = ts.tv_nsec;
 	}
 
 	return copy_to_user(arg, extoff, sizeof(*extoff)) ? -EFAULT : 0;
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -1491,11 +1491,8 @@ static int
 	}
 	ptp_read_system_postts(sts);
 
-	if (sts && bp->ts_window_adjust) {
-		s64 ns = timespec64_to_ns(&sts->post_ts);
-
-		sts->post_ts = ns_to_timespec64(ns - bp->ts_window_adjust);
-	}
+	if (sts && bp->ts_window_adjust)
+		sts->post_sts.systime -= bp->ts_window_adjust;
 
 	time_ns = ioread32(&bp->reg->time_ns);
 	time_sec = ioread32(&bp->reg->time_sec);
@@ -4595,8 +4592,8 @@ ptp_ocp_summary_show(struct seq_file *s,
 		struct timespec64 sys_ts;
 		s64 pre_ns, post_ns, ns;
 
-		pre_ns = timespec64_to_ns(&sts.pre_ts);
-		post_ns = timespec64_to_ns(&sts.post_ts);
+		pre_ns = ktime_to_ns(sts.pre_sts.systime);
+		post_ns = ktime_to_ns(sts.post_sts.systime);
 		ns = (pre_ns + post_ns) / 2;
 		ns += (s64)bp->utc_tai_offset * NSEC_PER_SEC;
 		sys_ts = ns_to_timespec64(ns);
--- a/drivers/ptp/ptp_vmclock.c
+++ b/drivers/ptp/ptp_vmclock.c
@@ -101,7 +101,6 @@ static int vmclock_get_crosststamp(struc
 				   struct timespec64 *tspec)
 {
 	ktime_t deadline = ktime_add(ktime_get(), VMCLOCK_MAX_WAIT);
-	struct system_time_snapshot systime_snapshot;
 	uint64_t cycle, delta, seq, frac_sec;
 
 #ifdef CONFIG_X86
@@ -132,17 +131,15 @@ static int vmclock_get_crosststamp(struc
 		 * will be derived from the *same* counter value.
 		 *
 		 * If the system isn't using the same counter, then the value
-		 * from ktime_get_snapshot_id() will still be used as pre_ts, and
-		 * ptp_read_system_postts() is called to populate postts after
-		 * calling get_cycles().
-		 *
-		 * The conversion to timespec64 happens further down, outside
-		 * the seq_count loop.
+		 * from ptp_read_system_prets() will still be used as pre_ts,
+		 * and ptp_read_system_postts() is called to populate postts
+		 * after calling get_cycles().
 		 */
 		if (sts) {
-			ktime_get_snapshot_id(CLOCK_REALTIME, &systime_snapshot);
-			if (systime_snapshot.cs_id == st->cs_id) {
-				cycle = systime_snapshot.cycles;
+			ptp_read_system_prets(sts);
+			if (sts->pre_sts.cs_id == st->cs_id) {
+				cycle = sts->pre_sts.cycles;
+				sts->post_sts = sts->pre_sts;
 			} else {
 				cycle = get_cycles();
 				ptp_read_system_postts(sts);
@@ -180,12 +177,6 @@ static int vmclock_get_crosststamp(struc
 		system_counter->cs_id = st->cs_id;
 	}
 
-	if (sts) {
-		sts->pre_ts = ktime_to_timespec64(systime_snapshot.systime);
-		if (systime_snapshot.cs_id == st->cs_id)
-			sts->post_ts = sts->pre_ts;
-	}
-
 	return 0;
 }
 
--- a/include/linux/ptp_clock_kernel.h
+++ b/include/linux/ptp_clock_kernel.h
@@ -12,6 +12,7 @@
 #include <linux/pps_kernel.h>
 #include <linux/ptp_clock.h>
 #include <linux/timecounter.h>
+#include <linux/timekeeping.h>
 #include <linux/skbuff.h>
 
 #define PTP_CLOCK_NAME_LEN	32
@@ -45,13 +46,13 @@ struct system_device_crosststamp;
 
 /**
  * struct ptp_system_timestamp - system time corresponding to a PHC timestamp
- * @pre_ts: system timestamp before capturing PHC
- * @post_ts: system timestamp after capturing PHC
- * @clockid: clock-base used for capturing the system timestamps
+ * @pre_sts:	system time snapshot before capturing PHC
+ * @post_sts:	system time snapshot after capturing PHC
+ * @clockid:	clock-base used for capturing the system timestamps
  */
 struct ptp_system_timestamp {
-	struct timespec64 pre_ts;
-	struct timespec64 post_ts;
+	struct system_time_snapshot pre_sts;
+	struct system_time_snapshot post_sts;
 	clockid_t clockid;
 };
 
@@ -510,13 +511,13 @@ static inline ktime_t ptp_convert_timest
 static inline void ptp_read_system_prets(struct ptp_system_timestamp *sts)
 {
 	if (sts)
-		ktime_get_clock_ts64(sts->clockid, &sts->pre_ts);
+		ktime_get_snapshot_id(sts->clockid, &sts->pre_sts);
 }
 
 static inline void ptp_read_system_postts(struct ptp_system_timestamp *sts)
 {
 	if (sts)
-		ktime_get_clock_ts64(sts->clockid, &sts->post_ts);
+		ktime_get_snapshot_id(sts->clockid, &sts->post_sts);
 }
 
 #endif


^ permalink raw reply

* [patch V2 23/25] timekeeping: Add support for AUX clock cross timestamping
From: Thomas Gleixner @ 2026-05-29 20:01 UTC (permalink / raw)
  To: LKML
  Cc: David Woodhouse, Miroslav Lichvar, John Stultz, Stephen Boyd,
	Anna-Maria Behnsen, Frederic Weisbecker, thomas.weissschuh,
	Arthur Kiyanovski, Rodolfo Giometti, Vincent Donnefort,
	Marc Zyngier, Oliver Upton, kvmarm, Oliver Upton, Richard Cochran,
	netdev, Takashi Iwai, Miri Korenblit, Johannes Berg, Jacob Keller,
	Tony Nguyen, Saeed Mahameed, Peter Hilber, Michael S. Tsirkin,
	virtualization, linux-wireless, linux-sound, David Woodhouse,
	Vadim Fedorenko
In-Reply-To: <20260529193435.921555544@kernel.org>

From: Thomas Gleixner <tglx@kernel.org>

Now that all prerequisites are in place add the final support for AUX
clocks in get_device_system_crosststamp(), which enables the PTP layer to
support hardware cross timestamps with a new IOTCL.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: Arthur Kiyanovski <akiyano@amazon.com>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
---
 kernel/time/timekeeping.c |    6 ++++++
 1 file changed, 6 insertions(+)
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1517,6 +1517,12 @@ int get_device_system_crosststamp(int (*
 		tkd = &tk_core;
 		offs = &tk_core.timekeeper.offs_real;
 		break;
+	case CLOCK_AUX ... CLOCK_AUX_LAST:
+		tkd = aux_get_tk_data(xtstamp->clock_id);
+		if (!tkd)
+			return -ENODEV;
+		offs = &tkd->timekeeper.offs_aux;
+		break;
 	default:
 		WARN_ON_ONCE(1);
 		return -ENODEV;


^ permalink raw reply

* [patch V2 22/25] timekeeping: Remove system_device_crosststamp::sys_realtime
From: Thomas Gleixner @ 2026-05-29 20:01 UTC (permalink / raw)
  To: LKML
  Cc: David Woodhouse, Miroslav Lichvar, John Stultz, Stephen Boyd,
	Anna-Maria Behnsen, Frederic Weisbecker, thomas.weissschuh,
	Arthur Kiyanovski, Rodolfo Giometti, Vincent Donnefort,
	Marc Zyngier, Oliver Upton, kvmarm, Oliver Upton, Richard Cochran,
	netdev, Takashi Iwai, Miri Korenblit, Johannes Berg, Jacob Keller,
	Tony Nguyen, Saeed Mahameed, Peter Hilber, Michael S. Tsirkin,
	virtualization, linux-wireless, linux-sound, David Woodhouse,
	Vadim Fedorenko
In-Reply-To: <20260529193435.921555544@kernel.org>

From: Thomas Gleixner <tglx@kernel.org>

All users are converted to sys_systime.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: David Woodhouse <dwmw@amazon.co.uk>
Tested-by: Arthur Kiyanovski <akiyano@amazon.com>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
---
 include/linux/timekeeping.h |    7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -318,7 +318,6 @@ struct system_counterval_t {
  * @clock_id:		System time Clock ID to capture
  * @device:		Device time
  * @sys_counter:	Clocksource counter value simultaneous with device time
- * @sys_realtime:	Realtime simultaneous with device time
  * @sys_systime:	System time for @clock_id
  * @sys_monoraw:	Monotonic raw simultaneous with device time
  */
@@ -326,11 +325,7 @@ struct system_device_crosststamp {
 	clockid_t			clock_id;
 	ktime_t				device;
 	struct system_counterval_t	sys_counter;
-	union {
-		/* realtime goes away once all users are converted */
-		ktime_t			sys_realtime;
-		ktime_t			sys_systime;
-	};
+	ktime_t				sys_systime;
 	ktime_t				sys_monoraw;
 };
 




^ permalink raw reply

* [patch V2 21/25] ALSA: hda/common: Use system_device_crosststamp::sys_systime
From: Thomas Gleixner @ 2026-05-29 20:01 UTC (permalink / raw)
  To: LKML
  Cc: David Woodhouse, Miroslav Lichvar, John Stultz, Stephen Boyd,
	Anna-Maria Behnsen, Frederic Weisbecker, thomas.weissschuh,
	Arthur Kiyanovski, Rodolfo Giometti, Vincent Donnefort,
	Marc Zyngier, Oliver Upton, kvmarm, Oliver Upton, Richard Cochran,
	netdev, Takashi Iwai, Miri Korenblit, Johannes Berg, Jacob Keller,
	Tony Nguyen, Saeed Mahameed, Peter Hilber, Michael S. Tsirkin,
	virtualization, linux-wireless, linux-sound, David Woodhouse,
	Vadim Fedorenko
In-Reply-To: <20260529193435.921555544@kernel.org>

From: Thomas Gleixner <tglx@kernel.org>

sys_systime is an alias for sys_realtime. The latter will be removed so
switch the code over to the new naming scheme.

No functional change.

Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: Arthur Kiyanovski <akiyano@amazon.com>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
---
 sound/hda/common/controller.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
--- a/sound/hda/common/controller.c
+++ b/sound/hda/common/controller.c
@@ -525,7 +525,7 @@ static int azx_get_time_info(struct snd_
 			break;
 
 		default:
-			*system_ts = ktime_to_timespec64(xtstamp.sys_realtime);
+			*system_ts = ktime_to_timespec64(xtstamp.sys_systime);
 			break;
 
 		}




^ 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