Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mshv: Align huge page stride with guest mapping

Ensure that a stride larger than 1 (huge page) is only used when page
points to a head of a huge page and both the guest frame number (gfn) and
the operation size (page_count) are aligned to the huge page size
(PTRS_PER_PMD). This matches the hypervisor requirement that map/unmap
operations for huge pages must be guest-aligned and cover a full huge page.

Add mshv_chunk_stride() to encapsulate this alignment and page-order
validation, and plumb a huge_page flag into the region chunk handlers.
This prevents issuing large-page map/unmap/share operations that the
hypervisor would reject due to misaligned guest mappings.

Fixes: abceb4297bf8 ("mshv: Fix huge page handling in memory region traversal")
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Reviewed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Stanislav Kinsburskii and committed by
Wei Liu
259add0d 49f49d47

+62 -31
+62 -31
drivers/hv/mshv_regions.c
··· 20 20 #define MSHV_MAP_FAULT_IN_PAGES PTRS_PER_PMD 21 21 22 22 /** 23 + * mshv_chunk_stride - Compute stride for mapping guest memory 24 + * @page : The page to check for huge page backing 25 + * @gfn : Guest frame number for the mapping 26 + * @page_count: Total number of pages in the mapping 27 + * 28 + * Determines the appropriate stride (in pages) for mapping guest memory. 29 + * Uses huge page stride if the backing page is huge and the guest mapping 30 + * is properly aligned; otherwise falls back to single page stride. 31 + * 32 + * Return: Stride in pages, or -EINVAL if page order is unsupported. 33 + */ 34 + static int mshv_chunk_stride(struct page *page, 35 + u64 gfn, u64 page_count) 36 + { 37 + unsigned int page_order; 38 + 39 + /* 40 + * Use single page stride by default. For huge page stride, the 41 + * page must be compound and point to the head of the compound 42 + * page, and both gfn and page_count must be huge-page aligned. 43 + */ 44 + if (!PageCompound(page) || !PageHead(page) || 45 + !IS_ALIGNED(gfn, PTRS_PER_PMD) || 46 + !IS_ALIGNED(page_count, PTRS_PER_PMD)) 47 + return 1; 48 + 49 + page_order = folio_order(page_folio(page)); 50 + /* The hypervisor only supports 2M huge page */ 51 + if (page_order != PMD_ORDER) 52 + return -EINVAL; 53 + 54 + return 1 << page_order; 55 + } 56 + 57 + /** 23 58 * mshv_region_process_chunk - Processes a contiguous chunk of memory pages 24 59 * in a region. 25 60 * @region : Pointer to the memory region structure. ··· 80 45 int (*handler)(struct mshv_mem_region *region, 81 46 u32 flags, 82 47 u64 page_offset, 83 - u64 page_count)) 48 + u64 page_count, 49 + bool huge_page)) 84 50 { 85 - u64 count, stride; 86 - unsigned int page_order; 51 + u64 gfn = region->start_gfn + page_offset; 52 + u64 count; 87 53 struct page *page; 88 - int ret; 54 + int stride, ret; 89 55 90 56 page = region->pages[page_offset]; 91 57 if (!page) 92 58 return -EINVAL; 93 59 94 - page_order = folio_order(page_folio(page)); 95 - /* The hypervisor only supports 4K and 2M page sizes */ 96 - if (page_order && page_order != PMD_ORDER) 97 - return -EINVAL; 60 + stride = mshv_chunk_stride(page, gfn, page_count); 61 + if (stride < 0) 62 + return stride; 98 63 99 - stride = 1 << page_order; 100 - 101 - /* Start at stride since the first page is validated */ 64 + /* Start at stride since the first stride is validated */ 102 65 for (count = stride; count < page_count; count += stride) { 103 66 page = region->pages[page_offset + count]; 104 67 ··· 104 71 if (!page) 105 72 break; 106 73 107 - /* Break if page size changes */ 108 - if (page_order != folio_order(page_folio(page))) 74 + /* Break if stride size changes */ 75 + if (stride != mshv_chunk_stride(page, gfn + count, 76 + page_count - count)) 109 77 break; 110 78 } 111 79 112 - ret = handler(region, flags, page_offset, count); 80 + ret = handler(region, flags, page_offset, count, stride > 1); 113 81 if (ret) 114 82 return ret; 115 83 ··· 142 108 int (*handler)(struct mshv_mem_region *region, 143 109 u32 flags, 144 110 u64 page_offset, 145 - u64 page_count)) 111 + u64 page_count, 112 + bool huge_page)) 146 113 { 147 114 long ret; 148 115 ··· 197 162 198 163 static int mshv_region_chunk_share(struct mshv_mem_region *region, 199 164 u32 flags, 200 - u64 page_offset, u64 page_count) 165 + u64 page_offset, u64 page_count, 166 + bool huge_page) 201 167 { 202 - struct page *page = region->pages[page_offset]; 203 - 204 - if (PageHuge(page) || PageTransCompound(page)) 168 + if (huge_page) 205 169 flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE; 206 170 207 171 return hv_call_modify_spa_host_access(region->partition->pt_id, ··· 222 188 223 189 static int mshv_region_chunk_unshare(struct mshv_mem_region *region, 224 190 u32 flags, 225 - u64 page_offset, u64 page_count) 191 + u64 page_offset, u64 page_count, 192 + bool huge_page) 226 193 { 227 - struct page *page = region->pages[page_offset]; 228 - 229 - if (PageHuge(page) || PageTransCompound(page)) 194 + if (huge_page) 230 195 flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE; 231 196 232 197 return hv_call_modify_spa_host_access(region->partition->pt_id, ··· 245 212 246 213 static int mshv_region_chunk_remap(struct mshv_mem_region *region, 247 214 u32 flags, 248 - u64 page_offset, u64 page_count) 215 + u64 page_offset, u64 page_count, 216 + bool huge_page) 249 217 { 250 - struct page *page = region->pages[page_offset]; 251 - 252 - if (PageHuge(page) || PageTransCompound(page)) 218 + if (huge_page) 253 219 flags |= HV_MAP_GPA_LARGE_PAGE; 254 220 255 221 return hv_call_map_gpa_pages(region->partition->pt_id, ··· 327 295 328 296 static int mshv_region_chunk_unmap(struct mshv_mem_region *region, 329 297 u32 flags, 330 - u64 page_offset, u64 page_count) 298 + u64 page_offset, u64 page_count, 299 + bool huge_page) 331 300 { 332 - struct page *page = region->pages[page_offset]; 333 - 334 - if (PageHuge(page) || PageTransCompound(page)) 301 + if (huge_page) 335 302 flags |= HV_UNMAP_GPA_LARGE_PAGE; 336 303 337 304 return hv_call_unmap_gpa_pages(region->partition->pt_id,