Re: [PATCH v3 1/2] system/memory: Use qemu_ram_{copy, move}() in ram device region accessors

Gavin Shan <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
On 7/20/26 7:12 PM, Philippe Mathieu-Daudé wrote:
> On 17/7/26 15:55, Michael S. Tsirkin wrote:
>> On Fri, Jul 17, 2026 at 02:49:53PM +0100, Peter Maydell wrote:
>>> On Fri, 17 Jul 2026 at 14:24, Michael S. Tsirkin <[email protected]> wrote:
>>>> On Thu, Jul 09, 2026 at 07:52:12PM +1000, Gavin Shan wrote:
>>>>> Could you help to confirm if the conclusion is to have unified qemu_ram_{copy, move}()
>>>>> for all architectures? In qemu_ram_{copy, move}(), the fixed sized accesses like
>>>>> 1/2/4/8 bytes are handled by __builtin_{memcpy, memmove}() and fall back to
>>>>> memcpy/memmove() for other sized accesses. It's basically what was proposed by
>>>>> Michael :-)
>>>>
>>>> No. __builtin_ and  memcpy/memmove are same on modern linuxes.
>>>>
>>>> I'd say as a 1st step, on x86 hosts we can do a fixed size
>>>> memmove for 1/2/4/8, on other hosts
>>>> *aligned* 1/2/4/8 bytes can do
>>>>   __atomic_store ... __ATOMIC_RELAXED
>>>> and the rest can hobble along on memmove
>>>> and hope for the best.
>>>
>>> I don't think we should be doing something that's x86-host specific,
>>> as I've mentioned previously.
>>>
>>> -- PMM
>>
>> Shrug. Of course we already have arch specific code in asm.
>> But we can make the unaligned 1/2/4/8 for x86 host a separate
>> optimization on top. They are unlikely to be common.
> 
> Note we have include/qemu/host-pci-mmio.h with s390x specific
> optimizations in util/s390x_pci_mmio.c ...
> 

Philippe, Thanks for pointing it out that s390x already had private PCI
MMIO accessors.

If Peter is fine with two variants for x86 and non-x86 architectures.
I can post (v4) for further review. That will be something like below
and let me know if there are any other improvements are needed.

----- include/system/memory.h

+/**
+ * qemu_ram_copy: copy data to ramblock
+ *
+ * @dst: destination where the data is copied to
+ * @src: source where the data is copied from
+ * @n: length of data to be copied
+ *
+ * Copy @n bytes from @src to @dst with the assumption that @src and @dst
+ * do not overlap. For x86, the fixed sized accesses like 1/2/4/8 bytes are
+ * handled by __builtin_memcpy() no matter whether @src or @dst is aligned
+ * to the access size, and fall back to memcpy() for other access sizes. For
+ * other architectures, data is copied from @src to @dst in units of 1/2/4/8
+ * bytes, ensuring proper alignment in each unit and order among the units.
+ */
+void qemu_ram_copy(void *dest, const void *src, size_t n);
+
+/**
+ * qemu_ram_move: move data to ramblock
+ *
+ * @dst: destination where the data is moved to
+ * @src: source where the data is moved from
+ * @n: length of data to be moved
+ *
+ * Move @n bytes from @src to @dst with the assumption that @src and @dst
+ * can overlap. For x86, the fixed sized accesses like 1/2/4/8 bytes are
+ * handled by __builtin_memmove() no matter whether @src or @dst is aligned
+ * to the access size, and fall back to memmove() for other access sizes.
+ * For other architectures, data is moved from @src to @dst in units of
+ * 1/2/4/8 bytes, ensuring proper alignment in each unit and order among
+ * the units.
+ */
+void qemu_ram_move(void *dest, const void *src, size_t n);
+

----- system/physmem.c

+static void qemu_ram_unsafe_copy(void *dst, const void *src, size_t n)
+{
+    switch (n) {
+    case 1:
+        __builtin_memcpy(dst, src, 1);
+        break;
+    case 2:
+        __builtin_memcpy(dst, src, 2);
+        break;
+    case 4:
+        __builtin_memcpy(dst, src, 4);
+        break;
+    case 8:
+        __builtin_memcpy(dst, src, 8);
+        break;
+    default:
+        memcpy(dst, src, n);
+    }
+}
+
+static void qemu_ram_unsafe_move(void *dst, const void *src, size_t n)
+{
+    switch (n) {
+    case 1:
+        __builtin_memmove(dst, src, 1);
+        break;
+    case 2:
+        __builtin_memmove(dst, src, 2);
+        break;
+    case 4:
+        __builtin_memmove(dst, src, 4);
+        break;
+    case 8:
+        __builtin_memmove(dst, src, 8);
+        break;
+    default:
+        memmove(dst, src, n);
+    }
+}
+
+static void qemu_ram_safe_copy(void *dst, const void *src,
+                               size_t n, size_t max_step)
+{
+    uintptr_t test, step;
+
+    /* Aligned maximal step */
+    max_step = pow2floor(max_step);
+
+    while (n) {
+        test = (uintptr_t)src | (uintptr_t)dst | n | max_step;
+        step = test & -test;
+
+        switch (step) {
+        case 1:
+            qatomic_set((uint8_t *)dst, qatomic_read((uint8_t *)src));
+            src += 1;
+            dst += 1;
+            n -= 1;
+            break;
+        case 2:
+            qatomic_set((uint16_t *)dst, qatomic_read((uint16_t *)src));
+            src += 2;
+            dst += 2;
+            n -= 2;
+            break;
+        case 4:
+            qatomic_set((uint32_t *)dst, qatomic_read((uint32_t *)src));
+            src += 4;
+            dst += 4;
+            n -= 4;
+            break;
+        case 8:
+        default:
+            qatomic_set((uint64_t *)dst, qatomic_read((uint64_t *)src));
+            src += 8;
+            dst += 8;
+            n -= 8;
+        }
+    }
+}
+
+static void qemu_ram_safe_copy_backwards(void *dst, const void *src,
+                                         size_t n, size_t max_step)
+{
+    uintptr_t test, step;
+
+    /* Aligned maximal step */
+    max_step = pow2floor(max_step);
+
+    /* End of the blocks */
+    src += n;
+    dst += n;
+
+    while (n) {
+        test = (uintptr_t)src | (uintptr_t)dst | n | max_step;
+        step = test & -test;
+
+        switch (step) {
+        case 1:
+            src -= 1;
+            dst -= 1;
+            n -= 1;
+            qatomic_set((uint8_t *)dst, qatomic_read((uint8_t *)src));
+            break;
+        case 2:
+            src -= 2;
+            dst -= 2;
+            n -= 2;
+            qatomic_set((uint16_t *)dst, qatomic_read((uint16_t *)src));
+            break;
+        case 4:
+            src -= 4;
+            dst -= 4;
+            n -= 4;
+            qatomic_set((uint32_t *)dst, qatomic_read((uint32_t *)src));
+            break;
+        case 8:
+        default:
+            src -= 8;
+            dst -= 8;
+            n -= 8;
+            qatomic_set((uint64_t *)dst, qatomic_read((uint64_t *)src));
+        }
+    }
+}
+
+/* x86 should work with __builtin_{memcpy, memmove}() for IO access */
+#if defined(__i386__) || defined(__x86_64__)
+#define HOST_UNALIGNED_VECTOR_MMIO_OK 1
+#else
+#define HOST_UNALIGNED_VECTOR_MMIO_OK 0
+#endif
+
+void qemu_ram_copy(void *dst, const void *src, size_t n)
+{
+    if (src == dst || n == 0) {
+        return;
+    }
+
+    if (HOST_UNALIGNED_VECTOR_MMIO_OK) {
+        qemu_ram_unsafe_copy(dst, src, n);
+    } else {
+        qemu_ram_safe_copy(dst, src, n, 8);
+    }
+}
+void qemu_ram_move(void *dst, const void *src, size_t n)
+{
+    if (src == dst || n == 0) {
+        return;
+    }
+
+    if (HOST_UNALIGNED_VECTOR_MMIO_OK) {
+        qemu_ram_unsafe_move(dst, src, n);
+    } else if (dst < src) {
+        qemu_ram_safe_copy(dst, src, n, src - dst);
+    } else {
+        qemu_ram_safe_copy_backwards(dst, src, n, dst - src);
+    }
+}


Thanks,
Gavin
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.