@samitouri / QOSamiQemu / commits / bec5e7c71c

system/memory: Use qemu_ram_move() for directly accessible regions

All ram device regions were turned to be indirectly accessible by commit 4a2e242bbb ("memory: Don't use memcpy for ram_device regions"). This leads to guest hang on attempt to build 'cuda-samples' as reported by Julia. The guest is started by the following command lines, with GH100 GPU card passed from the host. host$ lspci | grep GH100 0009:01:00.0 3D controller: NVIDIA Corporation GH100 [GH200 120GB / 480GB] (rev a1) host$ /home/sandbox/gavin/qemu.main/build/qemu-system-aarch64 \ -machine virt,gic-version=host,ras=on,highmem-mmio-size=4T \ -accel kvm -cpu host -smp cpus=48 -m size=8G \ -drive file=/home/gavin/sandbox/images/disk.qcow2,if=none,id=d0 \ -device virtio-blk-pci,id=vb0,bus=pcie.0,drive=d0,num-queues=4 \ -device vfio-pci-nohotplug,host=0009:01:00.0,bus=pcie.1.0 : guest$ cd cuda-samples/build guest$ make -j 20 clean guest$ make -j 20 : [ 54%] Linking CUDA executable graphMemoryNodes [ 54%] Built target graphMemoryNodes <no more output afterwards, guest becomes frozen here> guest$ qemu-system-aarch64: virtio: bogus descriptor or out of resources [ 555.814025] virtio_blk virtio0: [vda] new size: 268435456 512-byte logical blocks (137 GB/128 GiB) When the GPU's driver (NVidia open driver) is loaded on guest bootup, the memory blocks residing in the PCI BAR#4 of the GH100 GPU card can be presented to the guest through memory hot-add. The page cache can then be allocated from the hot added memory blocks when cuda-samples is being built. Afterwards, the page cache is sent to QEMU's virtio-blk device as part of the DMA request, the bounce buffer has to be used to accomodate the request as the corresponding memory region (MemoryRegion) is an indirectly accessible ram device region in qemu. However, the max bounce bufer size is only 4096 bytes by default and that is exhausted quickly, leading to a reset on the virtio-blk device and frozen guest eventually. QEMU ==== virtio_blk_handle_output virtio_blk_handle_vq virtio_blk_get_request virtqueue_pop virtqueue_split_pop virtqueue_map_desc address_space_map memory_access_is_direct # Return false memory_region_supports_direct_access (qemu) info mtree memory-region: pci_bridge_pci 0000000000000000-ffffffffffffffff (prio 0, container): pci_bridge_pci 0000042000000000-0000043fffffffff (prio 1, i/o): 0009:01:00.0 base BAR 4 0000042000000000-0000043fffffffff (prio 0, i/o): 0009:01:00.0 BAR 4 0000042000000000-000004379fffffff (prio 0, ramd): 0009:01:00.0 BAR 4 mmaps[0] This adds qemu_ram_move() where the aligned and small-sized accesses are handled by qatomics, and fall back to memmove() otherwise. The memove() for the directly accessible regions is replaced by qemu_ram_move() so that the issue covered by commit 4a2e242bbb (MMIO access instructions were optimized to SSE instructions) is fixed. This makes 'ram_device_mem_ops' redundant, paving the way to revert that commit to make the ram device region directly accessible again in the next patch. Besides, this also fixes the issue of the unexpected frozen reception on e1000 NIC in the scenario of DPDK due to the wrong Rx queue full indication caused by the following memcpy(), which is turned to 3 consective 'strb' instructions to the same location by glibc-2.24+ for aarch64. With this applied, the syntax of one-byte store is strictly ensured by a one-byte qatomic set. QEMU ==== e1000_receive_iov pci_dma_write pci_dma_rw dma_memory_rw dma_memory_rw_relaxed address_space_rw address_space_write flatview_write flatview_write_continue flatview_write_continue_step memcpy # 3 consective 'strb' instructions Reported-by: Julia Graham <jugraham@redhat.com> Reported-by: Liu Gang <liugang24219@sangfor.com.cn> Reported-by: Ding Hui <dinghui@sangfor.com.cn> Suggested-by: Michael S. Tsirkin <mst@redhat.com> Suggested-by: Peter Xu <peterx@redhat.com> Suggested-by: Richard Henderson <richard.henderson@linaro.org> Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Gavin Shan <gshan@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Link: https://lore.kernel.org/r/20260728031731.286666-3-gshan@redhat.com [peterx: remove src==dst check, fix doc, enhance comments, per PeterM, add R-b] Signed-off-by: Peter Xu <peterx@redhat.com>

