* Re: [PATCH v14 13/44] arm64: RMI: Define the user ABI
From: Steven Price @ 2026-06-04 15:27 UTC (permalink / raw)
To: Wei-Lin Chang, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve, Lorenzo.Pieralisi2
In-Reply-To: <vixej2afqamvr7eaksoepr3wmpwzp73rtgrageeyuawa6azotw@payxzyhymwbt>
On 26/05/2026 23:17, Wei-Lin Chang wrote:
> On Wed, May 13, 2026 at 02:17:21PM +0100, Steven Price wrote:
>> There is one CAP which identified the presence of CCA, and one ioctl.
>> The ioctl is used to populate memory during creation of the realm as
>> this requires the RMM to copy data from an unprotected address to the
>> protected memory - CCA does not support memory conversion where the
>> memory contents is preserved as this is incompatible with memory
>> encryption.
>
> Nit:
> I believe spelling out the CAP and ioctl names can improve the commit
> message. Also "memory conversion" is a little vague, maybe
>
> ... CCA does not support shared <-> private memory conversion where ...
>
> would make this clearer?
Thanks for the suggestions - yes I agree that would make it clearer.
Thanks,
Steve
> Thanks,
> Wei-Lin Chang
>
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> Changes since v13:
>> * KVM_ARM_VCPU_RMI_PSCI_COMPLETE removed.
>> * KVM_ARM_RMI_POPULATE documentation updated to reflect that the
>> structure is written by the kernel.
>> * CAP number bumped.
>> Changes since v12:
>> * Change KVM_ARM_RMI_POPULATE to update the structure with the amount
>> that has been progressed rather than return the number of bytes
>> populated.
>> * Describe the flag KVM_ARM_RMI_POPULATE_FLAGS_MEASURE.
>> * CAP number is bumped.
>> * NOTE: The PSCI ioctl may be removed in a future spec release.
>> Changes since v11:
>> * Completely reworked to be more implicit. Rather than having explicit
>> CAP operations to progress the realm construction these operations
>> are done when needed (on populating and on first vCPU run).
>> * Populate and PSCI complete are promoted to proper ioctls.
>> Changes since v10:
>> * Rename symbols from RME to RMI.
>> Changes since v9:
>> * Improvements to documentation.
>> * Bump the magic number for KVM_CAP_ARM_RME to avoid conflicts.
>> Changes since v8:
>> * Minor improvements to documentation following review.
>> * Bump the magic numbers to avoid conflicts.
>> Changes since v7:
>> * Add documentation of new ioctls
>> * Bump the magic numbers to avoid conflicts
>> Changes since v6:
>> * Rename some of the symbols to make their usage clearer and avoid
>> repetition.
>> Changes from v5:
>> * Actually expose the new VCPU capability (KVM_ARM_VCPU_REC) by bumping
>> KVM_VCPU_MAX_FEATURES - note this also exposes KVM_ARM_VCPU_HAS_EL2!
>> ---
>> Documentation/virt/kvm/api.rst | 40 ++++++++++++++++++++++++++++++++++
>> include/uapi/linux/kvm.h | 13 +++++++++++
>> 2 files changed, 53 insertions(+)
>>
>> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
>> index 52bbbb553ce1..ca68aae7faa2 100644
>> --- a/Documentation/virt/kvm/api.rst
>> +++ b/Documentation/virt/kvm/api.rst
>> @@ -6553,6 +6553,37 @@ KVM_S390_KEYOP_SSKE
>> Sets the storage key for the guest address ``guest_addr`` to the key
>> specified in ``key``, returning the previous value in ``key``.
>>
>> +4.145 KVM_ARM_RMI_POPULATE
>> +--------------------------
>> +
>> +:Capability: KVM_CAP_ARM_RMI
>> +:Architectures: arm64
>> +:Type: vm ioctl
>> +:Parameters: struct kvm_arm_rmi_populate (in/out)
>> +:Returns: 0 on success, < 0 on error
>> +
>> +::
>> +
>> + struct kvm_arm_rmi_populate {
>> + __u64 base;
>> + __u64 size;
>> + __u64 source_uaddr;
>> + __u32 flags;
>> + __u32 reserved;
>> + };
>> +
>> +Populate a region of protected address space by copying the data from the
>> +(non-protected) user space pointer provided into a protected region (backed by
>> +guestmem_fd). It implicitly sets the destination region to RIPAS RAM. This is
>> +only valid before any VCPUs have been run. The ioctl might not populate the
>> +entire region and in this case the kernel updates the fields `base`, `size` and
>> +`source_uaddr`. User space may have to repeatedly call it until `size` is 0 to
>> +populate the entire region.
>> +
>> +`flags` can be set to `KVM_ARM_RMI_POPULATE_FLAGS_MEASURE` to request that the
>> +populated data is hashed and added to the guest's Realm Initial Measurement
>> +(RIM).
>> +
>> .. _kvm_run:
>>
>> 5. The kvm_run structure
>> @@ -8904,6 +8935,15 @@ helpful if user space wants to emulate instructions which are not
>> This capability can be enabled dynamically even if VCPUs were already
>> created and are running.
>>
>> +7.47 KVM_CAP_ARM_RMI
>> +--------------------
>> +
>> +:Architectures: arm64
>> +:Target: VM
>> +:Parameters: None
>> +
>> +This capability indicates that support for CCA realms is available.
>> +
>> 8. Other capabilities.
>> ======================
>>
>> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
>> index 6c8afa2047bf..b8cff0938041 100644
>> --- a/include/uapi/linux/kvm.h
>> +++ b/include/uapi/linux/kvm.h
>> @@ -996,6 +996,7 @@ struct kvm_enable_cap {
>> #define KVM_CAP_S390_USER_OPEREXEC 246
>> #define KVM_CAP_S390_KEYOP 247
>> #define KVM_CAP_S390_VSIE_ESAMODE 248
>> +#define KVM_CAP_ARM_RMI 249
>>
>> struct kvm_irq_routing_irqchip {
>> __u32 irqchip;
>> @@ -1669,4 +1670,16 @@ struct kvm_pre_fault_memory {
>> __u64 padding[5];
>> };
>>
>> +/* Available with KVM_CAP_ARM_RMI, only for VMs with KVM_VM_TYPE_ARM_REALM */
>> +#define KVM_ARM_RMI_POPULATE _IOWR(KVMIO, 0xd7, struct kvm_arm_rmi_populate)
>> +#define KVM_ARM_RMI_POPULATE_FLAGS_MEASURE (1 << 0)
>> +
>> +struct kvm_arm_rmi_populate {
>> + __u64 base;
>> + __u64 size;
>> + __u64 source_uaddr;
>> + __u32 flags;
>> + __u32 reserved;
>> +};
>> +
>> #endif /* __LINUX_KVM_H */
>> --
>> 2.43.0
>>
^ permalink raw reply
* Re: [PATCH RESEND] stm class: remove unnecessary local variable in stm_write
From: Thorsten Blum @ 2026-06-04 15:25 UTC (permalink / raw)
To: Alexander Shishkin, Maxime Coquelin, Alexandre Torgue
Cc: linux-stm32, linux-arm-kernel, linux-kernel
In-Reply-To: <20260508144705.3940-3-thorsten.blum@linux.dev>
Gentle ping?
On Fri, May 08, 2026 at 04:47:06PM +0200, Thorsten Blum wrote:
> The local error variable and the corresponding if check in stm_write()
> are unnecessary. Remove them.
>
> Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
> ---
> drivers/hwtracing/stm/core.c | 8 +-------
> 1 file changed, 1 insertion(+), 7 deletions(-)
>
> diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
> index f48c6a8a0654..edf3d69a84a9 100644
> --- a/drivers/hwtracing/stm/core.c
> +++ b/drivers/hwtracing/stm/core.c
> @@ -602,17 +602,11 @@ static ssize_t notrace
> stm_write(struct stm_device *stm, struct stm_output *output,
> unsigned int chan, const char *buf, size_t count, struct stm_source_data *source)
> {
> - int err;
> -
> /* stm->pdrv is serialized against policy_mutex */
> if (!stm->pdrv)
> return -ENODEV;
>
> - err = stm->pdrv->write(stm->data, output, chan, buf, count, source);
> - if (err < 0)
> - return err;
> -
> - return err;
> + return stm->pdrv->write(stm->data, output, chan, buf, count, source);
> }
>
> static ssize_t stm_char_write(struct file *file, const char __user *buf,
^ permalink raw reply
* Re: [PATCH RFC v3 0/5] ZTE zx297520v3 clock bindings and driver
From: Philipp Zabel @ 2026-06-04 15:23 UTC (permalink / raw)
To: Stefan Dösinger, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Brian Masney
Cc: linux-clk, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <2062167.PYKUYFuaPT@strix>
Hi Stefan,
On Mi, 2026-06-03 at 23:49 +0300, Stefan Dösinger wrote:
> Hi Philipp,
>
> Am Mittwoch, 3. Juni 2026, 11:50:14 Ostafrikanische Zeit schrieben Sie:
> > When there is no interaction required when operating the clk/reset
> > bits, I prefer the reset driver sitting in drivers/reset as an aux
> > device, especially when register access can be abstracted via a shared
> > regmap. Some of the reset drivers under drivers/clk just predate the
> > aux bus.
>
> There are two interactions:
>
> The register lock because all LSP and at least one TOP register contains both
> clocks and resets.
That could be solved with regmap.
> Shared register definition: in the case of the LSP clocks breaking up the
> composite definition would sacrifice readability.
That is a matter of perspective.
I had a harder time validating that the resets[] array is properly
initialized from the two different composite arrays because of the
unordered reset_ids, and some remaining resets[] being filled via code.
It would be much simpler if you split the reset definitions out into a
single, separate, const array, indexed by reset id. In fact,
I would suggest this even if you don't intend to move the reset code.
> Neither of them are insurmountable and I can certainly arrange a separation if
> asked to - but my preference is to keep them together.
I'll leave this up to the clock maintainers.
regards
Philipp
^ permalink raw reply
* Re: [PATCH v14 10/44] arm64: RMI: Add support for SRO
From: Steven Price @ 2026-06-04 15:19 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve, WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <864ik0x22q.wl-maz@kernel.org>
On 21/05/2026 15:35, Marc Zyngier wrote:
> On Wed, 13 May 2026 14:17:18 +0100,
> Steven Price <steven.price@arm.com> wrote:
>>
>> RMM v2.0 introduces the concept of "Stateful RMI Operations" (SRO). This
>> means that an SMC can return with an operation still in progress. The
>> host is excepted to continue the operation until is reaches a conclusion
>> (either success or failure). During this process the RMM can request
>> additional memory ('donate') or hand memory back to the host
>> ('reclaim'). The host can request an in progress operation is cancelled,
>> but still continue the operation until it has completed (otherwise the
>> incomplete operation may cause future RMM operations to fail).
>>
>> The SRO is tracked using a struct rmi_sro_state object which keeps track
>> of any memory which has been allocated but not yet consumed by the RMM
>> or reclaimed from the RMM. This allows the memory to be reused in a
>> future request within the same operation. It will also permit an
>> operation to be done in a context where memory allocation may be
>> difficult (e.g. atomic context) with the option to abort the operation
>> and retry the memory allocation outside of the atomic context. The
>> memory stored in the struct rmi_sro_state object can then be reused on
>> the subsequent attempt.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> v14:
>> * SRO support has improved although is still not fully complete. The
>> infrastructure has been moved out of KVM.
>> ---
>> arch/arm64/include/asm/rmi_cmds.h | 1 +
>> arch/arm64/kernel/rmi.c | 359 ++++++++++++++++++++++++++++++
>> 2 files changed, 360 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
>> index eb213c8e6f26..1a7b0c8f1e38 100644
>> --- a/arch/arm64/include/asm/rmi_cmds.h
>> +++ b/arch/arm64/include/asm/rmi_cmds.h
>> @@ -35,6 +35,7 @@ struct rmi_sro_state {
>>
>> int rmi_delegate_range(phys_addr_t phys, unsigned long size);
>> int rmi_undelegate_range(phys_addr_t phys, unsigned long size);
>> +int free_delegated_page(phys_addr_t phys);
>>
>> static inline int rmi_delegate_page(phys_addr_t phys)
>> {
>> diff --git a/arch/arm64/kernel/rmi.c b/arch/arm64/kernel/rmi.c
>> index 08cef54acadb..a8107ca9bb6d 100644
>> --- a/arch/arm64/kernel/rmi.c
>> +++ b/arch/arm64/kernel/rmi.c
>> @@ -48,6 +48,365 @@ int rmi_undelegate_range(phys_addr_t phys, unsigned long size)
>> return ret;
>> }
>>
>> +static unsigned long donate_req_to_size(unsigned long donatereq)
>> +{
>> + unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
>> +
>> + switch (unit_size) {
>> + case 0:
>> + return PAGE_SIZE;
>> + case 1:
>> + return PMD_SIZE;
>> + case 2:
>> + return PUD_SIZE;
>> + case 3:
>> + return P4D_SIZE;
>
> How does this work when we have folded levels? If this is supposed to
> be the architected size, then it should actively express that:
>
> return BIT(unit_size * (PAGE_SHIFT - 3) + PAGE_SHIFT);
It doesn't work (as Gavin also pointed out). There's an existing macro
to make this even cleaner:
return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - unit_size));
>> + }
>> + unreachable();
>> +}
>> +
>> +static void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs_in,
>> + struct arm_smccc_1_2_regs *regs_out)
>> +{
>> + struct arm_smccc_1_2_regs regs = *regs_in;
>> + unsigned long status;
>> +
>> + do {
>> + arm_smccc_1_2_invoke(®s, regs_out);
>> + status = RMI_RETURN_STATUS(regs_out->a0);
>> + } while (status == RMI_BUSY || status == RMI_BLOCKED);
>> +}
>> +
>> +int free_delegated_page(phys_addr_t phys)
>> +{
>> + if (WARN_ON(rmi_undelegate_page(phys))) {
>
> Please drop this WARN_ON(). Or at least make it ONCE. Everywhere.
Happy to change to WARN_ON_ONCE(). I think we should keep a WARN of some
sort as this is causing Linux to leak pages - it's definitely something
the sysadmin would want to know about.
>> + /* Undelegate failed: leak the page */
>> + return -EBUSY;
>> + }
>> +
>> + free_page((unsigned long)phys_to_virt(phys));
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_ensure_capacity(struct rmi_sro_state *sro,
>> + unsigned long count)
>> +{
>> + if (WARN_ON_ONCE(sro->addr_count > RMI_MAX_ADDR_LIST))
>> + return -EOVERFLOW;
>> +
>> + if (count > RMI_MAX_ADDR_LIST - sro->addr_count)
>> + return -ENOSPC;
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_donate_contig(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + unsigned long donatereq,
>> + struct arm_smccc_1_2_regs *out_regs,
>> + gfp_t gfp)
>> +{
>> + unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
>> + unsigned long unit_size_bytes = donate_req_to_size(donatereq);
>> + unsigned long count = RMI_DONATE_COUNT(donatereq);
>> + unsigned long state = RMI_DONATE_STATE(donatereq);
>> + unsigned long size = unit_size_bytes * count;
>> + unsigned long addr_range;
>> + int ret;
>> + void *virt;
>> + phys_addr_t phys;
>> + struct arm_smccc_1_2_regs regs = {
>> + SMC_RMI_OP_MEM_DONATE,
>> + sro_handle
>> + };
>> +
>> + for (int i = 0; i < sro->addr_count; i++) {
>> + unsigned long entry = sro->addr_list[i];
>> +
>> + if (RMI_ADDR_RANGE_SIZE(entry) == unit_size &&
>> + RMI_ADDR_RANGE_COUNT(entry) == count &&
>> + RMI_ADDR_RANGE_STATE(entry) == state) {
>> + sro->addr_count--;
>> + swap(sro->addr_list[sro->addr_count],
>> + sro->addr_list[i]);
>> +
>> + goto out;
>> + }
>> + }
>> +
>> + ret = rmi_sro_ensure_capacity(sro, 1);
>> + if (ret)
>> + return ret;
>> +
>> + virt = alloc_pages_exact(size, gfp);
>> + if (!virt)
>> + return -ENOMEM;
>> + phys = virt_to_phys(virt);
>> +
>> + if (state == RMI_OP_MEM_DELEGATED) {
>> + if (rmi_delegate_range(phys, size)) {
>> + free_pages_exact(virt, size);
>> + return -ENXIO;
>> + }
>> + }
>> +
>> + addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
>> + FIELD_MODIFY(RMI_ADDR_RANGE_SIZE_MASK, &addr_range, unit_size);
>> + FIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, &addr_range, count);
>> + FIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, &addr_range, state);
>> +
>> + sro->addr_list[sro->addr_count] = addr_range;
>> +
>
> Shouldn't this be moved to a helper that ensures capacity, and returns
> an error otherwise?
I'm not sure quite what you are suggesting. I already have a
rmi_sro_ensure_capacity() helper. By this point we know there's space.
>> +out:
>> + regs.a2 = virt_to_phys(&sro->addr_list[sro->addr_count]);
>> + regs.a3 = 1;
>
> This could really do with context specific helpers that populate regs
> based on a set of parameters. I have no idea what this 1 here is, and
> the init is spread over too much code. Think of the children!
>
> That's valid for the whole patch.
That's a good point. SRO is a bit tricky because I wanted the actual SMC
call to be done in one place so we can handle all the RMI_INCOMPLETE
cases together. But I could certainly add some helpers to setup the
registers rather than assigning directly to regs.a<n>.
Thanks,
Steve
> M.
>> + rmi_smccc_invoke(®s, out_regs);
>> +
>> + unsigned long donated_granules = out_regs->a1;
>> + unsigned long donated_size = donated_granules << PAGE_SHIFT;
>> +
>> + if (donated_granules == 0) {
>> + /* No pages used by the RMM */
>> + sro->addr_count++;
>> + } else if (donated_size < size) {
>> + phys = sro->addr_list[sro->addr_count] & RMI_ADDR_RANGE_ADDR_MASK;
>> +
>> + /* Not all granules used by the RMM, free the remaining pages */
>> + for (long i = donated_size; i < size; i += PAGE_SIZE) {
>> + if (state == RMI_OP_MEM_DELEGATED)
>> + free_delegated_page(phys + i);
>> + else
>> + __free_page(phys_to_page(phys + i));
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_donate_noncontig(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + unsigned long donatereq,
>> + struct arm_smccc_1_2_regs *out_regs,
>> + gfp_t gfp)
>> +{
>> + unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
>> + unsigned long unit_size_bytes = donate_req_to_size(donatereq);
>> + unsigned long count = RMI_DONATE_COUNT(donatereq);
>> + unsigned long state = RMI_DONATE_STATE(donatereq);
>> + unsigned long found = 0;
>> + unsigned long addr_list_start = sro->addr_count;
>> + int ret;
>> + struct arm_smccc_1_2_regs regs = {
>> + SMC_RMI_OP_MEM_DONATE,
>> + sro_handle
>> + };
>> +
>> + for (int i = 0; i < addr_list_start && found < count; i++) {
>> + unsigned long entry = sro->addr_list[i];
>> +
>> + if (RMI_ADDR_RANGE_SIZE(entry) == unit_size &&
>> + RMI_ADDR_RANGE_COUNT(entry) == 1 &&
>> + RMI_ADDR_RANGE_STATE(entry) == state) {
>> + addr_list_start--;
>> + swap(sro->addr_list[addr_list_start],
>> + sro->addr_list[i]);
>> + found++;
>> + i--;
>> + }
>> + }
>> +
>> + ret = rmi_sro_ensure_capacity(sro, count - found);
>> + if (ret)
>> + return ret;
>> +
>> + while (found < count) {
>> + unsigned long addr_range;
>> + void *virt = alloc_pages_exact(unit_size_bytes, gfp);
>> + phys_addr_t phys;
>> +
>> + if (!virt)
>> + return -ENOMEM;
>> +
>> + phys = virt_to_phys(virt);
>> +
>> + if (state == RMI_OP_MEM_DELEGATED) {
>> + if (rmi_delegate_range(phys, unit_size_bytes)) {
>> + free_pages_exact(virt, unit_size_bytes);
>> + return -ENXIO;
>> + }
>> + }
>> +
>> + addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
>> + FIELD_MODIFY(RMI_ADDR_RANGE_SIZE_MASK, &addr_range, unit_size);
>> + FIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, &addr_range, 1);
>> + FIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, &addr_range, state);
>> +
>> + sro->addr_list[sro->addr_count++] = addr_range;
>> + found++;
>> + }
>> +
>> + regs.a2 = virt_to_phys(&sro->addr_list[addr_list_start]);
>> + regs.a3 = found;
>> + rmi_smccc_invoke(®s, out_regs);
>> +
>> + unsigned long donated_granules = out_regs->a1;
>> +
>> + if (WARN_ON(donated_granules & ((unit_size_bytes >> PAGE_SHIFT) - 1))) {
>> + /*
>> + * FIXME: RMM has only consumed part of a huge page, this leaks
>> + * the rest of the huge page
>> + */
>> + donated_granules = ALIGN(donated_granules,
>> + (unit_size_bytes >> PAGE_SHIFT));
>> + }
>> + unsigned long donated_blocks = donated_granules / (unit_size_bytes >> PAGE_SHIFT);
>> +
>> + if (WARN_ON(donated_blocks > found))
>> + donated_blocks = found;
>> +
>> + unsigned long undonated_blocks = found - donated_blocks;
>> +
>> + while (donated_blocks && undonated_blocks) {
>> + sro->addr_count--;
>> + swap(sro->addr_list[addr_list_start],
>> + sro->addr_list[sro->addr_count]);
>> + addr_list_start++;
>> +
>> + donated_blocks--;
>> + undonated_blocks--;
>> + }
>> + sro->addr_count -= donated_blocks;
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_donate(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + unsigned long donatereq,
>> + struct arm_smccc_1_2_regs *regs,
>> + gfp_t gfp)
>> +{
>> + unsigned long count = RMI_DONATE_COUNT(donatereq);
>> +
>> + if (WARN_ON(!count))
>> + return 0;
>> +
>> + if (RMI_DONATE_CONTIG(donatereq)) {
>> + return rmi_sro_donate_contig(sro, sro_handle, donatereq,
>> + regs, gfp);
>> + } else {
>> + return rmi_sro_donate_noncontig(sro, sro_handle, donatereq,
>> + regs, gfp);
>> + }
>> +}
>> +
>> +static int rmi_sro_reclaim(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + struct arm_smccc_1_2_regs *out_regs)
>> +{
>> + unsigned long capacity;
>> + struct arm_smccc_1_2_regs regs;
>> + int ret;
>> +
>> + ret = rmi_sro_ensure_capacity(sro, 1);
>> + if (ret)
>> + rmi_sro_free(sro);
>> +
>> + capacity = RMI_MAX_ADDR_LIST - sro->addr_count;
>> +
>> + regs = (struct arm_smccc_1_2_regs){
>> + SMC_RMI_OP_MEM_RECLAIM,
>> + sro_handle,
>> + virt_to_phys(&sro->addr_list[sro->addr_count]),
>> + capacity
>> + };
>> + rmi_smccc_invoke(®s, out_regs);
>> +
>> + if (WARN_ON_ONCE(out_regs->a1 > capacity))
>> + out_regs->a1 = capacity;
>> +
>> + sro->addr_count += out_regs->a1;
>> +
>> + return 0;
>> +}
>> +
>> +void rmi_sro_free(struct rmi_sro_state *sro)
>> +{
>> + for (int i = 0; i < sro->addr_count; i++) {
>> + unsigned long entry = sro->addr_list[i];
>> + unsigned long addr = RMI_ADDR_RANGE_ADDR(entry);
>> + unsigned long unit_size = RMI_ADDR_RANGE_SIZE(entry);
>> + unsigned long count = RMI_ADDR_RANGE_COUNT(entry);
>> + unsigned long state = RMI_ADDR_RANGE_STATE(entry);
>> + unsigned long size = donate_req_to_size(unit_size) * count;
>> +
>> + if (state == RMI_OP_MEM_DELEGATED) {
>> + if (WARN_ON(rmi_undelegate_range(addr, size))) {
>> + /* Leak the pages */
>> + continue;
>> + }
>> + }
>> + free_pages_exact(phys_to_virt(addr), size);
>> + }
>> +
>> + sro->addr_count = 0;
>> +}
>> +
>> +unsigned long rmi_sro_execute(struct rmi_sro_state *sro, gfp_t gfp)
>> +{
>> + unsigned long sro_handle;
>> + struct arm_smccc_1_2_regs regs;
>> + struct arm_smccc_1_2_regs *regs_in = &sro->regs;
>> +
>> + rmi_smccc_invoke(regs_in, ®s);
>> +
>> + sro_handle = regs.a1;
>> +
>> + while (RMI_RETURN_STATUS(regs.a0) == RMI_INCOMPLETE) {
>> + bool can_cancel = RMI_RETURN_CAN_CANCEL(regs.a0);
>> + int ret;
>> +
>> + switch (RMI_RETURN_MEMREQ(regs.a0)) {
>> + case RMI_OP_MEM_REQ_NONE:
>> + regs = (struct arm_smccc_1_2_regs){
>> + SMC_RMI_OP_CONTINUE, sro_handle, 0
>> + };
>> + rmi_smccc_invoke(®s, ®s);
>> + break;
>> + case RMI_OP_MEM_REQ_DONATE:
>> + ret = rmi_sro_donate(sro, sro_handle, regs.a2, ®s,
>> + gfp);
>> + break;
>> + case RMI_OP_MEM_REQ_RECLAIM:
>> + ret = rmi_sro_reclaim(sro, sro_handle, ®s);
>> + break;
>> + default:
>> + ret = WARN_ON(1);
>> + break;
>> + }
>> +
>> + if (ret) {
>> + if (can_cancel) {
>> + /*
>> + * FIXME: Handle cancelling properly!
>> + *
>> + * If the operation has failed due to memory
>> + * allocation failure then the information on
>> + * the memory allocation should be saved, so
>> + * that the allocation can be repeated outside
>> + * of any context which prevented the
>> + * allocation.
>
> Honestly, this is the sort of stuff that I'd expect to be solved
> *before* posting this code. Since this is so central to the whole
> memory management, it needs to be correct from day-1.
>
> If you can't make it work in time, then tone the supported features
> down. But FIXMEs and WARN_ONs are not the way to go.
>
> M.
>
^ permalink raw reply
* Re: [PATCH v14 10/44] arm64: RMI: Add support for SRO
From: Steven Price @ 2026-06-04 15:19 UTC (permalink / raw)
To: Gavin Shan, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve,
WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <872ce762-0a83-42a4-bbe8-b21ea6f4e1cf@redhat.com>
On 21/05/2026 05:38, Gavin Shan wrote:
> Hi Steven,
>
> On 5/13/26 11:17 PM, Steven Price wrote:
>> RMM v2.0 introduces the concept of "Stateful RMI Operations" (SRO). This
>> means that an SMC can return with an operation still in progress. The
>> host is excepted to continue the operation until is reaches a conclusion
>> (either success or failure). During this process the RMM can request
>> additional memory ('donate') or hand memory back to the host
>> ('reclaim'). The host can request an in progress operation is cancelled,
>> but still continue the operation until it has completed (otherwise the
>> incomplete operation may cause future RMM operations to fail).
>>
>> The SRO is tracked using a struct rmi_sro_state object which keeps track
>> of any memory which has been allocated but not yet consumed by the RMM
>> or reclaimed from the RMM. This allows the memory to be reused in a
>> future request within the same operation. It will also permit an
>> operation to be done in a context where memory allocation may be
>> difficult (e.g. atomic context) with the option to abort the operation
>> and retry the memory allocation outside of the atomic context. The
>> memory stored in the struct rmi_sro_state object can then be reused on
>> the subsequent attempt.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> v14:
>> * SRO support has improved although is still not fully complete. The
>> infrastructure has been moved out of KVM.
>> ---
>> arch/arm64/include/asm/rmi_cmds.h | 1 +
>> arch/arm64/kernel/rmi.c | 359 ++++++++++++++++++++++++++++++
>> 2 files changed, 360 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/
>> asm/rmi_cmds.h
>> index eb213c8e6f26..1a7b0c8f1e38 100644
>> --- a/arch/arm64/include/asm/rmi_cmds.h
>> +++ b/arch/arm64/include/asm/rmi_cmds.h
>> @@ -35,6 +35,7 @@ struct rmi_sro_state {
>> int rmi_delegate_range(phys_addr_t phys, unsigned long size);
>> int rmi_undelegate_range(phys_addr_t phys, unsigned long size);
>> +int free_delegated_page(phys_addr_t phys);
>> static inline int rmi_delegate_page(phys_addr_t phys)
>> {
>> diff --git a/arch/arm64/kernel/rmi.c b/arch/arm64/kernel/rmi.c
>> index 08cef54acadb..a8107ca9bb6d 100644
>> --- a/arch/arm64/kernel/rmi.c
>> +++ b/arch/arm64/kernel/rmi.c
>> @@ -48,6 +48,365 @@ int rmi_undelegate_range(phys_addr_t phys,
>> unsigned long size)
>> return ret;
>> }
>> +static unsigned long donate_req_to_size(unsigned long donatereq)
>> +{
>> + unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
>> +
>> + switch (unit_size) {
>> + case 0:
>> + return PAGE_SIZE;
>> + case 1:
>> + return PMD_SIZE;
>> + case 2:
>> + return PUD_SIZE;
>> + case 3:
>> + return P4D_SIZE;
>> + }
>> + unreachable();
>> +}
>> +
>
> It's worthy to have 'inline'.
Generally it's best to let the compiler make the decision. A small
static function like this is highly likely to be inlined by the compiler
already.
The exception of course is when the function is in a header and then
it's necessary to use "static inline".
> {P4D, PUD, PMD}_SIZE can be equal if there> are
> no P4D and PUD, depending on CONFIG_PGTABLE_LEVELS. In this case, can the
> 'unit_size' be translated to wrong value?
Technically yes. I think I can actually rewrite this more simply as:
static unsigned long donate_req_to_size(unsigned long donatereq)
{
unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(3 - unit_size));
}
which neatly sidesteps the CONFIG_PGTABLE_LEVELS problem too.
>> +static void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs_in,
>> + struct arm_smccc_1_2_regs *regs_out)
>> +{
>> + struct arm_smccc_1_2_regs regs = *regs_in;
>> + unsigned long status;
>> +
>> + do {
>> + arm_smccc_1_2_invoke(®s, regs_out);
>> + status = RMI_RETURN_STATUS(regs_out->a0);
>> + } while (status == RMI_BUSY || status == RMI_BLOCKED);
>> +}
>> +
>> +int free_delegated_page(phys_addr_t phys)
>> +{
>> + if (WARN_ON(rmi_undelegate_page(phys))) {
>> + /* Undelegate failed: leak the page */
>> + return -EBUSY;
>> + }
>> +
>> + free_page((unsigned long)phys_to_virt(phys));
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_ensure_capacity(struct rmi_sro_state *sro,
>> + unsigned long count)
>> +{
>> + if (WARN_ON_ONCE(sro->addr_count > RMI_MAX_ADDR_LIST))
>> + return -EOVERFLOW;
>> +
>> + if (count > RMI_MAX_ADDR_LIST - sro->addr_count)
>> + return -ENOSPC;
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_donate_contig(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + unsigned long donatereq,
>> + struct arm_smccc_1_2_regs *out_regs,
>> + gfp_t gfp)
>> +{
>> + unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
>> + unsigned long unit_size_bytes = donate_req_to_size(donatereq);
>> + unsigned long count = RMI_DONATE_COUNT(donatereq);
>> + unsigned long state = RMI_DONATE_STATE(donatereq);
>> + unsigned long size = unit_size_bytes * count;
>> + unsigned long addr_range;
>> + int ret;
>> + void *virt;
>> + phys_addr_t phys;
>> + struct arm_smccc_1_2_regs regs = {
>> + SMC_RMI_OP_MEM_DONATE,
>> + sro_handle
>> + };
>> +
>> + for (int i = 0; i < sro->addr_count; i++) {
>> + unsigned long entry = sro->addr_list[i];
>> +
>> + if (RMI_ADDR_RANGE_SIZE(entry) == unit_size &&
>> + RMI_ADDR_RANGE_COUNT(entry) == count &&
>> + RMI_ADDR_RANGE_STATE(entry) == state) {
>> + sro->addr_count--;
>> + swap(sro->addr_list[sro->addr_count],
>> + sro->addr_list[i]);
>> +
>> + goto out;
>> + }
>> + }
>> +
>> + ret = rmi_sro_ensure_capacity(sro, 1);
>> + if (ret)
>> + return ret;
>> +
>> + virt = alloc_pages_exact(size, gfp);
>> + if (!virt)
>> + return -ENOMEM;
>> + phys = virt_to_phys(virt);
>> +
>
> alloc_pages_exact() will fail if the requested size exceeds the maximal
> allowed
> size (1 << MAX_PAGE_ORDER). The maximal size is usually smaller than
> PUD_SIZE
> but PUD_SIZE is allowed by the RMM.
This is an area where to be honest I'm really not sure what to do.
Technically the RMM is allowed to ask for a contiguous range of 512GB
pages (on a 4K system - larger with larger page sizes) - but clearly no
real OS is going to be able to provide anything like that.
In practise we don't expect the RMM to do anything so crazy. It's not
really clear to be whether even 2MB (PMD_SIZE) is needed. But the spec
is written to be generic.
So my current approach is to calculate the required size and pass it
into alloc_pages_exact(). For "stupidly large" values this will fail and
Linux just doesn't support an RMM which attempts this. If there is ever
a usecase which needs this then we'd need to find a different method of
providing the memory (most likely some form of carveout to avoid
fragmentation). But my view is we should wait for that usecase to be
identified first.
>> + if (state == RMI_OP_MEM_DELEGATED) {
>> + if (rmi_delegate_range(phys, size)) {
>> + free_pages_exact(virt, size);
>> + return -ENXIO;
>> + }
>> + }
>> +
>> + addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
>> + FIELD_MODIFY(RMI_ADDR_RANGE_SIZE_MASK, &addr_range, unit_size);
>> + FIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, &addr_range, count);
>> + FIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, &addr_range, state);
>> +
>> + sro->addr_list[sro->addr_count] = addr_range;
>> +
>> +out:
>> + regs.a2 = virt_to_phys(&sro->addr_list[sro->addr_count]);
>> + regs.a3 = 1;
>> + rmi_smccc_invoke(®s, out_regs);
>> +
>> + unsigned long donated_granules = out_regs->a1;
>> + unsigned long donated_size = donated_granules << PAGE_SHIFT;
>> +
>> + if (donated_granules == 0) {
>> + /* No pages used by the RMM */
>> + sro->addr_count++;
>> + } else if (donated_size < size) {
>> + phys = sro->addr_list[sro->addr_count] &
>> RMI_ADDR_RANGE_ADDR_MASK;
>> +
>> + /* Not all granules used by the RMM, free the remaining pages */
>> + for (long i = donated_size; i < size; i += PAGE_SIZE) {
>> + if (state == RMI_OP_MEM_DELEGATED)
>> + free_delegated_page(phys + i);
>> + else
>> + __free_page(phys_to_page(phys + i));
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_donate_noncontig(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + unsigned long donatereq,
>> + struct arm_smccc_1_2_regs *out_regs,
>> + gfp_t gfp)
>> +{
>> + unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
>> + unsigned long unit_size_bytes = donate_req_to_size(donatereq);
>> + unsigned long count = RMI_DONATE_COUNT(donatereq);
>> + unsigned long state = RMI_DONATE_STATE(donatereq);
>> + unsigned long found = 0;
>> + unsigned long addr_list_start = sro->addr_count;
>> + int ret;
>> + struct arm_smccc_1_2_regs regs = {
>> + SMC_RMI_OP_MEM_DONATE,
>> + sro_handle
>> + };
>> +
>> + for (int i = 0; i < addr_list_start && found < count; i++) {
>> + unsigned long entry = sro->addr_list[i];
>> +
>> + if (RMI_ADDR_RANGE_SIZE(entry) == unit_size &&
>> + RMI_ADDR_RANGE_COUNT(entry) == 1 &&
>> + RMI_ADDR_RANGE_STATE(entry) == state) {
>> + addr_list_start--;
>> + swap(sro->addr_list[addr_list_start],
>> + sro->addr_list[i]);
>> + found++;
>> + i--;
>> + }
>> + }
>> +
>> + ret = rmi_sro_ensure_capacity(sro, count - found);
>> + if (ret)
>> + return ret;
>> +
>> + while (found < count) {
>> + unsigned long addr_range;
>> + void *virt = alloc_pages_exact(unit_size_bytes, gfp);
>> + phys_addr_t phys;
>> +
>> + if (!virt)
>> + return -ENOMEM;
>> +
>> + phys = virt_to_phys(virt);
>> +
>> + if (state == RMI_OP_MEM_DELEGATED) {
>> + if (rmi_delegate_range(phys, unit_size_bytes)) {
>> + free_pages_exact(virt, unit_size_bytes);
>> + return -ENXIO;
>> + }
>> + }
>> +
>> + addr_range = phys & RMI_ADDR_RANGE_ADDR_MASK;
>> + FIELD_MODIFY(RMI_ADDR_RANGE_SIZE_MASK, &addr_range, unit_size);
>> + FIELD_MODIFY(RMI_ADDR_RANGE_COUNT_MASK, &addr_range, 1);
>> + FIELD_MODIFY(RMI_ADDR_RANGE_STATE_MASK, &addr_range, state);
>> +
>> + sro->addr_list[sro->addr_count++] = addr_range;
>> + found++;
>> + }
>> +
>> + regs.a2 = virt_to_phys(&sro->addr_list[addr_list_start]);
>> + regs.a3 = found;
>> + rmi_smccc_invoke(®s, out_regs);
>> +
>> + unsigned long donated_granules = out_regs->a1;
>> +
>> + if (WARN_ON(donated_granules & ((unit_size_bytes >> PAGE_SHIFT) -
>> 1))) {
>> + /*
>> + * FIXME: RMM has only consumed part of a huge page, this leaks
>> + * the rest of the huge page
>> + */
>> + donated_granules = ALIGN(donated_granules,
>> + (unit_size_bytes >> PAGE_SHIFT));
>> + }
>> + unsigned long donated_blocks = donated_granules /
>> (unit_size_bytes >> PAGE_SHIFT);
>> +
>> + if (WARN_ON(donated_blocks > found))
>> + donated_blocks = found;
>> +
>> + unsigned long undonated_blocks = found - donated_blocks;
>> +
>> + while (donated_blocks && undonated_blocks) {
>> + sro->addr_count--;
>> + swap(sro->addr_list[addr_list_start],
>> + sro->addr_list[sro->addr_count]);
>> + addr_list_start++;
>> +
>> + donated_blocks--;
>> + undonated_blocks--;
>> + }
>> + sro->addr_count -= donated_blocks;
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_donate(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + unsigned long donatereq,
>> + struct arm_smccc_1_2_regs *regs,
>> + gfp_t gfp)
>> +{
>> + unsigned long count = RMI_DONATE_COUNT(donatereq);
>> +
>> + if (WARN_ON(!count))
>> + return 0;
>> +
>> + if (RMI_DONATE_CONTIG(donatereq)) {
>> + return rmi_sro_donate_contig(sro, sro_handle, donatereq,
>> + regs, gfp);
>> + } else {
>> + return rmi_sro_donate_noncontig(sro, sro_handle, donatereq,
>> + regs, gfp);
>> + }
>> +}
>> +
>> +static int rmi_sro_reclaim(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + struct arm_smccc_1_2_regs *out_regs)
>> +{
>> + unsigned long capacity;
>> + struct arm_smccc_1_2_regs regs;
>> + int ret;
>> +
>> + ret = rmi_sro_ensure_capacity(sro, 1);
>> + if (ret)
>> + rmi_sro_free(sro);
>> +
>> + capacity = RMI_MAX_ADDR_LIST - sro->addr_count;
>> +
>> + regs = (struct arm_smccc_1_2_regs){
>> + SMC_RMI_OP_MEM_RECLAIM,
>> + sro_handle,
>> + virt_to_phys(&sro->addr_list[sro->addr_count]),
>> + capacity
>> + };
>> + rmi_smccc_invoke(®s, out_regs);
>> +
>> + if (WARN_ON_ONCE(out_regs->a1 > capacity))
>> + out_regs->a1 = capacity;
>> +
>> + sro->addr_count += out_regs->a1;
>> +
>> + return 0;
>> +}
>> +
>> +void rmi_sro_free(struct rmi_sro_state *sro)
>> +{
>> + for (int i = 0; i < sro->addr_count; i++) {
>> + unsigned long entry = sro->addr_list[i];
>> + unsigned long addr = RMI_ADDR_RANGE_ADDR(entry);
>> + unsigned long unit_size = RMI_ADDR_RANGE_SIZE(entry);
>> + unsigned long count = RMI_ADDR_RANGE_COUNT(entry);
>> + unsigned long state = RMI_ADDR_RANGE_STATE(entry);
>> + unsigned long size = donate_req_to_size(unit_size) * count;
>> +
>> + if (state == RMI_OP_MEM_DELEGATED) {
>> + if (WARN_ON(rmi_undelegate_range(addr, size))) {
>> + /* Leak the pages */
>> + continue;
>> + }
>> + }
>> + free_pages_exact(phys_to_virt(addr), size);
>> + }
>> +
>> + sro->addr_count = 0;
>> +}
>> +
>> +unsigned long rmi_sro_execute(struct rmi_sro_state *sro, gfp_t gfp)
>> +{
>> + unsigned long sro_handle;
>> + struct arm_smccc_1_2_regs regs;
>> + struct arm_smccc_1_2_regs *regs_in = &sro->regs;
>> +
>> + rmi_smccc_invoke(regs_in, ®s);
>> +
>> + sro_handle = regs.a1;
>> +
>> + while (RMI_RETURN_STATUS(regs.a0) == RMI_INCOMPLETE) {
>> + bool can_cancel = RMI_RETURN_CAN_CANCEL(regs.a0);
>> + int ret;
>> +
>> + switch (RMI_RETURN_MEMREQ(regs.a0)) {
>> + case RMI_OP_MEM_REQ_NONE:
>> + regs = (struct arm_smccc_1_2_regs){
>> + SMC_RMI_OP_CONTINUE, sro_handle, 0
>> + };
>> + rmi_smccc_invoke(®s, ®s);
>> + break;
>
> 'ret' isn't initialized for case RMI_OP_MEM_REQ_NONE.
Good spot - ret should be initialised to 0.
Thanks,
Steve
>> + case RMI_OP_MEM_REQ_DONATE:
>> + ret = rmi_sro_donate(sro, sro_handle, regs.a2, ®s,
>> + gfp);
>> + break;
>> + case RMI_OP_MEM_REQ_RECLAIM:
>> + ret = rmi_sro_reclaim(sro, sro_handle, ®s);
>> + break;
>> + default:
>> + ret = WARN_ON(1);
>> + break;
>> + }
>> +
>> + if (ret) {
>> + if (can_cancel) {
>> + /*
>> + * FIXME: Handle cancelling properly!
>> + *
>> + * If the operation has failed due to memory
>> + * allocation failure then the information on
>> + * the memory allocation should be saved, so
>> + * that the allocation can be repeated outside
>> + * of any context which prevented the
>> + * allocation.
>> + */
>> + }
>> + if (WARN_ON(ret))
>> + return ret;
>> + }
>> + }
>> +
>> + return regs.a0;
>> +}
>> +
>> static int rmi_check_version(void)
>> {
>> struct arm_smccc_res res;
>
> Thanks,
> Gavin
>
^ permalink raw reply
* Re: [PATCH v14 10/44] arm64: RMI: Add support for SRO
From: Steven Price @ 2026-06-04 15:19 UTC (permalink / raw)
To: Aneesh Kumar K.V, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Emi Kisanuki, Vishal Annapurve,
WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <yq5a7bp0szrw.fsf@kernel.org>
On 19/05/2026 07:02, Aneesh Kumar K.V wrote:
> Steven Price <steven.price@arm.com> writes:
>
>> +static unsigned long donate_req_to_size(unsigned long donatereq)
>> +{
>> + unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
>> +
>> + switch (unit_size) {
>> + case 0:
>> + return PAGE_SIZE;
>> + case 1:
>> + return PMD_SIZE;
>> + case 2:
>> + return PUD_SIZE;
>> + case 3:
>> + return P4D_SIZE;
>> + }
>> + unreachable();
>> +}
>>
>> +
>> +static void rmi_smccc_invoke(struct arm_smccc_1_2_regs *regs_in,
>> + struct arm_smccc_1_2_regs *regs_out)
>> +{
>> + struct arm_smccc_1_2_regs regs = *regs_in;
>> + unsigned long status;
>> +
>> + do {
>> + arm_smccc_1_2_invoke(®s, regs_out);
>> + status = RMI_RETURN_STATUS(regs_out->a0);
>> + } while (status == RMI_BUSY || status == RMI_BLOCKED);
>> +}
>> +
>> +int free_delegated_page(phys_addr_t phys)
>> +{
>> + if (WARN_ON(rmi_undelegate_page(phys))) {
>> + /* Undelegate failed: leak the page */
>> + return -EBUSY;
>> + }
>> +
>> + free_page((unsigned long)phys_to_virt(phys));
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_ensure_capacity(struct rmi_sro_state *sro,
>> + unsigned long count)
>> +{
>> + if (WARN_ON_ONCE(sro->addr_count > RMI_MAX_ADDR_LIST))
>> + return -EOVERFLOW;
>> +
>> + if (count > RMI_MAX_ADDR_LIST - sro->addr_count)
>> + return -ENOSPC;
>> +
>> + return 0;
>> +}
>> +
>> +static int rmi_sro_donate_contig(struct rmi_sro_state *sro,
>> + unsigned long sro_handle,
>> + unsigned long donatereq,
>> + struct arm_smccc_1_2_regs *out_regs,
>> + gfp_t gfp)
>> +{
>> + unsigned long unit_size = RMI_DONATE_SIZE(donatereq);
>> + unsigned long unit_size_bytes = donate_req_to_size(donatereq);
>> + unsigned long count = RMI_DONATE_COUNT(donatereq);
>> + unsigned long state = RMI_DONATE_STATE(donatereq);
>> + unsigned long size = unit_size_bytes * count;
>> + unsigned long addr_range;
>>
>
> Looking at above and the related code, I am wondering whether we should
> use u64 instead of unsigned long for everything that the specification
> defines as 64-bit.
I'm split on this. The kernel makes use of "(unsigned) long" for a
register sized value in quite a few places. Not least in struct
arm_smccc_1_2_regs which is ultimately where most of the values are read
from or written to.
I'm not a great fan of the kernel's approach to using long like this -
there's a good argument that uintptr_t is more correct. Equally when
we're in arch code for a 64 bit architecture (i.e. "arm64") then we know
the size is u64.
The disadvantage here is that if I use u64 then there's a bunch of
implicit conversions going on between unsigned long and u64 - which
might come back to bite if anything changes. Hence my current view that
"unsigned long" is the best option here in the kernel.
Anyone else have any view on the best type here?
Thanks,
Steve
^ permalink raw reply
* [PATCH v2 1/2] KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU
From: Hyunwoo Kim @ 2026-06-04 15:12 UTC (permalink / raw)
To: tabba, maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will
Cc: linux-arm-kernel, kvmarm, imv4bel
In-Reply-To: <20260604151210.1304051-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.
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 06db299c37a89..02c5d6e5abcbf 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 v2 2/2] KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU
From: Hyunwoo Kim @ 2026-06-04 15:12 UTC (permalink / raw)
To: tabba, maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will
Cc: linux-arm-kernel, kvmarm, imv4bel
In-Reply-To: <20260604151210.1304051-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().
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))
+ 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);
+
hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/2] KVM: arm64: Sanitise host vCPU fields copied in flush_hyp_vcpu()
From: Hyunwoo Kim @ 2026-06-04 15:12 UTC (permalink / raw)
To: tabba, maz, oupton, joey.gouly, seiden, suzuki.poulose, yuzenghui,
catalin.marinas, will
Cc: linux-arm-kernel, kvmarm, 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.
v1 -> v2: split into two patches, one per field, per review.
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/kvm/hyp/nvhe/hyp-main.c | 11 +++++++++++
1 file changed, 11 insertions(+)
--
2.43.0
^ permalink raw reply
* [PATCH v2 5/5] arm64: mm: Defer remap of linear alias of data/bss
From: Ard Biesheuvel @ 2026-06-04 15:11 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-kernel, will, catalin.marinas, Ard Biesheuvel,
Kevin Brodsky, Mark Brown, Marc Zyngier
In-Reply-To: <20260604151151.150377-7-ardb+git@google.com>
From: Ard Biesheuvel <ardb@kernel.org>
Marking the linear alias of data/bss invalid involves calling
set_memory_valid(), which calls split_kernel_leaf_mapping() under the
hood.
On BBML2_NOABORT capable systems, this may result in the need to
allocate page tables at a time when the generic memory allocation APIs
are not yet available, resulting in a splat like
WARNING: arch/arm64/mm/mmu.c:821 at split_kernel_leaf_mapping+0x15c/0x170, CPU#0: swapper/0
Modules linked in:
CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 7.1.0-rc6 #1 PREEMPT(undef)
pstate: a04000c9 (NzCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : split_kernel_leaf_mapping+0x15c/0x170
lr : update_range_prot+0x40/0x128
sp : ffffc99ad3863c80
...
Call trace:
split_kernel_leaf_mapping+0x15c/0x170 (P)
update_range_prot+0x40/0x128
set_memory_valid+0x94/0xe0
mark_linear_data_alias_valid+0x54/0x68
map_mem+0x1fc/0x240
paging_init+0x48/0x210
setup_arch+0x274/0x338
start_kernel+0x98/0x538
__primary_switched+0x88/0x98
as reported by CKI automated testing.
So defer the boot-time call to mark_linear_data_alias_valid() to a later
time when page allocations can be made normally.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/mm/mmu.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index d68e691c093a..3134f1c1097c 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1095,6 +1095,9 @@ void __init mark_linear_text_alias_ro(void)
(unsigned long)__init_begin - (unsigned long)_text,
PAGE_KERNEL_RO);
+ /* Map the kernel data/bss as invalid in the linear map */
+ mark_linear_data_alias_valid(false);
+
/*
* Register a PM notifier to remap the linear alias of data/bss as
* valid read-only before hibernation. This is needed because the
@@ -1237,9 +1240,6 @@ static void __init map_mem(void)
__map_memblock(start, end, pgprot_tagged(PAGE_KERNEL),
flags);
}
-
- /* Map the kernel data/bss as invalid in the linear map */
- mark_linear_data_alias_valid(false);
}
void mark_rodata_ro(void)
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related
* [PATCH v2 4/5] KVM: arm64: Omit tag sync on stage-2 mappings of the zero page
From: Ard Biesheuvel @ 2026-06-04 15:11 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-kernel, will, catalin.marinas, Ard Biesheuvel,
Kevin Brodsky, Mark Brown, Marc Zyngier, stable
In-Reply-To: <20260604151151.150377-7-ardb+git@google.com>
From: Ard Biesheuvel <ardb@kernel.org>
Commit
f620d66af316 ("arm64: mte: Do not flag the zero page as PG_mte_tagged")
removed the PG_mte_tagged flag from the zero page, but missed a KVM code
path that may set this flag on the zero page when it is used in a
stage-2 CoW mapping of anonymous memory.
So disregard the zero page explicitly in sanitise_mte_tags().
Fixes: f620d66af316 ("arm64: mte: Do not flag the zero page as PG_mte_tagged")
Cc: <stable@vger.kernel.org> # 5.10.x
Suggested-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/kvm/mmu.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index d089c107d9b7..445d6cf035c9 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1479,6 +1479,11 @@ static void sanitise_mte_tags(struct kvm *kvm, kvm_pfn_t pfn,
if (!kvm_has_mte(kvm))
return;
+ if (is_zero_pfn(pfn)) {
+ WARN_ON_ONCE(nr_pages != 1);
+ return;
+ }
+
if (folio_test_hugetlb(folio)) {
/* Hugetlb has MTE flags set on head page only */
if (folio_try_hugetlb_mte_tagging(folio)) {
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related
* [PATCH v2 2/5] kasan: Move generic KASAN page tables out of BSS too
From: Ard Biesheuvel @ 2026-06-04 15:11 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-kernel, will, catalin.marinas, Ard Biesheuvel,
Kevin Brodsky, Mark Brown, Marc Zyngier, Andrey Ryabinin,
Alexander Potapenko, Andrey Konovalov, Dmitry Vyukov,
Vincenzo Frascino, kasan-dev
In-Reply-To: <20260604151151.150377-7-ardb+git@google.com>
From: Ard Biesheuvel <ardb@kernel.org>
Make sure that all KASAN page tables are emitted into the .pgtbl section
(provided that the arch has one - otherwise, fall back to page aligned
BSS)
This is needed because BSS itself is no longer accessible via the linear
map on arm64.
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Cc: kasan-dev@googlegroups.com
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
include/linux/linkage.h | 4 ++++
mm/kasan/init.c | 10 +++++-----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/include/linux/linkage.h b/include/linux/linkage.h
index b11660b706c5..53fe1f48fd28 100644
--- a/include/linux/linkage.h
+++ b/include/linux/linkage.h
@@ -39,6 +39,10 @@
#define __page_aligned_data __section(".data..page_aligned") __aligned(PAGE_SIZE)
#define __page_aligned_bss __section(".bss..page_aligned") __aligned(PAGE_SIZE)
+#ifndef __bss_pgtbl
+#define __bss_pgtbl __page_aligned_bss
+#endif
+
/*
* For assembly routines.
*
diff --git a/mm/kasan/init.c b/mm/kasan/init.c
index 9c880f607c6a..66a883887987 100644
--- a/mm/kasan/init.c
+++ b/mm/kasan/init.c
@@ -26,10 +26,10 @@
* - Latter it reused it as zero shadow to cover large ranges of memory
* that allowed to access, but not handled by kasan (vmalloc/vmemmap ...).
*/
-unsigned char kasan_early_shadow_page[PAGE_SIZE] __page_aligned_bss;
+unsigned char kasan_early_shadow_page[PAGE_SIZE] __bss_pgtbl;
#if CONFIG_PGTABLE_LEVELS > 4
-p4d_t kasan_early_shadow_p4d[MAX_PTRS_PER_P4D] __page_aligned_bss;
+p4d_t kasan_early_shadow_p4d[MAX_PTRS_PER_P4D] __bss_pgtbl;
static inline bool kasan_p4d_table(pgd_t pgd)
{
return pgd_page(pgd) == virt_to_page(lm_alias(kasan_early_shadow_p4d));
@@ -41,7 +41,7 @@ static inline bool kasan_p4d_table(pgd_t pgd)
}
#endif
#if CONFIG_PGTABLE_LEVELS > 3
-pud_t kasan_early_shadow_pud[MAX_PTRS_PER_PUD] __page_aligned_bss;
+pud_t kasan_early_shadow_pud[MAX_PTRS_PER_PUD] __bss_pgtbl;
static inline bool kasan_pud_table(p4d_t p4d)
{
return p4d_page(p4d) == virt_to_page(lm_alias(kasan_early_shadow_pud));
@@ -53,7 +53,7 @@ static inline bool kasan_pud_table(p4d_t p4d)
}
#endif
#if CONFIG_PGTABLE_LEVELS > 2
-pmd_t kasan_early_shadow_pmd[MAX_PTRS_PER_PMD] __page_aligned_bss;
+pmd_t kasan_early_shadow_pmd[MAX_PTRS_PER_PMD] __bss_pgtbl;
static inline bool kasan_pmd_table(pud_t pud)
{
return pud_page(pud) == virt_to_page(lm_alias(kasan_early_shadow_pmd));
@@ -65,7 +65,7 @@ static inline bool kasan_pmd_table(pud_t pud)
}
#endif
pte_t kasan_early_shadow_pte[MAX_PTRS_PER_PTE + PTE_HWTABLE_PTRS]
- __page_aligned_bss;
+ __bss_pgtbl;
static inline bool kasan_pte_table(pmd_t pmd)
{
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related
* [PATCH v2 3/5] arm64: Avoid double evaluation of __ptep_get()
From: Ard Biesheuvel @ 2026-06-04 15:11 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-kernel, will, catalin.marinas, Ard Biesheuvel,
Kevin Brodsky, Mark Brown, Marc Zyngier
In-Reply-To: <20260604151151.150377-7-ardb+git@google.com>
From: Ard Biesheuvel <ardb@kernel.org>
Sashiko warns that the new pte_valid_noncont() macro is used in a manner
where the argument (which performs a READ_ONCE() of the descriptor) is
evaluated twice.
Drop the macro that we just added, and move the check into the newly
added users.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/include/asm/pgtable.h | 4 ----
arch/arm64/mm/mmu.c | 14 ++++++++++----
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 491ba0a6492d..c9e4e00a9af2 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -181,10 +181,6 @@ static inline pteval_t __phys_to_pte_val(phys_addr_t phys)
* Returns true if the pte is valid and has the contiguous bit set.
*/
#define pte_valid_cont(pte) (pte_valid(pte) && pte_cont(pte))
-/*
- * Returns true if the pte is valid and has the contiguous bit cleared.
- */
-#define pte_valid_noncont(pte) (pte_valid(pte) && !pte_cont(pte))
/*
* Could the pte be present in the TLB? We must check mm_tlb_flush_pending
* so that we don't erroneously return false for pages that have been
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index be51f6cac86f..d68e691c093a 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -186,9 +186,12 @@ static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
static bool pte_range_has_valid_noncont(pte_t *ptep)
{
- for (int i = 0; i < CONT_PTES; i++)
- if (pte_valid_noncont(__ptep_get(&ptep[i])))
+ for (int i = 0; i < CONT_PTES; i++) {
+ pte_t pte = __ptep_get(&ptep[i]);
+
+ if (pte_valid(pte) && !pte_cont(pte))
return true;
+ }
return false;
}
@@ -291,9 +294,12 @@ static int init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
static bool pmd_range_has_valid_noncont(pmd_t *pmdp)
{
- for (int i = 0; i < CONT_PMDS; i++)
- if (pte_valid_noncont(pmd_pte(READ_ONCE(pmdp[i]))))
+ for (int i = 0; i < CONT_PMDS; i++) {
+ pte_t pte = pmd_pte(READ_ONCE(pmdp[i]));
+
+ if (pte_valid(pte) && !pte_cont(pte))
return true;
+ }
return false;
}
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related
* [PATCH v2 1/5] arm64: Rename page table BSS section to .bss..pgtbl
From: Ard Biesheuvel @ 2026-06-04 15:11 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-kernel, will, catalin.marinas, Ard Biesheuvel,
Kevin Brodsky, Mark Brown, Marc Zyngier
In-Reply-To: <20260604151151.150377-7-ardb+git@google.com>
From: Ard Biesheuvel <ardb@kernel.org>
Rename the .pgdir.bss section to .bss..pgtbl so that the compiler will
notice the leading ".bss" and mark it as NOBITS by default (rather than
PROGBITS, which would take up space in Image binary, forcing all of the
preceding BSS to be emitted into the image as well). This supersedes the
NOLOAD linker directive, which achieves the same thing, and can be
therefore be dropped.
Also, rename .pgdir to .pgtbl to be more generic, as page tables of
various levels will reside here.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/include/asm/linkage.h | 2 ++
arch/arm64/include/asm/mmu.h | 2 --
arch/arm64/kernel/vmlinux.lds.S | 8 ++++----
arch/arm64/mm/fixmap.c | 6 +++---
arch/arm64/mm/kasan_init.c | 2 +-
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/include/asm/linkage.h b/arch/arm64/include/asm/linkage.h
index 40bd17add539..8637f667667c 100644
--- a/arch/arm64/include/asm/linkage.h
+++ b/arch/arm64/include/asm/linkage.h
@@ -43,4 +43,6 @@
SYM_TYPED_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) \
bti c ;
+#define __bss_pgtbl __section(".bss..pgtbl") __aligned(PAGE_SIZE)
+
#endif
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index fb95754f2876..5e1211c540ab 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -13,8 +13,6 @@
#ifndef __ASSEMBLER__
-#define __pgtbl_bss __section(".pgdir.bss") __aligned(PAGE_SIZE)
-
#include <linux/refcount.h>
#include <asm/cpufeature.h>
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 2b0ebfb30c63..d3ed59abab38 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -352,11 +352,11 @@ SECTIONS
BSS_SECTION(SBSS_ALIGN, 0, PAGE_SIZE)
__pi___bss_start = __bss_start;
- /* fixmap BSS starts here - preceding data/BSS is omitted from the linear map */
- .pgdir.bss (NOLOAD) : ALIGN(PAGE_SIZE) {
- *(.pgdir.bss)
+ /* page table BSS starts here - preceding data/BSS is omitted from the linear map */
+ .pgtbl : ALIGN(PAGE_SIZE) {
+ *(.bss..pgtbl)
}
- ASSERT(ADDR(.pgdir.bss) == __bss_stop, ".pgdir.bss must follow BSS")
+ ASSERT(ADDR(.pgtbl) == __bss_stop, ".pgtbl must follow BSS")
. = ALIGN(PAGE_SIZE);
__pi_init_pg_dir = .;
diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
index 1a3bbd67dd76..f66a0016dd02 100644
--- a/arch/arm64/mm/fixmap.c
+++ b/arch/arm64/mm/fixmap.c
@@ -31,9 +31,9 @@ static_assert(NR_BM_PMD_TABLES == 1);
#define BM_PTE_TABLE_IDX(addr) __BM_TABLE_IDX(addr, PMD_SHIFT)
-static pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __pgtbl_bss;
-static pmd_t bm_pmd[PTRS_PER_PMD] __pgtbl_bss __maybe_unused;
-static pud_t bm_pud[PTRS_PER_PUD] __pgtbl_bss __maybe_unused;
+static pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __bss_pgtbl;
+static pmd_t bm_pmd[PTRS_PER_PMD] __bss_pgtbl __maybe_unused;
+static pud_t bm_pud[PTRS_PER_PUD] __bss_pgtbl __maybe_unused;
static inline pte_t *fixmap_pte(unsigned long addr)
{
diff --git a/arch/arm64/mm/kasan_init.c b/arch/arm64/mm/kasan_init.c
index dbf22cae82ee..3fcad956fdf7 100644
--- a/arch/arm64/mm/kasan_init.c
+++ b/arch/arm64/mm/kasan_init.c
@@ -214,7 +214,7 @@ asmlinkage void __init kasan_early_init(void)
* shadow pud_t[]/p4d_t[], which could end up getting corrupted
* when the linear region is mapped.
*/
- static pte_t tbl[PTRS_PER_PTE] __pgtbl_bss;
+ static pte_t tbl[PTRS_PER_PTE] __bss_pgtbl;
pgd_t *pgdp = pgd_offset_k(KASAN_SHADOW_START);
set_pgd(pgdp, __pgd(__pa_symbol(tbl) | PGD_TYPE_TABLE));
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related
* [PATCH v2 0/5] fixes for data/bss linear alias unmap series
From: Ard Biesheuvel @ 2026-06-04 15:11 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-kernel, will, catalin.marinas, Ard Biesheuvel,
Kevin Brodsky, Mark Brown, Marc Zyngier
From: Ard Biesheuvel <ardb@kernel.org>
Fixes for the data/bss linear alias unmap series:
- Fix KASAN related issue reported by Mark, by moving all KASAN page
tables out of BSS [on arm64], not just the ones defined under
arch/arm64
- Fix two issues spotted by Sashiko
- Fix the CKI reported WARN() splat on BBML2_NOABORT systems that try to
split block mappings too early.
Changes since v1:
- Split .bss..pgtbl changes off into separate patch
- Take Catalin's suggestion on how to address MTE issue with the
read-only zero page
- Improved commit logs
Cc: Kevin Brodsky <kevin.brodsky@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Marc Zyngier <maz@kernel.org>
Ard Biesheuvel (5):
arm64: Rename page table BSS section to .bss..pgtbl
kasan: Move generic KASAN page tables out of BSS too
arm64: Avoid double evaluation of __ptep_get()
KVM: arm64: Omit tag sync on stage-2 mappings of the zero page
arm64: mm: Defer remap of linear alias of data/bss
arch/arm64/include/asm/linkage.h | 2 ++
arch/arm64/include/asm/mmu.h | 2 --
arch/arm64/include/asm/pgtable.h | 4 ----
arch/arm64/kernel/vmlinux.lds.S | 8 ++++----
arch/arm64/kvm/mmu.c | 5 +++++
arch/arm64/mm/fixmap.c | 6 +++---
arch/arm64/mm/kasan_init.c | 2 +-
arch/arm64/mm/mmu.c | 20 +++++++++++++-------
include/linux/linkage.h | 4 ++++
mm/kasan/init.c | 10 +++++-----
10 files changed, 37 insertions(+), 26 deletions(-)
base-commit: 63e0b6a5b6934d6a919d1c65ea185303200a1874
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply
* Re: [PATCH v6 7/8] perf cs-etm: Synthesize callchains for instruction samples
From: James Clark @ 2026-06-04 15:07 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users, Leo Yan,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-7-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> From: Leo Yan <leo.yan@linaro.org>
>
> CS ETM already records branches into the thread stack, but instruction
> samples do not carry synthesized callchains. It misses to support the
> callchain and no output with the itrace option 'g'.
>
> Allocate a callchain buffer per queue and use thread_stack__sample()
> when synthesizing instruction samples. Advertise PERF_SAMPLE_CALLCHAIN
> on the synthetic instruction event.
>
> Allocate the callchain stack with one more entry than requested, as the
> first entry is reserved for storing context information.
>
> After:
>
> perf script --itrace=g16l64i100
>
> callchain_test 9187 [002] 599611.826599: 1 instructions:
> aaaae3ed0774 do_svc+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaae3ed0798 print+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaae3ed07b0 foo+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> aaaae3ed07c8 main+0xc (/home/kernel/leoy/test_cs_callchain/callchain_test)
> ffff8331225c __libc_start_call_main+0x7c (/usr/lib/aarch64-linux-gnu/libc.so.6)
> ffff8331233c call_init+0x9c (inlined)
> ffff8331233c __libc_start_main_impl+0x9c (inlined)
> aaaae3ed0670 _start+0x30 (/home/kernel/leoy/test_cs_callchain/callchain_test)
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 48 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 8d98e772ecb307381b5ed1b4bbc4056e8779b261..90e0beb910156093d8bd0f320bb0210aca95dd26 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -17,6 +17,7 @@
> #include <stdlib.h>
>
> #include "auxtrace.h"
> +#include "callchain.h"
> #include "color.h"
> #include "cs-etm.h"
> #include "cs-etm-decoder/cs-etm-decoder.h"
> @@ -85,6 +86,7 @@ struct cs_etm_auxtrace {
> struct cs_etm_traceid_queue {
> u8 trace_chan_id;
> u64 period_instructions;
> + u64 kernel_start;
> union perf_event *event_buf;
> struct thread *thread;
> struct thread *prev_packet_thread;
> @@ -92,6 +94,7 @@ struct cs_etm_traceid_queue {
> ocsd_ex_level el;
> unsigned int br_stack_sz;
> struct branch_stack *last_branch;
> + struct ip_callchain *callchain;
> struct cs_etm_packet *prev_packet;
> struct cs_etm_packet *packet;
> struct cs_etm_packet_queue packet_queue;
> @@ -640,6 +643,16 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> tidq->br_stack_sz = etm->synth_opts.last_branch_sz;
> }
>
> + if (etm->synth_opts.callchain) {
> + size_t sz = sizeof(struct ip_callchain);
> +
> + /* Add 1 to callchain_sz for callchain context */
> + sz += (etm->synth_opts.callchain_sz + 1) * sizeof(u64);
> + tidq->callchain = zalloc(sz);
You can use struct_size() to get the size for a flexible array struct:
zalloc(struct_size(tidq->callchain, ips,
etm->synth_opts.callchain_sz + 1));
> + if (!tidq->callchain)
> + goto out_free;
> + }
> +
> tidq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
> if (!tidq->event_buf)
> goto out_free;
> @@ -647,6 +660,7 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> return 0;
>
> out_free:
> + zfree(&tidq->callchain);
> zfree(&tidq->last_branch);
> zfree(&tidq->prev_packet);
> zfree(&tidq->packet);
> @@ -939,6 +953,7 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
> thread__zput(tidq->thread);
> thread__zput(tidq->prev_packet_thread);
> zfree(&tidq->event_buf);
> + zfree(&tidq->callchain);
> zfree(&tidq->last_branch);
> zfree(&tidq->prev_packet);
> zfree(&tidq->packet);
> @@ -1431,6 +1446,7 @@ static void cs_etm__set_thread(struct cs_etm_queue *etmq,
> tidq->thread = machine__idle_thread(machine);
>
> tidq->el = el;
> + tidq->kernel_start = machine__kernel_start(machine);
> }
>
> int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid,
> @@ -1561,6 +1577,25 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
> sample.branch_stack = tidq->last_branch;
> }
>
> + if (etm->synth_opts.callchain) {
> + if (tidq->kernel_start)
> + thread_stack__sample(tidq->thread, tidq->packet->cpu,
> + tidq->callchain,
> + etm->synth_opts.callchain_sz + 1,
> + sample.ip, tidq->kernel_start);
> + else
> + /*
> + * Clear the callchain when the kernel start address is
> + * not available yet. The empty callchain can then be
> + * consumed by cs_etm__inject_event().
> + */
> + memset(tidq->callchain, 0,
> + sizeof(struct ip_callchain) +
> + (etm->synth_opts.callchain_sz + 1) * sizeof(u64));
Ditto, but the rest looks good:
Reviewed-by: James Clark <james.clark@linaro.org>
> +
> + sample.callchain = tidq->callchain;
> + }
> +
> if (etm->synth_opts.inject) {
> ret = cs_etm__inject_event(etm, event, &sample,
> etm->instructions_sample_type);
> @@ -1724,6 +1759,9 @@ static int cs_etm__synth_events(struct cs_etm_auxtrace *etm,
> attr.branch_sample_type |= PERF_SAMPLE_BRANCH_HW_INDEX;
> }
>
> + if (etm->synth_opts.callchain)
> + attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
> +
> if (etm->synth_opts.instructions) {
> attr.config = PERF_COUNT_HW_INSTRUCTIONS;
> attr.sample_period = etm->synth_opts.period;
> @@ -3457,6 +3495,14 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
> PERF_IP_FLAG_TRACE_BEGIN |
> PERF_IP_FLAG_TRACE_END;
>
> + if (etm->synth_opts.callchain && !symbol_conf.use_callchain) {
> + symbol_conf.use_callchain = true;
> + if (callchain_register_param(&callchain_param) < 0) {
> + symbol_conf.use_callchain = false;
> + etm->synth_opts.callchain = false;
> + }
> + }
> +
> etm->session = session;
>
> etm->num_cpu = num_cpu;
> @@ -3508,7 +3554,8 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
> }
>
> etm->use_thread_stack = etm->synth_opts.thread_stack ||
> - etm->synth_opts.last_branch;
> + etm->synth_opts.last_branch ||
> + etm->synth_opts.callchain;
>
> err = cs_etm__synth_events(etm, session);
> if (err)
>
^ permalink raw reply
* RE: [PATCH v5 05/20] dma-pool: track decrypted atomic pools and select them via attrs
From: Aneesh Kumar K.V @ 2026-06-04 14:57 UTC (permalink / raw)
To: Michael Kelley, Jason Gunthorpe, Michael Kelley
Cc: iommu@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
Robin Murphy, Marek Szyprowski, Will Deacon, Marc Zyngier,
Steven Price, Suzuki K Poulose, Catalin Marinas, Jiri Pirko,
Mostafa Saleh, Petr Tesarik, Alexey Kardashevskiy, Dan Williams,
Xu Yilun, linuxppc-dev@lists.ozlabs.org,
linux-s390@vger.kernel.org, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86@kernel.org, Jiri Pirko
In-Reply-To: <SN6PR02MB4157F94C902B78E55E99372DD4102@SN6PR02MB4157.namprd02.prod.outlook.com>
Michael Kelley <mhklinux@outlook.com> writes:
> From: Jason Gunthorpe <jgg@ziepe.ca> Sent: Tuesday, June 2, 2026 5:55 PM
>>
>> On Tue, Jun 02, 2026 at 02:24:40PM +0000, Michael Kelley wrote:
>>
>> > Except that in a normal VM, the "unencrypted" pool attribute does *not*
>> > describe the state of the memory itself. In a normal VM, the memory is
>> > unencrypted, but the "unencrypted" pool attribute is false. That
>> > contradiction is the essence of my concern.
>>
>> I would argue no..
>>
>> When CC is enabled the default state of memory in a Linux environment
>> is "encrypted". You have to take a special action to "decrypt" it.
>>
>> Thus the default state of memory in a non-CC environment is also
>> paradoxically "encrypted" too.
>
> The need to have such an unnatural premise is usually an indication
> of a conceptual problem with the overall model, or perhaps just a
> terminology problem.
>
> Here's a proposal. The new DMA attribute is DMA_ATTR_CC_SHARED.
> Name the pool attribute "cc_shared" instead of "unencrypted". Having
> "cc_shared" set to false in a normal VM doesn't lead to the non-sensical
> situation of claiming that a normal VM is encrypted. The boolean
> "unencrypted" parameter that has been added to various calls also
> becomes "cc_shared". If "CC_SHARED" is a suitable name for the DMA
> attribute, it ought to be suitable as the pool attribute. And everything
> matches as well.
>
That is better. It would also simplify:
if (mem->unencrypted != !!(attrs & DMA_ATTR_CC_SHARED))
return NULL;
to
if (mem->cc_shared != !!(attrs & DMA_ATTR_CC_SHARED))
return NULL;
I already sent a v6 in the hope of getting this merged for the next
merge window. Should I send a v7, or would you prefer that I do the
rename on top of v6?
-aneesh
^ permalink raw reply
* Re: [syzbot] [kvmarm?] [kvm?] BUG: unable to handle kernel paging request in kvm_vgic_destroy
From: Dmitry Vyukov @ 2026-06-04 14:47 UTC (permalink / raw)
To: Marc Zyngier
Cc: syzbot, syzkaller, catalin.marinas, joey.gouly, kvm, kvmarm,
linux-arm-kernel, linux-kernel, oupton, suzuki.poulose,
syzkaller-bugs, will, yuzenghui
In-Reply-To: <86wlzu8ird.wl-maz@kernel.org>
On Mon, 2 Mar 2026 at 14:26, 'Marc Zyngier' via syzkaller
<syzkaller@googlegroups.com> wrote:
>
> Hi Dmitry,
>
> On Mon, 02 Mar 2026 12:59:41 +0000,
> Dmitry Vyukov <dvyukov@google.com> wrote:
> >
> > Hi Marc,
> >
> > The reproducer should print at the start specifically to address this concern:
> >
> > the reproducer may not work as expected: fault injection setup failed:
> > failed to write fault injection file
> >
> > Isn't it enough?
>
> Ah, I totally missed that, because things scroll really
> quickly... Apologies.
>
> I think it'd be a bit better to just stop the test if the
> preconditions are not met, as nothing useful will happen.
FTR filed https://github.com/google/syzkaller/issues/7450 for this.
> Thanks,
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
>
> --
> You received this message because you are subscribed to the Google Groups "syzkaller" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller+unsubscribe@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/syzkaller/86wlzu8ird.wl-maz%40kernel.org.
^ permalink raw reply
* [PATCH v3] net: stmmac: fix fatal bus error on resume by reinitializing RX buffers
From: Ding Hui @ 2026-06-04 14:45 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Russell King (Oracle), Maxime Chevallier, Ding Hui,
open list:STMMAC ETHERNET DRIVER,
moderated list:ARM/STM32 ARCHITECTURE,
moderated list:ARM/STM32 ARCHITECTURE, open list
Cc: j.raczynski, xiasanbo, yangchen11, liuxuanjun
From: Ding Hui <dinghui@lixiang.com>
On suspend, stmmac_suspend() calls stmmac_disable_all_queues() which
stops the RX NAPI, but the RX DMA engine may still be running for a
short window before stmmac_stop_all_dma() takes effect. During that
window the hardware can write incoming frames into the buffers pointed
to by the RX descriptors and write back the descriptors (clearing the
OWN bit and overwriting RDES0/1/2 with status/timestamp data). Because
NAPI is already disabled, the driver never refills these descriptors,
so the RX ring is left in a "consumed but not refilled" state with
stale content in the descriptor buffer-address fields.
On resume, stmmac_clear_descriptors() only re-arms the OWN bit and
does not repopulate the RX buffer address fields. When the DMA is
restarted it dereferences these stale addresses and triggers a fatal
bus error (not kernel panic, just a Fatal Bus Error interrupt and
RX DMA engine halts).
Fix this by introducing stmmac_reinit_rx_descriptors(), called from
stmmac_resume() immediately after stmmac_clear_descriptors(). The
helper iterates every RX descriptor slot and re-programs its buffer
address fields:
- For normal (page_pool) queues: restore RDES0/1 from buf->addr and
RDES2 from buf->sec_addr. The DMA mapping has remained valid across
suspend/resume because no pages were freed. Slots left NULL by a
prior GFP_ATOMIC failure in stmmac_rx_refill() before suspend
are re-allocated here with GFP_KERNEL;
-ENOMEM is returned and resume is aborted if allocation fails.
The slots with null buffer are unacceptable, because they will
cause a DMA suspend dead lock problem by the condition of
Current Descriptor Pointer == Descriptor Tail Pointer.
- For AF_XDP zero-copy queues: restore the DMA address from
xsk_buff_xdp_get_dma(buf->xdp). Slots with no xdp buffer
(e.g. TX-only socket, empty fill ring) attempt xsk_buff_alloc()
first; on failure the descriptor is zeroed so the DMA engine skips
the slot safely via an RBU event.
- For chain mode: call stmmac_mode_init() to rebuild the des3 next-
descriptor pointer chain, which hardware may have overwritten with
a PTP timestamp value (as noted in chain_mode.c:refill_desc3()).
After reprogramming all address fields, a final pass restores OWN=1
on every valid slot. This is necessary because set_sec_addr and
chain-mode init unconditionally overwrite des3 (clearing the OWN bit
set by stmmac_clear_descriptors()), and must run after all address
writes are complete.
Also fix stmmac_init_rx_buffers() to actually use its gfp_t flags
parameter instead of the hardcoded GFP_ATOMIC | __GFP_NOWARN.
Signed-off-by: Ding Hui <dinghui@lixiang.com>
---
Changes in v3:
- Re-allocate page_pool NULL slots (from prior GFP_ATOMIC failures)
with GFP_KERNEL in stmmac_reinit_rx_descriptors(); return -ENOMEM and
abort resume.
- For XSK NULL slots, attempt xsk_buff_alloc() first; fall back to
stmmac_clear_desc() only when allocation fails.
- Add a re-arm loop at the end of stmmac_reinit_rx_descriptors() to
restore OWN=1 on all valid slots, since set_sec_addr and
chain-mode init both write des3 unconditionally.
- stmmac_reinit_rx_descriptors() now returns int; stmmac_resume()
checks the return value and propagates -ENOMEM with mutex/rtnl cleanup.
- Fix stmmac_init_rx_buffers() to use its flags parameter instead of
hardcoded GFP_ATOMIC | __GFP_NOWARN.
(884d2b845477 ("net: stmmac: Add GFP_DMA32 for rx buffers if no 64
capability"))
- Run stmmac_reinit_rx_descriptors() after stmmac_clear_descriptors()
so that stmmac_clear_desc() on XSK NULL slots overrides the OWN
bit set by stmmac_clear_descriptors().
- Update commit message.
- Link to v2:
https://lore.kernel.org/netdev/20260526022620.501229-1-dinghui1111@163.com/
Changes in v2:
- Introducing stmmac_reinit_rx_descriptors() to reinitializing rx
buffers without any allocation.
- Modify commit log.
- Link to v1:
https://lore.kernel.org/netdev/20260515053856.2310369-1-dinghui1111@163.com/
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 161 +++++++++++++++++-
1 file changed, 160 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3591755ea30b..36428e4ba8fd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1660,7 +1660,7 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
{
struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
- gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
+ gfp_t gfp = flags;
if (priv->dma_cap.host_dma_width <= 32)
gfp |= GFP_DMA32;
@@ -1693,6 +1693,145 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
return 0;
}
+/**
+ * stmmac_reinit_rx_descriptors - re-program RX descriptor buffer addresses
+ * after stmmac_clear_descriptors()
+ * @priv: driver private structure
+ * @dma_conf: structure holding the dma data
+ * @queue: RX queue index
+ *
+ * Description: Called in the resume path after stmmac_clear_descriptors()
+ * has re-armed the OWN bit on every descriptor. Walk buf_pool[] and
+ * re-program the buffer-address fields of every RX descriptor from the
+ * buffers that are already attached to the queue. Slots whose page was
+ * never allocated (GFP_ATOMIC failure before suspend) are re-allocated
+ * here with GFP_KERNEL; the resume path is in process context.
+ *
+ * Between suspend and resume the hardware may have written back status/
+ * length information into the descriptor address fields (RDESx are reused
+ * for status on completion for GMAC4/XGMAC), so the address fields must be
+ * repopulated before the DMA is restarted.
+ *
+ * For XSK slots that have no xdp buffer at suspend time (TX-only socket,
+ * empty fill ring for Rx), xsk_buff_alloc() is attempted but does not
+ * return an error on failure because we can't identify a real TX-only
+ * socket from an alloc error (same as stmmac_alloc_rx_buffers_zc() in
+ * __init_dma_rx_desc_rings); on failure the descriptor is zeroed so the DMA
+ * engine skips the slot safely.
+ *
+ * To avoid the DMA stall after resume in non-XSK mode, this function
+ * re-allocates pages for NULL slots using GFP_KERNEL (the resume path runs
+ * in process context). If allocation fails, -%ENOMEM is returned immediately
+ * and the resume is aborted; the caller should report the error.
+ *
+ * This helper must be called after stmmac_clear_descriptors() and before
+ * stmmac_hw_setup() in stmmac_resume() because we need to wipe the OWN bit
+ * set in stmmac_clear_descriptors() for NULL slots in XSK mode.
+ */
+static int stmmac_reinit_rx_descriptors(struct stmmac_priv *priv,
+ struct stmmac_dma_conf *dma_conf,
+ u32 queue)
+{
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ struct stmmac_rx_buffer *buf;
+ struct dma_desc *p;
+ int i;
+
+ if (rx_q->xsk_pool) {
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ /* The XSK pool may not be fully populated (e.g.
+ * xdpsock TX-only, empty fill ring). Try to refill
+ * from the pool; on failure zero the descriptor so the
+ * DMA engine skips this slot safely.
+ */
+ if (!buf->xdp) {
+ buf->xdp = xsk_buff_alloc(rx_q->xsk_pool);
+ if (!buf->xdp) {
+ stmmac_clear_desc(priv, p);
+ continue;
+ }
+ }
+
+ stmmac_set_desc_addr(priv, p,
+ xsk_buff_xdp_get_dma(buf->xdp));
+ stmmac_set_desc_sec_addr(priv, p, 0, false);
+ }
+ } else {
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ /* buf->page can be NULL when stmmac_rx_refill() hit a
+ * GFP_ATOMIC failure before suspend and left the slot
+ * without a buffer. The resume path runs in process
+ * context, so re-allocate with GFP_KERNEL. Allocation
+ * failure aborts the resume.
+ */
+ if (!buf->page) {
+ int err;
+
+ err = stmmac_init_rx_buffers(priv, dma_conf, p,
+ i, GFP_KERNEL,
+ queue);
+ if (err)
+ return err;
+ /* stmmac_init_rx_buffers() already programmed
+ * the descriptor; skip the reprogramming below.
+ */
+ continue;
+ }
+
+ stmmac_set_desc_addr(priv, p, buf->addr);
+ stmmac_set_desc_sec_addr(priv, p, buf->sec_addr,
+ priv->sph_active &&
+ buf->sec_page);
+
+ if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
+ stmmac_init_desc3(priv, p);
+ }
+ }
+
+ /* Chain mode: re-link descriptor 'next' pointers. This is
+ * allocation-free; it just rewrites the per-descriptor next
+ * field which may have been clobbered by HW writeback.
+ */
+ if (priv->descriptor_mode == STMMAC_CHAIN_MODE) {
+ void *des = priv->extend_desc ? (void *)rx_q->dma_erx
+ : (void *)rx_q->dma_rx;
+
+ stmmac_mode_init(priv, des, rx_q->dma_rx_phy,
+ dma_conf->dma_rx_size, priv->extend_desc);
+ }
+
+ /* Re-arm OWN=1 on every valid slot.
+ *
+ * Two address-programming helpers write des3 unconditionally and
+ * therefore clear the OWN bit that stmmac_clear_descriptors() set:
+ *
+ * - stmmac_desc_ops.set_sec_addr (called by stmmac_set_desc_sec_addr()):
+ * writes des3 with upper_32_bits(addr).
+ *
+ * - stmmac_mode_ops.init() (called by stmmac_mode_init() above): writes
+ * des3 with the next-descriptor physical address.
+ *
+ * A single pass over valid slots restores OWN=1 after all descriptor
+ * fields have been written. NULL slots are left with OWN=0 for XSK mode
+ * so the Rx DMA engine stalls safely.
+ */
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ if (rx_q->xsk_pool ? !!buf->xdp : !!buf->page)
+ stmmac_set_rx_owner(priv, p, false);
+ }
+
+ return 0;
+}
+
/**
* stmmac_free_rx_buffer - free RX dma buffers
* @priv: private structure
@@ -8272,6 +8411,7 @@ int stmmac_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
+ u32 queue;
int ret;
if (priv->plat->resume) {
@@ -8321,6 +8461,25 @@ int stmmac_resume(struct device *dev)
stmmac_free_tx_skbufs(priv);
stmmac_clear_descriptors(priv, &priv->dma_conf);
+ /* Re-program the RX descriptor buffer-address fields. Slots that
+ * had no page at suspend time (GFP_ATOMIC failure) are re-allocated
+ * here with GFP_KERNEL; XSK slots without an xdp buffer are refilled
+ * from the pool if possible. Any unrecoverable allocation failure
+ * is reported so the resume can be aborted cleanly.
+ */
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ ret = stmmac_reinit_rx_descriptors(priv, &priv->dma_conf,
+ queue);
+ if (ret) {
+ netdev_err(priv->dev,
+ "%s: rx desc reinit failed on queue %u\n",
+ __func__, queue);
+ mutex_unlock(&priv->lock);
+ rtnl_unlock();
+ return ret;
+ }
+ }
+
ret = stmmac_hw_setup(ndev);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v14 09/44] arm64: RMI: Provide functions to delegate/undelegate ranges of memory
From: Steven Price @ 2026-06-04 14:43 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve, WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <867bowx3qx.wl-maz@kernel.org>
On 21/05/2026 14:59, Marc Zyngier wrote:
> On Wed, 13 May 2026 14:17:17 +0100,
> Steven Price <steven.price@arm.com> wrote:
>>
>> The RMM requires memory is 'delegated' to it so that it can be used
>> either for a realm guest or for various tracking purposes within the RMM
>> (e.g. for metadata or page tables). Memory that has been delegated
>> cannot be accessed by the host (it will result in a Granule Protection
>> Fault).
>>
>> Undelegation may fail if the memory is still in use by the RMM. This
>> shouldn't happen (Linux should ensure it has destroyed the RMM objects
>> before attempting to undelegate). In the event that it does happen this
>> points to a programming bug and the only reasonable approach is for the
>> physical pages to be leaked - it is up to the caller of
>> rmi_undelegate_range() to handle this.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> v14:
>> * Split into separate patch and moved out of KVM
>> ---
>> arch/arm64/include/asm/rmi_cmds.h | 13 +++++++++++
>> arch/arm64/kernel/rmi.c | 36 +++++++++++++++++++++++++++++++
>> 2 files changed, 49 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
>> index 9078a2920a7c..eb213c8e6f26 100644
>> --- a/arch/arm64/include/asm/rmi_cmds.h
>> +++ b/arch/arm64/include/asm/rmi_cmds.h
>> @@ -33,6 +33,19 @@ struct rmi_sro_state {
>> } while (RMI_RETURN_STATUS(res.a0) == RMI_BUSY || \
>> RMI_RETURN_STATUS(res.a0) == RMI_BLOCKED)
>>
>> +int rmi_delegate_range(phys_addr_t phys, unsigned long size);
>> +int rmi_undelegate_range(phys_addr_t phys, unsigned long size);
>> +
>> +static inline int rmi_delegate_page(phys_addr_t phys)
>> +{
>> + return rmi_delegate_range(phys, PAGE_SIZE);
>> +}
>> +
>> +static inline int rmi_undelegate_page(phys_addr_t phys)
>> +{
>> + return rmi_undelegate_range(phys, PAGE_SIZE);
>> +}
>> +
>> bool rmi_is_available(void);
>>
>> unsigned long rmi_sro_execute(struct rmi_sro_state *sro, gfp_t gfp);
>> diff --git a/arch/arm64/kernel/rmi.c b/arch/arm64/kernel/rmi.c
>> index 52a415e99500..08cef54acadb 100644
>> --- a/arch/arm64/kernel/rmi.c
>> +++ b/arch/arm64/kernel/rmi.c
>> @@ -12,6 +12,42 @@ static bool arm64_rmi_is_available;
>> unsigned long rmm_feat_reg0;
>> unsigned long rmm_feat_reg1;
>>
>> +int rmi_delegate_range(phys_addr_t phys, unsigned long size)
>> +{
>> + unsigned long ret = 0;
>> + unsigned long top = phys + size;
>> + unsigned long out_top;
>> +
>> + while (phys < top) {
>> + ret = rmi_granule_range_delegate(phys, top, &out_top);
>> + if (ret == RMI_SUCCESS)
>> + phys = out_top;
>> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
>> + return ret;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +int rmi_undelegate_range(phys_addr_t phys, unsigned long size)
>> +{
>> + unsigned long ret = 0;
>> + unsigned long top = phys + size;
>> + unsigned long out_top;
>> +
>> + WARN_ON(size == 0);
>
> I find it odd to warn on size = 0. After all, free(NULL) is not an
> error. But even then, you continue feeding this to the RMM.
Ok, I'll admit that this is left over debugging - although this is a
condition that shouldn't happen.
Note that the while() condition prevents this from actually getting to
the RMM.
I'll drop the WARN_ON() since it's confusing.
Thanks,
Steve
> You also don't seem to be bothered with that on the delegation side...
>
>> +
>> + while (phys < top) {
>> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
>> + if (ret == RMI_SUCCESS)
>> + phys = out_top;
>
> and size==0 doesn't violate any of the failure conditions listed in
> B4.5.18.2 (beta2). Will you end-up looping around forever?
>
> Same questions for the delegation, obviously.
>
> M.
>
^ permalink raw reply
* Re: [PATCH v6 6/8] perf cs-etm: Filter synthesized branch samples
From: James Clark @ 2026-06-04 14:42 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users, Leo Yan,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-6-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> From: Leo Yan <leo.yan@linaro.org>
>
> CS ETM currently emits branch samples for every decoded branch when
> branch synthesis is enabled. This delivers redundant info when users
> request only call or return branches.
>
> Add a branch filter derived from the itrace "calls" and "returns" options.
> When no filter is set, keep the existing behavior and emit all branch
> samples. When call or return filtering is requested, only synthesize branch
> samples whose flags match the selected branch types, including trace
> begin and end markers.
>
> Before:
>
> perf script -F +callindent
>
> callchain_test 9187 [002] 599611.826599: 1 branches: main ffff83312258 __libc_start_call_main+0x78 (/usr/lib/aarch64-linux-gnu/libc.so.6)
> callchain_test 9187 [002] 599611.826599: 1 branches: foo aaaae3ed07c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: print aaaae3ed07ac foo+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: do_svc aaaae3ed0794 print+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: aaaae3ed077c do_svc+0x14 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: vectors aaaae3ed0780 do_svc+0x18 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: ffff800080010c00 vectors+0x400 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080010c24 vectors+0x424 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114dc el0t_64_sync+0xd4 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114f8 el0t_64_sync+0xf0 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011528 el0t_64_sync+0x120 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011538 el0t_64_sync+0x130 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800080011568 el0t_64_sync+0x160 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff80008001159c el0t_64_sync+0x194 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800081829110 el0t_64_sync_handler+0x18 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff800081829140 el0t_64_sync_handler+0x48 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0_svc ffff800081829194 el0t_64_sync_handler+0x9c ([kernel.kallsyms])
>
> After:
>
> callchain_test 9187 [002] 599611.826599: 1 branches: main ffff83312258 __libc_start_call_main+0x78 (/usr/lib/aarch64-linux-gnu/libc.so.6)
> callchain_test 9187 [002] 599611.826599: 1 branches: foo aaaae3ed07c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: print aaaae3ed07ac foo+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: do_svc aaaae3ed0794 print+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: vectors aaaae3ed0780 do_svc+0x18 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff80008001159c el0t_64_sync+0x194 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0_svc ffff800081829194 el0t_64_sync_handler+0x9c ([kernel.kallsyms])
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index b31d0dd46a45dc365edd7c2f9e9b2eb077ca23db..8d98e772ecb307381b5ed1b4bbc4056e8779b261 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -71,6 +71,7 @@ struct cs_etm_auxtrace {
> int num_cpu;
> u64 latest_kernel_timestamp;
> u32 auxtrace_type;
> + u32 branches_filter;
> u64 branches_sample_type;
> u64 branches_id;
> u64 instructions_sample_type;
> @@ -1596,6 +1597,10 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
> } dummy_bs;
> u64 ip;
>
> + if (etm->branches_filter &&
> + !(etm->branches_filter & tidq->prev_packet->flags))
> + return 0;
> +
> ip = cs_etm__last_executed_instr(tidq->prev_packet);
>
> event->sample.header.type = PERF_RECORD_SAMPLE;
> @@ -3442,6 +3447,16 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
> etm->synth_opts.thread_stack = session->itrace_synth_opts->thread_stack;
> }
>
> + if (etm->synth_opts.calls)
> + etm->branches_filter |= PERF_IP_FLAG_CALL |
> + PERF_IP_FLAG_TRACE_BEGIN |
> + PERF_IP_FLAG_TRACE_END;
> +
> + if (etm->synth_opts.returns)
> + etm->branches_filter |= PERF_IP_FLAG_RETURN |
> + PERF_IP_FLAG_TRACE_BEGIN |
> + PERF_IP_FLAG_TRACE_END;
> +
This changes the default "perf script" output quite significantly and
will possibly break people's workflows. synth_opts.calls is true by
default but synth_opts.returns is false so we lose all the returns that
we used to have. Not sure if the new behavior is more consistent with
other tools so we can justify changing it? Personally I think including
returns by default made more sense, and it's a more literal
representation of the flow.
itrace.txt says the default is "all events i.e. the same as
--itrace=iybxwpe", but I thought the default was branches? At least for
Coresight it is, so I'm a bit confused.
> etm->session = session;
>
> etm->num_cpu = num_cpu;
>
^ permalink raw reply
* Re: [PATCH v3 2/2] kconfig: Remove the architecture specific config for Propeller
From: Will Deacon @ 2026-06-04 14:39 UTC (permalink / raw)
To: Rong Xu
Cc: Nathan Chancellor, Yabin Cui, Han Shen, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, H. Peter Anvin,
Kees Cook, Nicolas Schier, Linus Walleij, Arnd Bergmann,
Mathieu Desnoyers, Miguel Ojeda, Peter Zijlstra, Jinjie Ruan,
Lukas Bulwahn, linux-kernel, Juergen Gross, Helge Deller,
Ryan Roberts, Marc Zyngier, Ard Biesheuvel, Vincent Donnefort,
Alice Ryhl, x86, linux-arm-kernel
In-Reply-To: <CAF1bQ=S-p+Mi80a8C8pZzJ3E3bW_5tr6WBUKVV1LaXC9iBor=g@mail.gmail.com>
On Wed, Jun 03, 2026 at 03:15:02PM -0700, Rong Xu wrote:
> On Tue, Jun 2, 2026 at 6:54 PM Nathan Chancellor <nathan@kernel.org> wrote:
> >
> > On Tue, Jun 02, 2026 at 10:52:48AM -0700, Rong Xu wrote:
> > > On Tue, Jun 2, 2026 at 2:43 AM Will Deacon <will@kernel.org> wrote:
> > > > I still don't think it has anything to do with the arch. If the compiler
> > > > supports the option, then we can use it. The arch code in the kernel
> > > > doesn't need to do anything, right? So can you just check if the
> > > > compiler accepts the option using a 'depends on $(cc-option, ...)' line?
> > >
> > > Yes, arch code in the kernel does not need to do anything—it is just a marker.
> > >
> > > I understand your concern. I can use (cc-options,...) in PROPELLER_CLANG config.
> > > But I will not use -fbasic-block-address-map for backward compatiliby reason.
> > > I would use "-fbasic-block-sections=list=/dev/null".
> > >
> > > I'll send the updated patch shortly.
> >
> > Technically, an architecture needs to add the section generated by this
> > compiler option to their linker script to avoid an orphan section
> > warning (or error from CONFIG_WERROR) if enabled, as has been done in
> > this series.
> >
> > I worry that moving to a dynamic check will cause build breakage if an
> > LLVM target gains support for Propeller without having their kernel
> > image linker script adjusted. Maybe that will not happen very often and
> > even if it does, I do not mind taking on the maintenance burden of
> > fixing it but there is a cost of moving to a dynamic check like this.
> >
>
> This is true.
>
> That said, these orphan sections usually won't impact correctness. The
> linker will group these together even withgout the link script change,
> even though it generates a lot of warnings.
>
> With moving to a dynamic check, correct usage of this feature remains
> the user's responsibility -- I think this is Will's point from the
> very beginning.
> As noted in your previous response, the dynamic check is primarily
> intended to prevent "allmodconfig" build failures.
>
> I am comfortable with either the arch_kconfig or dynamic_check
> approach. Once a preferred solution is decided, please let me know so
> I can submit v4 for review.
I continue to object to the pointless ARCH_ selection and much prefer
having the core Kconfig check the cc-option.
Will
^ permalink raw reply
* [PATCH] phy: freescale: imx8qm-lvds-phy: Fix missing pm_runtime_disable() on probe error path
From: Felix Gu @ 2026-06-04 14:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Liu Ying
Cc: linux-phy, imx, linux-arm-kernel, linux-kernel, Felix Gu
If mixel_lvds_phy_reset() fails in probe after pm_runtime_enable(),
the function returns directly without calling pm_runtime_disable(),
leaving runtime PM permanently enabled for the device.
Fix this by using devm_pm_runtime_enable() so that cleanup is
automatic on any probe failure or driver unbind. This also allows
removing the manual err label and the .remove callback.
Fixes: 06ff622d61d2 ("phy: freescale: Add i.MX8qm Mixel LVDS PHY support")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 24 +++++++-----------------
1 file changed, 7 insertions(+), 17 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
index ece357443521..c662f91e598c 100644
--- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
+++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
@@ -345,7 +345,9 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev)
dev_set_drvdata(dev, priv);
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
ret = mixel_lvds_phy_reset(dev);
if (ret) {
@@ -355,17 +357,15 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev)
for (i = 0; i < PHY_NUM; i++) {
lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL);
- if (!lvds_phy) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!lvds_phy)
+ return -ENOMEM;
phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops);
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
dev_err(dev, "failed to create PHY for channel%d: %d\n",
i, ret);
- goto err;
+ return ret;
}
lvds_phy->phy = phy;
@@ -379,19 +379,10 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev)
if (IS_ERR(phy_provider)) {
ret = PTR_ERR(phy_provider);
dev_err(dev, "failed to register PHY provider: %d\n", ret);
- goto err;
+ return ret;
}
return 0;
-err:
- pm_runtime_disable(dev);
-
- return ret;
-}
-
-static void mixel_lvds_phy_remove(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
}
static int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev)
@@ -432,7 +423,6 @@ MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match);
static struct platform_driver mixel_lvds_phy_driver = {
.probe = mixel_lvds_phy_probe,
- .remove = mixel_lvds_phy_remove,
.driver = {
.pm = &mixel_lvds_phy_pm_ops,
.name = "mixel-lvds-phy",
---
base-commit: a225caacc36546a09586e3ece36c0313146e7da9
change-id: 20260604-lvds-d67cb619df17
Best regards,
--
Felix Gu <ustc.gu@gmail.com>
^ permalink raw reply related
* Re: [PATCH v5 05/20] dma-pool: track decrypted atomic pools and select them via attrs
From: Jason Gunthorpe @ 2026-06-04 14:30 UTC (permalink / raw)
To: Michael Kelley
Cc: Aneesh Kumar K.V, iommu@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
Robin Murphy, Marek Szyprowski, Will Deacon, Marc Zyngier,
Steven Price, Suzuki K Poulose, Catalin Marinas, Jiri Pirko,
Mostafa Saleh, Petr Tesarik, Alexey Kardashevskiy, Dan Williams,
Xu Yilun, linuxppc-dev@lists.ozlabs.org,
linux-s390@vger.kernel.org, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86@kernel.org, Jiri Pirko
In-Reply-To: <SN6PR02MB4157F94C902B78E55E99372DD4102@SN6PR02MB4157.namprd02.prod.outlook.com>
On Thu, Jun 04, 2026 at 02:05:35PM +0000, Michael Kelley wrote:
> From: Jason Gunthorpe <jgg@ziepe.ca> Sent: Tuesday, June 2, 2026 5:55 PM
> >
> > On Tue, Jun 02, 2026 at 02:24:40PM +0000, Michael Kelley wrote:
> >
> > > Except that in a normal VM, the "unencrypted" pool attribute does *not*
> > > describe the state of the memory itself. In a normal VM, the memory is
> > > unencrypted, but the "unencrypted" pool attribute is false. That
> > > contradiction is the essence of my concern.
> >
> > I would argue no..
> >
> > When CC is enabled the default state of memory in a Linux environment
> > is "encrypted". You have to take a special action to "decrypt" it.
> >
> > Thus the default state of memory in a non-CC environment is also
> > paradoxically "encrypted" too.
>
> The need to have such an unnatural premise is usually an indication
> of a conceptual problem with the overall model, or perhaps just a
> terminology problem.
Oh yes I do think the AMD derived terminogy is aweful :(
> Here's a proposal. The new DMA attribute is DMA_ATTR_CC_SHARED.
> Name the pool attribute "cc_shared" instead of "unencrypted".
Yeah maybe. I sometimes imagine replacing the encrypted/decrypted
names with cc_shared too just to make it sane.
> "cc_shared" set to false in a normal VM doesn't lead to the non-sensical
> situation of claiming that a normal VM is encrypted.
It seems like a good idea to me
Jason
^ permalink raw reply
* Re: [PATCH v3 23/32] vfio: use iova_to_phys_length for efficient unmap
From: Jason Gunthorpe @ 2026-06-04 14:27 UTC (permalink / raw)
To: Guanghui Feng
Cc: adrian.larumbe, airlied, alex, alikernel-developer, baolu.lu,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <20260603151804.1963871-24-guanghuifeng@linux.alibaba.com>
On Wed, Jun 03, 2026 at 11:17:55PM +0800, Guanghui Feng wrote:
> Use iommu_iova_to_phys_length() to get PTE page size, allowing
> traversal by actual mapping granularity instead of PAGE_SIZE steps.
>
> Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
> Acked-by: Shiqiang Zhang <shiyu.zsq@linux.alibaba.com>
> Acked-by: Simon Guo <wei.guo.simon@linux.alibaba.com>
> ---
> drivers/vfio/vfio_iommu_type1.c | 27 ++++++++++++++++++++++-----
> 1 file changed, 22 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
> index c8151ba54de3..115d88d7003e 100644
> --- a/drivers/vfio/vfio_iommu_type1.c
> +++ b/drivers/vfio/vfio_iommu_type1.c
> @@ -1177,25 +1177,42 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
>
> iommu_iotlb_gather_init(&iotlb_gather);
> while (pos < dma->size) {
> - size_t unmapped, len;
> + size_t unmapped, len, pgsize;
> phys_addr_t phys, next;
> dma_addr_t iova = dma->iova + pos;
>
> - phys = iommu_iova_to_phys(domain->domain, iova);
> - if (WARN_ON(!phys)) {
> + /* Single page table walk returns both phys and PTE size */
> + phys = iommu_iova_to_phys_length(domain->domain, iova,
> + &pgsize);
> + if (WARN_ON(phys == PHYS_ADDR_MAX)) {
> pos += PAGE_SIZE;
> continue;
> }
> + if (WARN_ON(!pgsize || pgsize < PAGE_SIZE))
> + pgsize = PAGE_SIZE;
>
> /*
> * To optimize for fewer iommu_unmap() calls, each of which
> * may require hardware cache flushing, try to find the
> * largest contiguous physical memory chunk to unmap.
> + *
> + * mapped_length already accounts for contiguous entries
> + * from iova, then try to join following physically
> + * contiguous PTEs.
> */
> - for (len = PAGE_SIZE; pos + len < dma->size; len += PAGE_SIZE) {
> - next = iommu_iova_to_phys(domain->domain, iova + len);
> + len = min_t(size_t, pgsize, dma->size - pos);
> + for (; pos + len < dma->size; ) {
> + size_t next_pgsize;
> +
> + next = iommu_iova_to_phys_length(domain->domain,
> + iova + len,
> + &next_pgsize);
vfio should not be calling it twice, the core code needs to give the
best length as efficiently as it can. not open coding this in callers.
I think I've said this three times now
Jason
^ 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