Some hardware accelerators DMA directly to the host physical address
of a guest-resident buffer (e.g. set up via IOMMUFD). The HPA is
contiguous only within a single host backing page; a buffer larger
than the smallest backing page in use may span non-contiguous host
pages and fault.

Introduce qemu_ram_backend_pagesize_min(), which returns the smallest
page size among RAM regions backed by a memory-backend object. Falls
back to qemu_real_host_page_size() if no memory-backend RAM blocks
are found. Callers can use this to bound the size of buffers that
must remain host-physically contiguous.

A subsequent Tegra241 CMDQV support patch will use this to bound the
command queue size exposed to the guest.

Cc: Peter Xu <[email protected]>
Cc: "Philippe Mathieu-Daudé" <[email protected]>
Signed-off-by: Shameer Kolothum <[email protected]>
---
 include/system/ramblock.h |  1 +
 system/physmem.c          | 29 +++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/include/system/ramblock.h b/include/system/ramblock.h
index 4435f8d55f..7f7ea53557 100644
--- a/include/system/ramblock.h
+++ b/include/system/ramblock.h
@@ -175,6 +175,7 @@ int qemu_ram_get_fd(const RAMBlock *rb);
 
 size_t qemu_ram_pagesize(const RAMBlock *block);
 size_t qemu_ram_pagesize_largest(void);
+size_t qemu_ram_backend_pagesize_min(void);
 #include "exec/target_page.h"
 #include "exec/hwaddr.h"
 
diff --git a/system/physmem.c b/system/physmem.c
index 46b36c7b10..7ccec52aa2 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -1999,6 +1999,35 @@ size_t qemu_ram_pagesize_largest(void)
     return largest;
 }
 
+/*
+ * Returns the smallest page size among RAM regions backed by a
+ * memory-backend object. Falls back to qemu_real_host_page_size() if no
+ * memory-backend RAM blocks are found.
+ */
+size_t qemu_ram_backend_pagesize_min(void)
+{
+    RAMBlock *rb;
+    size_t pg, min_pg = SIZE_MAX;
+
+    RAMBLOCK_FOREACH(rb) {
+        MemoryRegion *mr = rb->mr;
+
+        if (!mr || !memory_region_is_ram(mr)) {
+            continue;
+        }
+        if (!object_dynamic_cast(mr->owner, TYPE_MEMORY_BACKEND)) {
+            continue;
+        }
+
+        pg = qemu_ram_pagesize(rb);
+        if (pg && pg < min_pg) {
+            min_pg = pg;
+        }
+    }
+
+    return (min_pg == SIZE_MAX) ? qemu_real_host_page_size() : min_pg;
+}
+
 static int memory_try_enable_merging(void *addr, size_t len)
 {
     if (!machine_mem_merge(current_machine)) {
-- 
2.43.0


Reply via email to