Gavin Shan committed Jul 28, 2026 at 13:17 UTC bec5e7c71cc9770a900dac6930d076fe7ee9e57b
3 files changed +82 -5
hw/remote/vfio-user-obj.c
+2 -2
@@ -375,9 +375,9 @@ static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset,
375 ram_ptr = memory_region_get_ram_ptr(mr);
376
377 if (is_write) {
378 - memmove((ram_ptr + offset), buf, size);
378 + qemu_ram_move((ram_ptr + offset), buf, size);
379 } else {
380 - memmove(buf, (ram_ptr + offset), size);
380 + qemu_ram_move(buf, (ram_ptr + offset), size);
381 }
382
383 return 0;
include/system/memory.h
+34 -1
@@ -2668,6 +2668,39 @@ void address_space_register_map_client(AddressSpace *as, QEMUBH *bh);
2668 void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh);
2669
2670 /* Internal functions, part of the implementation of address_space_read. */
2671 +
2672 +/**
2673 + * qemu_ram_move: move data from or to ramblock
2674 + *
2675 + * @dst: destination where the data is moved to
2676 + * @src: source where the data is moved from
2677 + * @n: length of data to be moved
2678 + *
2679 + * Move @n bytes from @src to @dst, the memory areas may overlap. This
2680 + * provides the same semantics as memmove(), plus an additional stronger
2681 + * guarantee: if @n is 1, 2 or 4 or 8 bytes, and @src and @dst are both
2682 + * naturally aligned for that access size, then both the load and the store
2683 + * will be done as a single atomic access (with the semantics of
2684 + * qatomic_read() and qatomic_set()).
2685 + *
2686 + * This is the underlying function that we use to implement accesses by
2687 + * a guest vCPU or a device DMA operation to a ram block. The atomic
2688 + * guarantee is needed for two major cases: (A) When the ram block is
2689 + * backed by a PCI BAR passed through from a host device (and so it might
2690 + * be hardware registers that must be accessed exactly once at the right
2691 + * width); (B) When an emulated device updates a data structure shared in
2692 + * guest memory with guest software (e.g. a network device's set of tx and
2693 + * rx descriptor blocks), if a write to memory is accidentally performed
2694 + * multiple times then it can break the guest code when it busy polls the
2695 + * guest memory.
2696 + *
2697 + * We don't attempt to perform the exact access when it would be unaligned
2698 + * because this can't be done on all host architectures. Although this is
2699 + * strictly speaking not doing what would happen on real hardware, we don't
2700 + * think there are going to be situations where that matters in practice.
2701 + */
2702 +void qemu_ram_move(void *dst, const void *src, size_t n);
2703 +
2704 MemTxResult address_space_read_full(const AddressSpace *as, hwaddr addr,
2705 MemTxAttrs attrs, void *buf, hwaddr len);
2706 MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr,
@@ -2741,7 +2774,7 @@ MemTxResult address_space_read(const AddressSpace *as, hwaddr addr,
2774 mr = flatview_translate(fv, addr, &addr1, &l, false, attrs);
2775 if (len == l && memory_access_is_direct(mr, false, attrs)) {
2776 ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
2744 - memmove(buf, ptr, len);
2777 + qemu_ram_move(buf, ptr, len);
2778 } else {
2779 result = flatview_read_continue(fv, addr, attrs, buf, len,
2780 addr1, l, mr);
system/physmem.c
+46 -2
@@ -3158,6 +3158,50 @@ void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size)
3158 invalidate_and_set_dirty(mr, addr, size);
3159 }
3160
3161 +void qemu_ram_move(void *dst, const void *src, size_t n)
3162 +{
3163 + uintptr_t test, len;
3164 +
3165 + if (n == 0) {
3166 + return;
3167 + }
3168 +
3169 + /*
3170 + * Calculate "the lowest set bit" over @src, @dst and @n, result put
3171 + * into @len (which guarantees a power-of-two). With that and the
3172 + * later check (len!=n), it makes sure that we will only do the atomic
3173 + * ops when:
3174 + *
3175 + * (1) @n is a power-of-two
3176 + * (2) @src and @dst addresses are both aligned to @n
3177 + */
3178 + test = (uintptr_t)src | (uintptr_t)dst | n;
3179 + len = test & -test;
3180 +
3181 + /* Overlapping buffers, unaligned or oversized access */
3182 + if (n > 8 || len != n) {
3183 + memmove(dst, src, n);
3184 + return;
3185 + }
3186 +
3187 + switch (len) {
3188 + case 1:
3189 + qatomic_set((uint8_t *)dst, qatomic_read((uint8_t *)src));
3190 + break;
3191 + case 2:
3192 + qatomic_set((uint16_t *)dst, qatomic_read((uint16_t *)src));
3193 + break;
3194 + case 4:
3195 + qatomic_set((uint32_t *)dst, qatomic_read((uint32_t *)src));
3196 + break;
3197 + case 8:
3198 + qatomic_set((uint64_t *)dst, qatomic_read((uint64_t *)src));
3199 + break;
3200 + default:
3201 + g_assert_not_reached();
3202 + }
3203 +}
3204 +
3205 int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr)
3206 {
3207 unsigned access_size_max = mr->ops->valid.max_access_size;
@@ -3270,7 +3314,7 @@ static MemTxResult flatview_write_continue_step(MemTxAttrs attrs,
3314 uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l,
3315 false, true);
3316
3273 - memmove(ram_ptr, buf, *l);
3317 + qemu_ram_move(ram_ptr, buf, *l);
3318 invalidate_and_set_dirty(mr, mr_addr, *l);
3319
3320 return MEMTX_OK;
@@ -3363,7 +3407,7 @@ static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf,
3407 uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l,
3408 false, false);
3409
3366 - memmove(buf, ram_ptr, *l);
3410 + qemu_ram_move(buf, ram_ptr, *l);
3411
3412 return MEMTX_OK;
3413 }