History

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().

Matthew Wilcox (Oracle) committed 4 years ago

Commit 2aff7a41 parent aef13de6 files · +58 −50Browse files

Unified · Split

6 files
  1. include/linux/hugetlb.h +5 −0
  2. include/linux/rmap.h +12 −5
  3. mm/internal.h +9 −6
  4. mm/migrate.c +3 −2
  5. mm/page_vma_mapped.c +26 −32
  6. mm/rmap.c +3 −5
include/linux/hugetlb.h+5−0
@@ -970,6 +970,11 @@ static inline struct hstate *page_hstate(struct page *page) · 969 unmodified lines
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+12−5
@@ -11,6 +11,7 @@ · 10 unmodified lines
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, · 184 unmodified lines
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,7 +219,9 @@ struct page_vma_mapped_walk { · 4 unmodified lines
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, \
@@ -224,7 +229,9 @@ struct page_vma_mapped_walk { · 1 unmodified line
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 { · 2 unmodified lines
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+9−6
@@ -10,6 +10,7 @@ · 9 unmodified lines
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) · 459 unmodified lines
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+3−2
@@ -174,7 +174,8 @@ void putback_movable_pages(struct list_head *l) · 173 unmodified lines
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, · 3 unmodified lines
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+26−32
@@ -53,18 +53,6 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw) · 52 unmodified lines
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) · 45 unmodified lines
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) · 4 unmodified lines
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) · 18 unmodified lines
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) · 4 unmodified lines
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 @@ restart: · 28 unmodified lines
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 @@ restart: · 5 unmodified lines
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 @@ restart: · 7 unmodified lines
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 @@ next_pte: · 50 unmodified lines
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+3−5
@@ -940,7 +940,7 @@ static bool page_mkclean_one(struct page *page, struct vm_area_struct *vma, · 939 unmodified lines
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, · 490 unmodified lines
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, · 287 unmodified lines
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)) {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t