* [PATCH v3 3/3] ARM: dts: sunxi: add support for NetCube Systems OpenNMC (dobermann)
From: Lukas Schmid @ 2026-06-06 19:16 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Maxime Ripard
Cc: Lukas Schmid, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-riscv
In-Reply-To: <20260606191649.2347704-1-lukas.schmid@netcube.li>
NetCube Systems OpenNMC is an open replacement for APC SmartSlot Management
Cards. It is based on the Nagami System-on-Module. It breaks out the
following interfaces:
- 10/100 Mbps Ethernet
- USB Type-C OTG using a TUSB320 (usb0)
- USB Type-C Console Port using a CH340 (uart3)
- USB Type-A Host with internal CH334 USB-Hub (usb1)
- MicroSD Slot with Card-Detect (mmc0)
- WiFi/Bluetooth using the modules built-in ESP32
- SmartSlot serial interface (uart4)
- DS3232 RTC with CR1220 Battery Backup
- Extension connector providing SPI,I2C,USB,CAN,UART for future use.
Signed-off-by: Lukas Schmid <lukas.schmid@netcube.li>
---
arch/arm/boot/dts/allwinner/Makefile | 2 +
.../sun8i-t113s-netcube-dobermann.dts | 149 ++++++++++++++++++
2 files changed, 151 insertions(+)
create mode 100644 arch/arm/boot/dts/allwinner/sun8i-t113s-netcube-dobermann.dts
diff --git a/arch/arm/boot/dts/allwinner/Makefile b/arch/arm/boot/dts/allwinner/Makefile
index f71392a55df8..220a20734dc2 100644
--- a/arch/arm/boot/dts/allwinner/Makefile
+++ b/arch/arm/boot/dts/allwinner/Makefile
@@ -201,6 +201,7 @@ DTC_FLAGS_sun8i-h3-orangepi-pc := -@
DTC_FLAGS_sun8i-h3-bananapi-m2-plus-v1.2 := -@
DTC_FLAGS_sun8i-h3-orangepi-pc-plus := -@
DTC_FLAGS_sun8i-t113s-netcube-nagami-basic-carrier := -@
+DTC_FLAGS_sun8i-t113s-netcube-dobermann := -@
DTC_FLAGS_sun8i-v3s-netcube-kumquat := -@
dtb-$(CONFIG_MACH_SUN8I) += \
sun8i-a23-evb.dtb \
@@ -261,6 +262,7 @@ dtb-$(CONFIG_MACH_SUN8I) += \
sun8i-s3-lichee-zero-plus.dtb \
sun8i-s3-pinecube.dtb \
sun8i-t113s-mangopi-mq-r-t113.dtb \
+ sun8i-t113s-netcube-dobermann.dts \
sun8i-t113s-netcube-nagami-basic-carrier.dtb \
sun8i-t113s-netcube-nagami-keypad-carrier.dtb \
sun8i-t3-cqa3t-bv3.dtb \
diff --git a/arch/arm/boot/dts/allwinner/sun8i-t113s-netcube-dobermann.dts b/arch/arm/boot/dts/allwinner/sun8i-t113s-netcube-dobermann.dts
new file mode 100644
index 000000000000..d7765caffe2a
--- /dev/null
+++ b/arch/arm/boot/dts/allwinner/sun8i-t113s-netcube-dobermann.dts
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2026 Lukas Schmid <lukas.schmid@netcube.li>
+ */
+
+/dts-v1/;
+#include "sun8i-t113s-netcube-nagami.dtsi"
+
+#include <dt-bindings/leds/common.h>
+
+/ {
+ model = "NetCube Systems OpenNMC (dobermann)";
+ compatible = "netcube,dobermann", "netcube,nagami",
+ "allwinner,sun8i-t113s";
+
+ aliases {
+ serial2 = &uart4; // UART on SmartSlot
+ rtc0 = &ds3232;
+ rtc1 = &rtc; // not battery backed
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led_heartbeat_green: led-heartbeat-green {
+ gpios = <&pio 6 14 GPIO_ACTIVE_HIGH>; /* PG14 */
+ linux,default-trigger = "heartbeat";
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_HEARTBEAT;
+ };
+ };
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&i2c2 {
+ status = "okay";
+
+ tusb320: typec@60 {
+ compatible = "ti,tusb320";
+ reg = <0x60>;
+ interrupts-extended = <&pio 3 22 IRQ_TYPE_LEVEL_LOW>; /* PD22 */
+ };
+
+ ds3232: rtc@68 {
+ compatible = "dallas,ds3232";
+ reg = <0x68>;
+ };
+};
+
+/* microSD Card Slot on the board */
+&mmc0 {
+ vmmc-supply = <®_vcc3v3>;
+ disable-wp;
+ bus-width = <4>;
+ cd-gpios = <&pio 6 15 GPIO_ACTIVE_LOW>; /* PG15 */
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&pio {
+ gpio-line-names = "", "", "", "", // PA
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "SMART_TX", "SMART_RX", // PB
+ "EXT_IO3", "EXT_IO2", "CONSOLE_TX", "CONSOLE_RX",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "eMMC_CLK", "eMMC_CMD", // PC
+ "eMMC_D2", "eMMC_D1", "eMMC_D0", "eMMC_D3",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "", // PD
+ "", "", "", "",
+ "", "USB_SEC_EN", "EXT_SPI_nCS", "EXT_SPI_SCK",
+ "EXT_SPI_MOSI", "EXT_SPI_MISO", "EXT_IO5", "EXT_IO4",
+ "SMART_SEL", "", "", "",
+ "I2C2_SCL", "I2C2_SDA", "TUSB320_nINT", "",
+ "", "", "", "",
+ "", "", "", "",
+ "ETH_CRSDV", "ETH_RXD0", "ETH_RXD1", "ETH_TXCK", // PE
+ "ETH_TXD0", "ETH_TXD1", "ETH_TXEN", "",
+ "ETH_MDC", "ETH_MDIO", "I2C3_nINT", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "uSD_D1", "uSD_D0", "uSD_CLK", "uSD_CMD", // PF
+ "uSD_D3", "uSD_D2", "TUSB320_ID", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "ESP_CLK", "ESP_CMD", "ESP_D0", "ESP_D1", // PG
+ "ESP_D2", "ESP_D3", "ESP_TXD", "ESP_RXD",
+ "ESP_nBOOT", "ESP_nRST", "I2C3_SCL", "I2C3_SDA",
+ "EXT_IO1", "EXT_IO0", "LED_HEARTBEAT", "SD_DETECT",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "";
+};
+
+/* SmartSlot serial */
+&uart4 {
+ pinctrl-0 = <&uart4_pb_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+};
+
+&usb_otg {
+ extcon = <&tusb320 0>;
+ dr_mode = "otg";
+ status = "okay";
+};
+
+&usbphy {
+ usb0_id_det-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */
+ status = "okay";
+};
--
2.47.3
^ permalink raw reply related
* [PATCH v3 1/3] dt-bindings: arm: sunxi: Add NetCube Systems OpenNMC (dobermann)
From: Lukas Schmid @ 2026-06-06 19:16 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Maxime Ripard
Cc: Lukas Schmid, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-riscv
In-Reply-To: <20260606191649.2347704-1-lukas.schmid@netcube.li>
The OpenNMC is an open replacement for APC SmartSlot management cards
based on the Nagami System-on-Module.
Signed-off-by: Lukas Schmid <lukas.schmid@netcube.li>
---
Documentation/devicetree/bindings/arm/sunxi.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/sunxi.yaml b/Documentation/devicetree/bindings/arm/sunxi.yaml
index e6443c266fa1..077b65507645 100644
--- a/Documentation/devicetree/bindings/arm/sunxi.yaml
+++ b/Documentation/devicetree/bindings/arm/sunxi.yaml
@@ -598,6 +598,7 @@ properties:
- description: NetCube Systems Nagami SoM based boards
items:
- enum:
+ - netcube,dobermann
- netcube,nagami-basic-carrier
- netcube,nagami-keypad-carrier
- const: netcube,nagami
--
2.47.3
^ permalink raw reply related
* [PATCH v3 0/3] Add support for NetCube Systems OpenNMC (dobermann)
From: Lukas Schmid @ 2026-06-06 19:16 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Maxime Ripard
Cc: Lukas Schmid, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-riscv
This series adds support for the NetCube Systems OpenNMC
Changes in v3:
- actually add the Makefile changes
Changes in v2:
- fixed ordering of compatible enum
- fixed gpio line names
Signed-off-by: Lukas Schmid <lukas.schmid@netcube.li>
---
Lukas Schmid (3):
dt-bindings: arm: sunxi: Add NetCube Systems OpenNMC (dobermann)
riscv: dts: allwinner: d1s-t113: Add uart4 pinctrl required by NetCube
Systems OpenNMC
ARM: dts: sunxi: add support for NetCube Systems OpenNMC (dobermann)
.../devicetree/bindings/arm/sunxi.yaml | 1 +
arch/arm/boot/dts/allwinner/Makefile | 2 +
.../sun8i-t113s-netcube-dobermann.dts | 149 ++++++++++++++++++
.../boot/dts/allwinner/sunxi-d1s-t113.dtsi | 6 +
4 files changed, 158 insertions(+)
create mode 100644 arch/arm/boot/dts/allwinner/sun8i-t113s-netcube-dobermann.dts
--
2.47.3
^ permalink raw reply
* [PATCH v3 2/3] riscv: dts: allwinner: d1s-t113: Add uart4 pinctrl required by NetCube Systems OpenNMC
From: Lukas Schmid @ 2026-06-06 19:16 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Maxime Ripard
Cc: Lukas Schmid, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, linux-riscv
In-Reply-To: <20260606191649.2347704-1-lukas.schmid@netcube.li>
Added the "uart4_pb_pins" pinctrl used by the OpenNMC
Signed-off-by: Lukas Schmid <lukas.schmid@netcube.li>
---
arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi b/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
index 82cc85acccb1..00fddedfa36f 100644
--- a/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
+++ b/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi
@@ -191,6 +191,12 @@ uart3_pb_pins: uart3-pb-pins {
pins = "PB6", "PB7";
function = "uart3";
};
+
+ /omit-if-no-ref/
+ uart4_pb_pins: uart4-pb-pins {
+ pins = "PB2", "PB3";
+ function = "uart4";
+ };
};
ccu: clock-controller@2001000 {
--
2.47.3
^ permalink raw reply related
* [PATCH v3 2/2] KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU
From: Hyunwoo Kim @ 2026-06-06 17:56 UTC (permalink / raw)
To: tabba, maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will
Cc: linux-arm-kernel, kvmarm, stable, imv4bel
In-Reply-To: <20260606175614.83273-1-imv4bel@gmail.com>
flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU
on every run. The vGIC list register save and restore use used_lrs as
their loop bound and expect it to stay within the number of implemented
list registers. While this is generally the case, flush_hyp_vcpu()
copies vgic_v3 verbatim and does not enforce this, so a value provided
by the host is used at EL2 to index vgic_lr[] and access ICH_LR<n>_EL2
(host -> EL2).
Fix by clamping used_lrs to the number of implemented list registers
after the copy, as the trusted path already does in
vgic_flush_lr_state(). The number of implemented list registers is
constant after init, so it is replicated once from
kvm_vgic_global_state.nr_lr into hyp_gicv3_nr_lr rather than read on
every entry.
Cc: stable@vger.kernel.org
Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()")
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
---
arch/arm64/include/asm/kvm_hyp.h | 1 +
arch/arm64/kvm/arm.c | 2 ++
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 9 +++++++++
3 files changed, 12 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index 8d06b62e7188..e9b2b0c40ec6 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icache_flags);
extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits);
extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl);
extern unsigned long kvm_nvhe_sym(hyp_nr_cpus);
+extern unsigned int kvm_nvhe_sym(hyp_gicv3_nr_lr);
#endif /* __ARM64_KVM_HYP_H__ */
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 9453321ef8c6..9ffd5d4079e6 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -2426,6 +2426,8 @@ static int __init init_subsystems(void)
switch (err) {
case 0:
vgic_present = true;
+ if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
+ kvm_nvhe_sym(hyp_gicv3_nr_lr) = kvm_vgic_global_state.nr_lr;
break;
case -ENODEV:
case -ENXIO:
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 02c5d6e5abcb..a0da08caa6c2 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -24,6 +24,9 @@
DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
+/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */
+unsigned int hyp_gicv3_nr_lr;
+
void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt);
static void __hyp_sve_save_guest(struct kvm_vcpu *vcpu)
@@ -142,6 +145,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
+ /* Bound used_lrs by the number of implemented list registers. */
+ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
+ min_t(unsigned int,
+ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
+ hyp_gicv3_nr_lr);
+
hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v3 1/2] KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU
From: Hyunwoo Kim @ 2026-06-06 17:56 UTC (permalink / raw)
To: tabba, maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will
Cc: linux-arm-kernel, kvmarm, stable, imv4bel
In-Reply-To: <20260606175614.83273-1-imv4bel@gmail.com>
flush_hyp_vcpu() copies the host vCPU context into the hyp's private
vCPU on every run. ctxt_to_vcpu() expects a guest context to have a
NULL __hyp_running_vcpu, which is only ever set on the host context, so
that it resolves the vCPU via container_of(). While this is generally
the case, flush_hyp_vcpu() copies the context verbatim and does not
enforce this, so a value provided by the host is dereferenced at EL2
(host -> EL2).
Fix by clearing __hyp_running_vcpu after the copy.
Cc: stable@vger.kernel.org
Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()")
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
---
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 06db299c37a8..02c5d6e5abcb 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -128,6 +128,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
+ /* __hyp_running_vcpu must be NULL in a guest context. */
+ hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL;
+
hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
--
2.43.0
^ permalink raw reply related
* [PATCH v3 0/2] KVM: arm64: Sanitise host vCPU fields copied in flush_hyp_vcpu()
From: Hyunwoo Kim @ 2026-06-06 17:56 UTC (permalink / raw)
To: tabba, maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will
Cc: linux-arm-kernel, kvmarm, stable, imv4bel
flush_hyp_vcpu() copies the host vCPU context and vGIC state into the
hyp's private vCPU on every run. This series sanitises two fields that
it currently copies verbatim (host -> EL2): __hyp_running_vcpu is
cleared in the guest context, and used_lrs is bounded by the number of
implemented list registers.
Changes in v3:
- 2/2: replicate kvm_vgic_global_state.nr_lr into hyp_gicv3_nr_lr
once at init (guarded by gicv3_cpuif), instead of reading
ICH_VTR_EL2 on every entry behind a gicv3_cpuif gate. (Marc)
- v2: https://lore.kernel.org/all/20260604151210.1304051-1-imv4bel@gmail.com/
Changes in v2:
- split into two patches, one per field, per review.
- v1: https://lore.kernel.org/all/aiFe-CXo-XVTFz1g@v4bel/
Hyunwoo Kim (2):
KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU
KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU
arch/arm64/include/asm/kvm_hyp.h | 1 +
arch/arm64/kvm/arm.c | 2 ++
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 12 ++++++++++++
3 files changed, 15 insertions(+)
--
2.43.0
^ permalink raw reply
* Re: [PATCH 2/2] nvme-apple: Prevent tag collision across queues even if tag space is shared
From: Nick Chan @ 2026-06-06 16:46 UTC (permalink / raw)
To: Sven Peter, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg
Cc: asahi, linux-arm-kernel, linux-nvme, linux-kernel, stable,
Yuriy Havrylyuk
In-Reply-To: <a0b0bea4-998e-4196-a2b0-9fcaf531d9f3@kernel.org>
Sven Peter 於 2026/6/7 凌晨12:12 寫道:
> On 06.06.26 15:25, Nick Chan wrote:
>> From: Yuriy Havrylyuk <yhavry@gmail.com>
>>
>> Apple NVMe controllers require tags of pending commands to not be shared
>> across admin and IO queues. However, on Apple A11 without linear SQ, it is
>> not possible for either queue to skip over some tags and must go from 0 to
>> the configured maximum before wrapping around.
>>
>> If a pending command tag is duplicated across queues, the firmware
>> crashes with: "duplicate tag error for tag N", with N being the tag.
>>
>> Instead of partitioning the tag space, which is not possible without
>> linear SQ,
>
> Isn't that just what the pci.c driver does with NVME_QUIRK_SHARED_TAGS
> for the T2 macs or what we do in this driver with
> if (anv->hw->has_lsq_nvmmu)
> anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH;
> ?
After adjusting the apple_nvme_submit_cmd_t8015() function to account for
the admin queue depth, it seems that the existing workaround for M1 of
reserving two tags for the admin queue works on A11 as well.
Will post a much simplified v2.
Best regards,
Nick Chan
>
>
> Sven
>
^ permalink raw reply
* Re: [PATCH 2/2] nvme-apple: Prevent tag collision across queues even if tag space is shared
From: Sven Peter @ 2026-06-06 16:12 UTC (permalink / raw)
To: Nick Chan, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg
Cc: asahi, linux-arm-kernel, linux-nvme, linux-kernel, stable,
Yuriy Havrylyuk
In-Reply-To: <20260606-prevent-tag-collision-t8015-v1-2-93ccf4eca550@gmail.com>
On 06.06.26 15:25, Nick Chan wrote:
> From: Yuriy Havrylyuk <yhavry@gmail.com>
>
> Apple NVMe controllers require tags of pending commands to not be shared
> across admin and IO queues. However, on Apple A11 without linear SQ, it is
> not possible for either queue to skip over some tags and must go from 0 to
> the configured maximum before wrapping around.
>
> If a pending command tag is duplicated across queues, the firmware
> crashes with: "duplicate tag error for tag N", with N being the tag.
>
> Instead of partitioning the tag space, which is not possible without
> linear SQ,
Isn't that just what the pci.c driver does with NVME_QUIRK_SHARED_TAGS
for the T2 macs or what we do in this driver with
if (anv->hw->has_lsq_nvmmu)
anv->tagset.reserved_tags = APPLE_NVME_AQ_DEPTH;
?
Sven
^ permalink raw reply
* Re: [PATCH bpf-next] bpf: Replace scratch PTE atomically when allocating arena pages
From: Catalin Marinas @ 2026-06-06 16:06 UTC (permalink / raw)
To: Tejun Heo
Cc: bot+bpf-ci, void, arighi, changwoo, ast, andrii, daniel,
martin.lau, memxor, peterz, will, tglx, mingo, bp, dave.hansen,
akpm, david, rppt, emil, sched-ext, bpf, x86, linux-arm-kernel,
linux-mm, linux-kernel, eddyz87, yonghong.song, clm,
ihor.solodrai
In-Reply-To: <8f133924fbf8d259340f3057e505f663@kernel.org>
On Tue, Jun 02, 2026 at 12:09:11PM -1000, Tejun Heo wrote:
> On Mon, Jun 01, 2026 at 08:15:34PM +0000, bot+bpf-ci@kernel.org wrote:
> > After the real page is installed without a flush, can that stale
> > kaddr -> scratch_page translation persist, so that later kernel-side
> > accesses at kaddr reach the shared per-arena scratch page instead of
> > the freshly allocated page?
>
> It can on x86, but it's harmless: that CPU faulted on an unallocated
> address and got scratch-recovered, so reaching either the scratch or the
> real page is fine. No flush needed.
I think for arm64 it will be slightly different. After making the pte
invalid, we flush the TLBs and subsequent access will be fault. However,
ptep_try_set() is missing __set_pte_complete() with the necessary
barriers. A subsequent access may fault rather than hit the old or the
new page. Something like below, as a fixup for 258df8fce42f ("mm: Add
ptep_try_set() for lockless empty-slot installs"):
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 3ce0f2a6cab6..dc8525431273 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -1838,7 +1838,11 @@ static inline bool ptep_try_set(pte_t *ptep, pte_t new_pte)
{
pteval_t old = 0;
- return try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte));
+ if (!try_cmpxchg(&pte_val(*ptep), &old, pte_val(new_pte)))
+ return false;
+
+ __set_pte_complete(new_pte);
+ return true;
}
#define ptep_try_set ptep_try_set
--
Catalin
^ permalink raw reply related
* Re: [PATCH 2/2] nvme-apple: Prevent tag collision across queues even if tag space is shared
From: Nick Chan @ 2026-06-06 15:51 UTC (permalink / raw)
To: David Laight
Cc: Sven Peter, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, asahi, linux-arm-kernel,
linux-nvme, linux-kernel, stable, Yuriy Havrylyuk
In-Reply-To: <20260606152930.6f2bf4ed@pumpkin>
David Laight 於 2026/6/6 晚上10:29 寫道:
> On Sat, 06 Jun 2026 21:25:26 +0800
> Nick Chan <towinchenmi@gmail.com> wrote:
>
>> From: Yuriy Havrylyuk <yhavry@gmail.com>
>>
>> Apple NVMe controllers require tags of pending commands to not be shared
>> across admin and IO queues. However, on Apple A11 without linear SQ, it is
>> not possible for either queue to skip over some tags and must go from 0 to
>> the configured maximum before wrapping around.
>>
>> If a pending command tag is duplicated across queues, the firmware
>> crashes with: "duplicate tag error for tag N", with N being the tag.
>>
>> Instead of partitioning the tag space, which is not possible without
>> linear SQ, prevent tag collisions by keeping track of which tags are
>> currently in-flight across either queues, and return BLK_STS_RESOURCE to
>> temporaily block command submission when a collision would have occurred.
>
> I look at using the atomic64_xxx() functions rather than the bitmask ones.
> The for_each_bit_set() loop is then an atmomic64_andnot() call.
That does in fact simplify the loop code. However, using the atomic function
complicates the apple_nvme_reserve_tag_t8015() and
apple_nvme_release_tag_t8015() since those functions deal with set/clear a
a bit in terms of an integer (the tag).
Especially in the apple_nvme_reserve_tag_t8015() function the function body
is then
u64 tag_bit = BIT(nvme_tag_from_cid(cmd->common.command_id));
return !(atomic64_fetch_or(tag_bit, &anv->t8015_active_tags) & tag_bit);
The function would need to explictly convert the tag to a bit, and then
explictly extract the bit value after performing the atomic operation,
both which of could have been done by test_and_set_bit().
So I do not see any overall benefit for using atomic_xxx() functions.
Best Regards,
Nick Chan
>
> -- David
>
>
>>
>> Cc: stable@vger.kernel.org
>> Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support")
>> Signed-off-by: Yuriy Havrylyuk <yhavry@gmail.com>
>> Co-developed-by: Nick Chan <towinchenmi@gmail.com>
>> Signed-off-by: Nick Chan <towinchenmi@gmail.com>
>> ---
>> drivers/nvme/host/apple.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 65 insertions(+)
>>
>> diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
>> index c1115e27a0d6..6354edf27225 100644
>> --- a/drivers/nvme/host/apple.c
>> +++ b/drivers/nvme/host/apple.c
>> @@ -203,6 +203,20 @@ struct apple_nvme {
>>
>> int irq;
>> spinlock_t lock;
>> +
>> + /*
>> + * Tags of pending commands must be unique across both Admin and IO
>> + * queue. However, on T8015, unlike T8103, without linear submission
>> + * queues, it is not possible for the either queue to skip some tags,
>> + * and both queues must go from 0 to their respective configured
>> + * maximum.
>> + *
>> + * Instead of reserving some tags for the admin queue, use a bitfield
>> + * to keep track of pending commands on either queue, and temporaily
>> + * block command submission by returning BLK_STS_RESOURCE until the
>> + * tag is freed on the other queue.
>> + */
>> + unsigned long t8015_active_tags;
>> };
>>
>> static_assert(sizeof(struct nvme_command) == 64);
>> @@ -290,6 +304,28 @@ static void apple_nvmmu_inval(struct apple_nvme_queue *q, unsigned int tag)
>> "NVMMU TCB invalidation failed\n");
>> }
>>
>> +static bool apple_nvme_reserve_tag_t8015(struct apple_nvme *anv,
>> + struct nvme_command *cmd)
>> +{
>> + u16 tag = nvme_tag_from_cid(cmd->common.command_id);
>> +
>> + if (WARN_ON_ONCE(tag >= BITS_PER_LONG))
>> + return false;
>> +
>> + return !test_and_set_bit(tag, &anv->t8015_active_tags);
>> +}
>> +
>> +static void apple_nvme_release_tag_t8015(struct apple_nvme *anv,
>> + __u16 command_id)
>> +{
>> + u16 tag = nvme_tag_from_cid(command_id);
>> +
>> + if (WARN_ON_ONCE(tag >= BITS_PER_LONG))
>> + return;
>> +
>> + clear_bit(tag, &anv->t8015_active_tags);
>> +}
>> +
>> static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q,
>> struct nvme_command *cmd)
>> {
>> @@ -652,6 +688,8 @@ static inline void apple_nvme_update_cq_head(struct apple_nvme_queue *q)
>> static bool apple_nvme_poll_cq(struct apple_nvme_queue *q,
>> struct io_comp_batch *iob)
>> {
>> + struct apple_nvme *anv = queue_to_apple_nvme(q);
>> + unsigned long completed_tags = 0;
>> bool found = false;
>>
>> while (apple_nvme_cqe_pending(q)) {
>> @@ -664,11 +702,26 @@ static bool apple_nvme_poll_cq(struct apple_nvme_queue *q,
>> dma_rmb();
>> apple_nvme_handle_cqe(q, iob, q->cq_head);
>> apple_nvme_update_cq_head(q);
>> +
>> + if (!anv->hw->has_lsq_nvmmu) {
>> + struct nvme_completion *cqe = &q->cqes[q->cq_head];
>> + u16 tag = nvme_tag_from_cid(READ_ONCE(cqe->command_id));
>> +
>> + if (!WARN_ON_ONCE(tag >= BITS_PER_LONG))
>> + __set_bit(tag, &completed_tags);
>> + }
>> }
>>
>> if (found)
>> writel(q->cq_head, q->cq_db);
>>
>> + if (!anv->hw->has_lsq_nvmmu && completed_tags) {
>> + unsigned long tag_bit;
>> +
>> + for_each_set_bit(tag_bit, &completed_tags, BITS_PER_LONG)
>> + clear_bit(tag_bit, &anv->t8015_active_tags);
>> + }
>> +
>> return found;
>> }
>>
>> @@ -790,6 +843,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
>> if (ret)
>> return ret;
>>
>> + if (!anv->hw->has_lsq_nvmmu &&
>> + !apple_nvme_reserve_tag_t8015(anv, cmnd)) {
>> + ret = BLK_STS_RESOURCE;
>> + goto out_free_cmd;
>> + }
>> +
>> if (blk_rq_nr_phys_segments(req)) {
>> ret = apple_nvme_map_data(anv, req, cmnd);
>> if (ret)
>> @@ -806,6 +865,9 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
>> return BLK_STS_OK;
>>
>> out_free_cmd:
>> + if (!anv->hw->has_lsq_nvmmu)
>> + apple_nvme_release_tag_t8015(anv, cmnd->common.command_id);
>> +
>> nvme_cleanup_cmd(req);
>> return ret;
>> }
>> @@ -1165,6 +1227,9 @@ static void apple_nvme_reset_work(struct work_struct *work)
>> if (ret)
>> goto out;
>>
>> + if (!anv->hw->has_lsq_nvmmu)
>> + WRITE_ONCE(anv->t8015_active_tags, 0);
>> +
>> dev_dbg(anv->dev, "Starting admin queue");
>> apple_nvme_init_queue(&anv->adminq);
>> nvme_unquiesce_admin_queue(&anv->ctrl);
>>
>
^ permalink raw reply
* [soc:for-next] BUILD SUCCESS fb3fcb406b8882a91d1e51bae97101802fc991e1
From: kernel test robot @ 2026-06-06 15:20 UTC (permalink / raw)
To: Linus Walleij; +Cc: linux-arm-kernel, arm
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
branch HEAD: fb3fcb406b8882a91d1e51bae97101802fc991e1 soc: document merges
elapsed time: 2514m
configs tested: 281
configs skipped: 6
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-16.1.0
arc allmodconfig clang-17
arc allmodconfig gcc-15.2.0
arc allnoconfig gcc-15.2.0
arc allyesconfig gcc-15.2.0
arc defconfig gcc-16.1.0
arc randconfig-001-20260605 gcc-8.5.0
arc randconfig-001-20260606 clang-23
arc randconfig-002-20260605 gcc-8.5.0
arc randconfig-002-20260606 clang-23
arm allnoconfig clang-23
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-17
arm allyesconfig gcc-15.2.0
arm defconfig clang-23
arm randconfig-001-20260605 gcc-10.5.0
arm randconfig-001-20260606 clang-23
arm randconfig-002-20260605 gcc-13.4.0
arm randconfig-002-20260606 clang-23
arm randconfig-003-20260605 clang-23
arm randconfig-003-20260606 clang-23
arm randconfig-004-20260605 gcc-8.5.0
arm randconfig-004-20260606 clang-23
arm64 allmodconfig clang-19
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-16.1.0
arm64 randconfig-001 gcc-8.5.0
arm64 randconfig-001-20260605 gcc-9.5.0
arm64 randconfig-002 gcc-14.3.0
arm64 randconfig-002-20260605 gcc-10.5.0
arm64 randconfig-003 clang-23
arm64 randconfig-003-20260605 gcc-11.5.0
arm64 randconfig-004 clang-23
arm64 randconfig-004-20260605 clang-23
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-16.1.0
csky randconfig-001 gcc-10.5.0
csky randconfig-001-20260605 gcc-16.1.0
csky randconfig-002 gcc-10.5.0
csky randconfig-002-20260605 gcc-9.5.0
hexagon allmodconfig clang-17
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-15.2.0
hexagon defconfig clang-23
hexagon randconfig-001 gcc-11.5.0
hexagon randconfig-001-20260605 clang-20
hexagon randconfig-001-20260606 gcc-11.5.0
hexagon randconfig-002 gcc-11.5.0
hexagon randconfig-002-20260605 clang-23
hexagon randconfig-002-20260606 gcc-11.5.0
i386 allmodconfig gcc-14
i386 allnoconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 buildonly-randconfig-001 gcc-14
i386 buildonly-randconfig-001-20260605 clang-20
i386 buildonly-randconfig-001-20260606 gcc-13
i386 buildonly-randconfig-002 clang-20
i386 buildonly-randconfig-002-20260605 clang-20
i386 buildonly-randconfig-002-20260606 gcc-13
i386 buildonly-randconfig-003 clang-20
i386 buildonly-randconfig-003-20260605 clang-20
i386 buildonly-randconfig-003-20260606 gcc-13
i386 buildonly-randconfig-004 gcc-14
i386 buildonly-randconfig-004-20260605 clang-20
i386 buildonly-randconfig-004-20260606 gcc-13
i386 buildonly-randconfig-005 gcc-14
i386 buildonly-randconfig-005-20260605 gcc-12
i386 buildonly-randconfig-005-20260606 gcc-13
i386 buildonly-randconfig-006 gcc-14
i386 buildonly-randconfig-006-20260605 gcc-14
i386 buildonly-randconfig-006-20260606 gcc-13
i386 defconfig clang-22
i386 randconfig-001 clang-22
i386 randconfig-001-20260605 clang-22
i386 randconfig-001-20260606 clang-20
i386 randconfig-002 gcc-14
i386 randconfig-002-20260605 clang-22
i386 randconfig-002-20260606 clang-20
i386 randconfig-003 gcc-14
i386 randconfig-003-20260605 gcc-14
i386 randconfig-003-20260606 clang-20
i386 randconfig-004 clang-22
i386 randconfig-004-20260605 gcc-14
i386 randconfig-004-20260606 clang-20
i386 randconfig-005 gcc-14
i386 randconfig-005-20260605 clang-22
i386 randconfig-005-20260606 clang-20
i386 randconfig-006 gcc-14
i386 randconfig-006-20260605 gcc-14
i386 randconfig-006-20260606 clang-20
i386 randconfig-007 gcc-14
i386 randconfig-007-20260605 clang-22
i386 randconfig-007-20260606 clang-20
i386 randconfig-011-20260605 clang-22
i386 randconfig-012-20260605 clang-22
i386 randconfig-013-20260605 clang-22
i386 randconfig-014-20260605 clang-22
i386 randconfig-015-20260605 clang-22
i386 randconfig-016-20260605 clang-22
i386 randconfig-017-20260605 clang-22
loongarch allmodconfig clang-19
loongarch allnoconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-23
loongarch randconfig-001 gcc-11.5.0
loongarch randconfig-001-20260605 clang-18
loongarch randconfig-001-20260606 gcc-11.5.0
loongarch randconfig-002 gcc-11.5.0
loongarch randconfig-002-20260605 gcc-16.1.0
loongarch randconfig-002-20260606 gcc-11.5.0
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-17
m68k allyesconfig gcc-15.2.0
m68k defconfig clang-23
m68k defconfig gcc-16.1.0
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-23
microblaze defconfig gcc-16.1.0
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips malta_kvm_defconfig gcc-16.1.0
nios2 allmodconfig clang-23
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-17
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-23
nios2 defconfig gcc-11.5.0
nios2 randconfig-001 gcc-11.5.0
nios2 randconfig-001-20260605 gcc-8.5.0
nios2 randconfig-001-20260606 gcc-11.5.0
nios2 randconfig-002 gcc-11.5.0
nios2 randconfig-002-20260605 gcc-11.5.0
nios2 randconfig-002-20260606 gcc-11.5.0
openrisc allmodconfig clang-23
openrisc allmodconfig gcc-15.2.0
openrisc allnoconfig clang-17
openrisc allnoconfig gcc-15.2.0
openrisc defconfig gcc-16.1.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-17
parisc allnoconfig gcc-15.2.0
parisc allyesconfig gcc-15.2.0
parisc defconfig gcc-16.1.0
parisc randconfig-001-20260605 gcc-8.5.0
parisc randconfig-001-20260606 gcc-8.5.0
parisc randconfig-002-20260605 gcc-11.5.0
parisc randconfig-002-20260606 gcc-8.5.0
parisc64 defconfig clang-23
parisc64 defconfig gcc-16.1.0
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-17
powerpc allnoconfig gcc-15.2.0
powerpc randconfig-001-20260605 gcc-8.5.0
powerpc randconfig-001-20260606 gcc-8.5.0
powerpc randconfig-002-20260605 gcc-9.5.0
powerpc randconfig-002-20260606 gcc-8.5.0
powerpc tqm8560_defconfig gcc-16.1.0
powerpc64 randconfig-001-20260605 clang-23
powerpc64 randconfig-001-20260606 gcc-8.5.0
powerpc64 randconfig-002-20260605 gcc-13.4.0
powerpc64 randconfig-002-20260606 gcc-8.5.0
riscv allmodconfig clang-23
riscv allnoconfig clang-17
riscv allnoconfig gcc-15.2.0
riscv allyesconfig clang-16
riscv allyesconfig clang-17
riscv defconfig clang-23
riscv defconfig gcc-16.1.0
riscv randconfig-001 gcc-8.5.0
riscv randconfig-001-20260605 gcc-8.5.0
riscv randconfig-002 clang-23
riscv randconfig-002-20260605 clang-23
s390 allmodconfig clang-18
s390 allnoconfig clang-17
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig clang-18
s390 defconfig gcc-16.1.0
s390 randconfig-001 gcc-11.5.0
s390 randconfig-001-20260605 clang-23
s390 randconfig-002 clang-23
s390 randconfig-002-20260605 clang-23
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-17
sh allnoconfig gcc-15.2.0
sh allyesconfig gcc-15.2.0
sh defconfig gcc-14
sh defconfig gcc-16.1.0
sh randconfig-001 gcc-16.1.0
sh randconfig-001-20260605 gcc-16.1.0
sh randconfig-002 gcc-14.3.0
sh randconfig-002-20260605 gcc-10.5.0
sparc allnoconfig clang-17
sparc allnoconfig gcc-15.2.0
sparc defconfig gcc-16.1.0
sparc randconfig-001-20260605 gcc-8.5.0
sparc randconfig-002-20260605 gcc-8.5.0
sparc64 allmodconfig clang-23
sparc64 defconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260605 clang-23
sparc64 randconfig-002-20260605 clang-22
um allmodconfig clang-19
um allnoconfig clang-17
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-15.2.0
um defconfig clang-23
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260605 clang-23
um randconfig-002-20260605 clang-22
um x86_64_defconfig clang-23
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-17
x86_64 allnoconfig clang-20
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260605 gcc-14
x86_64 buildonly-randconfig-001-20260606 gcc-14
x86_64 buildonly-randconfig-002-20260605 gcc-14
x86_64 buildonly-randconfig-002-20260606 gcc-14
x86_64 buildonly-randconfig-003-20260605 gcc-14
x86_64 buildonly-randconfig-003-20260606 gcc-14
x86_64 buildonly-randconfig-004-20260605 gcc-14
x86_64 buildonly-randconfig-004-20260606 gcc-14
x86_64 buildonly-randconfig-005-20260605 gcc-14
x86_64 buildonly-randconfig-005-20260606 gcc-14
x86_64 buildonly-randconfig-006-20260605 gcc-14
x86_64 buildonly-randconfig-006-20260606 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-22
x86_64 randconfig-001-20260605 clang-22
x86_64 randconfig-001-20260606 gcc-14
x86_64 randconfig-002-20260605 clang-22
x86_64 randconfig-002-20260606 gcc-14
x86_64 randconfig-003-20260605 clang-22
x86_64 randconfig-003-20260606 gcc-14
x86_64 randconfig-004-20260605 gcc-13
x86_64 randconfig-004-20260606 gcc-14
x86_64 randconfig-005-20260605 clang-22
x86_64 randconfig-005-20260606 gcc-14
x86_64 randconfig-006-20260605 gcc-14
x86_64 randconfig-006-20260606 gcc-14
x86_64 randconfig-011-20260605 clang-22
x86_64 randconfig-011-20260606 gcc-14
x86_64 randconfig-012-20260605 gcc-14
x86_64 randconfig-012-20260606 gcc-14
x86_64 randconfig-013-20260605 clang-22
x86_64 randconfig-013-20260606 gcc-14
x86_64 randconfig-014-20260605 clang-22
x86_64 randconfig-014-20260606 gcc-14
x86_64 randconfig-015-20260605 gcc-14
x86_64 randconfig-015-20260606 gcc-14
x86_64 randconfig-016-20260605 clang-22
x86_64 randconfig-016-20260606 gcc-14
x86_64 randconfig-071-20260605 gcc-14
x86_64 randconfig-072-20260605 gcc-14
x86_64 randconfig-073-20260605 clang-22
x86_64 randconfig-074-20260605 gcc-14
x86_64 randconfig-075-20260605 gcc-12
x86_64 randconfig-076-20260605 gcc-14
x86_64 rhel-9.4 clang-22
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-22
x86_64 rhel-9.4-kselftests clang-22
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-17
xtensa allnoconfig gcc-15.2.0
xtensa allyesconfig clang-23
xtensa randconfig-001-20260605 gcc-12.5.0
xtensa randconfig-002-20260605 gcc-8.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH 2/2] nvme-apple: Prevent tag collision across queues even if tag space is shared
From: Nick Chan @ 2026-06-06 15:08 UTC (permalink / raw)
To: Sven Peter, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg
Cc: asahi, linux-arm-kernel, linux-nvme, linux-kernel, stable,
Yuriy Havrylyuk
In-Reply-To: <20260606-prevent-tag-collision-t8015-v1-2-93ccf4eca550@gmail.com>
Nick Chan 於 2026/6/6 晚上9:25 寫道:
> From: Yuriy Havrylyuk <yhavry@gmail.com>
>
> Apple NVMe controllers require tags of pending commands to not be shared
> across admin and IO queues. However, on Apple A11 without linear SQ, it is
> not possible for either queue to skip over some tags and must go from 0 to
> the configured maximum before wrapping around.
>
> If a pending command tag is duplicated across queues, the firmware
> crashes with: "duplicate tag error for tag N", with N being the tag.
>
> Instead of partitioning the tag space, which is not possible without
> linear SQ, prevent tag collisions by keeping track of which tags are
> currently in-flight across either queues, and return BLK_STS_RESOURCE to
> temporaily block command submission when a collision would have occurred.
>
> Cc: stable@vger.kernel.org
> Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support")
> Signed-off-by: Yuriy Havrylyuk <yhavry@gmail.com>
> Co-developed-by: Nick Chan <towinchenmi@gmail.com>
> Signed-off-by: Nick Chan <towinchenmi@gmail.com>
> ---
> drivers/nvme/host/apple.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 65 insertions(+)
There are some issues with this version that make it not actually work, so a v2
will sent.
>
> diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
> index c1115e27a0d6..6354edf27225 100644
> --- a/drivers/nvme/host/apple.c
> +++ b/drivers/nvme/host/apple.c
> @@ -203,6 +203,20 @@ struct apple_nvme {
>
> int irq;
> spinlock_t lock;
> +
> + /*
> + * Tags of pending commands must be unique across both Admin and IO
> + * queue. However, on T8015, unlike T8103, without linear submission
> + * queues, it is not possible for the either queue to skip some tags,
> + * and both queues must go from 0 to their respective configured
> + * maximum.
> + *
> + * Instead of reserving some tags for the admin queue, use a bitfield
> + * to keep track of pending commands on either queue, and temporaily
> + * block command submission by returning BLK_STS_RESOURCE until the
> + * tag is freed on the other queue.
> + */
> + unsigned long t8015_active_tags;
> };
>
> static_assert(sizeof(struct nvme_command) == 64);
> @@ -290,6 +304,28 @@ static void apple_nvmmu_inval(struct apple_nvme_queue *q, unsigned int tag)
> "NVMMU TCB invalidation failed\n");
> }
>
> +static bool apple_nvme_reserve_tag_t8015(struct apple_nvme *anv,
> + struct nvme_command *cmd)
> +{
> + u16 tag = nvme_tag_from_cid(cmd->common.command_id);
> +
> + if (WARN_ON_ONCE(tag >= BITS_PER_LONG))
> + return false;
> +
> + return !test_and_set_bit(tag, &anv->t8015_active_tags);
> +}
> +
> +static void apple_nvme_release_tag_t8015(struct apple_nvme *anv,
> + __u16 command_id)
> +{
> + u16 tag = nvme_tag_from_cid(command_id);
> +
> + if (WARN_ON_ONCE(tag >= BITS_PER_LONG))
> + return;
> +
> + clear_bit(tag, &anv->t8015_active_tags);
> +}
> +
> static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q,
> struct nvme_command *cmd)
> {
> @@ -652,6 +688,8 @@ static inline void apple_nvme_update_cq_head(struct apple_nvme_queue *q)
> static bool apple_nvme_poll_cq(struct apple_nvme_queue *q,
> struct io_comp_batch *iob)
> {
> + struct apple_nvme *anv = queue_to_apple_nvme(q);
> + unsigned long completed_tags = 0;
> bool found = false;
>
> while (apple_nvme_cqe_pending(q)) {
> @@ -664,11 +702,26 @@ static bool apple_nvme_poll_cq(struct apple_nvme_queue *q,
> dma_rmb();
> apple_nvme_handle_cqe(q, iob, q->cq_head);
> apple_nvme_update_cq_head(q);
> +
> + if (!anv->hw->has_lsq_nvmmu) {
> + struct nvme_completion *cqe = &q->cqes[q->cq_head];
> + u16 tag = nvme_tag_from_cid(READ_ONCE(cqe->command_id));
Reading command ID here is too late since cq head has already been updated.
> +
> + if (!WARN_ON_ONCE(tag >= BITS_PER_LONG))
> + __set_bit(tag, &completed_tags);
> + }
> }
>
> if (found)
> writel(q->cq_head, q->cq_db);
>
> + if (!anv->hw->has_lsq_nvmmu && completed_tags) {
> + unsigned long tag_bit;
> +
> + for_each_set_bit(tag_bit, &completed_tags, BITS_PER_LONG)
> + clear_bit(tag_bit, &anv->t8015_active_tags);
> + }
> +
> return found;
> }
>
> @@ -790,6 +843,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
> if (ret)
> return ret;
>
> + if (!anv->hw->has_lsq_nvmmu &&
> + !apple_nvme_reserve_tag_t8015(anv, cmnd)) {
> + ret = BLK_STS_RESOURCE;
> + goto out_free_cmd;
Note goto out_free_cmd here.
> + }
> +
> if (blk_rq_nr_phys_segments(req)) {
> ret = apple_nvme_map_data(anv, req, cmnd);
> if (ret)
> @@ -806,6 +865,9 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
> return BLK_STS_OK;
>
> out_free_cmd:
> + if (!anv->hw->has_lsq_nvmmu)
> + apple_nvme_release_tag_t8015(anv, cmnd->common.command_id);
Combined with above this makes any attempted use of a in-use tag release that
tag, making the workaround ineffective. (and allows nvme to still "work" if
the tester is (un)lucky).
> +
> nvme_cleanup_cmd(req);
> return ret;
> }
> @@ -1165,6 +1227,9 @@ static void apple_nvme_reset_work(struct work_struct *work)
> if (ret)
> goto out;
>
> + if (!anv->hw->has_lsq_nvmmu)
> + WRITE_ONCE(anv->t8015_active_tags, 0);
> +
> dev_dbg(anv->dev, "Starting admin queue");
> apple_nvme_init_queue(&anv->adminq);
> nvme_unquiesce_admin_queue(&anv->ctrl);
>
Best regards,
Nick Chan
^ permalink raw reply
* [PATCH net-next] net: airoha: Add TCP LRO support
From: Lorenzo Bianconi @ 2026-06-06 14:45 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Alexander Lobakin, linux-arm-kernel, linux-mediatek, netdev,
Madhur Agrawal
Add hardware TCP Large Receive Offload (LRO) support to the airoha_eth
driver, leveraging the EN7581/AN7583 SoC's 8 dedicated LRO hardware queues
mapped to RX queues 24–31. LRO hw offloading does not support
Scatter-Gather (SG) so it is required to increase the page_pool allocation
order to 2 for RX queues 24–31 (LRO queues).
Performance comparison between GRO and hw LRO has been carried out using
a 10Gbps NIC:
GRO: ~2.7 Gbps
LRO: ~8.1 Gbps
Please note with respect to the previous implementation, page_pool
allocation order has been reduced from 5 to 2.
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
Changes in v1:
- Please note this patch depends on the following patch not applied yet
to net-next
https://lore.kernel.org/netdev/20260606-airoha_qdma_users-no-atomic-v1-1-86e2d6a1bfaf@kernel.org/T/#u
- Restrict LRO to single user QDMA.
- Introduce some more sanity checks.
- Disable scatter-gather for LRO queues.
- Run netif_receive_skb() for LRO packets.
- Link to v3: https://lore.kernel.org/r/20260528-airoha-eth-lro-v3-1-dd09c1fb000e@kernel.org
Changes in RFC v3:
- Fix double-free of the page_pool of airoha_qdma_lro_rx_process()
fails.
- Set AIROHA_LRO_PAGE_ORDER according to PAGE_SIZE.
- Add missig gso metadata for the LRO packet.
- Link to v2: https://lore.kernel.org/r/20260526-airoha-eth-lro-v2-1-24e2a9e7a397@kernel.org
Changes in RFC v2:
- Improve performances fixing buf_size computation.
- Fix possible overflow in REG_CDM_LRO_LIMIT() register configuration.
- Require the device to be not running before configuring LRO.
- Fix configuration order in airoha_fe_lro_is_enabled().
- Check skb header length in airoha_qdma_lro_rx_process().
- Do not check net_device feature in airoha_qdma_rx_process() before
executing airoha_qdma_lro_rx_process() but rely on
airoha_qdma_lro_rx_process() logic.
- Fix possible double recycle in airoha_qdma_rx_process() for LRO
packets.
- Always use AIROHA_RXQ_LRO_MAX_AGG_COUNT macro for max LRO aggregated
fragments in airoha_fe_lro_init_rx_queue().
- Link to v1: https://lore.kernel.org/r/20260520-airoha-eth-lro-v1-1-129cc33766e9@kernel.org
---
drivers/net/ethernet/airoha/airoha_eth.c | 267 +++++++++++++++++++++++++++---
drivers/net/ethernet/airoha/airoha_eth.h | 24 +++
drivers/net/ethernet/airoha/airoha_regs.h | 22 ++-
3 files changed, 290 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index a5fe08864fa3..7431b901b7fd 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -12,6 +12,7 @@
#include <net/dst_metadata.h>
#include <net/page_pool/helpers.h>
#include <net/pkt_cls.h>
+#include <net/tcp.h>
#include <uapi/linux/ppp_defs.h>
#include "airoha_regs.h"
@@ -486,6 +487,48 @@ static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth)
CDM_CRSN_QSEL_Q1));
}
+static void airoha_fe_lro_init_rx_queue(struct airoha_eth *eth, int qdma_id,
+ int lro_queue_index, int qid,
+ int buf_size)
+{
+ int id = qdma_id + 1;
+
+ airoha_fe_rmw(eth, REG_CDM_LRO_LIMIT(id),
+ CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK,
+ FIELD_PREP(CDM_LRO_AGG_SIZE_MASK, buf_size) |
+ FIELD_PREP(CDM_LRO_AGG_NUM_MASK,
+ AIROHA_RXQ_LRO_MAX_AGG_COUNT));
+ airoha_fe_rmw(eth, REG_CDM_LRO_AGE_TIME(id),
+ CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK,
+ FIELD_PREP(CDM_LRO_AGE_TIME_MASK,
+ AIROHA_RXQ_LRO_MAX_AGE_TIME) |
+ FIELD_PREP(CDM_LRO_AGG_TIME_MASK,
+ AIROHA_RXQ_LRO_MAX_AGG_TIME));
+ airoha_fe_rmw(eth, REG_CDM_LRO_RXQ(id, lro_queue_index),
+ LRO_RXQ_MASK(lro_queue_index),
+ __field_prep(LRO_RXQ_MASK(lro_queue_index), qid));
+ airoha_fe_set(eth, REG_CDM_LRO_EN(id), BIT(lro_queue_index));
+}
+
+static void airoha_fe_lro_disable(struct airoha_eth *eth, int qdma_id)
+{
+ int i, id = qdma_id + 1;
+
+ airoha_fe_clear(eth, REG_CDM_LRO_EN(id), LRO_RXQ_EN_MASK);
+ airoha_fe_clear(eth, REG_CDM_LRO_LIMIT(id),
+ CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK);
+ airoha_fe_clear(eth, REG_CDM_LRO_AGE_TIME(id),
+ CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK);
+ for (i = 0; i < AIROHA_MAX_NUM_LRO_QUEUES; i++)
+ airoha_fe_clear(eth, REG_CDM_LRO_RXQ(id, i), LRO_RXQ_MASK(i));
+}
+
+static bool airoha_fe_lro_is_enabled(struct airoha_eth *eth, int qdma_id)
+{
+ return airoha_fe_get(eth, REG_CDM_LRO_EN(qdma_id + 1),
+ LRO_RXQ_EN_MASK);
+}
+
static int airoha_fe_init(struct airoha_eth *eth)
{
airoha_fe_maccr_init(eth);
@@ -603,6 +646,7 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q)
e->dma_addr = page_pool_get_dma_addr(page) + offset;
e->dma_len = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size));
+ WRITE_ONCE(desc->tcp_ts_reply, 0);
val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len);
WRITE_ONCE(desc->ctrl, cpu_to_le32(val));
WRITE_ONCE(desc->addr, cpu_to_le32(e->dma_addr));
@@ -644,6 +688,104 @@ airoha_qdma_get_gdm_dev(struct airoha_eth *eth, struct airoha_qdma_desc *desc)
return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV);
}
+static int airoha_qdma_lro_rx_process(struct sk_buff *skb,
+ struct airoha_qdma_desc *desc)
+{
+ u32 desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+ u32 len, th_off, tcp_ack_seq, agg_count, data_off;
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+ u32 msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
+ u32 msg2 = le32_to_cpu(READ_ONCE(desc->msg2));
+ u32 msg3 = le32_to_cpu(READ_ONCE(desc->msg3));
+ struct tcphdr *th, _th;
+ u16 tcp_win, l2_len;
+ bool ipv4, ipv6;
+
+ agg_count = FIELD_GET(QDMA_ETH_RXMSG_AGG_COUNT_MASK, msg2);
+ if (agg_count <= 1)
+ return 0;
+
+ ipv4 = FIELD_GET(QDMA_ETH_RXMSG_IP4_MASK, msg1);
+ ipv6 = FIELD_GET(QDMA_ETH_RXMSG_IP6_MASK, msg1);
+ if (!ipv4 && !ipv6)
+ return -EOPNOTSUPP;
+
+ l2_len = FIELD_GET(QDMA_ETH_RXMSG_L2_LEN_MASK, msg2);
+ len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
+ if (ipv4) {
+ struct iphdr *iph, _iph;
+
+ iph = skb_header_pointer(skb, l2_len, sizeof(*iph), &_iph);
+ if (!iph)
+ return -EINVAL;
+
+ if (iph->protocol != IPPROTO_TCP)
+ return -EOPNOTSUPP;
+
+ if (iph->ihl < 5)
+ return -EINVAL;
+
+ iph = skb_header_pointer(skb, l2_len, iph->ihl << 2, &_iph);
+ if (!iph)
+ return -EINVAL;
+
+ iph->tot_len = cpu_to_be16(len - l2_len);
+ iph->check = 0;
+ iph->check = ip_fast_csum((void *)iph, iph->ihl);
+ th_off = l2_len + (iph->ihl << 2);
+ } else {
+ struct ipv6hdr *ip6h, _ip6h;
+
+ ip6h = skb_header_pointer(skb, l2_len, sizeof(*ip6h), &_ip6h);
+ if (!ip6h)
+ return -EINVAL;
+
+ if (ip6h->nexthdr != NEXTHDR_TCP)
+ return -EOPNOTSUPP;
+
+ th_off = l2_len + sizeof(*ip6h);
+ ip6h->payload_len = cpu_to_be16(len - th_off);
+ }
+
+ tcp_win = FIELD_GET(QDMA_ETH_RXMSG_TCP_WIN_MASK, msg3);
+ tcp_ack_seq = le32_to_cpu(READ_ONCE(desc->data));
+
+ th = skb_header_pointer(skb, th_off, sizeof(*th), &_th);
+ if (!th)
+ return -EINVAL;
+
+ th->ack_seq = cpu_to_be32(tcp_ack_seq);
+ th->window = cpu_to_be16(tcp_win);
+
+ /* Check tcp timestamp option */
+ if (th->doff == (sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) / 4) {
+ u32 topt;
+
+ th = skb_header_pointer(skb, th_off, th->doff << 2, &_th);
+ if (!th)
+ return -EINVAL;
+
+ topt = get_unaligned_be32(th + 1);
+ if (topt == ((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+ (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {
+ u8 *ptr = (u8 *)th + sizeof(*th) + 2 * sizeof(__be32);
+ __le32 tcp_ts_reply = READ_ONCE(desc->tcp_ts_reply);
+
+ put_unaligned_be32(le32_to_cpu(tcp_ts_reply), ptr);
+ }
+ }
+
+ data_off = th_off + (th->doff << 2);
+ if (len <= data_off)
+ return -EINVAL;
+
+ shinfo->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
+ shinfo->gso_size = (len - data_off) / agg_count;
+ shinfo->gso_segs = agg_count;
+
+ return 0;
+}
+
static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
{
enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool);
@@ -694,9 +836,17 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
__skb_put(q->skb, len);
skb_mark_for_recycle(q->skb);
q->skb->dev = netdev;
- q->skb->protocol = eth_type_trans(q->skb, netdev);
q->skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(q->skb, qid);
+
+ if (airoha_qdma_lro_rx_process(q->skb, desc) < 0) {
+ netdev->stats.rx_dropped++;
+ dev_kfree_skb(q->skb);
+ q->skb = NULL;
+ continue;
+ }
+
+ q->skb->protocol = eth_type_trans(q->skb, netdev);
} else { /* scattered frame */
struct skb_shared_info *shinfo = skb_shinfo(q->skb);
int nr_frags = shinfo->nr_frags;
@@ -741,7 +891,10 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
false);
done++;
- napi_gro_receive(&q->napi, q->skb);
+ if (skb_is_gso(q->skb))
+ netif_receive_skb(q->skb);
+ else
+ napi_gro_receive(&q->napi, q->skb);
q->skb = NULL;
continue;
free_frag:
@@ -787,12 +940,10 @@ static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget)
static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
struct airoha_qdma *qdma, int ndesc)
{
- const struct page_pool_params pp_params = {
- .order = 0,
+ struct page_pool_params pp_params = {
.pool_size = 256,
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
.dma_dir = DMA_FROM_DEVICE,
- .max_len = PAGE_SIZE,
.nid = NUMA_NO_NODE,
.dev = qdma->eth->dev,
.napi = &q->napi,
@@ -800,9 +951,10 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
struct airoha_eth *eth = qdma->eth;
int qid = q - &qdma->q_rx[0], thr;
dma_addr_t dma_addr;
+ bool lro_q;
- q->buf_size = PAGE_SIZE / 2;
q->qdma = qdma;
+ lro_q = airoha_qdma_is_lro_queue(q);
q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
GFP_KERNEL);
@@ -814,6 +966,9 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
if (!q->desc)
return -ENOMEM;
+ pp_params.order = lro_q ? AIROHA_LRO_PAGE_ORDER : 0;
+ pp_params.max_len = PAGE_SIZE << pp_params.order;
+
q->page_pool = page_pool_create(&pp_params);
if (IS_ERR(q->page_pool)) {
int err = PTR_ERR(q->page_pool);
@@ -822,6 +977,7 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
return err;
}
+ q->buf_size = lro_q ? pp_params.max_len : pp_params.max_len / 2;
q->ndesc = ndesc;
netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
@@ -835,7 +991,12 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
FIELD_PREP(RX_RING_THR_MASK, thr));
airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head));
- airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid), RX_RING_SG_EN_MASK);
+ if (lro_q)
+ airoha_qdma_clear(qdma, REG_RX_SCATTER_CFG(qid),
+ RX_RING_SG_EN_MASK);
+ else
+ airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid),
+ RX_RING_SG_EN_MASK);
airoha_qdma_fill_rx_queue(q);
@@ -857,6 +1018,7 @@ static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q)
page_pool_get_dma_dir(q->page_pool));
page_pool_put_full_page(q->page_pool, page, false);
/* Reset DMA descriptor */
+ WRITE_ONCE(desc->tcp_ts_reply, 0);
WRITE_ONCE(desc->ctrl, 0);
WRITE_ONCE(desc->addr, 0);
WRITE_ONCE(desc->data, 0);
@@ -1778,6 +1940,18 @@ static int airoha_dev_open(struct net_device *netdev)
struct airoha_gdm_port *port = dev->port;
u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ int qdma_id = qdma - ð->qdma[0];
+
+ /* HW LRO is configured on the QDMA and it is shared between
+ * all the devices using it. Refuse to open a second device on
+ * the same QDMA if LRO is enabled on any device sharing it.
+ */
+ if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
+ netdev_warn(netdev, "required to disable LRO on QDMA%d\n",
+ qdma_id);
+ return -EBUSY;
+ }
netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(dev, true);
@@ -1785,13 +1959,13 @@ static int airoha_dev_open(struct net_device *netdev)
return err;
if (netdev_uses_dsa(netdev))
- airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
+ airoha_fe_set(eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
else
- airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
+ airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
- cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ cur_len = airoha_fe_get(eth, REG_GDM_LEN_CFG(port->id),
GDM_LONG_LEN_MASK);
if (!port->users || len > cur_len) {
/* Opening a sibling net_device with a larger MTU updates the
@@ -1799,7 +1973,7 @@ static int airoha_dev_open(struct net_device *netdev)
* multiple net_devices with different MTUs to share the same
* GDM port.
*/
- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
FIELD_PREP(GDM_LONG_LEN_MASK, len));
@@ -1811,10 +1985,9 @@ static int airoha_dev_open(struct net_device *netdev)
GLOBAL_CFG_RX_DMA_EN_MASK);
qdma->users++;
- if (!airoha_is_lan_gdm_dev(dev) &&
- airoha_ppe_is_enabled(qdma->eth, 1))
+ if (!airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1))
pse_port = FE_PSE_PORT_PPE2;
- airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
pse_port);
return 0;
@@ -1849,6 +2022,7 @@ static int airoha_dev_stop(struct net_device *netdev)
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_eth *eth = qdma->eth;
int i;
netif_tx_disable(netdev);
@@ -1857,10 +2031,9 @@ static int airoha_dev_stop(struct net_device *netdev)
netdev_tx_reset_subqueue(netdev, i);
if (--port->users)
- airoha_set_port_mtu(dev->eth, port);
+ airoha_set_port_mtu(eth, port);
else
- airoha_set_gdm_port_fwd_cfg(qdma->eth,
- REG_GDM_FWD_CFG(port->id),
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
if (!--qdma->users) {
@@ -2154,6 +2327,56 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev)
}
}
+static netdev_features_t airoha_dev_fix_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma = dev->qdma;
+
+ if (qdma->users > 1)
+ features &= ~NETIF_F_LRO;
+
+ return features;
+}
+
+static int airoha_dev_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ netdev_features_t diff = netdev->features ^ features;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ int qdma_id = qdma - ð->qdma[0];
+
+ if (!(diff & NETIF_F_LRO))
+ return 0;
+
+ if (features & NETIF_F_LRO) {
+ int i, lro_queue_index = 0;
+
+ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+ struct airoha_queue *q = &qdma->q_rx[i];
+ u32 size;
+
+ if (!q->ndesc)
+ continue;
+
+ if (!airoha_qdma_is_lro_queue(q))
+ continue;
+
+ size = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size));
+ size = min_t(u32, size, CDM_LRO_AGG_SIZE_MASK);
+ airoha_fe_lro_init_rx_queue(eth, qdma_id,
+ lro_queue_index, i, size);
+ lro_queue_index++;
+ }
+ } else {
+ airoha_fe_lro_disable(eth, qdma_id);
+ }
+
+ return 0;
+}
+
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
@@ -3082,6 +3305,8 @@ static const struct net_device_ops airoha_netdev_ops = {
.ndo_stop = airoha_dev_stop,
.ndo_change_mtu = airoha_dev_change_mtu,
.ndo_select_queue = airoha_dev_select_queue,
+ .ndo_fix_features = airoha_dev_fix_features,
+ .ndo_set_features = airoha_dev_set_features,
.ndo_start_xmit = airoha_dev_xmit,
.ndo_get_stats64 = airoha_dev_get_stats64,
.ndo_set_mac_address = airoha_dev_set_macaddr,
@@ -3169,11 +3394,9 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
netdev->ethtool_ops = &airoha_ethtool_ops;
netdev->max_mtu = AIROHA_MAX_MTU;
netdev->watchdog_timeo = 5 * HZ;
- netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO6 |
- NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO |
- NETIF_F_HW_TC;
- netdev->features |= netdev->hw_features;
- netdev->vlan_features = netdev->hw_features;
+ netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO;
+ netdev->features |= AIROHA_HW_FEATURES;
+ netdev->vlan_features = AIROHA_HW_FEATURES;
SET_NETDEV_DEV(netdev, eth->dev);
/* reserve hw queues for HTB offloading */
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index e89c2dff411f..71d439f89f02 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -44,6 +44,18 @@
(_n) == 15 ? 128 : \
(_n) == 0 ? 1024 : 16)
+#define AIROHA_LRO_PAGE_ORDER order_base_2(SZ_16K / PAGE_SIZE)
+#define AIROHA_MAX_NUM_LRO_QUEUES 8
+#define AIROHA_RXQ_LRO_EN_MASK 0xff000000
+#define AIROHA_RXQ_LRO_MAX_AGG_COUNT 64
+#define AIROHA_RXQ_LRO_MAX_AGG_TIME 100
+#define AIROHA_RXQ_LRO_MAX_AGE_TIME 2000
+
+#define AIROHA_HW_FEATURES \
+ (NETIF_F_IP_CSUM | NETIF_F_RXCSUM | \
+ NETIF_F_TSO6 | NETIF_F_IPV6_CSUM | \
+ NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_TC)
+
#define PSE_RSV_PAGES 128
#define PSE_QUEUE_RSV_PAGES 64
@@ -671,6 +683,18 @@ static inline bool airoha_is_7583(struct airoha_eth *eth)
return eth->soc->version == 0x7583;
}
+static inline bool airoha_qdma_is_lro_queue(struct airoha_queue *q)
+{
+ struct airoha_qdma *qdma = q->qdma;
+ int qid = q - &qdma->q_rx[0];
+
+ /* EN7581 SoC supports at most 8 LRO rx queues */
+ BUILD_BUG_ON(hweight32(AIROHA_RXQ_LRO_EN_MASK) >
+ AIROHA_MAX_NUM_LRO_QUEUES);
+
+ return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
+}
+
int airoha_get_fe_port(struct airoha_gdm_dev *dev);
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
index 436f3c8779c1..dfc786583774 100644
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -122,6 +122,20 @@
#define CDM_CRSN_QSEL_REASON_MASK(_n) \
GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3))
+#define REG_CDM_LRO_RXQ(_n, _m) (CDM_BASE(_n) + 0x78 + ((_m) & 0x4))
+#define LRO_RXQ_MASK(_n) GENMASK(4 + (((_n) & 0x3) << 3), ((_n) & 0x3) << 3)
+
+#define REG_CDM_LRO_EN(_n) (CDM_BASE(_n) + 0x80)
+#define LRO_RXQ_EN_MASK GENMASK(7, 0)
+
+#define REG_CDM_LRO_LIMIT(_n) (CDM_BASE(_n) + 0x84)
+#define CDM_LRO_AGG_NUM_MASK GENMASK(23, 16)
+#define CDM_LRO_AGG_SIZE_MASK GENMASK(15, 0)
+
+#define REG_CDM_LRO_AGE_TIME(_n) (CDM_BASE(_n) + 0x88)
+#define CDM_LRO_AGE_TIME_MASK GENMASK(31, 16)
+#define CDM_LRO_AGG_TIME_MASK GENMASK(15, 0)
+
#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n)
#define GDM_PAD_EN_MASK BIT(28)
#define GDM_DROP_CRC_ERR_MASK BIT(23)
@@ -883,9 +897,15 @@
#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21)
#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16)
#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0)
+/* RX MSG2 */
+#define QDMA_ETH_RXMSG_AGG_COUNT_MASK GENMASK(31, 24)
+#define QDMA_ETH_RXMSG_L2_LEN_MASK GENMASK(6, 0)
+/* RX MSG3 */
+#define QDMA_ETH_RXMSG_AGG_LEN_MASK GENMASK(31, 16)
+#define QDMA_ETH_RXMSG_TCP_WIN_MASK GENMASK(15, 0)
struct airoha_qdma_desc {
- __le32 rsv;
+ __le32 tcp_ts_reply;
__le32 ctrl;
__le32 addr;
__le32 data;
---
base-commit: 1fc7e55677b09ff8a38f90fb5563f80b067d5475
change-id: 20260520-airoha-eth-lro-a5d1c3631811
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply related
* Re: [PATCH 2/2] nvme-apple: Prevent tag collision across queues even if tag space is shared
From: David Laight @ 2026-06-06 14:29 UTC (permalink / raw)
To: Nick Chan
Cc: Sven Peter, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, asahi, linux-arm-kernel,
linux-nvme, linux-kernel, stable, Yuriy Havrylyuk
In-Reply-To: <20260606-prevent-tag-collision-t8015-v1-2-93ccf4eca550@gmail.com>
On Sat, 06 Jun 2026 21:25:26 +0800
Nick Chan <towinchenmi@gmail.com> wrote:
> From: Yuriy Havrylyuk <yhavry@gmail.com>
>
> Apple NVMe controllers require tags of pending commands to not be shared
> across admin and IO queues. However, on Apple A11 without linear SQ, it is
> not possible for either queue to skip over some tags and must go from 0 to
> the configured maximum before wrapping around.
>
> If a pending command tag is duplicated across queues, the firmware
> crashes with: "duplicate tag error for tag N", with N being the tag.
>
> Instead of partitioning the tag space, which is not possible without
> linear SQ, prevent tag collisions by keeping track of which tags are
> currently in-flight across either queues, and return BLK_STS_RESOURCE to
> temporaily block command submission when a collision would have occurred.
I look at using the atomic64_xxx() functions rather than the bitmask ones.
The for_each_bit_set() loop is then an atmomic64_andnot() call.
-- David
>
> Cc: stable@vger.kernel.org
> Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support")
> Signed-off-by: Yuriy Havrylyuk <yhavry@gmail.com>
> Co-developed-by: Nick Chan <towinchenmi@gmail.com>
> Signed-off-by: Nick Chan <towinchenmi@gmail.com>
> ---
> drivers/nvme/host/apple.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 65 insertions(+)
>
> diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
> index c1115e27a0d6..6354edf27225 100644
> --- a/drivers/nvme/host/apple.c
> +++ b/drivers/nvme/host/apple.c
> @@ -203,6 +203,20 @@ struct apple_nvme {
>
> int irq;
> spinlock_t lock;
> +
> + /*
> + * Tags of pending commands must be unique across both Admin and IO
> + * queue. However, on T8015, unlike T8103, without linear submission
> + * queues, it is not possible for the either queue to skip some tags,
> + * and both queues must go from 0 to their respective configured
> + * maximum.
> + *
> + * Instead of reserving some tags for the admin queue, use a bitfield
> + * to keep track of pending commands on either queue, and temporaily
> + * block command submission by returning BLK_STS_RESOURCE until the
> + * tag is freed on the other queue.
> + */
> + unsigned long t8015_active_tags;
> };
>
> static_assert(sizeof(struct nvme_command) == 64);
> @@ -290,6 +304,28 @@ static void apple_nvmmu_inval(struct apple_nvme_queue *q, unsigned int tag)
> "NVMMU TCB invalidation failed\n");
> }
>
> +static bool apple_nvme_reserve_tag_t8015(struct apple_nvme *anv,
> + struct nvme_command *cmd)
> +{
> + u16 tag = nvme_tag_from_cid(cmd->common.command_id);
> +
> + if (WARN_ON_ONCE(tag >= BITS_PER_LONG))
> + return false;
> +
> + return !test_and_set_bit(tag, &anv->t8015_active_tags);
> +}
> +
> +static void apple_nvme_release_tag_t8015(struct apple_nvme *anv,
> + __u16 command_id)
> +{
> + u16 tag = nvme_tag_from_cid(command_id);
> +
> + if (WARN_ON_ONCE(tag >= BITS_PER_LONG))
> + return;
> +
> + clear_bit(tag, &anv->t8015_active_tags);
> +}
> +
> static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q,
> struct nvme_command *cmd)
> {
> @@ -652,6 +688,8 @@ static inline void apple_nvme_update_cq_head(struct apple_nvme_queue *q)
> static bool apple_nvme_poll_cq(struct apple_nvme_queue *q,
> struct io_comp_batch *iob)
> {
> + struct apple_nvme *anv = queue_to_apple_nvme(q);
> + unsigned long completed_tags = 0;
> bool found = false;
>
> while (apple_nvme_cqe_pending(q)) {
> @@ -664,11 +702,26 @@ static bool apple_nvme_poll_cq(struct apple_nvme_queue *q,
> dma_rmb();
> apple_nvme_handle_cqe(q, iob, q->cq_head);
> apple_nvme_update_cq_head(q);
> +
> + if (!anv->hw->has_lsq_nvmmu) {
> + struct nvme_completion *cqe = &q->cqes[q->cq_head];
> + u16 tag = nvme_tag_from_cid(READ_ONCE(cqe->command_id));
> +
> + if (!WARN_ON_ONCE(tag >= BITS_PER_LONG))
> + __set_bit(tag, &completed_tags);
> + }
> }
>
> if (found)
> writel(q->cq_head, q->cq_db);
>
> + if (!anv->hw->has_lsq_nvmmu && completed_tags) {
> + unsigned long tag_bit;
> +
> + for_each_set_bit(tag_bit, &completed_tags, BITS_PER_LONG)
> + clear_bit(tag_bit, &anv->t8015_active_tags);
> + }
> +
> return found;
> }
>
> @@ -790,6 +843,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
> if (ret)
> return ret;
>
> + if (!anv->hw->has_lsq_nvmmu &&
> + !apple_nvme_reserve_tag_t8015(anv, cmnd)) {
> + ret = BLK_STS_RESOURCE;
> + goto out_free_cmd;
> + }
> +
> if (blk_rq_nr_phys_segments(req)) {
> ret = apple_nvme_map_data(anv, req, cmnd);
> if (ret)
> @@ -806,6 +865,9 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
> return BLK_STS_OK;
>
> out_free_cmd:
> + if (!anv->hw->has_lsq_nvmmu)
> + apple_nvme_release_tag_t8015(anv, cmnd->common.command_id);
> +
> nvme_cleanup_cmd(req);
> return ret;
> }
> @@ -1165,6 +1227,9 @@ static void apple_nvme_reset_work(struct work_struct *work)
> if (ret)
> goto out;
>
> + if (!anv->hw->has_lsq_nvmmu)
> + WRITE_ONCE(anv->t8015_active_tags, 0);
> +
> dev_dbg(anv->dev, "Starting admin queue");
> apple_nvme_init_queue(&anv->adminq);
> nvme_unquiesce_admin_queue(&anv->ctrl);
>
^ permalink raw reply
* [PATCH 2/2] nvme-apple: Prevent tag collision across queues even if tag space is shared
From: Nick Chan @ 2026-06-06 13:25 UTC (permalink / raw)
To: Sven Peter, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg
Cc: asahi, linux-arm-kernel, linux-nvme, linux-kernel, stable,
Nick Chan, Yuriy Havrylyuk
In-Reply-To: <20260606-prevent-tag-collision-t8015-v1-0-93ccf4eca550@gmail.com>
From: Yuriy Havrylyuk <yhavry@gmail.com>
Apple NVMe controllers require tags of pending commands to not be shared
across admin and IO queues. However, on Apple A11 without linear SQ, it is
not possible for either queue to skip over some tags and must go from 0 to
the configured maximum before wrapping around.
If a pending command tag is duplicated across queues, the firmware
crashes with: "duplicate tag error for tag N", with N being the tag.
Instead of partitioning the tag space, which is not possible without
linear SQ, prevent tag collisions by keeping track of which tags are
currently in-flight across either queues, and return BLK_STS_RESOURCE to
temporaily block command submission when a collision would have occurred.
Cc: stable@vger.kernel.org
Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support")
Signed-off-by: Yuriy Havrylyuk <yhavry@gmail.com>
Co-developed-by: Nick Chan <towinchenmi@gmail.com>
Signed-off-by: Nick Chan <towinchenmi@gmail.com>
---
drivers/nvme/host/apple.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
index c1115e27a0d6..6354edf27225 100644
--- a/drivers/nvme/host/apple.c
+++ b/drivers/nvme/host/apple.c
@@ -203,6 +203,20 @@ struct apple_nvme {
int irq;
spinlock_t lock;
+
+ /*
+ * Tags of pending commands must be unique across both Admin and IO
+ * queue. However, on T8015, unlike T8103, without linear submission
+ * queues, it is not possible for the either queue to skip some tags,
+ * and both queues must go from 0 to their respective configured
+ * maximum.
+ *
+ * Instead of reserving some tags for the admin queue, use a bitfield
+ * to keep track of pending commands on either queue, and temporaily
+ * block command submission by returning BLK_STS_RESOURCE until the
+ * tag is freed on the other queue.
+ */
+ unsigned long t8015_active_tags;
};
static_assert(sizeof(struct nvme_command) == 64);
@@ -290,6 +304,28 @@ static void apple_nvmmu_inval(struct apple_nvme_queue *q, unsigned int tag)
"NVMMU TCB invalidation failed\n");
}
+static bool apple_nvme_reserve_tag_t8015(struct apple_nvme *anv,
+ struct nvme_command *cmd)
+{
+ u16 tag = nvme_tag_from_cid(cmd->common.command_id);
+
+ if (WARN_ON_ONCE(tag >= BITS_PER_LONG))
+ return false;
+
+ return !test_and_set_bit(tag, &anv->t8015_active_tags);
+}
+
+static void apple_nvme_release_tag_t8015(struct apple_nvme *anv,
+ __u16 command_id)
+{
+ u16 tag = nvme_tag_from_cid(command_id);
+
+ if (WARN_ON_ONCE(tag >= BITS_PER_LONG))
+ return;
+
+ clear_bit(tag, &anv->t8015_active_tags);
+}
+
static void apple_nvme_submit_cmd_t8015(struct apple_nvme_queue *q,
struct nvme_command *cmd)
{
@@ -652,6 +688,8 @@ static inline void apple_nvme_update_cq_head(struct apple_nvme_queue *q)
static bool apple_nvme_poll_cq(struct apple_nvme_queue *q,
struct io_comp_batch *iob)
{
+ struct apple_nvme *anv = queue_to_apple_nvme(q);
+ unsigned long completed_tags = 0;
bool found = false;
while (apple_nvme_cqe_pending(q)) {
@@ -664,11 +702,26 @@ static bool apple_nvme_poll_cq(struct apple_nvme_queue *q,
dma_rmb();
apple_nvme_handle_cqe(q, iob, q->cq_head);
apple_nvme_update_cq_head(q);
+
+ if (!anv->hw->has_lsq_nvmmu) {
+ struct nvme_completion *cqe = &q->cqes[q->cq_head];
+ u16 tag = nvme_tag_from_cid(READ_ONCE(cqe->command_id));
+
+ if (!WARN_ON_ONCE(tag >= BITS_PER_LONG))
+ __set_bit(tag, &completed_tags);
+ }
}
if (found)
writel(q->cq_head, q->cq_db);
+ if (!anv->hw->has_lsq_nvmmu && completed_tags) {
+ unsigned long tag_bit;
+
+ for_each_set_bit(tag_bit, &completed_tags, BITS_PER_LONG)
+ clear_bit(tag_bit, &anv->t8015_active_tags);
+ }
+
return found;
}
@@ -790,6 +843,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
if (ret)
return ret;
+ if (!anv->hw->has_lsq_nvmmu &&
+ !apple_nvme_reserve_tag_t8015(anv, cmnd)) {
+ ret = BLK_STS_RESOURCE;
+ goto out_free_cmd;
+ }
+
if (blk_rq_nr_phys_segments(req)) {
ret = apple_nvme_map_data(anv, req, cmnd);
if (ret)
@@ -806,6 +865,9 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
return BLK_STS_OK;
out_free_cmd:
+ if (!anv->hw->has_lsq_nvmmu)
+ apple_nvme_release_tag_t8015(anv, cmnd->common.command_id);
+
nvme_cleanup_cmd(req);
return ret;
}
@@ -1165,6 +1227,9 @@ static void apple_nvme_reset_work(struct work_struct *work)
if (ret)
goto out;
+ if (!anv->hw->has_lsq_nvmmu)
+ WRITE_ONCE(anv->t8015_active_tags, 0);
+
dev_dbg(anv->dev, "Starting admin queue");
apple_nvme_init_queue(&anv->adminq);
nvme_unquiesce_admin_queue(&anv->ctrl);
--
2.54.0
^ permalink raw reply related
* [PATCH 1/2] nvme-apple: Only limit admin queue tag space when with Linear SQ is present
From: Nick Chan @ 2026-06-06 13:25 UTC (permalink / raw)
To: Sven Peter, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg
Cc: asahi, linux-arm-kernel, linux-nvme, linux-kernel, stable,
Nick Chan
In-Reply-To: <20260606-prevent-tag-collision-t8015-v1-0-93ccf4eca550@gmail.com>
Apple NVMe controllers require tags of pending commands to not be shared
across admin and IO queues. However, on Apple A11 without linear SQ, it is
not possible for either queue to skip over some tags and must go from 0 to
the configured maximum before wrapping around.
As a result, in order to prevent tag collision, dynamic tag reservation
while a command is in-flight becomes necessary. In this context, there is
no reason to limit the admin queue's tag space, as it is not helpful in
preventing tag collision.
Cc: stable@vger.kernel.org
Fixes: 04d8ecf37b5e ("nvme: apple: Add Apple A11 support")
Signed-off-by: Nick Chan <towinchenmi@gmail.com>
---
drivers/nvme/host/apple.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
index c692fc73babf..c1115e27a0d6 100644
--- a/drivers/nvme/host/apple.c
+++ b/drivers/nvme/host/apple.c
@@ -1303,7 +1303,10 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv)
anv->admin_tagset.ops = &apple_nvme_mq_admin_ops;
anv->admin_tagset.nr_hw_queues = 1;
- anv->admin_tagset.queue_depth = APPLE_NVME_AQ_MQ_TAG_DEPTH;
+ if (anv->hw->has_lsq_nvmmu)
+ anv->admin_tagset.queue_depth = APPLE_NVME_AQ_MQ_TAG_DEPTH;
+ else
+ anv->admin_tagset.queue_depth = anv->hw->max_queue_depth - 1;
anv->admin_tagset.timeout = NVME_ADMIN_TIMEOUT;
anv->admin_tagset.numa_node = NUMA_NO_NODE;
anv->admin_tagset.cmd_size = sizeof(struct apple_nvme_iod);
--
2.54.0
^ permalink raw reply related
* [PATCH 0/2] nvme-apple: Prevent tag collision across queues on Apple A11
From: Nick Chan @ 2026-06-06 13:25 UTC (permalink / raw)
To: Sven Peter, Janne Grunau, Neal Gompa, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg
Cc: asahi, linux-arm-kernel, linux-nvme, linux-kernel, stable,
Nick Chan, Yuriy Havrylyuk
Apple NVMe controllers require tags of pending commands to not be shared
across admin and IO queues.
If a pending command tag is duplicated across queues, the firmware
crashes with: "duplicate tag error for tag N", with N being the tag.
On Apple M1 or above, this is worked around by partitioning the tag
space between the admin and IO queue.
However, on Apple A11 without linear SQ, it is not possible for either
queue to skip over some tags and must go from 0 to the configured maximum
before wrapping around.
Instead of partitioning the tag space, which is not possible without
linear SQ, prevent tag collisions by keeping track of which tags are
currently in-flight across either queues, and return BLK_STS_RESOURCE to
temporaily block command submission when a collision would have occured.
While fixing the issue, it became apparent the admin queue tag space
is limited even on Apple A11. There is no reason to do this as it hampers
performance and does not help preventing tag collisions, so also allow
the admin queue to use the full tag space.
Tested on iPhone 8, iPhone X and Macbook Pro (14-inch, M1 Pro, 2021).
Signed-off-by: Nick Chan <towinchenmi@gmail.com>
---
Nick Chan (1):
nvme-apple: Only limit admin queue tag space when with Linear SQ is present
Yuriy Havrylyuk (1):
nvme-apple: Prevent tag collision across queues even if tag space is shared
drivers/nvme/host/apple.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 69 insertions(+), 1 deletion(-)
---
base-commit: e43ffb69e0438cddd72aaa30898b4dc446f664f8
change-id: 20260606-prevent-tag-collision-t8015-1c8adb3234de
Best regards,
--
Nick Chan <towinchenmi@gmail.com>
^ permalink raw reply
* [PATCH] hwrng: atmel - drop __maybe_unused from atmel_trng_pm_ops
From: Thorsten Blum @ 2026-06-06 13:17 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Nicolas Ferre, Alexandre Belloni,
Claudiu Beznea, Sakari Ailus, AngeloGioacchino Del Regno
Cc: Thorsten Blum, linux-crypto, linux-arm-kernel, linux-kernel
Since atmel_trng_driver keeps atmel_trng_pm_ops referenced and pm_ptr()
uses IS_ENABLED(), which allows the compiler to optimize away unused
variables, drop the redundant __maybe_unused annotation.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
drivers/char/hw_random/atmel-rng.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c
index 6ed24be3481d..10082add0886 100644
--- a/drivers/char/hw_random/atmel-rng.c
+++ b/drivers/char/hw_random/atmel-rng.c
@@ -186,7 +186,7 @@ static int __maybe_unused atmel_trng_runtime_resume(struct device *dev)
return atmel_trng_init(trng);
}
-static const struct dev_pm_ops __maybe_unused atmel_trng_pm_ops = {
+static const struct dev_pm_ops atmel_trng_pm_ops = {
SET_RUNTIME_PM_OPS(atmel_trng_runtime_suspend,
atmel_trng_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
base-commit: 79bbe453e5bfa6e1c6aa2e8329bfc8f152b81c9b
^ permalink raw reply related
* Re: [PATCH v3 1/3] dt-bindings: display: msm: qcm2290: Add Shikra MDSS
From: Dmitry Baryshkov @ 2026-06-06 12:23 UTC (permalink / raw)
To: Nabige Aala
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Krishna Manikandan,
Loic Poulain, Bjorn Andersson, Konrad Dybcio, Will Deacon,
Robin Murphy, Joerg Roedel (AMD), linux-arm-msm, dri-devel,
freedreno, devicetree, linux-kernel, iommu, linux-arm-kernel
In-Reply-To: <20260605-shikra-display-v3-1-9846ba5fe635@oss.qualcomm.com>
On Fri, Jun 05, 2026 at 03:48:51PM +0530, Nabige Aala wrote:
> Shikra reuses the same MDSS/DPU 6.5 hardware as QCM2290. Extend
> the existing qcm2290 bindings to cover Shikra by adding fallback
> compatible chains for MDSS, DPU and DSI controller nodes rather
> than introducing a separate binding file.
>
> Signed-off-by: Nabige Aala <nabige.aala@oss.qualcomm.com>
> ---
> .../bindings/display/msm/dsi-controller-main.yaml | 4 ++++
> .../bindings/display/msm/qcom,qcm2290-dpu.yaml | 7 +++++--
> .../bindings/display/msm/qcom,qcm2290-mdss.yaml | 22 +++++++++++++++-------
> 3 files changed, 24 insertions(+), 9 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml
> index dbc0613e427e..ab2cfd6d6e3e 100644
> --- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml
> +++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml
> @@ -57,6 +57,10 @@ properties:
> - const: qcom,eliza-dsi-ctrl
> - const: qcom,sm8750-dsi-ctrl
> - const: qcom,mdss-dsi-ctrl
> + - items:
> + - const: qcom,shikra-dsi-ctrl
> + - const: qcom,qcm2290-dsi-ctrl
> + - const: qcom,mdss-dsi-ctrl
Wrong sort order, qcm2290 < sm8750
> - enum:
> - qcom,dsi-ctrl-6g-qcm2290
> - qcom,mdss-dsi-ctrl # This should always come with an SoC-specific compatible
> diff --git a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-dpu.yaml b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-dpu.yaml
> index be6cd8adb3b6..e166a73651df 100644
> --- a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-dpu.yaml
> +++ b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-dpu.yaml
> @@ -13,8 +13,11 @@ $ref: /schemas/display/msm/dpu-common.yaml#
>
> properties:
> compatible:
> - const: qcom,qcm2290-dpu
> -
> + oneOf:
> + - const: qcom,qcm2290-dpu
> + - items:
> + - const: qcom,shikra-dpu
> + - const: qcom,qcm2290-dpu
> reg:
> items:
> - description: Address offset and size for mdp register set
> diff --git a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml
> index bb09ecd1a5b4..ef21b2c263f2 100644
> --- a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml
> +++ b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml
> @@ -4,7 +4,7 @@
> $id: http://devicetree.org/schemas/display/msm/qcom,qcm2290-mdss.yaml#
> $schema: http://devicetree.org/meta-schemas/core.yaml#
>
> -title: Qualcomm QCM220 Display MDSS
> +title: Qualcomm QCM2290 and Shikra Display MDSS
>
> maintainers:
> - Loic Poulain <loic.poulain@linaro.org>
> @@ -12,13 +12,18 @@ maintainers:
> description:
> Device tree bindings for MSM Mobile Display Subsystem(MDSS) that encapsulates
> sub-blocks like DPU display controller and DSI. Device tree bindings of MDSS
> - are mentioned for QCM2290 target.
> + are mentioned for QCM2290 and Shikra targets. Shikra uses the same MDSS/DPU/DSI
> + hardware as QCM2290 (DPU 6.5) and shares the same register layout.
>
> $ref: /schemas/display/msm/mdss-common.yaml#
>
> properties:
> compatible:
> - const: qcom,qcm2290-mdss
> + oneOf:
> + - const: qcom,qcm2290-mdss
> + - items:
> + - const: qcom,shikra-mdss
> + - const: qcom,qcm2290-mdss
>
> clocks:
> items:
> @@ -52,7 +57,11 @@ patternProperties:
>
> properties:
> compatible:
> - const: qcom,qcm2290-dpu
> + oneOf:
> + - const: qcom,qcm2290-dpu
> + - items:
> + - const: qcom,shikra-dpu
> + - const: qcom,qcm2290-dpu
Use contains:
>
> "^dsi@[0-9a-f]+$":
> type: object
> @@ -60,9 +69,8 @@ patternProperties:
>
> properties:
> compatible:
> - items:
> - - const: qcom,qcm2290-dsi-ctrl
> - - const: qcom,mdss-dsi-ctrl
> + contains:
> + const: qcom,qcm2290-dsi-ctrl
>
> "^phy@[0-9a-f]+$":
> type: object
>
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply
* Re: [PATCH v3 2/3] arm64: defconfig: Enable ILI7807S DSI panel driver
From: Dmitry Baryshkov @ 2026-06-06 12:17 UTC (permalink / raw)
To: Nabige Aala
Cc: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Krishna Manikandan,
Loic Poulain, Bjorn Andersson, Konrad Dybcio, Will Deacon,
Robin Murphy, Joerg Roedel (AMD), linux-arm-msm, dri-devel,
freedreno, devicetree, linux-kernel, iommu, linux-arm-kernel
In-Reply-To: <20260605-shikra-display-v3-2-9846ba5fe635@oss.qualcomm.com>
On Fri, Jun 05, 2026 at 03:48:52PM +0530, Nabige Aala wrote:
> Enable the ILI7807S 1080x1920 video-mode DSI panel driver as a module,
> used on the Shikra CQM EVK board.
>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Signed-off-by: Nabige Aala <nabige.aala@oss.qualcomm.com>
> ---
> arch/arm64/configs/defconfig | 1 +
> 1 file changed, 1 insertion(+)
Okay, you are really not reading any feedback. This patch doesn't belong
here. Move it to the series adding the actual DT with the panel.
--
With best wishes
Dmitry
^ permalink raw reply
* Re: [PATCH v2 2/2] KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU
From: Marc Zyngier @ 2026-06-06 10:52 UTC (permalink / raw)
To: Hyunwoo Kim
Cc: tabba, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will, linux-arm-kernel, kvmarm
In-Reply-To: <aiPfuAmM8JbtT6GY@v4bel>
On Sat, 06 Jun 2026 09:52:08 +0100,
Hyunwoo Kim <imv4bel@gmail.com> wrote:
>
> On Fri, Jun 05, 2026 at 09:58:09AM +0100, Marc Zyngier wrote:
> > On Thu, 04 Jun 2026 16:12:03 +0100,
> > Hyunwoo Kim <imv4bel@gmail.com> wrote:
> > >
> > > flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU
> > > on every run. The vGIC list register save and restore use used_lrs as
> > > their loop bound and expect it to stay within the number of implemented
> > > list registers. While this is generally the case, flush_hyp_vcpu()
> > > copies vgic_v3 verbatim and does not enforce this, so a value provided
> > > by the host is used at EL2 to index vgic_lr[] and access ICH_LR<n>_EL2
> > > (host -> EL2).
> > >
> > > Fix by clamping used_lrs to the number of implemented list registers
> > > after the copy, as the trusted path already does in
> > > vgic_flush_lr_state().
> > >
> > > Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()")
> > > Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
> > > ---
> > > arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 ++++++++
> > > 1 file changed, 8 insertions(+)
> > >
> > > diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > > index 02c5d6e5abcbf..cd807fdb11ba8 100644
> > > --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > > +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > > @@ -7,6 +7,7 @@
> > > #include <hyp/adjust_pc.h>
> > > #include <hyp/switch.h>
> > >
> > > +#include <asm/arch_gicv3.h>
> > > #include <asm/pgtable-types.h>
> > > #include <asm/kvm_asm.h>
> > > #include <asm/kvm_emulate.h>
> > > @@ -142,6 +143,13 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
> > >
> > > hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
> > >
> > > + /* Bound used_lrs by the number of implemented list registers. */
> > > + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
> >
> > There is no pKVM support without a GICv3 CPU interface, and absolutely
> > everything already assumes it. Why do we need this extra check?
> >
> > > + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
> > > + min_t(unsigned int,
> > > + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
> > > + (read_gicreg(ICH_VTR_EL2) & 0xf) + 1);
> > > +
> >
> > Reading ICH_VTR_EL2 on each entry is going to cause some really heavy
> > trapping under NV, and we should avoid this.
> >
> > kvm_vgic_global_state.nr_lr contains this information, and it should
> > only be a matter of replicating it (or compute it once) at init time.
>
> Does this approach look reasonable to you?
>
> ---
>
> diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
> index 8d06b62e7188..25199769a1d6 100644
> --- a/arch/arm64/include/asm/kvm_hyp.h
> +++ b/arch/arm64/include/asm/kvm_hyp.h
> @@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icache_flags);
> extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits);
> extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl);
> extern unsigned long kvm_nvhe_sym(hyp_nr_cpus);
> +extern unsigned int kvm_nvhe_sym(hyp_vgic_nr_lr);
>
> #endif /* __ARM64_KVM_HYP_H__ */
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 9453321ef8c6..891fe2c7b854 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -2426,6 +2426,7 @@ static int __init init_subsystems(void)
> switch (err) {
> case 0:
> vgic_present = true;
> + kvm_nvhe_sym(hyp_vgic_nr_lr) = kvm_vgic_global_state.nr_lr;
You probably want to guard this on kvm_vgic_global_state.gicv3_cpuif
to be sure this stays to 0 when running with a GICv2 implementation.
And maybe rename it to hyp_gicv3_nr_lr to avoid any confusion.
> break;
> case -ENODEV:
> case -ENXIO:
> diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> index 06db299c37a8..8bb9362bc284 100644
> --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> @@ -128,6 +128,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
>
> hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
>
> + /* A guest context must keep a NULL __hyp_running_vcpu. */
> + hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL;
> +
> hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
> hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
> hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
> @@ -139,6 +142,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
>
> hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
>
> + /* Bound used_lrs by the number of implemented list registers. */
> + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
> + min_t(unsigned int,
> + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
> + hyp_vgic_nr_lr);
> +
> hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid;
> }
>
> diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c
> index d461981616d9..ebc6b4afc336 100644
> --- a/arch/arm64/kvm/hyp/nvhe/setup.c
> +++ b/arch/arm64/kvm/hyp/nvhe/setup.c
> @@ -20,6 +20,7 @@
> #include <nvhe/trap_handler.h>
>
> unsigned long hyp_nr_cpus;
> +unsigned int hyp_vgic_nr_lr;
You could probably move this close to the place where it is used
instead of an unrelated file.
Other than that, and with the vcpu-related hunk in its own patch, this
looks reasonable.
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply
* Re: [PATCH v2 2/2] KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU
From: Hyunwoo Kim @ 2026-06-06 8:52 UTC (permalink / raw)
To: Marc Zyngier
Cc: tabba, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will, linux-arm-kernel, kvmarm, imv4bel
In-Reply-To: <86zf19tlcu.wl-maz@kernel.org>
On Fri, Jun 05, 2026 at 09:58:09AM +0100, Marc Zyngier wrote:
> On Thu, 04 Jun 2026 16:12:03 +0100,
> Hyunwoo Kim <imv4bel@gmail.com> wrote:
> >
> > flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU
> > on every run. The vGIC list register save and restore use used_lrs as
> > their loop bound and expect it to stay within the number of implemented
> > list registers. While this is generally the case, flush_hyp_vcpu()
> > copies vgic_v3 verbatim and does not enforce this, so a value provided
> > by the host is used at EL2 to index vgic_lr[] and access ICH_LR<n>_EL2
> > (host -> EL2).
> >
> > Fix by clamping used_lrs to the number of implemented list registers
> > after the copy, as the trusted path already does in
> > vgic_flush_lr_state().
> >
> > Fixes: be66e67f1750 ("KVM: arm64: Use the pKVM hyp vCPU structure in handle___kvm_vcpu_run()")
> > Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
> > ---
> > arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 ++++++++
> > 1 file changed, 8 insertions(+)
> >
> > diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > index 02c5d6e5abcbf..cd807fdb11ba8 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
> > @@ -7,6 +7,7 @@
> > #include <hyp/adjust_pc.h>
> > #include <hyp/switch.h>
> >
> > +#include <asm/arch_gicv3.h>
> > #include <asm/pgtable-types.h>
> > #include <asm/kvm_asm.h>
> > #include <asm/kvm_emulate.h>
> > @@ -142,6 +143,13 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
> >
> > hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
> >
> > + /* Bound used_lrs by the number of implemented list registers. */
> > + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
>
> There is no pKVM support without a GICv3 CPU interface, and absolutely
> everything already assumes it. Why do we need this extra check?
>
> > + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
> > + min_t(unsigned int,
> > + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
> > + (read_gicreg(ICH_VTR_EL2) & 0xf) + 1);
> > +
>
> Reading ICH_VTR_EL2 on each entry is going to cause some really heavy
> trapping under NV, and we should avoid this.
>
> kvm_vgic_global_state.nr_lr contains this information, and it should
> only be a matter of replicating it (or compute it once) at init time.
Does this approach look reasonable to you?
---
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index 8d06b62e7188..25199769a1d6 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icache_flags);
extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits);
extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl);
extern unsigned long kvm_nvhe_sym(hyp_nr_cpus);
+extern unsigned int kvm_nvhe_sym(hyp_vgic_nr_lr);
#endif /* __ARM64_KVM_HYP_H__ */
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 9453321ef8c6..891fe2c7b854 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -2426,6 +2426,7 @@ static int __init init_subsystems(void)
switch (err) {
case 0:
vgic_present = true;
+ kvm_nvhe_sym(hyp_vgic_nr_lr) = kvm_vgic_global_state.nr_lr;
break;
case -ENODEV:
case -ENXIO:
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 06db299c37a8..8bb9362bc284 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -128,6 +128,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
+ /* A guest context must keep a NULL __hyp_running_vcpu. */
+ hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL;
+
hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
@@ -139,6 +142,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
+ /* Bound used_lrs by the number of implemented list registers. */
+ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
+ min_t(unsigned int,
+ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
+ hyp_vgic_nr_lr);
+
hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid;
}
diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c
index d461981616d9..ebc6b4afc336 100644
--- a/arch/arm64/kvm/hyp/nvhe/setup.c
+++ b/arch/arm64/kvm/hyp/nvhe/setup.c
@@ -20,6 +20,7 @@
#include <nvhe/trap_handler.h>
unsigned long hyp_nr_cpus;
+unsigned int hyp_vgic_nr_lr;
#define hyp_percpu_size ((unsigned long)__per_cpu_end - \
(unsigned long)__per_cpu_start)
>
> Thanks,
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
Best regards,
Hyunwoo Kim
^ permalink raw reply related
* Re: [PATCH bpf-next v2 8/8] selftests/bpf: add tests to validate KASAN on JIT programs
From: Alexis Lothoré @ 2026-06-06 8:51 UTC (permalink / raw)
To: Yonghong Song, Alexis Lothoré, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Song Liu, Jiri Olsa,
John Fastabend, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Shuah Khan, Maxime Coquelin,
Alexandre Torgue, Ihor Solodrai
Cc: ebpf, Bastien Curutchet, Thomas Petazzoni, bpf, linux-kernel,
linux-kselftest, linux-stm32, linux-arm-kernel
In-Reply-To: <d717e193-f3b3-4022-8b0f-5e94595dd69b@linux.dev>
On Sat Jun 6, 2026 at 6:09 AM CEST, Yonghong Song wrote:
[...]
>>> But when I am running another same test './test_progs -t kasan', there is no kasan reports.
>> Ok, I guess you are missing kasan_multi_shot on your kernel command
>> line: without this option, only the first report is generated, then
>> KASAN does not emit additional report until you restart your kernel.
>> Could you please try adding it and running the tests again ?
>
> Thanks! Adding 'kasan_multi_shot' to the kernel command line indeed fixed the problem.
> It would be great if you can mention 'kasan_multi_shot' is needed in kernel command
> line in cover letter and in patch 8.
Yes, that's definitely missing, I'll make sure to add it. Thanks again
for the test !
Alexis
--
Alexis Lothoré, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* Re: [RESEND,v2 1/2] dt-bindings: memory-controllers: mtk-smi: Add support for mt8189
From: Zhengnan Chen (陈征南) @ 2026-06-06 8:06 UTC (permalink / raw)
To: robh@kernel.org, matthias.bgg@gmail.com,
Yong Wu (吴勇), conor+dt@kernel.org, krzk@kernel.org,
AngeloGioacchino Del Regno
Cc: linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
Congcong Yao (姚聪聪),
devicetree@vger.kernel.org, Project_Global_Chrome_Upstream_Group
In-Reply-To: <50e53fd9-4cca-4cac-9f8d-868de3a8a7bc@kernel.org>
On Sun, 2026-05-24 at 21:08 +0200, Krzysztof Kozlowski wrote:
> On 27/04/2026 09:04, mtk20898 wrote:
> > From: Zhengnan Chen <zhengnan.chen@mediatek.com>
> >
> > Add binding description for mt8189.
> >
> > The clocks number of mt8189 smi-sub common has a bit difference.
> > Its clock count is 2, while mt8195 has 3. Therefore, the minimum
> > number of clocks is changed to 2, with the third one being
> > optional.
>
> Then why does the binding say that mt8195 has two clocks? You already
> received exactly this question.
>
The MT8195 SMI_sub_common has always been designed with three clocks.
We have reviewed the HW design of the MT8189 SMI_sub_common and
confirmed that the MT8189 should also follow the MT8195's settings and
maintain a three-clock DTS configuration.
We will send out the next version as soon as possible.
> >
> > About what smi-sub-common is, please check the below diagram,
> > we add it in mediatek,smi-common.yaml file.
> >
> > Signed-off-by: Zhengnan Chen <zhengnan.chen@mediatek.com>
> > Reviewed-by: AngeloGioacchino Del Regno <
> > angelogioacchino.delregno@collabora.com>
>
>
> No need to resend this. You received comments at v2 and you should
> have
> implemented them.
>
OK, we will remove this in next version.
> Best regards,
> Krzysztof
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox