Skip to content

Commit 2aff7a4

Browse files
author
Matthew Wilcox (Oracle)
committed
mm: Convert page_vma_mapped_walk to work on PFNs
page_mapped_in_vma() really just wants to walk one page, but as the code stands, if passed the head page of a compound page, it will walk every page in the compound page. Extract pfn/nr_pages/pgoff from the struct page early, so they can be overridden by page_mapped_in_vma(). Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
1 parent aef13de commit 2aff7a4

File tree

6 files changed

+58
-50
lines changed

6 files changed

+58
-50
lines changed

include/linux/hugetlb.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,11 @@ static inline struct hstate *page_hstate(struct page *page)
970970
return NULL;
971971
}
972972

973+
static inline struct hstate *size_to_hstate(unsigned long size)
974+
{
975+
return NULL;
976+
}
977+
973978
static inline unsigned long huge_page_size(struct hstate *h)
974979
{
975980
return PAGE_SIZE;

include/linux/rmap.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/rwsem.h>
1212
#include <linux/memcontrol.h>
1313
#include <linux/highmem.h>
14+
#include <linux/pagemap.h>
1415

1516
/*
1617
* The anon_vma heads a list of private "related" vmas, to scan if
@@ -201,11 +202,13 @@ int make_device_exclusive_range(struct mm_struct *mm, unsigned long start,
201202

202203
/* Avoid racy checks */
203204
#define PVMW_SYNC (1 << 0)
204-
/* Look for migarion entries rather than present PTEs */
205+
/* Look for migration entries rather than present PTEs */
205206
#define PVMW_MIGRATION (1 << 1)
206207

207208
struct page_vma_mapped_walk {
208-
struct page *page;
209+
unsigned long pfn;
210+
unsigned long nr_pages;
211+
pgoff_t pgoff;
209212
struct vm_area_struct *vma;
210213
unsigned long address;
211214
pmd_t *pmd;
@@ -216,15 +219,19 @@ struct page_vma_mapped_walk {
216219

217220
#define DEFINE_PAGE_VMA_WALK(name, _page, _vma, _address, _flags) \
218221
struct page_vma_mapped_walk name = { \
219-
.page = _page, \
222+
.pfn = page_to_pfn(_page), \
223+
.nr_pages = compound_nr(page), \
224+
.pgoff = page_to_pgoff(page), \
220225
.vma = _vma, \
221226
.address = _address, \
222227
.flags = _flags, \
223228
}
224229

225230
#define DEFINE_FOLIO_VMA_WALK(name, _folio, _vma, _address, _flags) \
226231
struct page_vma_mapped_walk name = { \
227-
.page = &_folio->page, \
232+
.pfn = folio_pfn(_folio), \
233+
.nr_pages = folio_nr_pages(_folio), \
234+
.pgoff = folio_pgoff(_folio), \
228235
.vma = _vma, \
229236
.address = _address, \
230237
.flags = _flags, \
@@ -233,7 +240,7 @@ struct page_vma_mapped_walk {
233240
static inline void page_vma_mapped_walk_done(struct page_vma_mapped_walk *pvmw)
234241
{
235242
/* HugeTLB pte is set to the relevant page table entry without pte_mapped. */
236-
if (pvmw->pte && !PageHuge(pvmw->page))
243+
if (pvmw->pte && !is_vm_hugetlb_page(pvmw->vma))
237244
pte_unmap(pvmw->pte);
238245
if (pvmw->ptl)
239246
spin_unlock(pvmw->ptl);

mm/internal.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/fs.h>
1111
#include <linux/mm.h>
1212
#include <linux/pagemap.h>
13+
#include <linux/rmap.h>
1314
#include <linux/tracepoint-defs.h>
1415

1516
struct folio_batch;
@@ -475,18 +476,20 @@ vma_address(struct page *page, struct vm_area_struct *vma)
475476
}
476477

477478
/*
478-
* Then at what user virtual address will none of the page be found in vma?
479+
* Then at what user virtual address will none of the range be found in vma?
479480
* Assumes that vma_address() already returned a good starting address.
480-
* If page is a compound head, the entire compound page is considered.
481481
*/
482-
static inline unsigned long
483-
vma_address_end(struct page *page, struct vm_area_struct *vma)
482+
static inline unsigned long vma_address_end(struct page_vma_mapped_walk *pvmw)
484483
{
484+
struct vm_area_struct *vma = pvmw->vma;
485485
pgoff_t pgoff;
486486
unsigned long address;
487487

488-
VM_BUG_ON_PAGE(PageKsm(page), page); /* KSM page->index unusable */
489-
pgoff = page_to_pgoff(page) + compound_nr(page);
488+
/* Common case, plus ->pgoff is invalid for KSM */
489+
if (pvmw->nr_pages == 1)
490+
return pvmw->address + PAGE_SIZE;
491+
492+
pgoff = pvmw->pgoff + pvmw->nr_pages;
490493
address = vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
491494
/* Check for address beyond vma (or wrapped through 0?) */
492495
if (address < vma->vm_start || address > vma->vm_end)

mm/migrate.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ void putback_movable_pages(struct list_head *l)
174174
static bool remove_migration_pte(struct page *page, struct vm_area_struct *vma,
175175
unsigned long addr, void *old)
176176
{
177-
DEFINE_PAGE_VMA_WALK(pvmw, old, vma, addr, PVMW_SYNC | PVMW_MIGRATION);
177+
DEFINE_PAGE_VMA_WALK(pvmw, (struct page *)old, vma, addr,
178+
PVMW_SYNC | PVMW_MIGRATION);
178179
struct page *new;
179180
pte_t pte;
180181
swp_entry_t entry;
@@ -184,7 +185,7 @@ static bool remove_migration_pte(struct page *page, struct vm_area_struct *vma,
184185
if (PageKsm(page))
185186
new = page;
186187
else
187-
new = page - pvmw.page->index +
188+
new = page - pvmw.pgoff +
188189
linear_page_index(vma, pvmw.address);
189190

190191
#ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION

mm/page_vma_mapped.c

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,6 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw)
5353
return true;
5454
}
5555

56-
static inline bool pfn_is_match(struct page *page, unsigned long pfn)
57-
{
58-
unsigned long page_pfn = page_to_pfn(page);
59-
60-
/* normal page and hugetlbfs page */
61-
if (!PageTransCompound(page) || PageHuge(page))
62-
return page_pfn == pfn;
63-
64-
/* THP can be referenced by any subpage */
65-
return pfn >= page_pfn && pfn - page_pfn < thp_nr_pages(page);
66-
}
67-
6856
/**
6957
* check_pte - check if @pvmw->page is mapped at the @pvmw->pte
7058
* @pvmw: page_vma_mapped_walk struct, includes a pair pte and page for checking
@@ -116,7 +104,17 @@ static bool check_pte(struct page_vma_mapped_walk *pvmw)
116104
pfn = pte_pfn(*pvmw->pte);
117105
}
118106

119-
return pfn_is_match(pvmw->page, pfn);
107+
return (pfn - pvmw->pfn) < pvmw->nr_pages;
108+
}
109+
110+
/* Returns true if the two ranges overlap. Careful to not overflow. */
111+
static bool check_pmd(unsigned long pfn, struct page_vma_mapped_walk *pvmw)
112+
{
113+
if ((pfn + HPAGE_PMD_NR - 1) < pvmw->pfn)
114+
return false;
115+
if (pfn > pvmw->pfn + pvmw->nr_pages - 1)
116+
return false;
117+
return true;
120118
}
121119

122120
static void step_forward(struct page_vma_mapped_walk *pvmw, unsigned long size)
@@ -127,7 +125,7 @@ static void step_forward(struct page_vma_mapped_walk *pvmw, unsigned long size)
127125
}
128126

129127
/**
130-
* page_vma_mapped_walk - check if @pvmw->page is mapped in @pvmw->vma at
128+
* page_vma_mapped_walk - check if @pvmw->pfn is mapped in @pvmw->vma at
131129
* @pvmw->address
132130
* @pvmw: pointer to struct page_vma_mapped_walk. page, vma, address and flags
133131
* must be set. pmd, pte and ptl must be NULL.
@@ -152,8 +150,8 @@ static void step_forward(struct page_vma_mapped_walk *pvmw, unsigned long size)
152150
*/
153151
bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
154152
{
155-
struct mm_struct *mm = pvmw->vma->vm_mm;
156-
struct page *page = pvmw->page;
153+
struct vm_area_struct *vma = pvmw->vma;
154+
struct mm_struct *mm = vma->vm_mm;
157155
unsigned long end;
158156
pgd_t *pgd;
159157
p4d_t *p4d;
@@ -164,32 +162,26 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
164162
if (pvmw->pmd && !pvmw->pte)
165163
return not_found(pvmw);
166164

167-
if (unlikely(PageHuge(page))) {
165+
if (unlikely(is_vm_hugetlb_page(vma))) {
166+
unsigned long size = pvmw->nr_pages * PAGE_SIZE;
168167
/* The only possible mapping was handled on last iteration */
169168
if (pvmw->pte)
170169
return not_found(pvmw);
171170

172171
/* when pud is not present, pte will be NULL */
173-
pvmw->pte = huge_pte_offset(mm, pvmw->address, page_size(page));
172+
pvmw->pte = huge_pte_offset(mm, pvmw->address, size);
174173
if (!pvmw->pte)
175174
return false;
176175

177-
pvmw->ptl = huge_pte_lockptr(page_hstate(page), mm, pvmw->pte);
176+
pvmw->ptl = huge_pte_lockptr(size_to_hstate(size), mm,
177+
pvmw->pte);
178178
spin_lock(pvmw->ptl);
179179
if (!check_pte(pvmw))
180180
return not_found(pvmw);
181181
return true;
182182
}
183183

184-
/*
185-
* Seek to next pte only makes sense for THP.
186-
* But more important than that optimization, is to filter out
187-
* any PageKsm page: whose page->index misleads vma_address()
188-
* and vma_address_end() to disaster.
189-
*/
190-
end = PageTransCompound(page) ?
191-
vma_address_end(page, pvmw->vma) :
192-
pvmw->address + PAGE_SIZE;
184+
end = vma_address_end(pvmw);
193185
if (pvmw->pte)
194186
goto next_pte;
195187
restart:
@@ -224,7 +216,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
224216
if (likely(pmd_trans_huge(pmde))) {
225217
if (pvmw->flags & PVMW_MIGRATION)
226218
return not_found(pvmw);
227-
if (pmd_page(pmde) != page)
219+
if (!check_pmd(pmd_pfn(pmde), pvmw))
228220
return not_found(pvmw);
229221
return true;
230222
}
@@ -236,7 +228,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
236228
return not_found(pvmw);
237229
entry = pmd_to_swp_entry(pmde);
238230
if (!is_migration_entry(entry) ||
239-
pfn_swap_entry_to_page(entry) != page)
231+
!check_pmd(swp_offset(entry), pvmw))
240232
return not_found(pvmw);
241233
return true;
242234
}
@@ -250,7 +242,8 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
250242
* cleared *pmd but not decremented compound_mapcount().
251243
*/
252244
if ((pvmw->flags & PVMW_SYNC) &&
253-
PageTransCompound(page)) {
245+
transparent_hugepage_active(vma) &&
246+
(pvmw->nr_pages >= HPAGE_PMD_NR)) {
254247
spinlock_t *ptl = pmd_lock(mm, pvmw->pmd);
255248

256249
spin_unlock(ptl);
@@ -307,7 +300,8 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
307300
int page_mapped_in_vma(struct page *page, struct vm_area_struct *vma)
308301
{
309302
struct page_vma_mapped_walk pvmw = {
310-
.page = page,
303+
.pfn = page_to_pfn(page),
304+
.nr_pages = 1,
311305
.vma = vma,
312306
.flags = PVMW_SYNC,
313307
};

mm/rmap.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ static bool page_mkclean_one(struct page *page, struct vm_area_struct *vma,
940940
*/
941941
mmu_notifier_range_init(&range, MMU_NOTIFY_PROTECTION_PAGE,
942942
0, vma, vma->vm_mm, address,
943-
vma_address_end(page, vma));
943+
vma_address_end(&pvmw));
944944
mmu_notifier_invalidate_range_start(&range);
945945

946946
while (page_vma_mapped_walk(&pvmw)) {
@@ -1437,8 +1437,7 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
14371437
* Note that the page can not be free in this function as call of
14381438
* try_to_unmap() must hold a reference on the page.
14391439
*/
1440-
range.end = PageKsm(page) ?
1441-
address + PAGE_SIZE : vma_address_end(page, vma);
1440+
range.end = vma_address_end(&pvmw);
14421441
mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, vma->vm_mm,
14431442
address, range.end);
14441443
if (PageHuge(page)) {
@@ -1732,8 +1731,7 @@ static bool try_to_migrate_one(struct page *page, struct vm_area_struct *vma,
17321731
* Note that the page can not be free in this function as call of
17331732
* try_to_unmap() must hold a reference on the page.
17341733
*/
1735-
range.end = PageKsm(page) ?
1736-
address + PAGE_SIZE : vma_address_end(page, vma);
1734+
range.end = vma_address_end(&pvmw);
17371735
mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, vma->vm_mm,
17381736
address, range.end);
17391737
if (PageHuge(page)) {

0 commit comments

Comments
 (0)