* Re: [PATCH] drm/mediatek: Convert legacy DRM logging to drm_* helpers in mtk_dsi.c
From: CK Hu (胡俊光) @ 2026-05-18 8:29 UTC (permalink / raw)
To: p.zabel@pengutronix.de, AngeloGioacchino Del Regno,
abhiraj21put@gmail.com, chunkuang.hu@kernel.org,
airlied@gmail.com, matthias.bgg@gmail.com, simona@ffwll.ch
Cc: dri-devel@lists.freedesktop.org,
linux-mediatek@lists.infradead.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20260420052008.5417-1-abhiraj21put@gmail.com>
On Mon, 2026-04-20 at 10:50 +0530, Abhishek Rajput wrote:
> External email : Please do not click links or open attachments until you have verified the sender or the content.
>
>
> Replace DRM_INFO(), DRM_WARN() and DRM_ERROR() calls in
> drivers/gpu/drm/mediatek/mtk_dsi.c with the corresponding
> drm_info(), drm_warn() and drm_err() helpers.
>
> The drm_*() logging helpers take a struct drm_device * argument,
> allowing the DRM core to prefix log messages with the correct device
> name and instance. This is required to correctly distinguish log
> messages on systems with multiple GPUs.
>
> This change aligns the radeon driver with the DRM TODO item:
> "Convert logging to drm_* functions with drm_device parameter".
Reviewed-by: CK Hu <ck.hu@mediatek.com>
>
> Signed-off-by: Abhishek Rajput <abhiraj21put@gmail.com>
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
> index 0e2bcd5f67b7..a67ad575f5f0 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dsi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
> @@ -510,6 +510,7 @@ static void mtk_dsi_config_vdo_timing_per_line_lp(struct mtk_dsi *dsi)
> u32 delta;
> struct mtk_phy_timing *timing = &dsi->phy_timing;
> struct videomode *vm = &dsi->vm;
> + struct drm_device *drm = dsi->bridge.dev;
>
> if (dsi->format == MIPI_DSI_FMT_RGB565)
> dsi_tmp_buf_bpp = 2;
> @@ -543,7 +544,7 @@ static void mtk_dsi_config_vdo_timing_per_line_lp(struct mtk_dsi *dsi)
> horizontal_backporch_byte /
> horizontal_front_back_byte;
> } else {
> - DRM_WARN("HFP + HBP less than d-phy, FPS will under 60Hz\n");
> + drm_warn(drm, "HFP + HBP less than d-phy, FPS will under 60Hz\n");
> }
>
> if ((dsi->mode_flags & MIPI_DSI_HS_PKT_END_ALIGNED) &&
> @@ -623,12 +624,13 @@ static s32 mtk_dsi_wait_for_irq_done(struct mtk_dsi *dsi, u32 irq_flag,
> {
> s32 ret = 0;
> unsigned long jiffies = msecs_to_jiffies(timeout);
> + struct drm_device *drm = dsi->bridge.dev;
>
> ret = wait_event_interruptible_timeout(dsi->irq_wait_queue,
> dsi->irq_data & irq_flag,
> jiffies);
> if (ret == 0) {
> - DRM_WARN("Wait DSI IRQ(0x%08x) Timeout\n", irq_flag);
> + drm_warn(drm, "Wait DSI IRQ(0x%08x) Timeout\n", irq_flag);
>
> mtk_dsi_enable(dsi);
> mtk_dsi_reset_engine(dsi);
> @@ -663,9 +665,10 @@ static s32 mtk_dsi_switch_to_cmd_mode(struct mtk_dsi *dsi, u8 irq_flag, u32 t)
> {
> mtk_dsi_irq_data_clear(dsi, irq_flag);
> mtk_dsi_set_cmd_mode(dsi);
> + struct drm_device *drm = dsi->bridge.dev;
>
> if (!mtk_dsi_wait_for_irq_done(dsi, irq_flag, t)) {
> - DRM_ERROR("failed to switch cmd mode\n");
> + drm_err(drm, "failed to switch cmd mode\n");
> return -ETIME;
> } else {
> return 0;
> @@ -849,11 +852,12 @@ static void mtk_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
> struct drm_atomic_state *state)
> {
> struct mtk_dsi *dsi = bridge_to_dsi(bridge);
> + struct drm_device *drm = bridge->dev;
> int ret;
>
> ret = mtk_dsi_poweron(dsi);
> if (ret < 0)
> - DRM_ERROR("failed to power on dsi\n");
> + drm_err(drm, "failed to power on dsi\n");
> }
>
> static void mtk_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge,
> @@ -916,7 +920,7 @@ static int mtk_dsi_encoder_init(struct drm_device *drm, struct mtk_dsi *dsi)
> ret = drm_simple_encoder_init(drm, &dsi->encoder,
> DRM_MODE_ENCODER_DSI);
> if (ret) {
> - DRM_ERROR("Failed to encoder init to drm\n");
> + drm_err(drm, "Failed to encoder init to drm\n");
> return ret;
> }
>
> @@ -932,7 +936,7 @@ static int mtk_dsi_encoder_init(struct drm_device *drm, struct mtk_dsi *dsi)
>
> dsi->connector = drm_bridge_connector_init(drm, &dsi->encoder);
> if (IS_ERR(dsi->connector)) {
> - DRM_ERROR("Unable to create bridge connector\n");
> + drm_err(drm, "Unable to create bridge connector\n");
> ret = PTR_ERR(dsi->connector);
> goto err_cleanup_encoder;
> }
> @@ -985,6 +989,7 @@ static int mtk_dsi_host_attach(struct mipi_dsi_host *host,
> {
> struct mtk_dsi *dsi = host_to_dsi(host);
> struct device *dev = host->dev;
> + struct drm_device *drm = dsi->bridge.dev;
> int ret;
>
> dsi->lanes = device->lanes;
> @@ -1012,7 +1017,7 @@ static int mtk_dsi_host_attach(struct mipi_dsi_host *host,
>
> ret = component_add(host->dev, &mtk_dsi_component_ops);
> if (ret) {
> - DRM_ERROR("failed to add dsi_host component: %d\n", ret);
> + drm_err(drm, "failed to add dsi_host component: %d\n", ret);
> drm_bridge_remove(&dsi->bridge);
> return ret;
> }
> @@ -1034,11 +1039,12 @@ static void mtk_dsi_wait_for_idle(struct mtk_dsi *dsi)
> {
> int ret;
> u32 val;
> + struct drm_device *drm = dsi->bridge.dev;
>
> ret = readl_poll_timeout(dsi->regs + DSI_INTSTA, val, !(val & DSI_BUSY),
> 4, 2000000);
> if (ret) {
> - DRM_WARN("polling dsi wait not busy timeout!\n");
> + drm_warn(drm, "polling dsi wait not busy timeout!\n");
>
> mtk_dsi_enable(dsi);
> mtk_dsi_reset_engine(dsi);
> @@ -1123,6 +1129,7 @@ static ssize_t mtk_dsi_host_transfer(struct mipi_dsi_host *host,
> const struct mipi_dsi_msg *msg)
> {
> struct mtk_dsi *dsi = host_to_dsi(host);
> + struct drm_device *drm = dsi->bridge.dev;
> ssize_t recv_cnt;
> u8 read_data[16];
> void *src_addr;
> @@ -1153,7 +1160,7 @@ static ssize_t mtk_dsi_host_transfer(struct mipi_dsi_host *host,
> }
>
> if (!msg->rx_buf) {
> - DRM_ERROR("dsi receive buffer size may be NULL\n");
> + drm_err(drm, "dsi receive buffer size may be NULL\n");
> ret = -EINVAL;
> goto restore_dsi_mode;
> }
> @@ -1177,7 +1184,7 @@ static ssize_t mtk_dsi_host_transfer(struct mipi_dsi_host *host,
> if (recv_cnt)
> memcpy(msg->rx_buf, src_addr, recv_cnt);
>
> - DRM_INFO("dsi get %zd byte data from the panel address(0x%x)\n",
> + drm_info(drm, "dsi get %zd byte data from the panel address(0x%x)\n",
> recv_cnt, *((u8 *)(msg->tx_buf)));
>
> restore_dsi_mode:
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v4 03/13] dma-pool: track decrypted atomic pools and select them via attrs
From: Aneesh Kumar K.V @ 2026-05-18 8:32 UTC (permalink / raw)
To: Alexey Kardashevskiy, iommu, linux-arm-kernel, linux-kernel,
linux-coco
Cc: Robin Murphy, Marek Szyprowski, Will Deacon, Marc Zyngier,
Steven Price, Suzuki K Poulose, Catalin Marinas, Jiri Pirko,
Jason Gunthorpe, Mostafa Saleh, Petr Tesarik, Dan Williams,
Xu Yilun, linuxppc-dev, linux-s390, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Alexander Gordeev, Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <df4b78cf-6192-4fe3-8ad5-df9e6fdda8e6@amd.com>
Alexey Kardashevskiy <aik@amd.com> writes:
> On 16/5/26 22:53, Alexey Kardashevskiy wrote:
>> On 12/5/26 19:03, Aneesh Kumar K.V (Arm) wrote:
...
>>> -static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
>>> +static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
>>> gfp_t gfp)
>>> {
>>> unsigned int order;
>>> @@ -113,12 +119,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
>>> * Memory in the atomic DMA pools must be unencrypted, the pools do not
>>> * shrink so no re-encryption occurs in dma_direct_free().
>>> */
>>> - ret = set_memory_decrypted((unsigned long)page_to_virt(page),
>>> + if (dma_pool->unencrypted) {
>>> + ret = set_memory_decrypted((unsigned long)page_to_virt(page),
>>> 1 << order);
>>> - if (ret)
>>> - goto remove_mapping;
>>> - ret = gen_pool_add_virt(pool, (unsigned long)addr, page_to_phys(page),
>>> - pool_size, NUMA_NO_NODE);
>>> + if (ret)
>>> + goto remove_mapping;
>>> + }
>>> +
>>> + ret = gen_pool_add_virt(dma_pool->pool, (unsigned long)addr,
>>> + page_to_phys(page), pool_size, NUMA_NO_NODE);
>
>
> This clause could go to the else branch.
>
>
Can you clarify this better?
>>> if (ret)
>>> goto encrypt_mapping;
>>> @@ -126,11 +135,15 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
>>> return 0;
>>> encrypt_mapping:
>>> - ret = set_memory_encrypted((unsigned long)page_to_virt(page),
>>> - 1 << order);
>>> - if (WARN_ON_ONCE(ret)) {
>>> - /* Decrypt succeeded but encrypt failed, purposely leak */
>>> - goto out;
>>> + if (dma_pool->unencrypted) {
>>> + int rc;
>>> +
>>> + rc = set_memory_encrypted((unsigned long)page_to_virt(page),
>>> + 1 << order);
>>> + if (WARN_ON_ONCE(rc)) {
>>> + /* Decrypt succeeded but encrypt failed, purposely leak */
>>> + goto out;
>>> + }
>>> }
>>> remove_mapping:
>>> #ifdef CONFIG_DMA_DIRECT_REMAP
>>> @@ -142,46 +155,52 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
>>> return ret;
>>> }
...
>>> bool dma_free_from_pool(struct device *dev, void *start, size_t size)
>>> {
>>> - struct gen_pool *pool = NULL;
>>> + struct dma_gen_pool *dma_pool = NULL;
>>> +
>>> + while ((dma_pool = dma_guess_pool(dma_pool, 0))) {
>>> - while ((pool = dma_guess_pool(pool, 0))) {
>>> - if (!gen_pool_has_addr(pool, (unsigned long)start, size))
>>> + if (!gen_pool_has_addr(dma_pool->pool, (unsigned long)start, size))
>>
>>
>> v3 of this just crashed here with dma_pool!=NULL but dma_pool->pool==NULL. continuing debugging... Thanks,
>
>
> dma_direct_free:
> dma_free_from_pool (loop over pools) -> false
> [here was a crash which I fixed by "if (!dma_pool->pool) continue"]
> swiotlb_find_pool (loop again) -> false
> __dma_direct_free_pages
> swiotlb_free
> swiotlb_find_pool (loop again)
> dma_free_contiguous => done.
>
> so that works but kinda hard to follow and there is some room for
> optimization. I do not normally have swiottlb when I test this and
> there is too many of this swiotlb stuff on the real direct dma mapping
> path imho. Thanks,
>
I will work on this in the next update. I can possibly drop the
swiotlb_find_pool from the swiotlb_free() path.
>>
>>
>>> continue;
>>> - gen_pool_free(pool, (unsigned long)start, size);
>>> +
>>> + gen_pool_free(dma_pool->pool, (unsigned long)start, size);
>>> return true;
>>> }
>>> diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
>>> index 1abd3e6146f4..ab4eccbaa076 100644
>>> --- a/kernel/dma/swiotlb.c
>>> +++ b/kernel/dma/swiotlb.c
>>> @@ -612,6 +612,7 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
>>> u64 phys_limit, gfp_t gfp)
>>> {
>>> struct page *page;
>>> + unsigned long attrs = 0;
>>> /*
>>> * Allocate from the atomic pools if memory is encrypted and
>>> @@ -623,8 +624,12 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
>>> if (!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
>>> return NULL;
>>> + /* swiotlb considered decrypted by default */
>>> + if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
>>> + attrs = DMA_ATTR_CC_SHARED;
>>> +
>>> return dma_alloc_from_pool(dev, bytes, &vaddr, gfp,
>>> - dma_coherent_ok);
>>> + attrs, dma_coherent_ok);
>>> }
>>> gfp &= ~GFP_ZONEMASK;
>>
>
> --
> Alexey
-aneesh
^ permalink raw reply
* Re: [PATCH] KVM: arm64: vgic-its: reject restored DTE with out-of-range num_eventid_bits
From: Marc Zyngier @ 2026-05-18 8:33 UTC (permalink / raw)
To: Michael Bommarito
Cc: Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <20260517174955.273004-1-michael.bommarito@gmail.com>
On Sun, 17 May 2026 18:49:55 +0100,
Michael Bommarito <michael.bommarito@gmail.com> wrote:
>
> Userspace can trigger a host-side denial of service via
> KVM_DEV_ARM_ITS_RESTORE_TABLES. A Device Table Entry whose Size
> field encodes num_eventid_bits > VITS_TYPER_IDBITS reaches
> scan_its_table() with a sign-extended ~18 EiB length, where the
> loop holds the per-ITS mutex and never calls cond_resched(),
> pinning a host CPU for a time linear in registered guest memslot
> size. The accessor is any process that can open /dev/kvm and
> create a VM. The same out-of-range value also disables a
> subsequent live bounds check on EventID, as described below.
>
> The MAPD command handler already rejects this case in the live
> path:
>
> if (valid && num_eventid_bits > VITS_TYPER_IDBITS)
> return E_ITS_MAPD_ITTSIZE_OOR;
>
> vgic_its_restore_dte() reconstructs num_eventid_bits from the DTE
> Size field but does not apply the same cap, so userspace can
> install device state that the live MAPD path is documented to
> reject. The restored value is stored in dev->num_eventid_bits and
> is then used by vgic_its_restore_itt():
>
> size_t max_size = BIT_ULL(dev->num_eventid_bits) * ite_esz;
> ret = scan_its_table(its, base, max_size, ite_esz, ...);
>
> scan_its_table() takes the size as int and assigns it to
> unsigned long in the callee:
>
> static int scan_its_table(struct vgic_its *its, gpa_t base,
> int size, u32 esz, ...)
> {
> unsigned long len = size;
>
> For num_eventid_bits = 28 the size_t value 0x80000000 truncates to
> INT_MIN as int and sign-extends to ~18 EiB as unsigned long. The
> scan loop then walks the registered guest memslot one ite_esz at
> a time with the per-ITS mutex held and no cond_resched(). In a
> QEMU TCG arm64 guest at EL2 on v7.1-rc1, with an empty ITT, the
> ioctl returned -EFAULT after about 14 seconds with a 256 MiB
> memslot and about 56 seconds with a 1 GiB memslot (linear in
> memslot size). The per-iteration cost on native arm64 KVM
> hardware will differ; the loop shape, and so the linear scaling,
> will not.
>
> The same out-of-range num_eventid_bits also disables the live
> vgic_its_check_event_id() bounds check, because event_id is u32
> and BIT_ULL(32) is unreachable in that comparison, leaving
> subsequent MAPI/MAPTI handling without an effective EventID cap.
>
> Mirror the MAPD cap in vgic_its_restore_dte() before allocating
> the device, so out-of-range restored DTEs are rejected with
> -EINVAL up front rather than triggering the int-truncated scan or
> installing a device whose num_eventid_bits silently disables the
> live bounds check. Sizes within [1, VITS_TYPER_IDBITS] are
> unaffected.
This commit message is way too verbose, and really looks like a copy
paste from an LLM analysis. This needs trimming (the initial paragraph
has most of it...).
>
> Fixes: 57a9a117154c ("KVM: arm64: vgic-its: Device table save/restore")
> Assisted-by: Claude:claude-opus-4-7
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
> ---
> arch/arm64/kvm/vgic/vgic-its.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
> index 2ea9f1c7ebcd0..a5dcf9a6a2854 100644
> --- a/arch/arm64/kvm/vgic/vgic-its.c
> +++ b/arch/arm64/kvm/vgic/vgic-its.c
> @@ -2307,6 +2307,15 @@ static int vgic_its_restore_dte(struct vgic_its *its, u32 id,
> /* dte entry is valid */
> offset = (entry & KVM_ITS_DTE_NEXT_MASK) >> KVM_ITS_DTE_NEXT_SHIFT;
>
> + /*
> + * The MAPD command rejects this case; mirror the cap here so a
> + * restored DTE cannot install an out-of-range num_eventid_bits
> + * that vgic_its_restore_itt() would then convert into a
> + * sign-extended scan_its_table() length.
> + */
Same here. Something like:
/* Mimic the MAPD behaviour and reject invalid EID bits */
would be enough.
> + if (num_eventid_bits > VITS_TYPER_IDBITS)
> + return -EINVAL;
> +
> if (!vgic_its_check_id(its, baser, id, NULL))
> return -EINVAL;
>
Otherwise looks good.
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v4 00/13] dma-mapping: Use DMA_ATTR_CC_SHARED through direct, pool and swiotlb paths
From: Jiri Pirko @ 2026-05-18 8:34 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: iommu, linux-arm-kernel, linux-kernel, linux-coco, Robin Murphy,
Marek Szyprowski, Will Deacon, Marc Zyngier, Steven Price,
Suzuki K Poulose, Catalin Marinas, Jason Gunthorpe, Mostafa Saleh,
Petr Tesarik, Alexey Kardashevskiy, Dan Williams, Xu Yilun,
linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <yq5amrxxxh0h.fsf@kernel.org>
Mon, May 18, 2026 at 10:23:58AM +0200, aneesh.kumar@kernel.org wrote:
>Jiri Pirko <jiri@resnulli.us> writes:
>
>> Tue, May 12, 2026 at 11:03:55AM +0200, aneesh.kumar@kernel.org wrote:
>>>This series propagates DMA_ATTR_CC_SHARED through the dma-direct,
>>>dma-pool, and swiotlb paths so that encrypted and decrypted DMA buffers
>>>are handled consistently.
>>>
>>>Today, the direct DMA path mostly relies on force_dma_unencrypted() for
>>>shared/decrypted buffer handling. This series consolidates the
>>>force_dma_unencrypted() checks in the top-level functions and ensures
>>>that the remaining DMA interfaces use DMA attributes to make the correct
>>>decisions.
>>
>> FWIW, the patchset in general looks good to me. I tested this with my
>> system_cc_shared dmabuf flow, works flawlessly.
>>
>> Thanks!
>>
>Thanks, Can I add
>
>Tested-by: Jiri Pirko <jiri@resnulli.us>
Tested-by: Jiri Pirko <jiri@nvidia.com>
Thanks.
^ permalink raw reply
* Re: [PATCH v6 13/19] dt-bindings: dma: ti: Add K3 BCDMA V2
From: Sai Sree Kartheek Adivi @ 2026-05-18 8:36 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
Frank.li, r-sharma3, gehariprasath
In-Reply-To: <20260430-orthodox-athletic-agama-be4111@quoll>
Hi Krzysztof,
On 30/04/26 12:59, Krzysztof Kozlowski wrote:
> On Tue, Apr 28, 2026 at 02:21:42PM +0530, Sai Sree Kartheek Adivi wrote:
>> New binding document for
> I don't see improvements.
>
>> Texas Instruments K3 Block Copy DMA (BCDMA) V2.
>>
>> BCDMA V2 is introduced as part of AM62L.
>>
>> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
>> ---
>> .../bindings/dma/ti/ti,am62l-dmss-bcdma.yaml | 121 ++++++++++++++++++
>> 1 file changed, 121 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/dma/ti/ti,am62l-dmss-bcdma.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/dma/ti/ti,am62l-dmss-bcdma.yaml b/Documentation/devicetree/bindings/dma/ti/ti,am62l-dmss-bcdma.yaml
>> new file mode 100644
>> index 0000000000000..28dcfce5633ce
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/dma/ti/ti,am62l-dmss-bcdma.yaml
>> @@ -0,0 +1,121 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +# Copyright (C) 2024-25 Texas Instruments Incorporated
>> +# Author: Sai Sree Kartheek Adivi <s-adivi@ti.com>
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/dma/ti/ti,am62l-dmss-bcdma.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Texas Instruments K3 DMSS BCDMA V2
>> +
>> +maintainers:
>> + - Sai Sree Kartheek Adivi <s-adivi@ti.com>
>> +
>> +description:
>> + The BCDMA V2 is intended to perform similar functions as the TR
>> + mode channels of K3 UDMA-P.
>> + BCDMA V2 includes block copy channels and Split channels.
>> +
>> + Block copy channels mainly used for memory to memory transfers, but with
>> + optional triggers a block copy channel can service peripherals by accessing
>> + directly to memory mapped registers or area.
>> +
>> + Split channels can be used to service PSI-L based peripherals.
>> + The peripherals can be PSI-L native or legacy, non PSI-L native peripherals
>> + with PDMAs. PDMA is tasked to act as a bridge between the PSI-L fabric and the
>> + legacy peripheral.
>> +
>> +allOf:
>> + - $ref: /schemas/dma/dma-controller.yaml#
>> +
>> +properties:
>> + compatible:
>> + const: ti,am62l-dmss-bcdma
>> +
>> + reg:
>> + items:
>> + - description: BCDMA Control & Status Registers region
>> + - description: Block Copy Channel Realtime Registers region
>> + - description: Channel Realtime Registers region
>> + - description: Ring Realtime Registers region
>> +
>> + reg-names:
>> + items:
>> + - const: gcfg
>> + - const: bchanrt
>> + - const: chanrt
>> + - const: ringrt
>> +
>> + "#address-cells":
>> + const: 0
> Why do you need address-cells?
>
>> +
>> + "#dma-cells":
>> + const: 4
>> + description: |
>> + cell 1: Trigger type for the channel
>> + 0 - disable / no trigger
>> + 1 - internal channel event
>> + 2 - external signal
>> + 3 - timer manager event
>> +
>> + cell 2: parameter for the trigger:
>> + if cell 1 is 0 (disable / no trigger):
>> + Unused, ignored
>> + if cell 1 is 1 (internal channel event):
>> + channel number whose TR event should trigger the current channel.
>> + if cell 1 is 2 or 3 (external signal or timer manager event):
>> + index of global interfaces that come into the DMA.
>> +
>> + Please refer to the device documentation for global interface indexes.
>> +
>> + cell 3: Channel number for the peripheral
>> +
>> + Please refer to the device documentation for the channel map.
>> +
>> + cell 4: ASEL value for the channel
>> +
>> + interrupts:
>> + minItems: 1
>> + maxItems: 144
>> + description:
>> + Interrupts for DMA channels.
> And interrupts are flexible because?
I understand the issues now:
1. address-cells: remove this property - it should be a fixed hardware
constant, not a binding property.
2. interrupts: replace minItems/maxItems ranges with a fixed count specific
to ti,am62l-dmss-bcdma hardware.
The binding should encode hardware facts as fixed properties, not
flexible ranges.
I'll address both in v7. Please let me know if I still misunderstood
something.
Best regards
>
> Best regards,
> Krzysztof
>
^ permalink raw reply
* Re: [PATCH] media: bcm2835-unicam: fix dummy buffer double free when register fails
From: Markus Elfring @ 2026-05-18 8:37 UTC (permalink / raw)
To: Dawei Feng, Zilin Guan, linux-media, kernel-list,
linux-rpi-kernel, linux-arm-kernel, bcm-kernel-feedback-list
Cc: stable, LKML, Florian Fainelli, Jianhao Xu, Mauro Carvalho Chehab,
Ray Jui, Scott Branden
In-Reply-To: <20260515152529.1007540-1-dawei.feng@seu.edu.cn>
…
> Set dummy_buf_cpu_addr to NULL after dma_free_coherent() in both the
> local error path and unicam_unregister_nodes() so later cleanup skips
…
so that?
> The bug was first flagged by an experimental analysis tool we are
> developing for kernel memory-management bugs while analyzing
> v6.13-rc1. The tool is still under development and is not yet publicly
> available. Manual inspection confirms that the bug is still
> present in v7.1-rc3.
Under which circumstances will the mentioned software revision gap
be adjusted accordingly?
Regards,
Markus
^ permalink raw reply
* Re: [PATCH v2 5/6] devfreq: Get and put module refcount when switching governor
From: Jie Zhan @ 2026-05-18 8:39 UTC (permalink / raw)
To: Yaxiong Tian, cwchoi00, cw00.choi, myungjoo.ham, kyungmin.park,
linux-pm, linux-arm-kernel
Cc: linuxarm, zhenglifeng1, zhangpengjie2, lihuisong, prime.zeng
In-Reply-To: <6f160e25-9903-4f49-8a95-1192b7acb96d@hisilicon.com>
On 5/15/2026 6:39 PM, Jie Zhan wrote:
>
>
> On 5/14/2026 2:50 PM, Yaxiong Tian wrote:
>>
>> 在 2026/5/13 17:38, Jie Zhan 写道:
>>> A governor module can be dynamically inserted or removed if compiled as a
>>> kernel module. 'devfreq->governor' would become NULL if the governor
>>> module is removed when it's in use.
>>>
>>> To prevent the governor module from being removed (except for force
>>> unload) when it's in use, get and put a refcount of the governor module
>>> when starting and stopping the governor.
>>>
>>> As a result, unloading a governor module in use returns an error, e.g.:
>>> $ cat governor
>>> performance
>>> $ rmmod governor_performance
>>> rmmod: ERROR: Module governor_performance is in use
>>>
>>> Signed-off-by: Jie Zhan <zhanjie9@hisilicon.com>
>>> ---
>>> drivers/devfreq/devfreq.c | 17 ++++++++++++++++-
>>> 1 file changed, 16 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
>>> index e1363ab69173..2ea42325d030 100644
>>> --- a/drivers/devfreq/devfreq.c
>>> +++ b/drivers/devfreq/devfreq.c
>>> @@ -345,24 +345,37 @@ static int devfreq_set_governor(struct devfreq *df,
>>> __func__, df->governor->name, ret);
>>> return ret;
>>> }
>>> + module_put(old_gov->owner);
>>> }
>>> /* Start the new governor */
>>> + if (!try_module_get(new_gov->owner)) {
>>> + df->governor = NULL;
>>> + return -EINVAL;
>>> + }
>>> +
>>
>> Here, new_gov has already been checked in try_then_request_governor() for whether the module can be obtained.
>>
>> I think it might be more reasonable to merge try_then_request_governor() and devfreq_set_governor(), so that when new_gov cannot be obtained, the operation of the old governor is not affected.
>>
> Well yeah, indeed! I'll take a look at how to implement this.
> Thanks for the suggestion.
>
> Jie
Hi Yaxiong,
A follow-up after digging deeper.
1. To be clear, the try_module_get() / module_put() pairs in
try_then_request_governor() and devfreq_set_governor() protect different
ranges. Assuming no FORCE_UNLOAD, the former protects the governor module
from the time it's queried until it's set as the working governor, while
the latter protects when the governor is in use.
However, I realized the former protection is no longer needed since I
removed the governor list lock in v2. devfreq_remove_governor() and the
'request and set' governor sequence are already synchronized by
'devfreq_list_lock'. Thus, patch 6 should be dropped.
2. devfreq_add_device() needs error prints because it's a probe path.
governor_store() doesn't want error prints since it's not a good practice
to print on a sysfs read/write. Merging the two functions will violate
either of them.
Thanks,
Jie
>>> df->governor = new_gov;
>>> ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
>>> if (ret) {
>>> dev_warn(dev, "%s: Governor %s not started(%d)\n",
>>> __func__, df->governor->name, ret);
>>> + module_put(new_gov->owner);
>>> /* Restore previous governor */
>>> df->governor = old_gov;
>>> if (!df->governor)
>>> return ret;
>>> + if (!try_module_get(old_gov->owner)) {
>>> + df->governor = NULL;
>>> + return -EINVAL;
>>> + }
>>> +
>>> ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
>>> if (ret) {
>>> dev_err(dev, "%s: restore Governor %s failed (%d)\n",
>>> __func__, df->governor->name, ret);
>>> + module_put(old_gov->owner);
>>> df->governor = NULL;
>>> return ret;
>>> }
>>> @@ -1040,9 +1053,11 @@ int devfreq_remove_device(struct devfreq *devfreq)
>>> devfreq_cooling_unregister(devfreq->cdev);
>>> - if (devfreq->governor)
>>> + if (devfreq->governor) {
>>> devfreq->governor->event_handler(devfreq,
>>> DEVFREQ_GOV_STOP, NULL);
>>> + module_put(devfreq->governor->owner);
>>> + }
>>> device_unregister(&devfreq->dev);
>>> return 0;
^ permalink raw reply
* RE: [PATCH V14 02/12] PCI: host-generic: Add common helpers for parsing Root Port properties
From: Sherry Sun @ 2026-05-18 8:42 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
Frank Li, s.hauer@pengutronix.de, kernel@pengutronix.de,
festevam@gmail.com, lpieralisi@kernel.org, kwilczynski@kernel.org,
mani@kernel.org, bhelgaas@google.com, Hongxing Zhu,
l.stach@pengutronix.de, imx@lists.linux.dev,
linux-pci@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260513224944.GA341451@bhelgaas>
> Subject: Re: [PATCH V14 02/12] PCI: host-generic: Add common helpers for
> parsing Root Port properties
>
> On Wed, Apr 22, 2026 at 05:35:39PM +0800, Sherry Sun wrote:
> > Introduce generic helper functions to parse Root Port device tree
> > nodes and extract common properties like reset GPIOs. This allows
> > multiple PCI host controller drivers to share the same parsing logic.
> >
> > Define struct pci_host_port to hold common Root Port properties
> > (currently only list of PERST# GPIO descriptors) and add
> > pci_host_common_parse_ports() to parse Root Port nodes from device
> tree.
> >
> > Also add the 'ports' list to struct pci_host_bridge for better
> > maintain parsed Root Port information.
> > ...
>
> > +static int pci_host_common_parse_port(struct device *dev,
> > + struct pci_host_bridge *bridge,
> > + struct device_node *node)
> > +{
> > + struct pci_host_port *port;
> > + int ret;
> > +
> > + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > + if (!port)
> > + return -ENOMEM;
> > +
> > + INIT_LIST_HEAD(&port->perst);
> > +
> > + ret = pci_host_common_parse_perst(dev, port, node);
> > + if (ret)
> > + return ret;
> > +
> > + /*
> > + * 1. PERST# found in RP or its child nodes - list is not empty, continue
> > + * 2. PERST# not found in RP/children, but found in RC node - return -
> ENODEV
> > + * to fallback legacy binding
> > + * 3. PERST# not found anywhere - list is empty, continue (optional
> PERST#)
> > + */
> > + if (list_empty(&port->perst)) {
> > + if (of_property_present(dev->of_node, "reset-gpios") ||
> > + of_property_present(dev->of_node, "reset-gpio"))
> > + return -ENODEV;
>
> This doesn't seem right to me. The parser of per-Root Port properties should
> not be responsible for deciding whether legacy methods are valid, i.e.,
> whether a property is in the Root Complex node. I think it's up to the caller
> to decide whether it needs to look elsewhere.
>
> I don't think this even needs to return a "success/failure" value because there
> may be more properties in the future, and not all will be required. This
> function can't tell which properties a specific driver requires and which are
> optional.
>
> The caller can check whether we found what it needs and fall back to a legacy
> method as needed.
Hi Bjorn,
The code here was suggested by Mani, https://lore.kernel.org/all/lnzprzrdwra7pn7d6m3sbj5pvjy64blwpjl6i3lmlnfbyho63b@czpyhpgz5vum/.
I think your suggestion here is reasonable, the per-Root Port parser shouldn't
check the RC-level binding. That's a policy decision that belongs to the caller.
Hi Mani, if you also agree, I'll rework this so that:
1. pci_host_common_parse_port() only parses properties from the Root Port
(and its children) without checking the RC node.
2. The function won't return failure for "property not found" - it will only return
errors for real failures (e.g., -ENOMEM, GPIO acquisition errors).
3. The legacy fallback logic will be moved to the caller, which can inspect the
parsed result and decide whether to fall back to the legacy binding.
>
> > + }
> > +
> > + INIT_LIST_HEAD(&port->list);
> > + list_add_tail(&port->list, &bridge->ports);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * pci_host_common_parse_ports - Parse Root Port nodes from device
> > +tree
> > + * @dev: Device pointer
> > + * @bridge: PCI host bridge
> > + *
> > + * This function iterates through child nodes of the host bridge and
> > +parses
> > + * Root Port properties (currently only reset GPIOs).
> > + *
> > + * Returns: 0 on success, -ENODEV if no ports found or PERST# found
> > +in RC node
> > + * (legacy binding should be used), Other negative error codes on failure.
> > + */
> > +int pci_host_common_parse_ports(struct device *dev, struct
> > +pci_host_bridge *bridge) {
> > + int ret = -ENODEV;
> > +
> > + for_each_available_child_of_node_scoped(dev->of_node, of_port) {
> > + if (!of_node_is_type(of_port, "pci"))
> > + continue;
> > + ret = pci_host_common_parse_port(dev, bridge, of_port);
> > + if (ret)
> > + goto err_cleanup;
> > + }
>
> I think we should export pci_host_common_parse_port() itself and drop this
> so we deal with a single Root Port, and drivers that support multiple RPs
> should include their own loop similar to this. That way the driver can do
> several things at once in each iteration of that loop, e.g., get resources, power
> up, configure, etc.
>
> I see that would require some rework of the devm_add_action_or_reset()
> cleanup.
Thanks for the suggestion, make sense.
I'll export pci_host_common_parse_port() to deal with a single Root Port
and drop pci_host_common_parse_ports().
>
> > + if (ret)
> > + return ret;
> > +
> > + return devm_add_action_or_reset(dev,
> pci_host_common_delete_ports,
> > + &bridge->ports);
> > +
> > +err_cleanup:
> > + pci_host_common_delete_ports(&bridge->ports);
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_host_common_parse_ports);
> > ...
>
> > + * struct pci_host_perst - PERST# GPIO descriptor
> > + * @list: List node for linking multiple PERST# GPIOs
> > + * @desc: GPIO descriptor for PERST# signal
> > + *
> > + * This structure holds a single PERST# GPIO descriptor.
> > + */
> > +struct pci_host_perst {
> > + struct list_head list;
> > + struct gpio_desc *desc;
> > +};
>
> How do we associate an element of this list with something?
>
> Based on the imx6 changes, I guess we don't; we don't even associate the
> pci_host_port with an RP. We just assert/deassert PERST# for every RP at
> once, and we do it for every GPIO associated with each RP.
>
> There's no way to assert PERST# for a single RP. I guess we don't need that?
You are right. In the current design, we assert/deassert PERST# for all GPIOs across
all Root Ports simultaneously - there is no per-RP independent PERST# control.
And I think we don't need it for now, for most platforms, multiple RPs typically share
a single PERST# signal or require synchronized reset sequencing during initialization.
I am not aware of a practical use case where independent per-RP PERST# control is
required at the host controller driver level during probe/remove.
The per-port structure is a natural result of parsing per-RP DT nodes (each Root Port
child node maps to one pci_host_port, with PERST# GPIOs collected from the RP and
its downstream nodes). Even if such a requirement does arise in the future, since
the controller driver now owns the multi-RP loop, the driver that needs per-RP PERST#
control could associate each parsed port with its own per-RP context and operate on
specific ports independently.
For the i.MX case (single Root Port), there's no need for per-RP PERST# control -- we
just assert/deassert all PERST# GPIOs at once.
>
> > +/**
> > + * struct pci_host_port - Generic Root Port properties
> > + * @list: List node for linking multiple ports
> > + * @perst: List of PERST# GPIO descriptors for this port and its
> > +children
> > + *
> > + * This structure contains common properties that can be parsed from
> > + * Root Port device tree nodes.
> > + */
> > +struct pci_host_port {
>
> "host_port" is not really a standard term. And despite the comments above
> and below, I don't think the list is restricted to Root Ports because we traverse
> the whole hierarchy below the RP.
Ok. How about struct pci_root_port_info to better reflect its purpose, I can also
highlight in the struct comment that it holds common properties parsed from a
Root Port device tree node and all PCIe bridge nodes under this Root Port (currently
only PERST# GPIOs). Or any other suggestions?
Best Regards
Sherry
>
> > + struct list_head list;
> > + struct list_head perst;
> > +};
>
> > + struct list_head ports; /* Root Port list (pci_host_port) */
^ permalink raw reply
* [PATCH v14 0/8] arm64: add ARCH_HAS_COPY_MC support
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong
This series continues Tong Tiangen's work on arm64 ARCH_HAS_COPY_MC
support. We encounter the same problem, and from a forward-looking
perspective, large-memory ARM machines such as Grace and Vera will suffer
more from this class of issues, which motivates us to push this feature
upstream.
Problem
=========
With the increase of memory capacity and density, the probability of memory
error also increases. The increasing size and density of server RAM in data
centers and clouds have shown increased uncorrectable memory errors.
Currently, more and more scenarios that can tolerate memory errors, such as
COW[1,2], KSM copy[3], coredump copy[4], khugepaged[5,6], uaccess copy[7],
etc.
Solution
=========
This patchset introduces a new processing framework on ARM64, which enables
ARM64 to support error recovery in the above scenarios, and more scenarios
can be expanded based on this in the future.
In arm64, memory error handling in do_sea(), which is divided into two cases:
1. If the user state consumed the memory errors, the solution is to kill
the user process and isolate the error page.
2. If the kernel state consumed the memory errors, the solution is to
panic.
For case 2, Undifferentiated panic may not be the optimal choice, as it can
be handled better. In some scenarios, we can avoid panic, such as uaccess,
if the uaccess fails due to memory error, only the user process will be
affected, returning an error to the caller and isolating the user page with
hardware memory errors is a better choice.
[1] commit d302c2398ba2 ("mm, hwpoison: when copy-on-write hits poison, take page offline")
[2] commit 1cb9dc4b475c ("mm: hwpoison: support recovery from HugePage copy-on-write faults")
[3] commit 6b970599e807 ("mm: hwpoison: support recovery from ksm_might_need_to_copy()")
[4] commit 245f09226893 ("mm: hwpoison: coredump: support recovery from dump_user_range()")
[5] commit 98c76c9f1ef7 ("mm/khugepaged: recover from poisoned anonymous memory")
[6] commit 12904d953364 ("mm/khugepaged: recover from poisoned file-backed memory")
[7] commit 278b917f8cb9 ("x86/mce: Add _ASM_EXTABLE_CPY for copy user access")
------------------
Test result:
Tested on Kunpeng 920.
1. copy_page(), copy_mc_page() basic function test pass, and the disassembly
contents remains the same before and after refactor.
2. copy_to/from_user() access kernel NULL pointer raise translation fault
and dump error message then die(), test pass.
3. Test following scenarios: copy_from_user(), get_user(), COW.
Before patched: trigger a hardware memory error then panic.
After patched: trigger a hardware memory error without panic.
Testing step:
step1. start an user-process.
step2. poison(einj) the user-process's page.
step3: user-process access the poison page in kernel mode, then trigger SEA.
step4: the kernel will not panic, only the user process is killed, the poison
page is isolated. (before patched, the kernel will panic in do_sea())
The above tests can also be reproduced using ras-tools, which provides
einj-based injection and validation for uaccess and COW scenarios.
Example usage:
einj_mem_uc futex # get_user
einj_mem_uc copyin # copy_to_user
einj_mem_uc copy-on-write # COW
Link: https://git.kernel.org/pub/scm/linux/kernel/git/aegl/ras-tools.git
------------------
Benefits
=========
According to Huawei's statistics from their storage products, memory errors
triggered in kernel-mode by COW and page cache read (uaccess) scenarios
account for more than 50%. With this patchset deployed, all kernel panics
caused by COW and page cache memory errors are eliminated.
Alibaba Cloud has also observed memory errors occurring in uaccess contexts.
Since V13:
1. Changed MC-safe functions to return an error rather than kill the user
process. When a user program invokes a syscall and the kernel encounters
a memory error during uaccess, killing the process is unexpected; the
syscall should return an error.
2. Added FEAT_MOPS support for the copy_page_mc paths.
3. Refactored copy_page() and memcpy() on top of the shared memcpy_template,
reducing duplicated assembly code.
Since v12:
Thanks to the suggestions of Jonathan, Mark, and Mauro, the following modifications
are made:
1. Rebase to latest kernel version.
2. Patch1, add Jonathan's and Mauro's review-by.
3. Patch2, modified do_apei_claim_sea() according to Mark's and Jonathan's suggestions,
and optimized the commit message according to Mark's suggestions(Added description of
the impact on regular copy_to_user()).
4. Patch3, optimized the commit message according to Mauro's suggestions and add Jonathan's
review-by.
5. Patch4, modified copy_mc_user_highpage() and Optimized the commit message according to
Jonathan's suggestions(no functional changes).
6. Patch5, optimized the commit message according to Mauro's suggestions.
7. Patch4/5, FEAT_MOPS is added to the code logic. Currently, the fixup is not performed
on the MOPS instruction.
8. Remove patch6 in v12 according to Jonathan's suggestions.
Since v11:
1. Rebase to latest kernel version 6.9-rc1.
2. Add patch 5, Since the problem described in "Since V10 Besides 3" has
been solved in a50026bdb867 ('iov_iter: get rid of 'copy_mc' flag').
3. Add the benefit of applying the patch set to our company to the description of patch0.
Since V10:
Accroding Mark's suggestion:
1. Merge V10's patch2 and patch3 to V11's patch2.
2. Patch2(V11): use new fixup_type for ld* in copy_to_user(), fix fatal
issues (NULL kernel pointeraccess) been fixup incorrectly.
3. Patch2(V11): refactoring the logic of do_sea().
4. Patch4(V11): Remove duplicate assembly logic and remove do_mte().
Besides:
1. Patch2(V11): remove st* insn's fixup, st* generally not trigger memory error.
2. Split a part of the logic of patch2(V11) to patch5(V11), for detail,
see patch5(V11)'s commit msg.
3. Remove patch6(v10) “arm64: introduce copy_mc_to_kernel() implementation”.
During modification, some problems that cannot be solved in a short
period are found. The patch will be released after the problems are
solved.
4. Add test result in this patch.
5. Modify patchset title, do not use machine check and remove "-next".
Since V9:
1. Rebase to latest kernel version 6.8-rc2.
2. Add patch 6/6 to support copy_mc_to_kernel().
Since V8:
1. Rebase to latest kernel version and fix topo in some of the patches.
2. According to the suggestion of Catalin, I attempted to modify the
return value of function copy_mc_[user]_highpage() to bytes not copied.
During the modification process, I found that it would be more
reasonable to return -EFAULT when copy error occurs (referring to the
newly added patch 4).
For ARM64, the implementation of copy_mc_[user]_highpage() needs to
consider MTE. Considering the scenario where data copying is successful
but the MTE tag copying fails, it is also not reasonable to return
bytes not copied.
3. Considering the recent addition of machine check safe support for
multiple scenarios, modify commit message for patch 5 (patch 4 for V8).
Since V7:
Currently, there are patches supporting recover from poison
consumption for the cow scenario[1]. Therefore, Supporting cow
scenario under the arm64 architecture only needs to modify the relevant
code under the arch/.
[1]https://lore.kernel.org/lkml/20221031201029.102123-1-tony.luck@intel.com/
Since V6:
Resend patches that are not merged into the mainline in V6.
Since V5:
1. Add patch2/3 to add uaccess assembly helpers.
2. Optimize the implementation logic of arm64_do_kernel_sea() in patch8.
3. Remove kernel access fixup in patch9.
All suggestion are from Mark.
Since V4:
1. According Michael's suggestion, add patch5.
2. According Mark's suggestiog, do some restructuring to arm64
extable, then a new adaptation of machine check safe support is made based
on this.
3. According Mark's suggestion, support machine check safe in do_mte() in
cow scene.
4. In V4, two patches have been merged into -next, so V5 not send these
two patches.
Since V3:
1. According to Robin's suggestion, direct modify user_ldst and
user_ldp in asm-uaccess.h and modify mte.S.
2. Add new macro USER_MC in asm-uaccess.h, used in copy_from_user.S
and copy_to_user.S.
3. According to Robin's suggestion, using micro in copy_page_mc.S to
simplify code.
4. According to KeFeng's suggestion, modify powerpc code in patch1.
5. According to KeFeng's suggestion, modify mm/extable.c and some code
optimization.
Since V2:
1. According to Mark's suggestion, all uaccess can be recovered due to
memory error.
2. Scenario pagecache reading is also supported as part of uaccess
(copy_to_user()) and duplication code problem is also solved.
Thanks for Robin's suggestion.
3. According Mark's suggestion, update commit message of patch 2/5.
4. According Borisllav's suggestion, update commit message of patch 1/5.
Since V1:
1.Consistent with PPC/x86, Using CONFIG_ARCH_HAS_COPY_MC instead of
ARM64_UCE_KERNEL_RECOVERY.
2.Add two new scenes, cow and pagecache reading.
3.Fix two small bug(the first two patch).
V1 in here:
https://lore.kernel.org/lkml/20220323033705.3966643-1-tongtiangen@huawei.com/
Ruidong Tian (3):
ACPI: APEI: GHES: use exception context to gate SIGBUS on poison
consumption
lib/test: memcpy_kunit: add copy_page() and copy_mc_page() tests
lib/tests: memcpy_kunit: add memcpy_mc() and memcpy_mc_large() test
Tong Tiangen (5):
uaccess: add generic fallback version of copy_mc_to_user()
arm64: add support for ARCH_HAS_COPY_MC
mm/hwpoison: return -EFAULT when copy fail in
copy_mc_[user]_highpage()
arm64: support copy_mc_[user]_highpage()
arm64: introduce copy_mc_to_kernel() implementation
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/asm-extable.h | 22 ++-
arch/arm64/include/asm/asm-uaccess.h | 4 +
arch/arm64/include/asm/extable.h | 1 +
arch/arm64/include/asm/mte.h | 9 +
arch/arm64/include/asm/page.h | 10 ++
arch/arm64/include/asm/string.h | 5 +
arch/arm64/include/asm/uaccess.h | 17 ++
arch/arm64/kernel/acpi.c | 2 +-
arch/arm64/lib/Makefile | 2 +
arch/arm64/lib/copy_mc_page.S | 44 +++++
arch/arm64/lib/copy_page.S | 62 +------
arch/arm64/lib/copy_page_template.S | 71 ++++++++
arch/arm64/lib/copy_to_user.S | 10 +-
arch/arm64/lib/memcpy.S | 253 ++-------------------------
arch/arm64/lib/memcpy_mc.S | 56 ++++++
arch/arm64/lib/memcpy_template.S | 249 ++++++++++++++++++++++++++
arch/arm64/lib/mte.S | 29 +++
arch/arm64/mm/copypage.c | 75 ++++++++
arch/arm64/mm/extable.c | 21 +++
arch/arm64/mm/fault.c | 30 +++-
arch/powerpc/include/asm/uaccess.h | 1 +
arch/x86/include/asm/uaccess.h | 1 +
drivers/acpi/apei/ghes.c | 36 ++--
include/acpi/ghes.h | 6 +-
include/linux/highmem.h | 16 +-
include/linux/uaccess.h | 8 +
lib/tests/memcpy_kunit.c | 178 ++++++++++++++++++-
mm/kasan/shadow.c | 12 ++
mm/khugepaged.c | 4 +-
30 files changed, 904 insertions(+), 331 deletions(-)
create mode 100644 arch/arm64/lib/copy_mc_page.S
create mode 100644 arch/arm64/lib/copy_page_template.S
create mode 100644 arch/arm64/lib/memcpy_mc.S
create mode 100644 arch/arm64/lib/memcpy_template.S
--
2.39.3
^ permalink raw reply
* [PATCH v14 3/8] arm64: add support for ARCH_HAS_COPY_MC
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong
In-Reply-To: <20260518084956.2538442-1-tianruidong@linux.alibaba.com>
From: Tong Tiangen <tongtiangen@huawei.com>
For the arm64 kernel, when it processes hardware memory errors for
synchronize notifications(do_sea()), if the errors is consumed within the
kernel, the current processing is panic. However, it is not optimal.
Take copy_from/to_user for example, If ld* triggers a memory error, even in
kernel mode, only the associated process is affected. Killing the user
process and isolating the corrupt page is a better choice.
Add new fixup type EX_TYPE_KACCESS_ERR_ZERO_MEM_ERR to identify insn
that can recover from memory errors triggered by access to kernel memory,
and this fixup type is used in __arch_copy_to_user(), This make the regular
copy_to_user() will handle kernel memory errors.
[Ruidong: handle EX_TYPE_UACCESS_CPY in fixup_exception_me()]
Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
Signed-off-by: Ruidong Tian <tianruidong@linux.alibaba.com>
---
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/asm-extable.h | 22 +++++++++++++++++++-
arch/arm64/include/asm/asm-uaccess.h | 4 ++++
arch/arm64/include/asm/extable.h | 1 +
arch/arm64/lib/copy_to_user.S | 10 +++++-----
arch/arm64/mm/extable.c | 21 +++++++++++++++++++
arch/arm64/mm/fault.c | 30 ++++++++++++++++++++--------
7 files changed, 75 insertions(+), 14 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index fe60738e5943..831b20d45893 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -21,6 +21,7 @@ config ARM64
select ARCH_HAS_CACHE_LINE_SIZE
select ARCH_HAS_CC_PLATFORM
select ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION
+ select ARCH_HAS_COPY_MC if ACPI_APEI_GHES
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DEBUG_VIRTUAL
select ARCH_HAS_DEBUG_VM_PGTABLE
diff --git a/arch/arm64/include/asm/asm-extable.h b/arch/arm64/include/asm/asm-extable.h
index d67e2fdd1aee..4980023f2fbd 100644
--- a/arch/arm64/include/asm/asm-extable.h
+++ b/arch/arm64/include/asm/asm-extable.h
@@ -11,6 +11,8 @@
#define EX_TYPE_KACCESS_ERR_ZERO 3
#define EX_TYPE_UACCESS_CPY 4
#define EX_TYPE_LOAD_UNALIGNED_ZEROPAD 5
+/* kernel access memory error safe */
+#define EX_TYPE_KACCESS_ERR_ZERO_MEM_ERR 6
/* Data fields for EX_TYPE_UACCESS_ERR_ZERO */
#define EX_DATA_REG_ERR_SHIFT 0
@@ -42,7 +44,7 @@
(.L__gpr_num_##gpr << EX_DATA_REG_##reg##_SHIFT)
#define _ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, zero) \
- __ASM_EXTABLE_RAW(insn, fixup, \
+ __ASM_EXTABLE_RAW(insn, fixup, \
EX_TYPE_UACCESS_ERR_ZERO, \
( \
EX_DATA_REG(ERR, err) | \
@@ -55,6 +57,17 @@
#define _ASM_EXTABLE_UACCESS(insn, fixup) \
_ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, wzr, wzr)
+#define _ASM_EXTABLE_KACCESS_ERR_ZERO_MEM_ERR(insn, fixup, err, zero) \
+ __ASM_EXTABLE_RAW(insn, fixup, \
+ EX_TYPE_KACCESS_ERR_ZERO_MEM_ERR, \
+ ( \
+ EX_DATA_REG(ERR, err) | \
+ EX_DATA_REG(ZERO, zero) \
+ ))
+
+#define _ASM_EXTABLE_KACCESS_MEM_ERR(insn, fixup) \
+ _ASM_EXTABLE_KACCESS_ERR_ZERO_MEM_ERR(insn, fixup, wzr, wzr)
+
/*
* Create an exception table entry for uaccess `insn`, which will branch to `fixup`
* when an unhandled fault is taken.
@@ -76,6 +89,13 @@
.macro _asm_extable_uaccess_cpy, insn, fixup, uaccess_is_write
__ASM_EXTABLE_RAW(\insn, \fixup, EX_TYPE_UACCESS_CPY, \uaccess_is_write)
.endm
+/*
+ * Create an exception table entry for kaccess `insn`, which will branch to
+ * `fixup` when an unhandled fault is taken.
+ */
+ .macro _asm_extable_kaccess_mem_err, insn, fixup
+ _ASM_EXTABLE_KACCESS_MEM_ERR(\insn, \fixup)
+ .endm
#else /* __ASSEMBLER__ */
diff --git a/arch/arm64/include/asm/asm-uaccess.h b/arch/arm64/include/asm/asm-uaccess.h
index 12aa6a283249..c8f0af5fde63 100644
--- a/arch/arm64/include/asm/asm-uaccess.h
+++ b/arch/arm64/include/asm/asm-uaccess.h
@@ -57,6 +57,10 @@ alternative_else_nop_endif
.endm
#endif
+#define KERNEL_MEM_ERR(l, x...) \
+9999: x; \
+ _asm_extable_kaccess_mem_err 9999b, l
+
#define USER(l, x...) \
9999: x; \
_asm_extable_uaccess 9999b, l
diff --git a/arch/arm64/include/asm/extable.h b/arch/arm64/include/asm/extable.h
index 9dc39612bdf5..47c851d7df4f 100644
--- a/arch/arm64/include/asm/extable.h
+++ b/arch/arm64/include/asm/extable.h
@@ -48,4 +48,5 @@ bool ex_handler_bpf(const struct exception_table_entry *ex,
#endif /* !CONFIG_BPF_JIT */
bool fixup_exception(struct pt_regs *regs, unsigned long esr);
+bool fixup_exception_me(struct pt_regs *regs);
#endif
diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S
index 819f2e3fc7a9..991d94ecc1a8 100644
--- a/arch/arm64/lib/copy_to_user.S
+++ b/arch/arm64/lib/copy_to_user.S
@@ -20,7 +20,7 @@
* x0 - bytes not copied
*/
.macro ldrb1 reg, ptr, val
- ldrb \reg, [\ptr], \val
+ KERNEL_MEM_ERR(9998f, ldrb \reg, [\ptr], \val)
.endm
.macro strb1 reg, ptr, val
@@ -28,7 +28,7 @@
.endm
.macro ldrh1 reg, ptr, val
- ldrh \reg, [\ptr], \val
+ KERNEL_MEM_ERR(9998f, ldrh \reg, [\ptr], \val)
.endm
.macro strh1 reg, ptr, val
@@ -36,7 +36,7 @@
.endm
.macro ldr1 reg, ptr, val
- ldr \reg, [\ptr], \val
+ KERNEL_MEM_ERR(9998f, ldr \reg, [\ptr], \val)
.endm
.macro str1 reg, ptr, val
@@ -44,7 +44,7 @@
.endm
.macro ldp1 reg1, reg2, ptr, val
- ldp \reg1, \reg2, [\ptr], \val
+ KERNEL_MEM_ERR(9998f, ldp \reg1, \reg2, [\ptr], \val)
.endm
.macro stp1 reg1, reg2, ptr, val
@@ -74,7 +74,7 @@ SYM_FUNC_START(__arch_copy_to_user)
9997: cmp dst, dstin
b.ne 9998f
// Before being absolutely sure we couldn't copy anything, try harder
- ldrb tmp1w, [srcin]
+KERNEL_MEM_ERR(9998f, ldrb tmp1w, [srcin])
USER(9998f, sttrb tmp1w, [dst])
add dst, dst, #1
9998: sub x0, end, dst // bytes not copied
diff --git a/arch/arm64/mm/extable.c b/arch/arm64/mm/extable.c
index 6e0528831cd3..f78ac7e92845 100644
--- a/arch/arm64/mm/extable.c
+++ b/arch/arm64/mm/extable.c
@@ -110,7 +110,28 @@ bool fixup_exception(struct pt_regs *regs, unsigned long esr)
return ex_handler_uaccess_cpy(ex, regs, esr);
case EX_TYPE_LOAD_UNALIGNED_ZEROPAD:
return ex_handler_load_unaligned_zeropad(ex, regs);
+ case EX_TYPE_KACCESS_ERR_ZERO_MEM_ERR:
+ return false;
}
BUG();
}
+
+bool fixup_exception_me(struct pt_regs *regs)
+{
+ const struct exception_table_entry *ex;
+
+ ex = search_exception_tables(instruction_pointer(regs));
+ if (!ex)
+ return false;
+
+ switch (ex->type) {
+ case EX_TYPE_UACCESS_CPY:
+ return ex_handler_uaccess_cpy(ex, regs, 0);
+ case EX_TYPE_UACCESS_ERR_ZERO:
+ case EX_TYPE_KACCESS_ERR_ZERO_MEM_ERR:
+ return ex_handler_uaccess_err_zero(ex, regs);
+ }
+
+ return false;
+}
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 0f3c5c7ca054..efbda54770be 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -858,21 +858,35 @@ static int do_bad(unsigned long far, unsigned long esr, struct pt_regs *regs)
return 1; /* "fault" */
}
+/*
+ * APEI claimed this as a firmware-first notification.
+ * Some processing deferred to task_work before ret_to_user().
+ */
+static int do_apei_claim_sea(struct pt_regs *regs)
+{
+ int ret;
+
+ ret = apei_claim_sea(regs);
+ if (ret)
+ return ret;
+
+ if (!user_mode(regs) && IS_ENABLED(CONFIG_ARCH_HAS_COPY_MC)) {
+ if (!fixup_exception_me(regs))
+ return -ENOENT;
+ }
+
+ return ret;
+}
+
static int do_sea(unsigned long far, unsigned long esr, struct pt_regs *regs)
{
const struct fault_info *inf;
unsigned long siaddr;
- inf = esr_to_fault_info(esr);
-
- if (user_mode(regs) && apei_claim_sea(regs) == 0) {
- /*
- * APEI claimed this as a firmware-first notification.
- * Some processing deferred to task_work before ret_to_user().
- */
+ if (do_apei_claim_sea(regs) == 0)
return 0;
- }
+ inf = esr_to_fault_info(esr);
if (esr & ESR_ELx_FnV) {
siaddr = 0;
} else {
--
2.39.3
^ permalink raw reply related
* [PATCH v14 4/8] mm/hwpoison: return -EFAULT when copy fail in copy_mc_[user]_highpage()
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong, Jonathan Cameron, Mauro Carvalho Chehab
In-Reply-To: <20260518084956.2538442-1-tianruidong@linux.alibaba.com>
From: Tong Tiangen <tongtiangen@huawei.com>
Currently, copy_mc_[user]_highpage() returns zero on success, or in case
of failures, the number of bytes that weren't copied.
While tracking the number of not copied works fine for x86 and PPC, There
are some difficulties in doing the same thing on ARM64 because there is no
available caller-saved register in copy_page()(lib/copy_page.S) to save
"bytes not copied", and the following copy_mc_page() will also encounter
the same problem.
Consider the caller of copy_mc_[user]_highpage() cannot do any processing
on the remaining data(The page has hardware errors), they only check if
copy was succeeded or not, make the interface more generic by using an
error code when copy fails (-EFAULT) or return zero on success.
Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
include/linux/highmem.h | 8 ++++----
mm/khugepaged.c | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index af03db851a1d..18dc4aca4aa1 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -427,8 +427,8 @@ static inline void copy_highpage(struct page *to, struct page *from)
/*
* If architecture supports machine check exception handling, define the
* #MC versions of copy_user_highpage and copy_highpage. They copy a memory
- * page with #MC in source page (@from) handled, and return the number
- * of bytes not copied if there was a #MC, otherwise 0 for success.
+ * page with #MC in source page (@from) handled, and return -EFAULT if there
+ * was a #MC, otherwise 0 for success.
*/
static inline int copy_mc_user_highpage(struct page *to, struct page *from,
unsigned long vaddr, struct vm_area_struct *vma)
@@ -447,7 +447,7 @@ static inline int copy_mc_user_highpage(struct page *to, struct page *from,
if (ret)
memory_failure_queue(page_to_pfn(from), 0);
- return ret;
+ return ret ? -EFAULT : 0;
}
static inline int copy_mc_highpage(struct page *to, struct page *from)
@@ -466,7 +466,7 @@ static inline int copy_mc_highpage(struct page *to, struct page *from)
if (ret)
memory_failure_queue(page_to_pfn(from), 0);
- return ret;
+ return ret ? -EFAULT : 0;
}
#else
static inline int copy_mc_user_highpage(struct page *to, struct page *from,
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index b8452dbdb043..cf1b78eed3c3 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -810,7 +810,7 @@ static enum scan_result __collapse_huge_page_copy(pte_t *pte, struct folio *foli
continue;
}
src_page = pte_page(pteval);
- if (copy_mc_user_highpage(page, src_page, src_addr, vma) > 0) {
+ if (copy_mc_user_highpage(page, src_page, src_addr, vma)) {
result = SCAN_COPY_MC;
break;
}
@@ -2143,7 +2143,7 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr,
}
for (i = 0; i < nr_pages; i++) {
- if (copy_mc_highpage(dst, folio_page(folio, i)) > 0) {
+ if (copy_mc_highpage(dst, folio_page(folio, i))) {
result = SCAN_COPY_MC;
goto rollback;
}
--
2.39.3
^ permalink raw reply related
* [PATCH v14 2/8] ACPI: APEI: GHES: use exception context to gate SIGBUS on poison consumption
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong
In-Reply-To: <20260518084956.2538442-1-tianruidong@linux.alibaba.com>
When a GHES SEA (Synchronous External Abort) fires while the CPU
was executing in kernel mode, it typically means that kernel code
itself consumed a poisoned memory location -- e.g. copy_from_user()
/ copy_to_user() invoked from a ioctl() or write() syscall touched
a poisoned user page or page-cache page on behalf of the task.
The expected behaviour in that case is that the faulting kernel
helper returns via its extable fixup and the syscall returns an
error (e.g. -EFAULT) to user space. It is NOT appropriate to deliver
SIGBUS to the current task: the task did not directly dereference
the poisoned address, the kernel did on its behalf, and the kernel
is able to recover.
Up to now ghes_handle_memory_failure() unconditionally promoted any
synchronous recoverable memory error to MF_ACTION_REQUIRED, which
ends up SIGBUS on current -- regardless of whether the poison was
consumed from user space or from inside the kernel on the task's
behalf. That kills tasks that should instead have seen a plain
syscall error.
To fix this, the execution mode in which the exception was taken
must be captured at the arch-level entry point, where pt_regs (and
hence user_mode(regs)) are still available. The estatus node that
later drains the error in IRQ / process context no longer has
access to the original regs.
Introduce:
enum context { NO_USE = -1, IN_KERNEL = 0, IN_USER = 1 };
and plumb the value all the way down to the queued estatus node:
* Add an 'enum context context' field to struct ghes_estatus_node
and record it in ghes_in_nmi_queue_one_entry().
* Extend ghes_notify_sea() and the internal
ghes_in_nmi_spool_from_list() with an enum context parameter.
Then consume the recorded context in ghes_handle_memory_failure()
for the GHES_SEV_RECOVERABLE / sync path:
flags = sync && context == IN_USER ? MF_ACTION_REQUIRED : 0;
i.e. MF_ACTION_REQUIRED (and thus SIGBUS via the task_work path) is
only raised for user-mode poison consumption. Synchronous errors
taken in kernel mode fall back to memory_failure_queue() with
flags=0, asynchronously isolating the poisoned page while letting
the faulting kernel helper's extable fixup return -EFAULT
to user space.
Paths that pass NO_USE are unaffected:
sync is false for them, so flags stays 0 as before.
Signed-off-by: Ruidong Tian <tianruidong@linux.alibaba.com>
---
arch/arm64/kernel/acpi.c | 2 +-
drivers/acpi/apei/ghes.c | 36 ++++++++++++++++++++----------------
include/acpi/ghes.h | 6 ++++--
3 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index 5891f92c2035..40d4a2913d51 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -409,7 +409,7 @@ int apei_claim_sea(struct pt_regs *regs)
*/
local_daif_restore(DAIF_ERRCTX);
nmi_enter();
- err = ghes_notify_sea();
+ err = ghes_notify_sea(user_mode(regs));
nmi_exit();
/*
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 3236a3ce79d6..6f265893cddf 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -529,7 +529,7 @@ static bool ghes_do_memory_failure(u64 physical_addr, int flags)
}
static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
- int sev, bool sync)
+ int sev, bool sync, enum context context)
{
int flags = -1;
int sec_sev = ghes_severity(gdata->error_severity);
@@ -543,7 +543,7 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
(gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED))
flags = MF_SOFT_OFFLINE;
if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE)
- flags = sync ? MF_ACTION_REQUIRED : 0;
+ flags = sync && context == IN_USER ? MF_ACTION_REQUIRED : 0;
if (flags != -1)
return ghes_do_memory_failure(mem_err->physical_addr, flags);
@@ -552,10 +552,10 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
}
static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata,
- int sev, bool sync)
+ int sev, bool sync, enum context context)
{
struct cper_sec_proc_arm *err = acpi_hest_get_payload(gdata);
- int flags = sync ? MF_ACTION_REQUIRED : 0;
+ int flags = sync && context == IN_USER ? MF_ACTION_REQUIRED : 0;
int length = gdata->error_data_length;
char error_type[120];
bool queued = false;
@@ -910,7 +910,8 @@ static void ghes_log_hwerr(int sev, guid_t *sec_type)
}
static void ghes_do_proc(struct ghes *ghes,
- const struct acpi_hest_generic_status *estatus)
+ const struct acpi_hest_generic_status *estatus,
+ enum context context)
{
int sev, sec_sev;
struct acpi_hest_generic_data *gdata;
@@ -937,11 +938,11 @@ static void ghes_do_proc(struct ghes *ghes,
atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err);
arch_apei_report_mem_error(sev, mem_err);
- queued = ghes_handle_memory_failure(gdata, sev, sync);
+ queued = ghes_handle_memory_failure(gdata, sev, sync, context);
} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
ghes_handle_aer(gdata);
} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
- queued = ghes_handle_arm_hw_error(gdata, sev, sync);
+ queued = ghes_handle_arm_hw_error(gdata, sev, sync, context);
} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
struct cxl_cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
@@ -1190,7 +1191,7 @@ static int ghes_proc(struct ghes *ghes)
if (ghes_print_estatus(NULL, ghes->generic, estatus))
ghes_estatus_cache_add(ghes->generic, estatus);
}
- ghes_do_proc(ghes, estatus);
+ ghes_do_proc(ghes, estatus, NO_USE);
out:
ghes_clear_estatus(ghes, estatus, buf_paddr, FIX_APEI_GHES_IRQ);
@@ -1297,7 +1298,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work)
len = cper_estatus_len(estatus);
node_len = GHES_ESTATUS_NODE_LEN(len);
- ghes_do_proc(estatus_node->ghes, estatus);
+ ghes_do_proc(estatus_node->ghes, estatus, estatus_node->context);
if (!ghes_estatus_cached(estatus)) {
generic = estatus_node->generic;
@@ -1335,7 +1336,8 @@ static void ghes_print_queued_estatus(void)
}
static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,
- enum fixed_addresses fixmap_idx)
+ enum fixed_addresses fixmap_idx,
+ enum context context)
{
struct acpi_hest_generic_status *estatus, tmp_header;
struct ghes_estatus_node *estatus_node;
@@ -1364,6 +1366,7 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,
if (!estatus_node)
return -ENOMEM;
+ estatus_node->context = context;
estatus_node->ghes = ghes;
estatus_node->generic = ghes->generic;
estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
@@ -1398,14 +1401,15 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,
}
static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list,
- enum fixed_addresses fixmap_idx)
+ enum fixed_addresses fixmap_idx,
+ enum context context)
{
int ret = -ENOENT;
struct ghes *ghes;
rcu_read_lock();
list_for_each_entry_rcu(ghes, rcu_list, list) {
- if (!ghes_in_nmi_queue_one_entry(ghes, fixmap_idx))
+ if (!ghes_in_nmi_queue_one_entry(ghes, fixmap_idx, context))
ret = 0;
}
rcu_read_unlock();
@@ -1488,7 +1492,7 @@ static LIST_HEAD(ghes_sea);
* Return 0 only if one of the SEA error sources successfully reported an error
* record sent from the firmware.
*/
-int ghes_notify_sea(void)
+int ghes_notify_sea(enum context context)
{
static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sea);
int rv;
@@ -1497,7 +1501,7 @@ int ghes_notify_sea(void)
return -ENOENT;
raw_spin_lock(&ghes_notify_lock_sea);
- rv = ghes_in_nmi_spool_from_list(&ghes_sea, FIX_APEI_GHES_SEA);
+ rv = ghes_in_nmi_spool_from_list(&ghes_sea, FIX_APEI_GHES_SEA, context);
raw_spin_unlock(&ghes_notify_lock_sea);
return rv;
@@ -1552,7 +1556,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
return ret;
raw_spin_lock(&ghes_notify_lock_nmi);
- if (!ghes_in_nmi_spool_from_list(&ghes_nmi, FIX_APEI_GHES_NMI))
+ if (!ghes_in_nmi_spool_from_list(&ghes_nmi, FIX_APEI_GHES_NMI, NO_USE))
ret = NMI_HANDLED;
raw_spin_unlock(&ghes_notify_lock_nmi);
@@ -1606,7 +1610,7 @@ static void ghes_nmi_init_cxt(void)
static int __ghes_sdei_callback(struct ghes *ghes,
enum fixed_addresses fixmap_idx)
{
- if (!ghes_in_nmi_queue_one_entry(ghes, fixmap_idx)) {
+ if (!ghes_in_nmi_queue_one_entry(ghes, fixmap_idx, NO_USE)) {
irq_work_queue(&ghes_proc_irq_work);
return 0;
diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h
index 8d7e5caef3f1..646cd5c3c0ca 100644
--- a/include/acpi/ghes.h
+++ b/include/acpi/ghes.h
@@ -33,10 +33,12 @@ struct ghes {
void __iomem *error_status_vaddr;
};
+enum context {NO_USE = -1, IN_KERNEL = 0, IN_USER = 1};
struct ghes_estatus_node {
struct llist_node llnode;
struct acpi_hest_generic *generic;
struct ghes *ghes;
+ enum context context;
};
struct ghes_estatus_cache {
@@ -135,9 +137,9 @@ static inline void *acpi_hest_get_next(struct acpi_hest_generic_data *gdata)
section = acpi_hest_get_next(section))
#ifdef CONFIG_ACPI_APEI_SEA
-int ghes_notify_sea(void);
+int ghes_notify_sea(enum context context);
#else
-static inline int ghes_notify_sea(void) { return -ENOENT; }
+static inline int ghes_notify_sea(enum context context) { return -ENOENT; }
#endif
struct notifier_block;
--
2.39.3
^ permalink raw reply related
* [PATCH v14 6/8] lib/test: memcpy_kunit: add copy_page() and copy_mc_page() tests
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong
In-Reply-To: <20260518084956.2538442-1-tianruidong@linux.alibaba.com>
Add KUnit tests for copy_page() and copy_mc_page(), modeled after
the existing memcpy_test() style: a static page-aligned src and a
two-page dst, filled with random bytes plus non-zero edges, then
verify byte-for-byte equality and that the adjacent page is
untouched. The copy_mc_page() case additionally checks the return
value is 0 on clean memory and is gated on CONFIG_ARCH_HAS_COPY_MC.
Signed-off-by: Ruidong Tian <tianruidong@linux.alibaba.com>
---
lib/tests/memcpy_kunit.c | 67 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 66 insertions(+), 1 deletion(-)
diff --git a/lib/tests/memcpy_kunit.c b/lib/tests/memcpy_kunit.c
index d36933554e46..85df53ccfb0c 100644
--- a/lib/tests/memcpy_kunit.c
+++ b/lib/tests/memcpy_kunit.c
@@ -493,6 +493,67 @@ static void memmove_overlap_test(struct kunit *test)
}
}
+/* --- Page-sized copy tests --- */
+
+static u8 page_src[PAGE_SIZE] __aligned(PAGE_SIZE);
+static u8 page_dst[PAGE_SIZE * 2] __aligned(PAGE_SIZE);
+static const u8 page_zero[PAGE_SIZE] __aligned(PAGE_SIZE);
+
+static void init_page(struct kunit *test)
+{
+ /* Get many bit patterns. */
+ get_random_bytes(page_src, PAGE_SIZE);
+
+ /* Make sure we have non-zero edges. */
+ set_random_nonzero(test, &page_src[0]);
+ set_random_nonzero(test, &page_src[PAGE_SIZE - 1]);
+
+ /* Explicitly zero the entire destination. */
+ memset(page_dst, 0, ARRAY_SIZE(page_dst));
+}
+
+static void copy_page_test(struct kunit *test)
+{
+ init_page(test);
+
+ /* Copy. */
+ copy_page(page_dst, page_src);
+
+ /* Verify byte-for-byte exact. */
+ KUNIT_ASSERT_EQ_MSG(test,
+ memcmp(page_dst, page_src, PAGE_SIZE), 0,
+ "copy_page content mismatch with random data");
+
+ /* Verify no overflow into second page. */
+ KUNIT_ASSERT_EQ_MSG(test,
+ memcmp(page_dst + PAGE_SIZE, page_zero, PAGE_SIZE), 0,
+ "copy_page overflow into adjacent page");
+}
+
+#ifdef CONFIG_ARCH_HAS_COPY_MC
+static void copy_mc_page_test(struct kunit *test)
+{
+ int ret;
+
+ init_page(test);
+
+ /* Copy and check return value. */
+ ret = copy_mc_page(page_dst, page_src);
+ KUNIT_ASSERT_EQ_MSG(test, ret, 0,
+ "copy_mc_page returned %d on clean memory", ret);
+
+ /* Verify byte-for-byte exact. */
+ KUNIT_ASSERT_EQ_MSG(test,
+ memcmp(page_dst, page_src, PAGE_SIZE), 0,
+ "copy_mc_page content mismatch with random data");
+
+ /* Verify no overflow into second page. */
+ KUNIT_ASSERT_EQ_MSG(test,
+ memcmp(page_dst + PAGE_SIZE, page_zero, PAGE_SIZE), 0,
+ "copy_mc_page overflow into adjacent page");
+}
+#endif /* CONFIG_ARCH_HAS_COPY_MC */
+
static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE(memset_test),
KUNIT_CASE(memcpy_test),
@@ -500,6 +561,10 @@ static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE_SLOW(memmove_test),
KUNIT_CASE_SLOW(memmove_large_test),
KUNIT_CASE_SLOW(memmove_overlap_test),
+ KUNIT_CASE(copy_page_test),
+#ifdef CONFIG_ARCH_HAS_COPY_MC
+ KUNIT_CASE(copy_mc_page_test),
+#endif
{}
};
@@ -510,5 +575,5 @@ static struct kunit_suite memcpy_test_suite = {
kunit_test_suite(memcpy_test_suite);
-MODULE_DESCRIPTION("test cases for memcpy(), memmove(), and memset()");
+MODULE_DESCRIPTION("test cases for memcpy(), memmove(), memset() and copy_page()");
MODULE_LICENSE("GPL");
--
2.39.3
^ permalink raw reply related
* [PATCH v14 1/8] uaccess: add generic fallback version of copy_mc_to_user()
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong, Mauro Carvalho Chehab, Jonathan Cameron
In-Reply-To: <20260518084956.2538442-1-tianruidong@linux.alibaba.com>
From: Tong Tiangen <tongtiangen@huawei.com>
x86/powerpc has it's implementation of copy_mc_to_user(), we add generic
fallback in include/linux/uaccess.h prepare for other architechures to
enable CONFIG_ARCH_HAS_COPY_MC.
Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
Acked-by: Michael Ellerman <mpe@ellerman.id.au>
Reviewed-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
arch/powerpc/include/asm/uaccess.h | 1 +
arch/x86/include/asm/uaccess.h | 1 +
include/linux/uaccess.h | 8 ++++++++
3 files changed, 10 insertions(+)
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index e98c628e3899..073de098d45a 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -432,6 +432,7 @@ copy_mc_to_user(void __user *to, const void *from, unsigned long n)
return n;
}
+#define copy_mc_to_user copy_mc_to_user
#endif
extern size_t copy_from_user_flushcache(void *dst, const void __user *src, size_t size);
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 3a0dd3c2b233..308b0854d1d5 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -496,6 +496,7 @@ copy_mc_to_kernel(void *to, const void *from, unsigned len);
unsigned long __must_check
copy_mc_to_user(void __user *to, const void *from, unsigned len);
+#define copy_mc_to_user copy_mc_to_user
#endif
/*
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 56328601218c..c53a65394f80 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -250,6 +250,14 @@ copy_mc_to_kernel(void *dst, const void *src, size_t cnt)
}
#endif
+#ifndef copy_mc_to_user
+static inline unsigned long __must_check
+copy_mc_to_user(void *dst, const void *src, size_t cnt)
+{
+ return copy_to_user(dst, src, cnt);
+}
+#endif
+
static __always_inline void pagefault_disabled_inc(void)
{
current->pagefault_disabled++;
--
2.39.3
^ permalink raw reply related
* [PATCH v14 8/8] lib/tests: memcpy_kunit: add memcpy_mc() and memcpy_mc_large() test
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong
In-Reply-To: <20260518084956.2538442-1-tianruidong@linux.alibaba.com>
memcpy_mc() is the Machine-Check safe memcpy variant that returns the
number of bytes NOT copied on a hardware memory error, or 0 on success.
Add two test cases modeled after the existing memcpy_test() and
memcpy_large_test() implementations:
Signed-off-by: Ruidong Tian <tianruidong@linux.alibaba.com>
---
lib/tests/memcpy_kunit.c | 113 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 112 insertions(+), 1 deletion(-)
diff --git a/lib/tests/memcpy_kunit.c b/lib/tests/memcpy_kunit.c
index 85df53ccfb0c..b4b2dafb50f1 100644
--- a/lib/tests/memcpy_kunit.c
+++ b/lib/tests/memcpy_kunit.c
@@ -552,6 +552,115 @@ static void copy_mc_page_test(struct kunit *test)
memcmp(page_dst + PAGE_SIZE, page_zero, PAGE_SIZE), 0,
"copy_mc_page overflow into adjacent page");
}
+/*
+ * memcpy_mc() is a Machine-Check safe memcpy variant.
+ * Signature: int memcpy_mc(void *dst, const void *src, size_t len)
+ * Returns: 0 on success, or number of bytes NOT copied on MC error.
+ *
+ * In the normal (no-poison) path it must behave identically to memcpy()
+ * and always return 0.
+ */
+static void memcpy_mc_test(struct kunit *test)
+{
+#define TEST_OP "memcpy_mc"
+ struct some_bytes control = {
+ .data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ },
+ };
+ struct some_bytes zero = { };
+ struct some_bytes middle = {
+ .data = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ },
+ };
+ struct some_bytes three = {
+ .data = { 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ },
+ };
+ struct some_bytes dest = { };
+ int ret, count;
+ u8 *ptr;
+
+ /* Verify static initializers. */
+ check(control, 0x20);
+ check(zero, 0);
+ compare("static initializers", dest, zero);
+
+ /* Verify assignment. */
+ dest = control;
+ compare("direct assignment", dest, control);
+
+ /* Verify complete overwrite. */
+ ret = memcpy_mc(dest.data, control.data, sizeof(dest.data));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ compare("complete overwrite", dest, control);
+
+ /* Verify middle overwrite: 7 bytes at offset 12. */
+ dest = control;
+ ret = memcpy_mc(dest.data + 12, zero.data, 7);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ compare("middle overwrite", dest, middle);
+
+ /* Verify zero-length copy is a no-op. */
+ dest = control;
+ ret = memcpy_mc(dest.data, zero.data, 0);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ compare("zero length", dest, control);
+
+ /* Verify argument side-effects aren't repeated. */
+ dest = control;
+ ptr = dest.data;
+ count = 1;
+ memcpy(ptr++, zero.data, count++);
+ ptr += 8;
+ memcpy(ptr++, zero.data, count++);
+ compare("argument side-effects", dest, three);
+#undef TEST_OP
+}
+
+static void memcpy_mc_large_test(struct kunit *test)
+{
+ init_large(test);
+
+ /* Sweep 1..1024 bytes x shifting offset to cover all template paths. */
+ for (int bytes = 1; bytes <= ARRAY_SIZE(large_src); bytes++) {
+ for (int offset = 0; offset < ARRAY_SIZE(large_src); offset++) {
+ int right_zero_pos = offset + bytes;
+ int right_zero_size = ARRAY_SIZE(large_dst) - right_zero_pos;
+ int ret;
+
+ ret = memcpy_mc(large_dst + offset, large_src, bytes);
+ KUNIT_ASSERT_EQ_MSG(test, ret, 0,
+ "memcpy_mc returned %d with size %d at offset %d",
+ ret, bytes, offset);
+
+ /* No write before copy area. */
+ KUNIT_ASSERT_EQ_MSG(test,
+ memcmp(large_dst, large_zero, offset), 0,
+ "with size %d at offset %d", bytes, offset);
+ /* No write after copy area. */
+ KUNIT_ASSERT_EQ_MSG(test,
+ memcmp(&large_dst[right_zero_pos], large_zero,
+ right_zero_size), 0,
+ "with size %d at offset %d", bytes, offset);
+ /* Byte-for-byte exact. */
+ KUNIT_ASSERT_EQ_MSG(test,
+ memcmp(large_dst + offset, large_src, bytes), 0,
+ "with size %d at offset %d", bytes, offset);
+
+ memset(large_dst + offset, 0, bytes);
+ }
+ cond_resched();
+ }
+}
#endif /* CONFIG_ARCH_HAS_COPY_MC */
static struct kunit_case memcpy_test_cases[] = {
@@ -564,6 +673,8 @@ static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE(copy_page_test),
#ifdef CONFIG_ARCH_HAS_COPY_MC
KUNIT_CASE(copy_mc_page_test),
+ KUNIT_CASE(memcpy_mc_test),
+ KUNIT_CASE_SLOW(memcpy_mc_large_test),
#endif
{}
};
@@ -575,5 +686,5 @@ static struct kunit_suite memcpy_test_suite = {
kunit_test_suite(memcpy_test_suite);
-MODULE_DESCRIPTION("test cases for memcpy(), memmove(), memset() and copy_page()");
+MODULE_DESCRIPTION("test cases for memcpy(), memmove(), memset(), copy_page() and memcpy_mc()");
MODULE_LICENSE("GPL");
--
2.39.3
^ permalink raw reply related
* [PATCH v14 5/8] arm64: support copy_mc_[user]_highpage()
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong
In-Reply-To: <20260518084956.2538442-1-tianruidong@linux.alibaba.com>
From: Tong Tiangen <tongtiangen@huawei.com>
Currently, many scenarios that can tolerate memory errors when copying page
have been supported in the kernel[1~5], all of which are implemented by
copy_mc_[user]_highpage(). arm64 should also support this mechanism.
Due to mte, arm64 needs to have its own copy_mc_[user]_highpage()
architecture implementation, macros __HAVE_ARCH_COPY_MC_HIGHPAGE and
__HAVE_ARCH_COPY_MC_USER_HIGHPAGE have been added to control it.
Add new helper copy_mc_page() which provide a page copy implementation with
hardware memory error safe. The code logic of copy_mc_page() is the same as
copy_page(), the main difference is that the ldp insn of copy_mc_page()
contains the fixup type EX_TYPE_KACCESS_ERR_ZERO_MEM_ERR, therefore, the
main logic is extracted to copy_page_template.S. In addition, the fixup of
MOPS insn is not considered at present.
[Ruidong: add FEAT_MOPS support]
[1] commit d302c2398ba2 ("mm, hwpoison: when copy-on-write hits poison, take page offline")
[2] commit 1cb9dc4b475c ("mm: hwpoison: support recovery from HugePage copy-on-write faults")
[3] commit 6b970599e807 ("mm: hwpoison: support recovery from ksm_might_need_to_copy()")
[4] commit 98c76c9f1ef7 ("mm/khugepaged: recover from poisoned anonymous memory")
[5] commit 12904d953364 ("mm/khugepaged: recover from poisoned file-backed memory")
Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
Signed-off-by: Ruidong Tian <tianruidong@linux.alibaba.com>
---
arch/arm64/include/asm/mte.h | 9 ++++
arch/arm64/include/asm/page.h | 10 ++++
arch/arm64/lib/Makefile | 2 +
arch/arm64/lib/copy_mc_page.S | 44 +++++++++++++++++
arch/arm64/lib/copy_page.S | 62 ++----------------------
arch/arm64/lib/copy_page_template.S | 71 +++++++++++++++++++++++++++
arch/arm64/lib/mte.S | 29 +++++++++++
arch/arm64/mm/copypage.c | 75 +++++++++++++++++++++++++++++
include/linux/highmem.h | 8 +++
9 files changed, 253 insertions(+), 57 deletions(-)
create mode 100644 arch/arm64/lib/copy_mc_page.S
create mode 100644 arch/arm64/lib/copy_page_template.S
diff --git a/arch/arm64/include/asm/mte.h b/arch/arm64/include/asm/mte.h
index 7f7b97e09996..a0b1757f4847 100644
--- a/arch/arm64/include/asm/mte.h
+++ b/arch/arm64/include/asm/mte.h
@@ -98,6 +98,11 @@ static inline bool try_page_mte_tagging(struct page *page)
void mte_zero_clear_page_tags(void *addr);
void mte_sync_tags(pte_t pte, unsigned int nr_pages);
void mte_copy_page_tags(void *kto, const void *kfrom);
+
+#ifdef CONFIG_ARCH_HAS_COPY_MC
+int mte_copy_mc_page_tags(void *kto, const void *kfrom);
+#endif
+
void mte_thread_init_user(void);
void mte_thread_switch(struct task_struct *next);
void mte_cpu_setup(void);
@@ -134,6 +139,10 @@ static inline void mte_sync_tags(pte_t pte, unsigned int nr_pages)
static inline void mte_copy_page_tags(void *kto, const void *kfrom)
{
}
+static inline int mte_copy_mc_page_tags(void *kto, const void *kfrom)
+{
+ return 0;
+}
static inline void mte_thread_init_user(void)
{
}
diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
index e25d0d18f6d7..f65818ee614a 100644
--- a/arch/arm64/include/asm/page.h
+++ b/arch/arm64/include/asm/page.h
@@ -29,6 +29,16 @@ void copy_user_highpage(struct page *to, struct page *from,
void copy_highpage(struct page *to, struct page *from);
#define __HAVE_ARCH_COPY_HIGHPAGE
+#ifdef CONFIG_ARCH_HAS_COPY_MC
+int copy_mc_page(void *to, const void *from);
+int copy_mc_highpage(struct page *to, struct page *from);
+#define __HAVE_ARCH_COPY_MC_HIGHPAGE
+
+int copy_mc_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr, struct vm_area_struct *vma);
+#define __HAVE_ARCH_COPY_MC_USER_HIGHPAGE
+#endif
+
struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
unsigned long vaddr);
#define vma_alloc_zeroed_movable_folio vma_alloc_zeroed_movable_folio
diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
index 448c917494f3..1f4c3f743a20 100644
--- a/arch/arm64/lib/Makefile
+++ b/arch/arm64/lib/Makefile
@@ -7,6 +7,8 @@ lib-y := clear_user.o delay.o copy_from_user.o \
lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o
+lib-$(CONFIG_ARCH_HAS_COPY_MC) += copy_mc_page.o
+
obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
obj-$(CONFIG_ARM64_MTE) += mte.o
diff --git a/arch/arm64/lib/copy_mc_page.S b/arch/arm64/lib/copy_mc_page.S
new file mode 100644
index 000000000000..ad1371e9e687
--- /dev/null
+++ b/arch/arm64/lib/copy_mc_page.S
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/linkage.h>
+#include <linux/const.h>
+#include <asm/assembler.h>
+#include <asm/page.h>
+#include <asm/cpufeature.h>
+#include <asm/alternative.h>
+#include <asm/asm-extable.h>
+#include <asm/asm-uaccess.h>
+
+/*
+ * Copy a page from src to dest (both are page aligned) with memory error safe
+ *
+ * Parameters:
+ * x0 - dest
+ * x1 - src
+ * Returns:
+ * x0 - Return 0 if copy success, or -EFAULT if anything goes wrong
+ * while copying.
+ */
+ .macro ldp1 reg1, reg2, ptr, val
+ KERNEL_MEM_ERR(9998f, ldp \reg1, \reg2, [\ptr, \val])
+ .endm
+
+ .macro cpy1 dst, src, count
+ .arch_extension mops
+ USER_CPY(9998f, 0, cpyfprt [\dst]!, [\src]!, \count!)
+ USER_CPY(9998f, 0, cpyfmrt [\dst]!, [\src]!, \count!)
+ USER_CPY(9998f, 0, cpyfert [\dst]!, [\src]!, \count!)
+ .endm
+
+SYM_FUNC_START(__pi_copy_mc_page)
+#include "copy_page_template.S"
+
+ mov x0, #0
+ ret
+
+9998: mov x0, #-EFAULT
+ ret
+
+SYM_FUNC_END(__pi_copy_mc_page)
+SYM_FUNC_ALIAS(copy_mc_page, __pi_copy_mc_page)
+EXPORT_SYMBOL(copy_mc_page)
diff --git a/arch/arm64/lib/copy_page.S b/arch/arm64/lib/copy_page.S
index e6374e7e5511..d0186bbf99f1 100644
--- a/arch/arm64/lib/copy_page.S
+++ b/arch/arm64/lib/copy_page.S
@@ -17,65 +17,13 @@
* x0 - dest
* x1 - src
*/
-SYM_FUNC_START(__pi_copy_page)
-#ifdef CONFIG_AS_HAS_MOPS
- .arch_extension mops
-alternative_if_not ARM64_HAS_MOPS
- b .Lno_mops
-alternative_else_nop_endif
-
- mov x2, #PAGE_SIZE
- cpypwn [x0]!, [x1]!, x2!
- cpymwn [x0]!, [x1]!, x2!
- cpyewn [x0]!, [x1]!, x2!
- ret
-.Lno_mops:
-#endif
- ldp x2, x3, [x1]
- ldp x4, x5, [x1, #16]
- ldp x6, x7, [x1, #32]
- ldp x8, x9, [x1, #48]
- ldp x10, x11, [x1, #64]
- ldp x12, x13, [x1, #80]
- ldp x14, x15, [x1, #96]
- ldp x16, x17, [x1, #112]
-
- add x0, x0, #256
- add x1, x1, #128
-1:
- tst x0, #(PAGE_SIZE - 1)
- stnp x2, x3, [x0, #-256]
- ldp x2, x3, [x1]
- stnp x4, x5, [x0, #16 - 256]
- ldp x4, x5, [x1, #16]
- stnp x6, x7, [x0, #32 - 256]
- ldp x6, x7, [x1, #32]
- stnp x8, x9, [x0, #48 - 256]
- ldp x8, x9, [x1, #48]
- stnp x10, x11, [x0, #64 - 256]
- ldp x10, x11, [x1, #64]
- stnp x12, x13, [x0, #80 - 256]
- ldp x12, x13, [x1, #80]
- stnp x14, x15, [x0, #96 - 256]
- ldp x14, x15, [x1, #96]
- stnp x16, x17, [x0, #112 - 256]
- ldp x16, x17, [x1, #112]
-
- add x0, x0, #128
- add x1, x1, #128
-
- b.ne 1b
-
- stnp x2, x3, [x0, #-256]
- stnp x4, x5, [x0, #16 - 256]
- stnp x6, x7, [x0, #32 - 256]
- stnp x8, x9, [x0, #48 - 256]
- stnp x10, x11, [x0, #64 - 256]
- stnp x12, x13, [x0, #80 - 256]
- stnp x14, x15, [x0, #96 - 256]
- stnp x16, x17, [x0, #112 - 256]
+ .macro ldp1 reg1, reg2, ptr, val
+ ldp \reg1, \reg2, [\ptr, \val]
+ .endm
+SYM_FUNC_START(__pi_copy_page)
+#include "copy_page_template.S"
ret
SYM_FUNC_END(__pi_copy_page)
SYM_FUNC_ALIAS(copy_page, __pi_copy_page)
diff --git a/arch/arm64/lib/copy_page_template.S b/arch/arm64/lib/copy_page_template.S
new file mode 100644
index 000000000000..d466b51c8ed9
--- /dev/null
+++ b/arch/arm64/lib/copy_page_template.S
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ */
+
+/*
+ * Copy a page from src to dest (both are page aligned)
+ *
+ * Parameters:
+ * x0 - dest
+ * x1 - src
+ */
+dstin .req x0
+src .req x1
+
+#ifdef CONFIG_AS_HAS_MOPS
+ .arch_extension mops
+alternative_if_not ARM64_HAS_MOPS
+ b .Lno_mops
+alternative_else_nop_endif
+ mov x2, #PAGE_SIZE
+ cpy1 dst, src, x2
+ b .Lexitfunc
+.Lno_mops:
+#endif
+
+ ldp1 x2, x3, x1, #0
+ ldp1 x4, x5, x1, #16
+ ldp1 x6, x7, x1, #32
+ ldp1 x8, x9, x1, #48
+ ldp1 x10, x11, x1, #64
+ ldp1 x12, x13, x1, #80
+ ldp1 x14, x15, x1, #96
+ ldp1 x16, x17, x1, #112
+
+ add x0, x0, #256
+ add x1, x1, #128
+1:
+ tst x0, #(PAGE_SIZE - 1)
+
+ stnp x2, x3, [x0, #-256]
+ ldp1 x2, x3, x1, #0
+ stnp x4, x5, [x0, #16 - 256]
+ ldp1 x4, x5, x1, #16
+ stnp x6, x7, [x0, #32 - 256]
+ ldp1 x6, x7, x1, #32
+ stnp x8, x9, [x0, #48 - 256]
+ ldp1 x8, x9, x1, #48
+ stnp x10, x11, [x0, #64 - 256]
+ ldp1 x10, x11, x1, #64
+ stnp x12, x13, [x0, #80 - 256]
+ ldp1 x12, x13, x1, #80
+ stnp x14, x15, [x0, #96 - 256]
+ ldp1 x14, x15, x1, #96
+ stnp x16, x17, [x0, #112 - 256]
+ ldp1 x16, x17, x1, #112
+
+ add x0, x0, #128
+ add x1, x1, #128
+
+ b.ne 1b
+
+ stnp x2, x3, [x0, #-256]
+ stnp x4, x5, [x0, #16 - 256]
+ stnp x6, x7, [x0, #32 - 256]
+ stnp x8, x9, [x0, #48 - 256]
+ stnp x10, x11, [x0, #64 - 256]
+ stnp x12, x13, [x0, #80 - 256]
+ stnp x14, x15, [x0, #96 - 256]
+ stnp x16, x17, [x0, #112 - 256]
+.Lexitfunc:
diff --git a/arch/arm64/lib/mte.S b/arch/arm64/lib/mte.S
index 5018ac03b6bf..9d4eeb76a838 100644
--- a/arch/arm64/lib/mte.S
+++ b/arch/arm64/lib/mte.S
@@ -80,6 +80,35 @@ SYM_FUNC_START(mte_copy_page_tags)
ret
SYM_FUNC_END(mte_copy_page_tags)
+#ifdef CONFIG_ARCH_HAS_COPY_MC
+/*
+ * Copy the tags from the source page to the destination one with memory error safe
+ * x0 - address of the destination page
+ * x1 - address of the source page
+ * Returns:
+ * x0 - Return 0 if copy success, or
+ * -EFAULT if anything goes wrong while copying.
+ */
+SYM_FUNC_START(mte_copy_mc_page_tags)
+ mov x2, x0
+ mov x3, x1
+ multitag_transfer_size x5, x6
+1:
+KERNEL_MEM_ERR(2f, ldgm x4, [x3])
+ stgm x4, [x2]
+ add x2, x2, x5
+ add x3, x3, x5
+ tst x2, #(PAGE_SIZE - 1)
+ b.ne 1b
+
+ mov x0, #0
+ ret
+
+2: mov x0, #-EFAULT
+ ret
+SYM_FUNC_END(mte_copy_mc_page_tags)
+#endif
+
/*
* Read tags from a user buffer (one tag per byte) and set the corresponding
* tags at the given kernel address. Used by PTRACE_POKEMTETAGS.
diff --git a/arch/arm64/mm/copypage.c b/arch/arm64/mm/copypage.c
index cd5912ba617b..9fd773baf17b 100644
--- a/arch/arm64/mm/copypage.c
+++ b/arch/arm64/mm/copypage.c
@@ -72,3 +72,78 @@ void copy_user_highpage(struct page *to, struct page *from,
flush_dcache_page(to);
}
EXPORT_SYMBOL_GPL(copy_user_highpage);
+
+#ifdef CONFIG_ARCH_HAS_COPY_MC
+/*
+ * Return -EFAULT if anything goes wrong while copying page or mte.
+ */
+int copy_mc_highpage(struct page *to, struct page *from)
+{
+ void *kto = page_address(to);
+ void *kfrom = page_address(from);
+ struct folio *src = page_folio(from);
+ struct folio *dst = page_folio(to);
+ unsigned int i, nr_pages;
+ int ret;
+
+ ret = copy_mc_page(kto, kfrom);
+ if (ret)
+ return -EFAULT;
+
+ if (kasan_hw_tags_enabled())
+ page_kasan_tag_reset(to);
+
+ if (!system_supports_mte())
+ return 0;
+
+ if (folio_test_hugetlb(src)) {
+ if (!folio_test_hugetlb_mte_tagged(src) ||
+ from != folio_page(src, 0))
+ return 0;
+
+ WARN_ON_ONCE(!folio_try_hugetlb_mte_tagging(dst));
+
+ /*
+ * Populate tags for all subpages.
+ *
+ * Don't assume the first page is head page since
+ * huge page copy may start from any subpage.
+ */
+ nr_pages = folio_nr_pages(src);
+ for (i = 0; i < nr_pages; i++) {
+ kfrom = page_address(folio_page(src, i));
+ kto = page_address(folio_page(dst, i));
+ ret = mte_copy_mc_page_tags(kto, kfrom);
+ if (ret)
+ return -EFAULT;
+ }
+ folio_set_hugetlb_mte_tagged(dst);
+ } else if (page_mte_tagged(from)) {
+ /* It's a new page, shouldn't have been tagged yet */
+ WARN_ON_ONCE(!try_page_mte_tagging(to));
+
+ ret = mte_copy_mc_page_tags(kto, kfrom);
+ if (ret)
+ return -EFAULT;
+ set_page_mte_tagged(to);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(copy_mc_highpage);
+
+int copy_mc_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr, struct vm_area_struct *vma)
+{
+ int ret;
+
+ ret = copy_mc_highpage(to, from);
+ if (ret)
+ return ret;
+
+ flush_dcache_page(to);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(copy_mc_user_highpage);
+#endif
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index 18dc4aca4aa1..f168c9d4ad0e 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -424,6 +424,7 @@ static inline void copy_highpage(struct page *to, struct page *from)
#endif
#ifdef copy_mc_to_kernel
+#ifndef __HAVE_ARCH_COPY_MC_USER_HIGHPAGE
/*
* If architecture supports machine check exception handling, define the
* #MC versions of copy_user_highpage and copy_highpage. They copy a memory
@@ -449,7 +450,9 @@ static inline int copy_mc_user_highpage(struct page *to, struct page *from,
return ret ? -EFAULT : 0;
}
+#endif
+#ifndef __HAVE_ARCH_COPY_MC_HIGHPAGE
static inline int copy_mc_highpage(struct page *to, struct page *from)
{
unsigned long ret;
@@ -468,20 +471,25 @@ static inline int copy_mc_highpage(struct page *to, struct page *from)
return ret ? -EFAULT : 0;
}
+#endif
#else
+#ifndef __HAVE_ARCH_COPY_MC_USER_HIGHPAGE
static inline int copy_mc_user_highpage(struct page *to, struct page *from,
unsigned long vaddr, struct vm_area_struct *vma)
{
copy_user_highpage(to, from, vaddr, vma);
return 0;
}
+#endif
+#ifndef __HAVE_ARCH_COPY_MC_HIGHPAGE
static inline int copy_mc_highpage(struct page *to, struct page *from)
{
copy_highpage(to, from);
return 0;
}
#endif
+#endif
static inline void memcpy_page(struct page *dst_page, size_t dst_off,
struct page *src_page, size_t src_off,
--
2.39.3
^ permalink raw reply related
* [PATCH v14 7/8] arm64: introduce copy_mc_to_kernel() implementation
From: Ruidong Tian @ 2026-05-18 8:49 UTC (permalink / raw)
To: catalin.marinas, will, rafael, tony.luck, guohanjun, mchehab,
xueshuai, tongtiangen, james.morse, robin.murphy, andreyknvl,
dvyukov, vincenzo.frascino, mpe, npiggin, ryabinin.a.a, glider,
christophe.leroy, aneesh.kumar, naveen.n.rao, tglx, mingo
Cc: linux-arm-kernel, linux-mm, linuxppc-dev, linux-kernel, kasan-dev,
tianruidong
In-Reply-To: <20260518084956.2538442-1-tianruidong@linux.alibaba.com>
From: Tong Tiangen <tongtiangen@huawei.com>
The copy_mc_to_kernel() helper is memory copy implementation that handles
source exceptions. It can be used in memory copy scenarios that tolerate
hardware memory errors(e.g: pmem_read/dax_copy_to_iter).
Currently, only x86 and ppc support this helper, Add this for ARM64 as
well, if ARCH_HAS_COPY_MC is defined, by implementing copy_mc_to_kernel()
and memcpy_mc() functions.
Because there is no caller-saved GPR is available for saving "bytes not
copied" in memcpy(), the memcpy_mc() is referenced to the implementation
of copy_from_user(). In addition, the fixup of MOPS insn is not considered
at present.
[Ruidong: refactor memcpy_mc on top of the new memcpy implementation.]
Signed-off-by: Tong Tiangen <tongtiangen@huawei.com>
Signed-off-by: Ruidong Tian <tianruidong@linux.alibaba.com>
---
arch/arm64/include/asm/string.h | 5 +
arch/arm64/include/asm/uaccess.h | 17 +++
arch/arm64/lib/Makefile | 2 +-
arch/arm64/lib/memcpy.S | 253 +++----------------------------
arch/arm64/lib/memcpy_mc.S | 56 +++++++
arch/arm64/lib/memcpy_template.S | 249 ++++++++++++++++++++++++++++++
mm/kasan/shadow.c | 12 ++
7 files changed, 359 insertions(+), 235 deletions(-)
create mode 100644 arch/arm64/lib/memcpy_mc.S
create mode 100644 arch/arm64/lib/memcpy_template.S
diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h
index 3a3264ff47b9..23eca4fb24fa 100644
--- a/arch/arm64/include/asm/string.h
+++ b/arch/arm64/include/asm/string.h
@@ -35,6 +35,10 @@ extern void *memchr(const void *, int, __kernel_size_t);
extern void *memcpy(void *, const void *, __kernel_size_t);
extern void *__memcpy(void *, const void *, __kernel_size_t);
+#define __HAVE_ARCH_MEMCPY_MC
+extern int memcpy_mc(void *, const void *, __kernel_size_t);
+extern int __memcpy_mc(void *, const void *, __kernel_size_t);
+
#define __HAVE_ARCH_MEMMOVE
extern void *memmove(void *, const void *, __kernel_size_t);
extern void *__memmove(void *, const void *, __kernel_size_t);
@@ -57,6 +61,7 @@ void memcpy_flushcache(void *dst, const void *src, size_t cnt);
*/
#define memcpy(dst, src, len) __memcpy(dst, src, len)
+#define memcpy_mc(dst, src, len) __memcpy_mc(dst, src, len)
#define memmove(dst, src, len) __memmove(dst, src, len)
#define memset(s, c, n) __memset(s, c, n)
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index b0c83a08dda9..93277eca2268 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -499,5 +499,22 @@ static inline size_t probe_subpage_writeable(const char __user *uaddr,
}
#endif /* CONFIG_ARCH_HAS_SUBPAGE_FAULTS */
+#ifdef CONFIG_ARCH_HAS_COPY_MC
+/**
+ * copy_mc_to_kernel - memory copy that handles source exceptions
+ *
+ * @to: destination address
+ * @from: source address
+ * @size: number of bytes to copy
+ *
+ * Return 0 for success, or bytes not copied.
+ */
+static inline unsigned long __must_check
+copy_mc_to_kernel(void *to, const void *from, unsigned long size)
+{
+ return memcpy_mc(to, from, size);
+}
+#define copy_mc_to_kernel copy_mc_to_kernel
+#endif
#endif /* __ASM_UACCESS_H */
diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
index 1f4c3f743a20..a5820e6c33d4 100644
--- a/arch/arm64/lib/Makefile
+++ b/arch/arm64/lib/Makefile
@@ -7,7 +7,7 @@ lib-y := clear_user.o delay.o copy_from_user.o \
lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o
-lib-$(CONFIG_ARCH_HAS_COPY_MC) += copy_mc_page.o
+lib-$(CONFIG_ARCH_HAS_COPY_MC) += copy_mc_page.o memcpy_mc.o
obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
diff --git a/arch/arm64/lib/memcpy.S b/arch/arm64/lib/memcpy.S
index 9b99106fb95f..ef6aea2de9b4 100644
--- a/arch/arm64/lib/memcpy.S
+++ b/arch/arm64/lib/memcpy.S
@@ -15,247 +15,32 @@
*
*/
-#define L(label) .L ## label
+ .macro ldrb1 reg, addr:vararg
+ ldrb \reg, \addr
+ .endm
-#define dstin x0
-#define src x1
-#define count x2
-#define dst x3
-#define srcend x4
-#define dstend x5
-#define A_l x6
-#define A_lw w6
-#define A_h x7
-#define B_l x8
-#define B_lw w8
-#define B_h x9
-#define C_l x10
-#define C_lw w10
-#define C_h x11
-#define D_l x12
-#define D_h x13
-#define E_l x14
-#define E_h x15
-#define F_l x16
-#define F_h x17
-#define G_l count
-#define G_h dst
-#define H_l src
-#define H_h srcend
-#define tmp1 x14
+ .macro ldr1 reg, addr:vararg
+ ldr \reg, \addr
+ .endm
-/* This implementation handles overlaps and supports both memcpy and memmove
- from a single entry point. It uses unaligned accesses and branchless
- sequences to keep the code small, simple and improve performance.
+ .macro ldp1 reg1, reg2, addr:vararg
+ ldp \reg1, \reg2, \addr
+ .endm
- Copies are split into 3 main cases: small copies of up to 32 bytes, medium
- copies of up to 128 bytes, and large copies. The overhead of the overlap
- check is negligible since it is only required for large copies.
+ .macro ret1
+ ret
+ .endm
- Large copies use a software pipelined loop processing 64 bytes per iteration.
- The destination pointer is 16-byte aligned to minimize unaligned accesses.
- The loop tail is handled by always copying 64 bytes from the end.
-*/
+ .macro cpy1 dst, src, count
+ .arch_extension mops
+ cpyp [\dst]!, [\src]!, \count!
+ cpym [\dst]!, [\src]!, \count!
+ cpye [\dst]!, [\src]!, \count!
+ .endm
-SYM_FUNC_START_LOCAL(__pi_memcpy_generic)
- add srcend, src, count
- add dstend, dstin, count
- cmp count, 128
- b.hi L(copy_long)
- cmp count, 32
- b.hi L(copy32_128)
-
- /* Small copies: 0..32 bytes. */
- cmp count, 16
- b.lo L(copy16)
- ldp A_l, A_h, [src]
- ldp D_l, D_h, [srcend, -16]
- stp A_l, A_h, [dstin]
- stp D_l, D_h, [dstend, -16]
- ret
-
- /* Copy 8-15 bytes. */
-L(copy16):
- tbz count, 3, L(copy8)
- ldr A_l, [src]
- ldr A_h, [srcend, -8]
- str A_l, [dstin]
- str A_h, [dstend, -8]
- ret
-
- .p2align 3
- /* Copy 4-7 bytes. */
-L(copy8):
- tbz count, 2, L(copy4)
- ldr A_lw, [src]
- ldr B_lw, [srcend, -4]
- str A_lw, [dstin]
- str B_lw, [dstend, -4]
- ret
-
- /* Copy 0..3 bytes using a branchless sequence. */
-L(copy4):
- cbz count, L(copy0)
- lsr tmp1, count, 1
- ldrb A_lw, [src]
- ldrb C_lw, [srcend, -1]
- ldrb B_lw, [src, tmp1]
- strb A_lw, [dstin]
- strb B_lw, [dstin, tmp1]
- strb C_lw, [dstend, -1]
-L(copy0):
- ret
-
- .p2align 4
- /* Medium copies: 33..128 bytes. */
-L(copy32_128):
- ldp A_l, A_h, [src]
- ldp B_l, B_h, [src, 16]
- ldp C_l, C_h, [srcend, -32]
- ldp D_l, D_h, [srcend, -16]
- cmp count, 64
- b.hi L(copy128)
- stp A_l, A_h, [dstin]
- stp B_l, B_h, [dstin, 16]
- stp C_l, C_h, [dstend, -32]
- stp D_l, D_h, [dstend, -16]
- ret
-
- .p2align 4
- /* Copy 65..128 bytes. */
-L(copy128):
- ldp E_l, E_h, [src, 32]
- ldp F_l, F_h, [src, 48]
- cmp count, 96
- b.ls L(copy96)
- ldp G_l, G_h, [srcend, -64]
- ldp H_l, H_h, [srcend, -48]
- stp G_l, G_h, [dstend, -64]
- stp H_l, H_h, [dstend, -48]
-L(copy96):
- stp A_l, A_h, [dstin]
- stp B_l, B_h, [dstin, 16]
- stp E_l, E_h, [dstin, 32]
- stp F_l, F_h, [dstin, 48]
- stp C_l, C_h, [dstend, -32]
- stp D_l, D_h, [dstend, -16]
- ret
-
- .p2align 4
- /* Copy more than 128 bytes. */
-L(copy_long):
- /* Use backwards copy if there is an overlap. */
- sub tmp1, dstin, src
- cbz tmp1, L(copy0)
- cmp tmp1, count
- b.lo L(copy_long_backwards)
-
- /* Copy 16 bytes and then align dst to 16-byte alignment. */
-
- ldp D_l, D_h, [src]
- and tmp1, dstin, 15
- bic dst, dstin, 15
- sub src, src, tmp1
- add count, count, tmp1 /* Count is now 16 too large. */
- ldp A_l, A_h, [src, 16]
- stp D_l, D_h, [dstin]
- ldp B_l, B_h, [src, 32]
- ldp C_l, C_h, [src, 48]
- ldp D_l, D_h, [src, 64]!
- subs count, count, 128 + 16 /* Test and readjust count. */
- b.ls L(copy64_from_end)
-
-L(loop64):
- stp A_l, A_h, [dst, 16]
- ldp A_l, A_h, [src, 16]
- stp B_l, B_h, [dst, 32]
- ldp B_l, B_h, [src, 32]
- stp C_l, C_h, [dst, 48]
- ldp C_l, C_h, [src, 48]
- stp D_l, D_h, [dst, 64]!
- ldp D_l, D_h, [src, 64]!
- subs count, count, 64
- b.hi L(loop64)
-
- /* Write the last iteration and copy 64 bytes from the end. */
-L(copy64_from_end):
- ldp E_l, E_h, [srcend, -64]
- stp A_l, A_h, [dst, 16]
- ldp A_l, A_h, [srcend, -48]
- stp B_l, B_h, [dst, 32]
- ldp B_l, B_h, [srcend, -32]
- stp C_l, C_h, [dst, 48]
- ldp C_l, C_h, [srcend, -16]
- stp D_l, D_h, [dst, 64]
- stp E_l, E_h, [dstend, -64]
- stp A_l, A_h, [dstend, -48]
- stp B_l, B_h, [dstend, -32]
- stp C_l, C_h, [dstend, -16]
- ret
-
- .p2align 4
-
- /* Large backwards copy for overlapping copies.
- Copy 16 bytes and then align dst to 16-byte alignment. */
-L(copy_long_backwards):
- ldp D_l, D_h, [srcend, -16]
- and tmp1, dstend, 15
- sub srcend, srcend, tmp1
- sub count, count, tmp1
- ldp A_l, A_h, [srcend, -16]
- stp D_l, D_h, [dstend, -16]
- ldp B_l, B_h, [srcend, -32]
- ldp C_l, C_h, [srcend, -48]
- ldp D_l, D_h, [srcend, -64]!
- sub dstend, dstend, tmp1
- subs count, count, 128
- b.ls L(copy64_from_start)
-
-L(loop64_backwards):
- stp A_l, A_h, [dstend, -16]
- ldp A_l, A_h, [srcend, -16]
- stp B_l, B_h, [dstend, -32]
- ldp B_l, B_h, [srcend, -32]
- stp C_l, C_h, [dstend, -48]
- ldp C_l, C_h, [srcend, -48]
- stp D_l, D_h, [dstend, -64]!
- ldp D_l, D_h, [srcend, -64]!
- subs count, count, 64
- b.hi L(loop64_backwards)
-
- /* Write the last iteration and copy 64 bytes from the start. */
-L(copy64_from_start):
- ldp G_l, G_h, [src, 48]
- stp A_l, A_h, [dstend, -16]
- ldp A_l, A_h, [src, 32]
- stp B_l, B_h, [dstend, -32]
- ldp B_l, B_h, [src, 16]
- stp C_l, C_h, [dstend, -48]
- ldp C_l, C_h, [src]
- stp D_l, D_h, [dstend, -64]
- stp G_l, G_h, [dstin, 48]
- stp A_l, A_h, [dstin, 32]
- stp B_l, B_h, [dstin, 16]
- stp C_l, C_h, [dstin]
- ret
-SYM_FUNC_END(__pi_memcpy_generic)
-
-#ifdef CONFIG_AS_HAS_MOPS
- .arch_extension mops
SYM_FUNC_START(__pi_memcpy)
-alternative_if_not ARM64_HAS_MOPS
- b __pi_memcpy_generic
-alternative_else_nop_endif
-
- mov dst, dstin
- cpyp [dst]!, [src]!, count!
- cpym [dst]!, [src]!, count!
- cpye [dst]!, [src]!, count!
- ret
+#include "memcpy_template.S"
SYM_FUNC_END(__pi_memcpy)
-#else
-SYM_FUNC_ALIAS(__pi_memcpy, __pi_memcpy_generic)
-#endif
SYM_FUNC_ALIAS(__memcpy, __pi_memcpy)
EXPORT_SYMBOL(__memcpy)
diff --git a/arch/arm64/lib/memcpy_mc.S b/arch/arm64/lib/memcpy_mc.S
new file mode 100644
index 000000000000..90624d35af4b
--- /dev/null
+++ b/arch/arm64/lib/memcpy_mc.S
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012-2021, Arm Limited.
+ *
+ * Adapted from the original at:
+ * https://github.com/ARM-software/optimized-routines/blob/afd6244a1f8d9229/string/aarch64/memcpy.S
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/asm-uaccess.h>
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64, unaligned accesses.
+ *
+ */
+
+ .macro ldrb1 reg, addr:vararg
+ KERNEL_MEM_ERR(9998f, ldrb \reg, \addr)
+ .endm
+
+ .macro ldr1 reg, addr:vararg
+ KERNEL_MEM_ERR(9998f, ldr \reg, \addr)
+ .endm
+
+ .macro ldp1 reg1, reg2, addr:vararg
+ KERNEL_MEM_ERR(9998f, ldp \reg1, \reg2, \addr)
+ .endm
+
+ .macro ret1
+ mov x0, #0
+ ret
+ .endm
+
+ .macro cpy1 dst, src, count
+ .arch_extension mops
+ USER_CPY(9998f, 0, cpyp [\dst]!, [\src]!, \count!)
+ USER_CPY(9996f, 0, cpym [\dst]!, [\src]!, \count!)
+ USER_CPY(9996f, 0, cpye [\dst]!, [\src]!, \count!)
+ .endm
+
+SYM_FUNC_START(__memcpy_mc)
+#include "memcpy_template.S"
+
+ // Exception fixups
+9996: b.cs 9998f
+ // Registers are in Option A format
+ add dst, dst, count
+9998: sub x0, dstend, dstin // bytes not copied
+ ret
+SYM_FUNC_END(__memcpy_mc)
+
+EXPORT_SYMBOL(__memcpy_mc)
+SYM_FUNC_ALIAS_WEAK(memcpy_mc, __memcpy_mc)
+EXPORT_SYMBOL(memcpy_mc)
diff --git a/arch/arm64/lib/memcpy_template.S b/arch/arm64/lib/memcpy_template.S
new file mode 100644
index 000000000000..205516c6e076
--- /dev/null
+++ b/arch/arm64/lib/memcpy_template.S
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012-2021, Arm Limited.
+ *
+ * Adapted from the original at:
+ * https://github.com/ARM-software/optimized-routines/blob/afd6244a1f8d9229/string/aarch64/memcpy.S
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/* Assumptions:
+ *
+ * ARMv8-a, AArch64, unaligned accesses.
+ *
+ */
+
+#define L(label) .L ## label
+
+#define dstin x0
+#define src x1
+#define count x2
+#define dst x3
+#define srcend x4
+#define dstend x5
+#define A_l x6
+#define A_lw w6
+#define A_h x7
+#define B_l x8
+#define B_lw w8
+#define B_h x9
+#define C_l x10
+#define C_lw w10
+#define C_h x11
+#define D_l x12
+#define D_h x13
+#define E_l x14
+#define E_h x15
+#define F_l x16
+#define F_h x17
+#define G_l count
+#define G_h dst
+#define H_l src
+#define H_h srcend
+#define tmp1 x14
+
+/* This implementation handles overlaps and supports both memcpy and memmove
+ from a single entry point. It uses unaligned accesses and branchless
+ sequences to keep the code small, simple and improve performance.
+
+ Copies are split into 3 main cases: small copies of up to 32 bytes, medium
+ copies of up to 128 bytes, and large copies. The overhead of the overlap
+ check is negligible since it is only required for large copies.
+
+ Large copies use a software pipelined loop processing 64 bytes per iteration.
+ The destination pointer is 16-byte aligned to minimize unaligned accesses.
+ The loop tail is handled by always copying 64 bytes from the end.
+*/
+
+#ifdef CONFIG_AS_HAS_MOPS
+alternative_if_not ARM64_HAS_MOPS
+ b L(no_mops):
+alternative_else_nop_endif
+
+ cpy1 dst, src, count
+ ret1
+#endif
+
+L(no_mops):
+ add srcend, src, count
+ add dstend, dstin, count
+ cmp count, 128
+ b.hi L(copy_long)
+ cmp count, 32
+ b.hi L(copy32_128)
+
+ /* Small copies: 0..32 bytes. */
+ cmp count, 16
+ b.lo L(copy16)
+ ldp1 A_l, A_h, [src]
+ ldp1 D_l, D_h, [srcend, -16]
+ stp A_l, A_h, [dstin]
+ stp D_l, D_h, [dstend, -16]
+ ret1
+
+ /* Copy 8-15 bytes. */
+L(copy16):
+ tbz count, 3, L(copy8)
+ ldr1 A_l, [src]
+ ldr1 A_h, [srcend, -8]
+ str A_l, [dstin]
+ str A_h, [dstend, -8]
+ ret1
+
+ .p2align 3
+ /* Copy 4-7 bytes. */
+L(copy8):
+ tbz count, 2, L(copy4)
+ ldr1 A_lw, [src]
+ ldr1 B_lw, [srcend, -4]
+ str A_lw, [dstin]
+ str B_lw, [dstend, -4]
+ ret1
+
+ /* Copy 0..3 bytes using a branchless sequence. */
+L(copy4):
+ cbz count, L(copy0)
+ lsr tmp1, count, 1
+ ldrb1 A_lw, [src]
+ ldrb1 C_lw, [srcend, -1]
+ ldrb1 B_lw, [src, tmp1]
+ strb A_lw, [dstin]
+ strb B_lw, [dstin, tmp1]
+ strb C_lw, [dstend, -1]
+L(copy0):
+ ret1
+
+ .p2align 4
+ /* Medium copies: 33..128 bytes. */
+L(copy32_128):
+ ldp1 A_l, A_h, [src]
+ ldp1 B_l, B_h, [src, 16]
+ ldp1 C_l, C_h, [srcend, -32]
+ ldp1 D_l, D_h, [srcend, -16]
+ cmp count, 64
+ b.hi L(copy128)
+ stp A_l, A_h, [dstin]
+ stp B_l, B_h, [dstin, 16]
+ stp C_l, C_h, [dstend, -32]
+ stp D_l, D_h, [dstend, -16]
+ ret1
+
+ .p2align 4
+ /* Copy 65..128 bytes. */
+L(copy128):
+ ldp1 E_l, E_h, [src, 32]
+ ldp1 F_l, F_h, [src, 48]
+ cmp count, 96
+ b.ls L(copy96)
+ ldp1 G_l, G_h, [srcend, -64]
+ ldp1 H_l, H_h, [srcend, -48]
+ stp G_l, G_h, [dstend, -64]
+ stp H_l, H_h, [dstend, -48]
+L(copy96):
+ stp A_l, A_h, [dstin]
+ stp B_l, B_h, [dstin, 16]
+ stp E_l, E_h, [dstin, 32]
+ stp F_l, F_h, [dstin, 48]
+ stp C_l, C_h, [dstend, -32]
+ stp D_l, D_h, [dstend, -16]
+ ret1
+
+ .p2align 4
+ /* Copy more than 128 bytes. */
+L(copy_long):
+ /* Use backwards copy if there is an overlap. */
+ sub tmp1, dstin, src
+ cbz tmp1, L(copy0)
+ cmp tmp1, count
+ b.lo L(copy_long_backwards)
+
+ /* Copy 16 bytes and then align dst to 16-byte alignment. */
+
+ ldp1 D_l, D_h, [src]
+ and tmp1, dstin, 15
+ bic dst, dstin, 15
+ sub src, src, tmp1
+ add count, count, tmp1 /* Count is now 16 too large. */
+ ldp1 A_l, A_h, [src, 16]
+ stp D_l, D_h, [dstin]
+ ldp1 B_l, B_h, [src, 32]
+ ldp1 C_l, C_h, [src, 48]
+ ldp1 D_l, D_h, [src, 64]!
+ subs count, count, 128 + 16 /* Test and readjust count. */
+ b.ls L(copy64_from_end)
+
+L(loop64):
+ stp A_l, A_h, [dst, 16]
+ ldp1 A_l, A_h, [src, 16]
+ stp B_l, B_h, [dst, 32]
+ ldp1 B_l, B_h, [src, 32]
+ stp C_l, C_h, [dst, 48]
+ ldp1 C_l, C_h, [src, 48]
+ stp D_l, D_h, [dst, 64]!
+ ldp1 D_l, D_h, [src, 64]!
+ subs count, count, 64
+ b.hi L(loop64)
+
+ /* Write the last iteration and copy 64 bytes from the end. */
+L(copy64_from_end):
+ ldp1 E_l, E_h, [srcend, -64]
+ stp A_l, A_h, [dst, 16]
+ ldp1 A_l, A_h, [srcend, -48]
+ stp B_l, B_h, [dst, 32]
+ ldp1 B_l, B_h, [srcend, -32]
+ stp C_l, C_h, [dst, 48]
+ ldp1 C_l, C_h, [srcend, -16]
+ stp D_l, D_h, [dst, 64]
+ stp E_l, E_h, [dstend, -64]
+ stp A_l, A_h, [dstend, -48]
+ stp B_l, B_h, [dstend, -32]
+ stp C_l, C_h, [dstend, -16]
+ ret1
+
+ .p2align 4
+
+ /* Large backwards copy for overlapping copies.
+ Copy 16 bytes and then align dst to 16-byte alignment. */
+L(copy_long_backwards):
+ ldp1 D_l, D_h, [srcend, -16]
+ and tmp1, dstend, 15
+ sub srcend, srcend, tmp1
+ sub count, count, tmp1
+ ldp1 A_l, A_h, [srcend, -16]
+ stp D_l, D_h, [dstend, -16]
+ ldp1 B_l, B_h, [srcend, -32]
+ ldp1 C_l, C_h, [srcend, -48]
+ ldp1 D_l, D_h, [srcend, -64]!
+ sub dstend, dstend, tmp1
+ subs count, count, 128
+ b.ls L(copy64_from_start)
+
+L(loop64_backwards):
+ stp A_l, A_h, [dstend, -16]
+ ldp1 A_l, A_h, [srcend, -16]
+ stp B_l, B_h, [dstend, -32]
+ ldp1 B_l, B_h, [srcend, -32]
+ stp C_l, C_h, [dstend, -48]
+ ldp1 C_l, C_h, [srcend, -48]
+ stp D_l, D_h, [dstend, -64]!
+ ldp1 D_l, D_h, [srcend, -64]!
+ subs count, count, 64
+ b.hi L(loop64_backwards)
+
+ /* Write the last iteration and copy 64 bytes from the start. */
+L(copy64_from_start):
+ ldp1 G_l, G_h, [src, 48]
+ stp A_l, A_h, [dstend, -16]
+ ldp1 A_l, A_h, [src, 32]
+ stp B_l, B_h, [dstend, -32]
+ ldp1 B_l, B_h, [src, 16]
+ stp C_l, C_h, [dstend, -48]
+ ldp1 C_l, C_h, [src]
+ stp D_l, D_h, [dstend, -64]
+ stp G_l, G_h, [dstin, 48]
+ stp A_l, A_h, [dstin, 32]
+ stp B_l, B_h, [dstin, 16]
+ stp C_l, C_h, [dstin]
+ ret1
diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c
index d286e0a04543..3128f0d9cc46 100644
--- a/mm/kasan/shadow.c
+++ b/mm/kasan/shadow.c
@@ -79,6 +79,18 @@ void *memcpy(void *dest, const void *src, size_t len)
}
#endif
+#ifdef __HAVE_ARCH_MEMCPY_MC
+#undef memcpy_mc
+int memcpy_mc(void *dest, const void *src, size_t len)
+{
+ if (!kasan_check_range(src, len, false, _RET_IP_) ||
+ !kasan_check_range(dest, len, true, _RET_IP_))
+ return (int)len;
+
+ return __memcpy_mc(dest, src, len);
+}
+#endif
+
void *__asan_memset(void *addr, int c, ssize_t len)
{
if (!kasan_check_range(addr, len, true, _RET_IP_))
--
2.39.3
^ permalink raw reply related
* Re: [RFC PATCH 1/2] KVM: arm64: Introduce S2 walker SKIP return options
From: Will Deacon @ 2026-05-18 8:52 UTC (permalink / raw)
To: Oliver Upton
Cc: Leonardo Bras, Marc Zyngier, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Fuad Tabba, Raghavendra Rao Ananta,
linux-arm-kernel, kvmarm, linux-kernel
In-Reply-To: <agq-RyKhBy4CIThm@kernel.org>
On Mon, May 18, 2026 at 12:22:47AM -0700, Oliver Upton wrote:
> On Fri, May 15, 2026 at 08:59:02PM +0100, Leonardo Bras wrote:
> > Introduce S2 walker return values:
> > - SKIP_CHILDREN: skip walking the children of the current node
> > - SKIP_SIBLINGS: skip waling the siblings of the current node
> >
> > Also, modify __kvm_pgtable_visit() to fulfil the hing on above return
> > values. Current walkers should not be impacted
>
> I'd rather see something based around new walk flags than introducing an
> entirely new mechanic around return values.
>
> e.g. you could split the LEAF flag into separate flags for blocks v.
> pages:
>
> KVM_PGTABLE_WALK_PAGE,
> KVM_PGTABLE_WALK_BLOCK,
> KVM_PGTABLE_WALK_LEAF = KVM_PGTABLE_WALK_PAGE |
> KVM_PGTABLE_WALK_BLOCK,
>
> and then let __kvm_pgtable_visit() decide how to steer the walk. You may
> need some special handling to get the address arithmetic right when
> skipping over a table of page descriptors.
I was wondering along similar lines, but maybe it would be useful just
to pass a maximum level to the walker logic? That feels like the most
general case without complicating the existing logic.
Will
^ permalink raw reply
* Re: [PATCH v22 09/13] mfd: psci-mfd: Add psci-reboot-mode child cell
From: Bartosz Golaszewski @ 2026-05-18 8:53 UTC (permalink / raw)
To: Shivendra Pratap
Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
devicetree, Florian Fainelli, Krzysztof Kozlowski,
Dmitry Baryshkov, Mukesh Ojha, Andre Draszik, Greg Kroah-Hartman,
Kathiravan Thirumoorthy, Srinivas Kandagatla, Bartosz Golaszewski,
Sebastian Reichel, Mark Rutland, Lorenzo Pieralisi,
Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty,
Andy Yan, Matthias Brugger, John Stultz, Moritz Fischer,
Bartosz Golaszewski, Sudeep Holla
In-Reply-To: <20260514-arm-psci-system_reset2-vendor-reboots-v22-9-28a5bde07483@oss.qualcomm.com>
On Thu, 14 May 2026 16:25:50 +0200, Shivendra Pratap
<shivendra.pratap@oss.qualcomm.com> said:
> The PSCI "reboot-mode" node does not define a compatible because it is a
> configuration of boot-states provided by the underlying firmware. With
> the new firmware-node based cells in mfd-core, this node can now be
> exposed as a proper child cell.
>
> Add the psci-reboot-mode child cell to the psci-mfd driver and associate
> the reboot-mode node as its firmware-node.
>
> Suggested-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
> ---
> drivers/mfd/psci-mfd.c | 18 ++++++++++++++++++
> drivers/power/reset/Kconfig | 2 +-
> 2 files changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/psci-mfd.c b/drivers/mfd/psci-mfd.c
> index 7affd6bb09dd83452664edeccb09290fe4f43186..852d8e294a438d4ee6789c556d16a83739af2013 100644
> --- a/drivers/mfd/psci-mfd.c
> +++ b/drivers/mfd/psci-mfd.c
> @@ -7,11 +7,29 @@
> #include <linux/module.h>
> #include <linux/of.h>
> #include <linux/platform_device.h>
> +#include <linux/property.h>
> +
> +static struct fwnode_handle *psci_reboot_mode_get_child_fwnode(struct device *parent)
> +{
> + struct fwnode_handle *fwnode;
> +
> + fwnode = fwnode_get_named_child_node(dev_fwnode(parent), "reboot-mode");
> + if (!fwnode_device_is_available(fwnode)) {
> + fwnode_handle_put(fwnode);
> + fwnode = NULL;
> + }
> +
> + return fwnode;
Is this reference put anywhere once the cell is torn down?
Bart
> +}
>
> static const struct mfd_cell psci_cells[] = {
> {
> .name = "psci-cpuidle-domain",
> },
> + {
> + .name = "psci-reboot-mode",
> + .get_child_fwnode = psci_reboot_mode_get_child_fwnode,
> + },
> };
>
> static int psci_mfd_probe(struct platform_device *pdev)
> diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
> index d9d1f768b8691abc3b32f2675519f2ddbaf19b84..79559206d5852ae5eae4c8eb2c390901cea7531c 100644
> --- a/drivers/power/reset/Kconfig
> +++ b/drivers/power/reset/Kconfig
> @@ -350,7 +350,7 @@ config NVMEM_REBOOT_MODE
>
> config PSCI_REBOOT_MODE
> bool "PSCI reboot mode driver"
> - depends on OF && ARM_PSCI_FW
> + depends on OF && ARM_PSCI_FW && MFD_PSCI
> select REBOOT_MODE
> help
> Say y here will enable PSCI reboot mode driver. This gets
>
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH 3/5] arm_mpam: add MPAM-Fb MSC firmware access support
From: Niyas Sait @ 2026-05-18 8:52 UTC (permalink / raw)
To: andre.przywara
Cc: ben.horgan, catalin.marinas, fenghuay, guohanjun, james.morse,
jic23, lenb, linux-acpi, linux-arm-kernel, linux-kernel,
lpieralisi, rafael, reinette.chatre, sudeep.holla, will
In-Reply-To: <20260429141339.3171205-4-andre.przywara@arm.com>
Hi Andre,
On Wed, Apr 29, 2026 at 04:13:37PM +0200, Andre Przywara wrote:
> +#define SCMI_CHAN_FLAGS_OFS 0x10
> +#define SCMI_CHAN_FLAGS_IRQ BIT(0)
> +#define SCMI_MSG_LENGTH_OFS 0x14
> +#define SCMI_MSG_HEADER_OFS 0x18
> +#define SCMI_MSG_PAYLOAD_OFS 0x1c
I think this will not work for the ACPI PCC Type 3 MPAM Fb path.
SCMI shared memory transport layout and ACPI Extended PCC subspace
shared memory layout use different offsets for the flags, length, command,
and payload fields.
For Extended PCC subspace, the layout is:
Flags @ 0x04
Length @ 0x08
Command @ 0x0c
Payload @ 0x10
SCMI shared memory layout uses:
Flags @ 0x10
Length @ 0x14
Command @ 0x18
Payload @ 0x1c
You will need to use the extended PCC subspace layout for the ACPI path.
> +static int mpam_fb_wait_for_channel(struct pcc_mbox_chan *chan,
> + bool free)
> +{
> + u32 status = free ? SCMI_CHAN_STATUS_FREE_BIT : 0;
> + u32 val;
> +
> + /*
> + * The channel should really be free always at this point, as we take
> + * a lock for every read or write request. Check the free bit anyway,
> + * for good measure and to catch corner cases.
> + */
> + return readl_poll_timeout(chan->shmem + SCMI_CHAN_STATUS_OFS, val,
> + (val & SCMI_CHAN_STATUS_FREE_BIT) == status,
> + 1, 10000);
> +}
This also assumes SCMI channel status completion semantics in shared memory.
For PCC Type 3 transport, completion should follow PCC Type 3 completion mechanisms.
Thanks,
Niyas
^ permalink raw reply
* Re: [PATCH v22 08/13] mfd: core: Add firmware-node support to MFD cells
From: Bartosz Golaszewski @ 2026-05-18 8:57 UTC (permalink / raw)
To: Shivendra Pratap
Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
devicetree, Florian Fainelli, Krzysztof Kozlowski,
Dmitry Baryshkov, Mukesh Ojha, Andre Draszik, Greg Kroah-Hartman,
Kathiravan Thirumoorthy, Srinivas Kandagatla, Bartosz Golaszewski,
Sebastian Reichel, Mark Rutland, Lorenzo Pieralisi,
Rafael J. Wysocki, Daniel Lezcano, Christian Loehle, Ulf Hansson,
Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty,
Andy Yan, Matthias Brugger, John Stultz, Moritz Fischer,
Bartosz Golaszewski, Sudeep Holla
In-Reply-To: <20260514-arm-psci-system_reset2-vendor-reboots-v22-8-28a5bde07483@oss.qualcomm.com>
On Thu, 14 May 2026 16:25:49 +0200, Shivendra Pratap
<shivendra.pratap@oss.qualcomm.com> said:
> MFD core has no way to register a child device using an explicit firmware
> node. This prevents drivers from registering child nodes when those nodes
> do not define a compatible string. One such example is the PSCI
> "reboot-mode" node, which omits a compatible string as it describes
> boot-states provided by the underlying firmware.
>
> Extend struct mfd_cell with a callback that allows drivers to provide an
> explicit firmware node. The node is added to the MFD child device during
> registration when none is assigned by device tree, ACPI, or software
> matching.
>
> Suggested-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
> ---
> drivers/mfd/mfd-core.c | 30 ++++++++++++++++++++++++++++++
> include/linux/mfd/core.h | 14 ++++++++++++++
> 2 files changed, 44 insertions(+)
>
> diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
> index 7aa32b90cf1eb7fa0a05bf3dc506e60a262c9850..cc2a2a924d6d3044e29a9f864b536ee325ed797b 100644
> --- a/drivers/mfd/mfd-core.c
> +++ b/drivers/mfd/mfd-core.c
> @@ -10,6 +10,7 @@
> #include <linux/kernel.h>
> #include <linux/platform_device.h>
> #include <linux/acpi.h>
> +#include <linux/fwnode.h>
> #include <linux/list.h>
> #include <linux/property.h>
> #include <linux/mfd/core.h>
> @@ -148,6 +149,11 @@ static int mfd_match_of_node_to_dev(struct platform_device *pdev,
> return 0;
> }
>
> +static void mfd_child_fwnode_put(void *data)
> +{
> + fwnode_handle_put(data);
> +}
Ah, this seems to answer my previous question, but...
> +
> static int mfd_add_device(struct device *parent, int id,
> const struct mfd_cell *cell,
> struct resource *mem_base,
> @@ -156,6 +162,7 @@ static int mfd_add_device(struct device *parent, int id,
> struct resource *res;
> struct platform_device *pdev;
> struct mfd_of_node_entry *of_entry, *tmp;
> + struct fwnode_handle *fwnode;
> bool disabled = false;
> int ret = -ENOMEM;
> int platform_id;
> @@ -224,6 +231,29 @@ static int mfd_add_device(struct device *parent, int id,
>
> mfd_acpi_add_device(cell, pdev);
>
> + if (!pdev->dev.fwnode && cell->get_child_fwnode) {
> + fwnode = cell->get_child_fwnode(parent);
> + if (fwnode) {
> + device_set_node(&pdev->dev, fwnode);
> +
> + /*
> + * platform_device_release() drops only of_node refs.
Which is a separate problem we're discussing elsewhere. It should probably drop
the fwnode reference it holds, not the one of of_node.
> + * Track non-OF fwnodes explicitly so they are put on
> + * all teardown paths.
> + */
> + if (!to_of_node(fwnode)) {
> + ret = devm_add_action(&pdev->dev,
> + mfd_child_fwnode_put,
> + fwnode);
What if the device never gets bound to the driver? The release will never be
called, this is why it's wrong to schedule devres actions for unbound devices
and one of the reasons for patch 1 in this series.
What I suggest for now is: in tear-down path: see if the cell has the
get_child_fwnode() callback and - if so - drop the reference. Add a big, fat
comment saying that this must be removed if we decide to switch to dropping the
device's fwnode reference in platform driver core which may happen soon.
Bart
> + if (ret) {
> + device_set_node(&pdev->dev, NULL);
> + fwnode_handle_put(fwnode);
> + goto fail_of_entry;
> + }
> + }
> + }
> + }
> +
> if (cell->pdata_size) {
> ret = platform_device_add_data(pdev,
> cell->platform_data, cell->pdata_size);
> diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h
> index faeea7abd688f223fb0b31cde0a9b69dfe2a61ff..abfc26c057d6ee46947ba2b6f2e99f420e74b127 100644
> --- a/include/linux/mfd/core.h
> +++ b/include/linux/mfd/core.h
> @@ -50,6 +50,7 @@
> #define MFD_DEP_LEVEL_HIGH 1
>
> struct irq_domain;
> +struct fwnode_handle;
> struct software_node;
>
> /* Matches ACPI PNP id, either _HID or _CID, or ACPI _ADR */
> @@ -80,6 +81,19 @@ struct mfd_cell {
>
> /* Software node for the device. */
> const struct software_node *swnode;
> + /*
> + * Callback to return an explicit firmware node.
> + * @parent: MFD parent device passed to mfd_add_devices().
> + *
> + * Called only if OF/ACPI matching did not assign a fwnode.
> + * Ownership of the returned reference is transferred to MFD core.
> + *
> + * Return a referenced fwnode or NULL if none is available.
> + *
> + * mfd_cell must be zero-initialized or get_child_fwnode must be NULL
> + * when unused.
> + */
> + struct fwnode_handle *(*get_child_fwnode)(struct device *parent);
>
> /*
> * Device Tree compatible string
>
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH v22 07/13] power: reset: Add psci-reboot-mode driver
From: Bartosz Golaszewski @ 2026-05-18 8:58 UTC (permalink / raw)
To: Shivendra Pratap
Cc: linux-pm, linux-kernel, linux-arm-msm, linux-arm-kernel,
devicetree, Florian Fainelli, Krzysztof Kozlowski,
Dmitry Baryshkov, Mukesh Ojha, Andre Draszik, Greg Kroah-Hartman,
Kathiravan Thirumoorthy, Srinivas Kandagatla, Sebastian Reichel,
Mark Rutland, Lorenzo Pieralisi, Rafael J. Wysocki,
Daniel Lezcano, Christian Loehle, Ulf Hansson, Lee Jones,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio, Arnd Bergmann, Souvik Chakravarty, Andy Yan,
Matthias Brugger, John Stultz, Moritz Fischer,
Bartosz Golaszewski, Sudeep Holla
In-Reply-To: <20260514-arm-psci-system_reset2-vendor-reboots-v22-7-28a5bde07483@oss.qualcomm.com>
On Thu, 14 May 2026 16:25:48 +0200, Shivendra Pratap
<shivendra.pratap@oss.qualcomm.com> said:
> PSCI supports different types of resets like SYSTEM_RESET, SYSTEM_RESET2
> ARCH WARM reset and SYSTEM_RESET2 vendor-specific resets. Currently
> there is no common driver that handles all supported psci resets at one
> place. Additionally, there is no common mechanism to issue the supported
> psci resets from userspace.
>
> Add a psci-reboot-mode driver, and define two types of PSCI resets,
> predefined-resets and vendor-specific resets. Predefined-resets are
> defined by psci driver and vendor-specific resets are defined by SoC
> vendors, under the psci:reboot-mode node of SoC device tree.
>
> Register the driver with the reboot-mode framework to interface these
> resets to userspace. When userspace initiates a supported command, pass
> the reset arguments to the PSCI driver to enable command-based reset.
>
> This change allows userspace to issue supported PSCI reset commands
> using the standard reboot system calls while enabling SoC vendors to
> define their specific resets for PSCI.
>
> Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
> ---
> MAINTAINERS | 1 +
> drivers/power/reset/Kconfig | 10 +++++
> drivers/power/reset/Makefile | 1 +
> drivers/power/reset/psci-reboot-mode.c | 72 ++++++++++++++++++++++++++++++++++
> 4 files changed, 84 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 36ba42209c0b332813a296880cd55798a5592d2a..4b0815c31679550f5ab719de4a5852990c7cc643 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -21342,6 +21342,7 @@ S: Maintained
> F: Documentation/devicetree/bindings/arm/psci.yaml
> F: drivers/firmware/psci/
> F: drivers/mfd/psci-mfd.c
> +F: drivers/power/reset/psci-reboot-mode.c
> F: include/linux/psci.h
> F: include/uapi/linux/psci.h
>
> diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
> index 124afb99febe92450b6ae322aeed3b63fa2070df..d9d1f768b8691abc3b32f2675519f2ddbaf19b84 100644
> --- a/drivers/power/reset/Kconfig
> +++ b/drivers/power/reset/Kconfig
> @@ -348,6 +348,16 @@ config NVMEM_REBOOT_MODE
> then the bootloader can read it and take different
> action according to the mode.
>
> +config PSCI_REBOOT_MODE
> + bool "PSCI reboot mode driver"
> + depends on OF && ARM_PSCI_FW
Can you add COMPILE_TEST coverage here too please?
> + select REBOOT_MODE
> + help
> + Say y here will enable PSCI reboot mode driver. This gets
> + the PSCI reboot mode arguments and passes them to psci
> + driver. psci driver uses these arguments for issuing
> + device reset into different boot states.
> +
> config POWER_MLXBF
> tristate "Mellanox BlueField power handling driver"
> depends on (GPIO_MLXBF2 || GPIO_MLXBF3) && ACPI
> diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
> index d7ae97241a838fe1b536b2f911868e7590d12e3b..02948622fe3d00e165f941108ab92ecb66b0f0e8 100644
> --- a/drivers/power/reset/Makefile
> +++ b/drivers/power/reset/Makefile
> @@ -40,5 +40,6 @@ obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
> obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
> obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
> obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o
> +obj-$(CONFIG_PSCI_REBOOT_MODE) += psci-reboot-mode.o
> obj-$(CONFIG_POWER_MLXBF) += pwr-mlxbf.o
> obj-$(CONFIG_POWER_RESET_QEMU_VIRT_CTRL) += qemu-virt-ctrl.o
> diff --git a/drivers/power/reset/psci-reboot-mode.c b/drivers/power/reset/psci-reboot-mode.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..845b2a1816ce53451dea8dfc4bffffda0d3e9293
> --- /dev/null
> +++ b/drivers/power/reset/psci-reboot-mode.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/psci.h>
> +#include <linux/reboot-mode.h>
> +#include <linux/types.h>
> +
> +/*
> + * Predefined modes:
> + * reset_type = 0
> + * cookie stored in magic[63:32]
> + */
> +#define PSCI_PREDEF_MAGIC(cookie) ((cookie) * BIT_ULL(32))
> +
> +static const struct reboot_mode_entry psci_resets[] = {
> + {
> + .name = "psci-system-reset",
> + .magic = PSCI_PREDEF_MAGIC(PSCI_RESET_TYPE_SYSTEM_RESET),
> + },
> + {
> + .name = "psci-system-reset2-arch-warm-reset",
> + .magic = PSCI_PREDEF_MAGIC(PSCI_RESET_TYPE_SYSTEM_RESET2_ARCH_WARM),
> + },
> +};
> +
> +/*
> + * magic is a pre-encoded value:
> + * reset_type in low 32 bits
> + * cookie in high 32 bits
> + */
> +static int psci_reboot_mode_write(struct reboot_mode_driver *reboot, u64 magic)
> +{
> + psci_set_reset_cmd(magic);
> + return 0;
> +}
> +
> +static int psci_reboot_mode_probe(struct platform_device *pdev)
> +{
> + struct reboot_mode_driver *reboot;
> + size_t count;
> + int ret;
> +
> + reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL);
> + if (!reboot)
> + return -ENOMEM;
> +
> + reboot_mode_driver_init(reboot, &pdev->dev, psci_reboot_mode_write);
> +
> + /* Skip PSCI SYSTEM_RESET2 modes if unsupported */
> + count = psci_has_system_reset2_support() ? ARRAY_SIZE(psci_resets) : 1;
> + ret = reboot_mode_add_predefined_modes(reboot, psci_resets, count);
> + if (ret)
> + return ret;
> +
> + return devm_reboot_mode_register(&pdev->dev, reboot);
> +}
> +
> +static struct platform_driver psci_reboot_mode_driver = {
> + .probe = psci_reboot_mode_probe,
> + .driver = {
> + .name = "psci-reboot-mode",
> + },
> +};
> +
You can drop the newline here.
> +module_platform_driver(psci_reboot_mode_driver);
> +
> +MODULE_LICENSE("GPL");
>
> --
> 2.34.1
>
>
With that:
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v2 5/6] devfreq: Get and put module refcount when switching governor
From: Yaxiong Tian @ 2026-05-18 9:00 UTC (permalink / raw)
To: Jie Zhan, cwchoi00, cw00.choi, myungjoo.ham, kyungmin.park,
linux-pm, linux-arm-kernel
Cc: linuxarm, zhenglifeng1, zhangpengjie2, lihuisong, prime.zeng
In-Reply-To: <66a14821-fd14-440e-b75f-98bc37eedba7@hisilicon.com>
在 2026/5/18 16:39, Jie Zhan 写道:
>
> On 5/15/2026 6:39 PM, Jie Zhan wrote:
>>
>> On 5/14/2026 2:50 PM, Yaxiong Tian wrote:
>>> 在 2026/5/13 17:38, Jie Zhan 写道:
>>>> A governor module can be dynamically inserted or removed if compiled as a
>>>> kernel module. 'devfreq->governor' would become NULL if the governor
>>>> module is removed when it's in use.
>>>>
>>>> To prevent the governor module from being removed (except for force
>>>> unload) when it's in use, get and put a refcount of the governor module
>>>> when starting and stopping the governor.
>>>>
>>>> As a result, unloading a governor module in use returns an error, e.g.:
>>>> $ cat governor
>>>> performance
>>>> $ rmmod governor_performance
>>>> rmmod: ERROR: Module governor_performance is in use
>>>>
>>>> Signed-off-by: Jie Zhan <zhanjie9@hisilicon.com>
>>>> ---
>>>> drivers/devfreq/devfreq.c | 17 ++++++++++++++++-
>>>> 1 file changed, 16 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
>>>> index e1363ab69173..2ea42325d030 100644
>>>> --- a/drivers/devfreq/devfreq.c
>>>> +++ b/drivers/devfreq/devfreq.c
>>>> @@ -345,24 +345,37 @@ static int devfreq_set_governor(struct devfreq *df,
>>>> __func__, df->governor->name, ret);
>>>> return ret;
>>>> }
>>>> + module_put(old_gov->owner);
>>>> }
>>>> /* Start the new governor */
>>>> + if (!try_module_get(new_gov->owner)) {
>>>> + df->governor = NULL;
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>> Here, new_gov has already been checked in try_then_request_governor() for whether the module can be obtained.
>>>
>>> I think it might be more reasonable to merge try_then_request_governor() and devfreq_set_governor(), so that when new_gov cannot be obtained, the operation of the old governor is not affected.
>>>
>> Well yeah, indeed! I'll take a look at how to implement this.
>> Thanks for the suggestion.
>>
>> Jie
> Hi Yaxiong,
>
> A follow-up after digging deeper.
>
> 1. To be clear, the try_module_get() / module_put() pairs in
> try_then_request_governor() and devfreq_set_governor() protect different
> ranges. Assuming no FORCE_UNLOAD, the former protects the governor module
> from the time it's queried until it's set as the working governor, while
> the latter protects when the governor is in use.
>
> However, I realized the former protection is no longer needed since I
> removed the governor list lock in v2. devfreq_remove_governor() and the
> 'request and set' governor sequence are already synchronized by
> 'devfreq_list_lock'. Thus, patch 6 should be dropped.
>
> 2. devfreq_add_device() needs error prints because it's a probe path.
> governor_store() doesn't want error prints since it's not a good practice
> to print on a sysfs read/write. Merging the two functions will violate
> either of them.
>
> Thanks,
> Jie
Okay, please ignore my suggestion.
>>>> df->governor = new_gov;
>>>> ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
>>>> if (ret) {
>>>> dev_warn(dev, "%s: Governor %s not started(%d)\n",
>>>> __func__, df->governor->name, ret);
>>>> + module_put(new_gov->owner);
>>>> /* Restore previous governor */
>>>> df->governor = old_gov;
>>>> if (!df->governor)
>>>> return ret;
>>>> + if (!try_module_get(old_gov->owner)) {
>>>> + df->governor = NULL;
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
>>>> if (ret) {
>>>> dev_err(dev, "%s: restore Governor %s failed (%d)\n",
>>>> __func__, df->governor->name, ret);
>>>> + module_put(old_gov->owner);
>>>> df->governor = NULL;
>>>> return ret;
>>>> }
>>>> @@ -1040,9 +1053,11 @@ int devfreq_remove_device(struct devfreq *devfreq)
>>>> devfreq_cooling_unregister(devfreq->cdev);
>>>> - if (devfreq->governor)
>>>> + if (devfreq->governor) {
>>>> devfreq->governor->event_handler(devfreq,
>>>> DEVFREQ_GOV_STOP, NULL);
>>>> + module_put(devfreq->governor->owner);
>>>> + }
>>>> device_unregister(&devfreq->dev);
>>>> return 0;
^ permalink raw reply
* RE: [PATCH] ARM: dts: aspeed: anacapa: name EDSFF and thermtrip SGPIO lines
From: Fu, Rex @ 2026-05-18 9:01 UTC (permalink / raw)
To: Andrew Jeffery, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Joel Stanley
Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org
In-Reply-To: <bae3a186da639118d88ad2632c5edf8963946dc1.camel@codeconstruct.com.au>
AMD General
Hi Andrew,
Thanks for the review.
I agree that the original commit message was too vague, especially since these GPIO line names are visible to userspace.
There is no new board revision or underlying hardware change involved. This is a naming correction for the existing Anacapa hardware design.
I will remove the "legacy or unused" wording. The previous names do not match the actual Anacapa usage: some lines were named as CPU-related signals but are used for EDSFF power-good monitoring, and the thermtrip lines used raw active-low signal names while userspace monitors the asserted condition.
Changing these names is appropriate because the userspace monitoring configuration is based on the platform signal names. Keeping the old names would make the DTS inconsistent with the hardware design and userspace configuration.
I will send a v2 with the exact old-to-new mappings and a more precise explanation in the commit message.
Best regards,
Rex Fu
BMC Engineer | AMD
Data Center Platform Engineering Group
O +886 (2) 2655-8885
----------------------------------------------------------------------------------------------------------------------------------
3F, No. 3-2 Yuanqu Street, Nangang District, Taipei 115, Taiwan
LinkedIn | Instagram | X | amd.com
-----Original Message-----
From: Andrew Jeffery <andrew@codeconstruct.com.au>
Sent: Monday, May 18, 2026 2:48 PM
To: Fu, Rex <Rex.Fu@amd.com>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; Joel Stanley <joel@jms.id.au>
Cc: devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-aspeed@lists.ozlabs.org; linux-kernel@vger.kernel.org
Subject: Re: [PATCH] ARM: dts: aspeed: anacapa: name EDSFF and thermtrip SGPIO lines
[You don't often get email from andrew@codeconstruct.com.au. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
Hello Rex,
On Thu, 2026-04-30 at 13:44 +0800, Rex Fu via B4 Relay wrote:
> From: Rex Fu <Rex.Fu@amd.com>
>
> Name the Anacapa SGPIO lines used for EDSFF power-good and thermtrip
> assertion signals.
>
> The affected lines replace legacy
>
Which are legacy?
> or unused
>
Which are unused?
> CPU-related names with the
> platform signal names used by userspace monitoring.
This is the kind of change that has the potential to break old userspace. Why is it appropriate? I'd like a more precise discussion in the commit message.
Was there some other underlying change (e.g. a new revision of the platform design)?
Andrew
>
> Signed-off-by: Rex Fu <Rex.Fu@amd.com>
> ---
> arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts | 10
> +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts
> b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts
> index 2cb7bd128d24..fe960bb7bc27 100644
> --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts
> +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-anacapa.dts
> @@ -912,7 +912,7 @@ &sgpiom0 {
> "PWRGD_PVDDIO_P0", "",
> "PWRGD_PVDDIO_MEM_S3_P0", "",
> "PWRGD_CHMP_CPU0_FPGA", "",
> - "PWRGD_CHIL_CPU0_FPGA", "",
> + "HPM_EDSFF_PG", "",
> "PWRGD_CHEH_CPU0_FPGA", "",
> "PWRGD_CHAD_CPU0_FPGA", "FM_BMC_READY_PLD",
> "", "",
> @@ -957,8 +957,8 @@ &sgpiom0 {
> "PDB_ALERT_R_N", "",
>
> /* L0-L7 line 176-191 */
> - "CPU0_SP7R1", "", "CPU0_SP7R2", "",
> - "CPU0_SP7R3", "", "CPU0_SP7R4", "",
> + "L_EDSFF2_PG", "", "L_EDSFF3_PG", "",
> + "R_EDSFF2_PG", "", "R_EDSFF3_PG", "",
> "CPU0_CORETYPE0", "", "CPU0_CORETYPE1", "",
> "CPU0_CORETYPE2", "", "FM_BIOS_POST_CMPLT_R_N", "",
>
> @@ -984,8 +984,8 @@ &sgpiom0 {
> "HPM_PWR_FAIL", "Port80_b0",
> "FM_DIMM_IP_FAIL", "Port80_b1",
> "FM_DIMM_AH_FAIL", "Port80_b2",
> - "HPM_AMC_THERMTRIP_R_L", "Port80_b3",
> - "FM_CPU0_THERMTRIP_N", "Port80_b4",
> + "AMC_THERMTRIP_ASSERT", "Port80_b3",
> + "CPU_THERMTRIP_ASSERT", "Port80_b4",
> "PVDDCR_SOC_P0_OCP_L", "Port80_b5",
> "CPLD_SGPIO_RDY", "Port80_b6",
> "", "Port80_b7",
>
> ---
> base-commit: 9974969c14031a097d6b45bcb7a06bb4aa525c40
> change-id: 20260430-anacapa-sgpio-edsff-thermtrip-acb228bf61be
>
> Best regards,
> --
> Rex Fu <Rex.Fu@amd.com>
>
^ permalink raw reply
* Re: [PATCH v6 16/22] drm: bridge: dw_hdmi: Use display_info is_hdmi and has_audio
From: Jani Nikula @ 2026-05-18 9:02 UTC (permalink / raw)
To: Jonas Karlman, Andrzej Hajda, Neil Armstrong, Robert Foss,
Heiko Stuebner, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Luca Ceresoli, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Cc: Liu Ying, Sandy Huang, Andy Yan, Chen-Yu Tsai, Christian Hewitt,
Diederik de Haas, Nicolas Frattaroli, Dmitry Baryshkov, dri-devel,
linux-arm-kernel, linux-rockchip, linux-amlogic, linux-sunxi, imx,
linux-kernel
In-Reply-To: <20260516183838.2024991-17-jonas@kwiboo.se>
On Sat, 16 May 2026, Jonas Karlman <jonas@kwiboo.se> wrote:
> - /*
> - * FIXME: This should use connector->display_info.is_hdmi and
> - * connector->display_info.has_audio from a path that has read the EDID
> - * and called drm_edid_connector_update().
> - */
> - edid = drm_edid_raw(drm_edid);
> -
> - dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
> - edid->width_cm, edid->height_cm);
> -
> - hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> - hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
Yay! \o/
>
> return drm_edid;
> }
--
Jani Nikula, Intel
^ 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