On Wed, Sep 09, 2026 at 03:59:22PM +0300, Alex Fishman wrote:
> Virtio-mem dynamic memslots are marked unmergeable so listeners can
> track their lifetimes independently. A vring part crossing the boundary
> between two such slots consequently cannot be contained in a single
> vhost memory region.
>
> Coalesce adjacent unmergeable sections only when a descriptor table,
> available ring, or used ring spans their boundary and the sections
> preserve a coherent GPA-to-HVA translation. Keep unrelated slots
> separate so activating them does not reshape the region containing the
> vring.
I don't get what does it have to do with vrings. If merging them like
this is ok, then it's always ok?
>
> Fixes: 533f5d667909 ("memory,vhost: Allow for marking memory device memory
> regions unmergeable")
>
> Buglink: https://redhat.atlassian.net/browse/RHEL-146583
>
> Signed-off-by: Alex Fishman <[email protected]>
No empty lines between trailers,please.
> ---
> hw/virtio/vhost.c | 72 +++++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 67 insertions(+), 5 deletions(-)
>
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index 371dca17dd..76910e2628 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -796,6 +796,70 @@ out:
> g_free(old_sections);
> }
>
> +static bool vhost_vring_part_crosses_boundary(uint64_t ring_gpa,
> + uint64_t ring_size,
> + uint64_t boundary)
> +{
> + return ring_size && ring_gpa < boundary &&
> + range_get_last(ring_gpa, ring_size) >= boundary;
> +}
> +
> +static bool vhost_vring_crosses_boundary(struct vhost_dev *dev,
> + uint64_t boundary)
> +{
> + int i;
> +
> + if (vhost_dev_has_iommu(dev)) {
> + return false;
> + }
> +
> + for (i = 0; i < dev->nvqs; i++) {
> + struct vhost_virtqueue *vq = &dev->vqs[i];
> +
> + if (vhost_vring_part_crosses_boundary(vq->desc_phys, vq->desc_size,
> + boundary) ||
> + vhost_vring_part_crosses_boundary(vq->avail_phys, vq->avail_size,
> + boundary) ||
> + vhost_vring_part_crosses_boundary(vq->used_phys, vq->used_size,
> + boundary)) {
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static bool vhost_sections_can_merge(struct vhost_dev *dev,
> + const MemoryRegionSection *prev_sec,
> + const MemoryRegionSection *section,
> + uint64_t section_gpa,
> + uintptr_t section_host)
> +{
> + uint64_t prev_gpa_start = prev_sec->offset_within_address_space;
> + uintptr_t prev_host_start =
> + (uintptr_t)memory_region_get_ram_ptr(prev_sec->mr) +
> + prev_sec->offset_within_region;
> + uint64_t offset;
> +
> + if (section->mr != prev_sec->mr || section_gpa < prev_gpa_start) {
> + return false;
> + }
> +
> + offset = section_gpa - prev_gpa_start;
> +
> + if (prev_host_start + offset != section_host) {
> + return false;
> + }
> +
> + if (!prev_sec->unmergeable && !section->unmergeable) {
> + return true;
> + }
> +
> + /* Only override an unmergeable boundary when a ring part spans it. */
> + return vhost_vring_crosses_boundary(
> + dev, section->offset_within_address_space);
> +}
> +
> /* Adds the section data to the tmp_section structure.
> * It relies on the listener calling us in memory address order
> * and for each region (via the _add and _nop methods) to
> @@ -833,7 +897,7 @@ static void vhost_region_add_section(struct vhost_dev
> *dev,
> mrs_size, mrs_host);
> }
>
> - if (dev->n_tmp_sections && !section->unmergeable) {
> + if (dev->n_tmp_sections) {
> /* Since we already have at least one section, lets see if
> * this extends it; since we're scanning in order, we only
> * have to look at the last one, and the FlatView that calls
> @@ -862,11 +926,9 @@ static void vhost_region_add_section(struct vhost_dev
> *dev,
> /* A way to cleanly fail here would be better */
> return;
> }
> - /* Offset from the start of the previous GPA to this GPA */
> - size_t offset = mrs_gpa - prev_gpa_start;
>
> - if (prev_host_start + offset == mrs_host &&
> - section->mr == prev_sec->mr && !prev_sec->unmergeable) {
> + if (vhost_sections_can_merge(dev, prev_sec, section,
> + mrs_gpa, mrs_host)) {
> uint64_t max_end = MAX(prev_host_end, mrs_host + mrs_size);
> need_add = false;
> prev_sec->offset_within_address_space =
> --
> 2.52.0