All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chen Pei <cp0613@linux.alibaba.com>
To: pbonzini@redhat.com, palmer@dabbelt.com,
	alistair.francis@wdc.com, liwei1518@gmail.com,
	daniel.barboza@oss.qualcomm.com, zhiwei_liu@linux.alibaba.com,
	chao.liu.zevorn@gmail.com, sunilvl@ventanamicro.com,
	jonathan.cameron@huawei.com, fan.ni@samsung.com,
	guoren@kernel.org
Cc: qemu-riscv@nongnu.org, qemu-devel@nongnu.org,
	Chen Pei <cp0613@linux.alibaba.com>
Subject: [PATCH 4/4] hw/cxl: Map committed HDM decoder ranges as RAM for direct DMA
Date: Tue,  2 Jun 2026 15:41:27 +0800	[thread overview]
Message-ID: <20260602074127.63819-5-cp0613@linux.alibaba.com> (raw)
In-Reply-To: <20260602074127.63819-1-cp0613@linux.alibaba.com>

The CXL Fixed Memory Window (CFMW) is registered with
memory_region_init_io() and has no backing ram_block.  As a result
address_space_map() on a guest physical address inside the CFMW
takes the bounce-buffer path and is bounded by
DEFAULT_MAX_BOUNCE_BUFFER_SIZE (4 KiB by default for the system
AddressSpace).  Once a Type-3 device is brought online as system RAM
(daxctl online-memory), the kernel happily allocates DMA buffers from
the CFMW range and any virtio operation whose scatter list exceeds
4 KiB or that overlaps with another in-flight transfer fails with:

    qemu-system-riscv64: virtio: bogus descriptor or out of resources

The bug is not RISC-V specific: CFMW registration and the bounce
buffer limit are both arch-agnostic, so any guest that onlines CXL
memory and issues DMA larger than 4 KiB into it is affected.  It
shows up first on RISC-V virt because that is where the rest of
this series enables the daxctl + virtio path end-to-end.

Reproduce on RISC-V virt with cxl=on and a single Type-3 device:

    cxl create-region -m -t ram -d decoder0.0 -w 1 mem0 -s 4G
    daxctl online-memory dax0.0
    free -h    # triggers the error and stalls the guest

Fix it by overlaying a RAM alias of the device's memory backend
(hostvmem / hostpmem) at the committed HDM decoder's HPA range, with
higher priority than the CFMW I/O region.  flatview_translate() then
hits the alias, address_space_map() returns a direct host pointer,
and DMA proceeds without bouncing.  This mirrors the existing QEMU
pattern of PCI BAR and IOMMU MR overlays.  The alias is torn down on
hdm_decoder_uncommit() so subsequent region tear-down + re-creation
works.

Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
 hw/mem/cxl_type3.c          | 81 +++++++++++++++++++++++++++++++++++++
 include/hw/cxl/cxl_device.h |  4 ++
 2 files changed, 85 insertions(+)

diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index 4739239da3..f962bce66a 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -24,6 +24,7 @@
 #include "qemu/module.h"
 #include "qemu/pmem.h"
 #include "qemu/range.h"
+#include "system/address-spaces.h"
 #include "qemu/rcu.h"
 #include "qemu/guest-random.h"
 #include "system/hostmem.h"
@@ -420,6 +421,11 @@ static void hdm_decoder_commit(CXLType3Dev *ct3d, int which)
     ComponentRegisters *cregs = &ct3d->cxl_cstate.crb;
     uint32_t *cache_mem = cregs->cache_mem_registers;
     uint32_t ctrl;
+    uint32_t low, high;
+    uint64_t decoder_base, decoder_size;
+    MemoryRegion *mr = NULL;
+    uint64_t dpa_offset = 0;
+    char *alias_name;
 
     ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + which * hdm_inc);
     /* TODO: Sanity checks that the decoder is possible */
@@ -427,6 +433,73 @@ static void hdm_decoder_commit(CXLType3Dev *ct3d, int which)
     ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED, 1);
 
     stl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + which * hdm_inc, ctrl);
