From: Stanislav Kinsburskii <[email protected]> Sent: Monday,
April 20, 2026 9:22 AM
>
> On Mon, Apr 13, 2026 at 09:08:16PM +0000, Michael Kelley wrote:
> > From: Stanislav Kinsburskii <[email protected]> Sent:
> > Monday, March 30, 2026 1:04 PM
> > >
[snip]
> > > @@ -57,60 +58,61 @@ static int mshv_chunk_stride(struct page *page,
> > > /**
> > > * mshv_region_process_chunk - Processes a contiguous chunk of memory
> > > pages
> > > * in a region.
> > > - * @region : Pointer to the memory region structure.
> > > - * @flags : Flags to pass to the handler.
> > > - * @page_offset: Offset into the region's pages array to start
> > > processing.
> > > - * @page_count : Number of pages to process.
> > > - * @handler : Callback function to handle the chunk.
> > > + * @region : Pointer to the memory region structure.
> > > + * @flags : Flags to pass to the handler.
> > > + * @pfn_offset: Offset into the region's PFNs array to start processing.
> > > + * @pfn_count : Number of PFNs to process.
> > > + * @handler : Callback function to handle the chunk.
> > > *
> > > - * This function scans the region's pages starting from @page_offset,
> > > - * checking for contiguous present pages of the same size (normal or
> > > huge).
> > > - * It invokes @handler for the chunk of contiguous pages found. Returns
> > > the
> > > - * number of pages handled, or a negative error code if the first page is
> > > - * not present or the handler fails.
> > > + * This function scans the region's PFNs starting from @pfn_offset,
> > > + * checking for contiguous valid PFNs backed by pages of the same size
> > > + * (normal or huge). It invokes @handler for the chunk of contiguous
> > > valid
> > > + * PFNs found. Returns the number of PFNs handled, or a negative error
> > > code
> > > + * if the first PFN is invalid or the handler fails.
> > > *
> > > - * Note: The @handler callback must be able to handle both normal and
> > > huge
> > > - * pages.
> > > + * Note: The @handler callback must be able to handle valid PFNs backed
> > > by
> > > + * both normal and huge pages.
> > > *
> > > * Return: Number of pages handled, or negative error code.
> > > */
> > > -static long mshv_region_process_chunk(struct mshv_mem_region *region,
> > > - u32 flags,
> > > - u64 page_offset, u64 page_count,
> > > - int (*handler)(struct mshv_mem_region
> > > *region,
> > > - u32 flags,
> > > - u64 page_offset,
> > > - u64 page_count,
> > > - bool huge_page))
> > > +static long mshv_region_process_pfns(struct mshv_mem_region *region,
> > > + u32 flags,
> > > + u64 pfn_offset, u64 pfn_count,
> > > + int (*handler)(struct mshv_mem_region
> > > *region,
> > > + u32 flags,
> > > + u64 pfn_offset,
> > > + u64 pfn_count,
> > > + bool huge_page))
> > > {
> > > - u64 gfn = region->start_gfn + page_offset;
> > > + u64 gfn = region->start_gfn + pfn_offset;
> > > u64 count;
> > > - struct page *page;
> > > + unsigned long pfn;
> > > int stride, ret;
> > >
> > > - page = region->mreg_pages[page_offset];
> > > - if (!page)
> > > + pfn = region->mreg_pfns[pfn_offset];
> > > + if (!pfn_valid(pfn))
> > > return -EINVAL;
> > >
> > > - stride = mshv_chunk_stride(page, gfn, page_count);
> > > + stride = mshv_chunk_stride(pfn_to_page(pfn), gfn, pfn_count);
> > > if (stride < 0)
> > > return stride;
> > >
> > > /* Start at stride since the first stride is validated */
> > > - for (count = stride; count < page_count; count += stride) {
> > > - page = region->mreg_pages[page_offset + count];
> > > + for (count = stride; count < pfn_count ; count += stride) {
> > > + pfn = region->mreg_pfns[pfn_offset + count];
> > >
> > > - /* Break if current page is not present */
> > > - if (!page)
> > > + /* Break if current pfn is invalid */
> > > + if (!pfn_valid(pfn))
> >
> > pfn_valid() is a relatively expensive test to be doing in a loop
> > on what may be every single page. It does an RCU lock/unlock
> > and make other checks that aren't necessary here. Since
> > mreg_pfns[] is populated from mm calls, the only invalid PFNs
> > would be MSHV_INVALID_PFN that code in this module has
> > explicitly put there. Just testing against MSHV_INVALID_PFN
> > would be a lot faster here and elsewhere in this module. It's
> > really a "pfn set/not set" test. Defining a pfn_set() macro
> > here in this module that tests against MSHV_INVALID_PFN
> > would accomplish the same thing more efficiently.
> >
>
> Yes, we could do it the way you suggest. For completeness, I should add
> that pfn_valid() is expensive only on 32-bit ARM and ARC, which we
> don’t care about.
>
Could you elaborate? On x86, I'm seeing that pfn_valid() is about
220 bytes of code. It's the version in include/linux/mmzone.h, not
the simple version in include/asm-generic/memory_model.h. The
latter is used only for CONFIG_FLATMEM=y. Or is the root partition
kernel build setting CONFIG_FLATMEM_MANUAL and hence getting
the simple version?
Michael