On Tue, Jul 28, 2026 at 01:17:28PM +1000, Gavin Shan wrote:
> All ram device regions was turned to be indirectly accessible by commit
> 4a2e242bbb ("memory: Don't use memcpy for ram_device regions"). This leads
> to a frozen guest where a NVidia GH100 GPU is passed from host. The memory
> in its PCI BAR#4 can be allocated as DMA target buffer. qemu has to take
> DMA bounce buffer in address_space_map() to cover the DMA request. However,
> the bounce buffer size is 4096 bytes only and it's exhaused very quickly
> when the guest has significant disk activities on compiling 'cuda-samples'.
> The full log and problem description can be found from PATCH[2/3]'s commit
> log.
> 
> Fix the issue handled in commit 4a2e242bbb by replacing memmove() with newly
> added qemu_ram_move() where the aligned and small-sized accesses are handled
> by qatomics, and fall back to memmove() otherwise, for the directly accessible
> regions. With this, we can revert 4a2e242bbb to make ram device region 
> directly
> accessible again and bypass the bounce buffer in address_space_map() where the
> guest hang happens.
> 
> PATCH[1] replaces memcpy() with memomve() for directly accessible regions
> PATCH[2] uses qemu_ram_move() for directly accessible regions
> PATCH[3] makes ram device region directly accessible again

Queued for 11.2, with fixups suggested by PeterM in v4 discussions:

https://lore.kernel.org/qemu-devel/CAFEAcA-y+vNK2u-rSq+SVjNZrhO2=BsUysEw2PtCdCZ=qyx...@mail.gmail.com/

Fixup:

diff --git a/include/system/memory.h b/include/system/memory.h
index d5fca96cea..16bf04ef07 100644
--- a/include/system/memory.h
+++ b/include/system/memory.h
@@ -2677,9 +2677,9 @@ void address_space_unregister_map_client(AddressSpace 
*as, QEMUBH *bh);
  * Move @n bytes from @src to @dst, the memory areas may overlap. This
  * provides the same semantics as memmove(), plus an additional stronger
  * guarantee: if @n is 1, 2 or 4 or 8 bytes, and @src and @dst are both
- * naturally aligned for that access size, and the memory areas do not
- * overlap, then both the load and the store will be done as a single
- * atomic access (with the semantics of qatomic_read() and qatomic_set()).
+ * naturally aligned for that access size, then both the load and the store
+ * will be done as a single atomic access (with the semantics of
+ * qatomic_read() and qatomic_set()).
  *
  * This is the underlying function that we use to implement accesses by
  * a guest vCPU or a device DMA operation to a ram block. The atomic
diff --git a/system/physmem.c b/system/physmem.c
index fbe7df2391..2f37cbeb07 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -3162,13 +3162,18 @@ void qemu_ram_move(void *dst, const void *src, size_t n)
 {
     uintptr_t test, len;
 
-    if (src == dst || n == 0) {
+    if (n == 0) {
         return;
     }
 
     /*
-     * Maximal length of aligned access that are determined by @src,
-     * @dst and @n
+     * Calculate "the lowest set bit" over @src, @dst and @n, result put
+     * into @len (which guarantees a power-of-two).  With that and the
+     * later check (len!=n), it makes sure that we will only do the atomic
+     * ops when:
+     *
+     * (1) @n is a power-of-two
+     * (2) @src and @dst addresses are both aligned to @n
      */
     test = (uintptr_t)src | (uintptr_t)dst | n;
     len = test & -test;

-- 
Peter Xu


Reply via email to