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




Reply via email to