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
