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

Gavin Shan <[email protected]> Mon, 27 Jul 2026 13:38:29 +1000
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
On 7/25/26 2:31 AM, Michael S. Tsirkin wrote:
> On Fri, Jul 24, 2026 at 09:56:48AM -0400, Peter Xu wrote:
>> On Fri, Jul 24, 2026 at 11:42:09AM +1000, Gavin Shan wrote:
>>> On 7/23/26 11:46 PM, Peter Xu wrote:
>>>> On Thu, Jul 23, 2026 at 05:05:08AM -0400, Michael S. Tsirkin wrote:

[...]

>>>> We should have another option that is not arch-dependent but keep the
>>>> unaligned behavior.
>>>>
>>>> For current master, AFAIU we do unaligned access for both ram_device and
>>>> rest. Say, even with ram_device_mem_ops, it has both .unaligned=true for
>>>> both .valid & .impl.  I think it means indeed we have unaligned behavior
>>>> even for ram_device.  It also means what matters in regards to the Realtek
>>>> bug was only about aligned access (with subpage presence).
>>>>
>>>> I think it means we can always keep unaligned to stick with
>>>> memcpy()/memmove(), but only use atomic ops for the aligned cases of
>>>> 1/2/4/8.  With that, I think we can also remove ram_device_mem_ops and fix
>>>> the bounce buffer issue.
>>>>
>>>> I think it means we'll stick with memcpy()/memmove() for all archs for
>>>> unaligned, which is again not safe... but that can be an existing but
>>>> separate problem to solve too.
>>>>
>>>
>>> Lets see if Peter Maydell and Michael are happy with this option. At least,
>>> we will have unified qemu_ram_move() for all architectures with this option.
>>> Note that qemu_ram_copy() won't be needed.
>>>
>>> I'm putting note on what's to be done in (v4) if this option is to be picked
>>> up. Let me know if there are missed points. It's basically combing what's done
>>> by ram_device_mem_ops to upper layer (e.g. in qemu_ram_move()).
>>>
>>>    address_space_write
>>>      address_space_to_flatview
>>>      flatview_write
>>>        flatview_translate
>>>        flatview_write_continue
>>>          flatview_write_continue_step
>>>            memmove                     // (A) to replace it with qemu_ram_move()
>>>
>>> /**
>>>   * 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. The access is atomic if the source and destination buffer
>>>   * aren't overlapped for a well aligned and small-sized access. Otherwise,
>>>   * fall back to the standard memmove().
>>>   */
>>> static void qemu_ram_move(void *dst, const void *src, size_t n)
>>> {
>>>      uintptr_t test, len;
>>>
>>>      if (src == dst || n == 0) {
>>>          return;
>>>      }
>>>
>>>      /* Overlapped buffers */
> 
> 
> overlapping
> 

Corrected in v4.

https://lore.kernel.org/qemu-arm/[email protected]/

>>>      if (src < (dst + n) && dst < (src + n)) {
>>
>> s/&&/||/?
>>
>> It's a bit weird to request memmove() for overlapped, e.g. I don't know if
>> P2P can overlap too when some fuzzer fills in some MMIO address shifted for
>> src/dst.. but I think I get what you want to simplify and it looks fine.
>>
>> Otherwise it looks good.
> 
> Why do we bother special casing overlapping buffers though?
> I do not get it, looks like rest of logic works exactly
> the same for overlapping and non.
> 

Indeed. The explicit check if src/dst are overlapping has been dropped in v4.

>>>          memmove(dst, src, n);
>>>          return;
>>>      }
>>>
>>>      test = (uintptr_t)src | (uintptr_t)dst | n;
>>>      len = test & -test;
>>>
>>>      /* Unaligned or oversized access */
>>>      if (n > 8 || len != n) {
>>>          memmove(dst, src, n);
>>>          return;
>>>      }
>>>
>>>      switch (len) {
>>>      case 1:
>>>          qatomic_set((uint8_t *)dst, qatomic_read((uint8_t *)src));
>>>          break;
>>>      case 2:
>>>          qatomic_set((uint16_t *)dst, qatomic_read((uint16_t *)src));
>>>          break;
>>>      case 4:
>>>          qatomic_set((uint32_t *)dst, qatomic_read((uint32_t *)src));
>>>          break;
>>>      case 8:
>>>          qatomic_set((uint64_t *)dst, qatomic_read((uint64_t *)src));
>>>          break;
>>>      default:
>>>          g_assert_not_reached();
>>>      }
>>> }
>>

[...]

Thanks,
Gavin