+
+    /*
+     * Create a RAM alias in system memory for the committed decoder range.
+     * This enables direct DMA mapping (address_space_map) for devices like
+     * virtio that need to DMA to/from CXL memory.  Without this, the CFMW
+     * I/O region would require bounce buffering which is limited to 4KB.
+     */
+    low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_LO + which * hdm_inc);
+    high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_HI + which * hdm_inc);
+    decoder_base = ((uint64_t)high << 32) | (low & 0xf0000000);
+
+    low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_LO + which * hdm_inc);
+    high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_HI + which * hdm_inc);
+    decoder_size = ((uint64_t)high << 32) | (low & 0xf0000000);
+
+    if (!decoder_base || !decoder_size) {
+        return;
+    }
+
+    /* Calculate DPA offset by summing sizes of preceding decoders */
+    for (int i = 0; i < which; i++) {
+        uint32_t prev_low, prev_high;
+        uint64_t prev_size;
+        uint32_t prev_ctrl;
+
+        prev_ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL +
+                             i * hdm_inc);
+        if (!FIELD_EX32(prev_ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED)) {
+            continue;
+        }
+        prev_low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_LO +
+                            i * hdm_inc);
+        prev_high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_HI +
+                             i * hdm_inc);
+        prev_size = ((uint64_t)prev_high << 32) | (prev_low & 0xf0000000);
+        dpa_offset += prev_size;
+    }
+
+    /* Determine which memory backend to alias */
+    if (ct3d->hostvmem) {
+        MemoryRegion *vmr = host_memory_backend_get_memory(ct3d->hostvmem);
+        uint64_t vmr_size = memory_region_size(vmr);
+
+        if (dpa_offset < vmr_size) {
+            mr = vmr;
+        }
+    }
+    if (!mr && ct3d->hostpmem) {
+        MemoryRegion *pmr = host_memory_backend_get_memory(ct3d->hostpmem);
+        uint64_t vmr_size = ct3d->hostvmem ?
+            memory_region_size(
+                host_memory_backend_get_memory(ct3d->hostvmem)) : 0;
+        mr = pmr;
+        dpa_offset -= vmr_size;
+    }
+
+    if (!mr) {
+        return;
+    }
+
+    alias_name = g_strdup_printf("cxl-hdm%d-ram-alias", which);
+    memory_region_init_alias(&ct3d->hdm_ram_alias[which], OBJECT(ct3d),
+                             alias_name, mr, dpa_offset, decoder_size);
+    memory_region_add_subregion_overlap(get_system_memory(), decoder_base,
+                                        &ct3d->hdm_ram_alias[which], 1);
+    ct3d->hdm_ram_alias_valid[which] = true;
+    g_free(alias_name);
 }
 
 static void hdm_decoder_uncommit(CXLType3Dev *ct3d, int which)
@@ -442,6 +515,14 @@ static void hdm_decoder_uncommit(CXLType3Dev *ct3d, int which)
     ctrl = FIELD_DP32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED, 0);
 
     stl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + which * hdm_inc, ctrl);
+
+    /* Remove the RAM alias if it was added during commit */
+    if (ct3d->hdm_ram_alias_valid[which]) {
+        memory_region_del_subregion(get_system_memory(),
+                                    &ct3d->hdm_ram_alias[which]);
+        object_unparent(OBJECT(&ct3d->hdm_ram_alias[which]));
+        ct3d->hdm_ram_alias_valid[which] = false;
+    }
 }
 
 static int ct3d_qmp_uncor_err_to_cxl(CxlUncorErrorType qmp_err)
diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h
index 393f312217..07deef2e2c 100644
--- a/include/hw/cxl/cxl_device.h
+++ b/include/hw/cxl/cxl_device.h
@@ -714,6 +714,10 @@ struct CXLType3Dev {
     /* State */
     AddressSpace hostvmem_as;
     AddressSpace hostpmem_as;
+
+    /* RAM aliases for HDM decoders - enables direct DMA mapping */
+    MemoryRegion hdm_ram_alias[CXL_HDM_DECODER_COUNT];
+    bool hdm_ram_alias_valid[CXL_HDM_DECODER_COUNT];
     CXLComponentState cxl_cstate;
     CXLDeviceState cxl_dstate;
     CXLCCI cci; /* Primary PCI mailbox CCI */
-- 
2.50.1



  parent reply	other threads:[~2026-06-02  7:49 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-02  7:41 [PATCH 0/4] hw/riscv/virt: Add CXL support and fix virtio DMA into CXL memory Chen Pei
2026-06-02  7:41 ` [PATCH 1/4] hw/riscv/virt: Add CXL support to the RISC-V virt machine Chen Pei
2026-06-09 12:41   ` Jonathan Cameron
2026-06-10 12:46     ` Chen Pei
2026-06-02  7:41 ` [PATCH 2/4] hw/riscv/virt-acpi-build: Add _DEP to ACPI0017 for CXL host bridge dependency Chen Pei
2026-06-09 12:47   ` Jonathan Cameron
2026-06-09 12:56     ` Peter Maydell
2026-06-10 12:49     ` Chen Pei
2026-06-09 15:08   ` Sunil V L
2026-06-02  7:41 ` [PATCH 3/4] hw/riscv/virt, gpex: Provide 32-bit MMIO window for CXL host bridges Chen Pei
2026-06-09 12:56   ` Jonathan Cameron
2026-06-10 12:58     ` Chen Pei
2026-06-02  7:41 ` Chen Pei [this message]
2026-06-02  8:04   ` [PATCH 4/4] hw/cxl: Map committed HDM decoder ranges as RAM for direct DMA Chen Pei
2026-06-09 12:36   ` Jonathan Cameron
2026-06-10 13:03     ` Chen Pei

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260602074127.63819-5-cp0613@linux.alibaba.com \
    --to=cp0613@linux.alibaba.com \
    --cc=alistair.francis@wdc.com \
    --cc=chao.liu.zevorn@gmail.com \
    --cc=daniel.barboza@oss.qualcomm.com \
    --cc=fan.ni@samsung.com \
    --cc=guoren@kernel.org \
    --cc=jonathan.cameron@huawei.com \
    --cc=liwei1518@gmail.com \
    --cc=palmer@dabbelt.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-riscv@nongnu.org \
    --cc=sunilvl@ventanamicro.com \
    --cc=zhiwei_liu@linux.alibaba.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.