Re: [PATCH v2 3/6] mm: page_vma_mapped: support checking if a pfn is mapped into a vma

2022-02-02 Thread Matthew Wilcox
On Wed, Feb 02, 2022 at 10:33:04PM +0800, Muchun Song wrote:
> page_vma_mapped_walk() is supposed to check if a page is mapped into a vma.
> However, not all page frames (e.g. PFN_DEV) have a associated struct page
> with it. There is going to be some duplicate codes similar with this function
> if someone want to check if a pfn (without a struct page) is mapped into a
> vma. So add support for checking if a pfn is mapped into a vma. In the next
> patch, the dax will use this new feature.

I'm coming to more or less the same solution for fixing the bug in
page_mapped_in_vma().  If you call it with a head page, it will look
for any page in the THP instead of the precise page.  I think we can do
a fairly significant simplification though, so I'm going to go off
and work on that next ...




[PATCH v2 3/6] mm: page_vma_mapped: support checking if a pfn is mapped into a vma

2022-02-02 Thread Muchun Song
page_vma_mapped_walk() is supposed to check if a page is mapped into a vma.
However, not all page frames (e.g. PFN_DEV) have a associated struct page
with it. There is going to be some duplicate codes similar with this function
if someone want to check if a pfn (without a struct page) is mapped into a
vma. So add support for checking if a pfn is mapped into a vma. In the next
patch, the dax will use this new feature.

Signed-off-by: Muchun Song 
---
 include/linux/rmap.h| 14 --
 include/linux/swapops.h | 13 +++---
 mm/internal.h   | 28 +---
 mm/page_vma_mapped.c| 68 +++--
 4 files changed, 83 insertions(+), 40 deletions(-)

diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index 221c3c6438a7..78373935ad49 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -204,9 +204,18 @@ int make_device_exclusive_range(struct mm_struct *mm, 
unsigned long start,
 #define PVMW_SYNC  (1 << 0)
 /* Look for migarion entries rather than present PTEs */
 #define PVMW_MIGRATION (1 << 1)
+/* Walk the page table by checking the pfn instead of a struct page */
+#define PVMW_PFN_WALK  (1 << 2)
 
 struct page_vma_mapped_walk {
-   struct page *page;
+   union {
+   struct page *page;
+   struct {
+   unsigned long pfn;
+   unsigned int nr;
+   pgoff_t index;
+   };
+   };
struct vm_area_struct *vma;
unsigned long address;
pmd_t *pmd;
@@ -218,7 +227,8 @@ struct page_vma_mapped_walk {
 static inline void page_vma_mapped_walk_done(struct page_vma_mapped_walk *pvmw)
 {
/* HugeTLB pte is set to the relevant page table entry without 
pte_mapped. */
-   if (pvmw->pte && !PageHuge(pvmw->page))
+   if (pvmw->pte && (pvmw->flags & PVMW_PFN_WALK ||
+ !PageHuge(pvmw->page)))
pte_unmap(pvmw->pte);
if (pvmw->ptl)
spin_unlock(pvmw->ptl);
diff --git a/include/linux/swapops.h b/include/linux/swapops.h
index d356ab4047f7..d28bf65fd6a5 100644
--- a/include/linux/swapops.h
+++ b/include/linux/swapops.h
@@ -247,17 +247,22 @@ static inline int is_writable_migration_entry(swp_entry_t 
entry)
 
 #endif
 
-static inline struct page *pfn_swap_entry_to_page(swp_entry_t entry)
+static inline unsigned long pfn_swap_entry_to_pfn(swp_entry_t entry)
 {
-   struct page *p = pfn_to_page(swp_offset(entry));
+   unsigned long pfn = swp_offset(entry);
 
/*
 * Any use of migration entries may only occur while the
 * corresponding page is locked
 */
-   BUG_ON(is_migration_entry(entry) && !PageLocked(p));
+   BUG_ON(is_migration_entry(entry) && !PageLocked(pfn_to_page(pfn)));
+
+   return pfn;
+}
 
-   return p;
+static inline struct page *pfn_swap_entry_to_page(swp_entry_t entry)
+{
+   return pfn_to_page(pfn_swap_entry_to_pfn(entry));
 }
 
 /*
diff --git a/mm/internal.h b/mm/internal.h
index deb9bda18e59..5458cd08df33 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -478,25 +478,35 @@ vma_address(struct page *page, struct vm_area_struct *vma)
 }
 
 /*
- * Then at what user virtual address will none of the page be found in vma?
- * Assumes that vma_address() already returned a good starting address.
- * If page is a compound head, the entire compound page is considered.
+ * Return the end of user virtual address at the specific offset within
+ * a vma.
  */
 static inline unsigned long
-vma_address_end(struct page *page, struct vm_area_struct *vma)
+vma_pgoff_address_end(pgoff_t pgoff, unsigned long nr_pages,
+ struct vm_area_struct *vma)
 {
-   pgoff_t pgoff;
-   unsigned long address;
+   unsigned long address = vma->vm_start;
 
-   VM_BUG_ON_PAGE(PageKsm(page), page);/* KSM page->index unusable */
-   pgoff = page_to_pgoff(page) + compound_nr(page);
-   address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
+   address += (pgoff + nr_pages - vma->vm_pgoff) << PAGE_SHIFT;
/* Check for address beyond vma (or wrapped through 0?) */
if (address < vma->vm_start || address > vma->vm_end)
address = vma->vm_end;
return address;
 }
 
+/*
+ * Return the end of user virtual address of a page within a vma. Assumes that
+ * vma_address() already returned a good starting address. If page is a 
compound
+ * head, the entire compound page is considered.
+ */
+static inline unsigned long
+vma_address_end(struct page *page, struct vm_area_struct *vma)
+{
+   VM_BUG_ON_PAGE(PageKsm(page), page);/* KSM page->index unusable */
+   return vma_pgoff_address_end(page_to_pgoff(page), compound_nr(page),
+vma);
+}
+
 static inline struct file *maybe_unlock_mmap_for_io(struct vm_fault *vmf,