Skip to content

Commit 16c29ed

Browse files
kirylsfrothwell
authored andcommitted
rmap: add argument to charge compound page
We're going to allow mapping of individual 4k pages of THP compound page. It means we cannot rely on PageTransHuge() check to decide if map/unmap small page or THP. The patch adds new argument to rmap functions to indicate whether we want to operate on whole compound page or only the small page. Signed-off-by: Kirill A. Shutemov <[email protected]> Tested-by: Sasha Levin <[email protected]> Tested-by: Aneesh Kumar K.V <[email protected]> Acked-by: Vlastimil Babka <[email protected]> Acked-by: Jerome Marchand <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Naoya Horiguchi <[email protected]> Cc: Steve Capper <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Christoph Lameter <[email protected]> Cc: David Rientjes <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 2c8a6f2 commit 16c29ed

File tree

10 files changed

+68
-48
lines changed

10 files changed

+68
-48
lines changed

include/linux/rmap.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,22 @@ static inline void anon_vma_merge(struct vm_area_struct *vma,
161161

162162
struct anon_vma *page_get_anon_vma(struct page *page);
163163

164+
/* bitflags for do_page_add_anon_rmap() */
165+
#define RMAP_EXCLUSIVE 0x01
166+
#define RMAP_COMPOUND 0x02
167+
164168
/*
165169
* rmap interfaces called when adding or removing pte of page
166170
*/
167171
void page_move_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
168-
void page_add_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
172+
void page_add_anon_rmap(struct page *, struct vm_area_struct *,
173+
unsigned long, bool);
169174
void do_page_add_anon_rmap(struct page *, struct vm_area_struct *,
170175
unsigned long, int);
171-
void page_add_new_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
176+
void page_add_new_anon_rmap(struct page *, struct vm_area_struct *,
177+
unsigned long, bool);
172178
void page_add_file_rmap(struct page *);
173-
void page_remove_rmap(struct page *);
179+
void page_remove_rmap(struct page *, bool);
174180

175181
void hugepage_add_anon_rmap(struct page *, struct vm_area_struct *,
176182
unsigned long);

kernel/events/uprobes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
175175
goto unlock;
176176

177177
get_page(kpage);
178-
page_add_new_anon_rmap(kpage, vma, addr);
178+
page_add_new_anon_rmap(kpage, vma, addr, false);
179179
mem_cgroup_commit_charge(kpage, memcg, false);
180180
lru_cache_add_active_or_unevictable(kpage, vma);
181181

@@ -188,7 +188,7 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
188188
ptep_clear_flush_notify(vma, addr, ptep);
189189
set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot));
190190

191-
page_remove_rmap(page);
191+
page_remove_rmap(page, false);
192192
if (!page_mapped(page))
193193
try_to_free_swap(page);
194194
pte_unmap_unlock(ptep, ptl);

mm/huge_memory.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ static int __do_huge_pmd_anonymous_page(struct mm_struct *mm,
828828

829829
entry = mk_huge_pmd(page, vma->vm_page_prot);
830830
entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
831-
page_add_new_anon_rmap(page, vma, haddr);
831+
page_add_new_anon_rmap(page, vma, haddr, true);
832832
mem_cgroup_commit_charge(page, memcg, false);
833833
lru_cache_add_active_or_unevictable(page, vma);
834834
pgtable_trans_huge_deposit(mm, pmd, pgtable);
@@ -1170,7 +1170,7 @@ static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,
11701170
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
11711171
memcg = (void *)page_private(pages[i]);
11721172
set_page_private(pages[i], 0);
1173-
page_add_new_anon_rmap(pages[i], vma, haddr);
1173+
page_add_new_anon_rmap(pages[i], vma, haddr, false);
11741174
mem_cgroup_commit_charge(pages[i], memcg, false);
11751175
lru_cache_add_active_or_unevictable(pages[i], vma);
11761176
pte = pte_offset_map(&_pmd, haddr);
@@ -1182,7 +1182,7 @@ static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,
11821182

11831183
smp_wmb(); /* make pte visible before pmd */
11841184
pmd_populate(mm, pmd, pgtable);
1185-
page_remove_rmap(page);
1185+
page_remove_rmap(page, true);
11861186
spin_unlock(ptl);
11871187

11881188
mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
@@ -1302,7 +1302,7 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
13021302
entry = mk_huge_pmd(new_page, vma->vm_page_prot);
13031303
entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
13041304
pmdp_huge_clear_flush_notify(vma, haddr, pmd);
1305-
page_add_new_anon_rmap(new_page, vma, haddr);
1305+
page_add_new_anon_rmap(new_page, vma, haddr, true);
13061306
mem_cgroup_commit_charge(new_page, memcg, false);
13071307
lru_cache_add_active_or_unevictable(new_page, vma);
13081308
set_pmd_at(mm, haddr, pmd, entry);
@@ -1312,7 +1312,7 @@ int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
13121312
put_huge_zero_page();
13131313
} else {
13141314
VM_BUG_ON_PAGE(!PageHead(page), page);
1315-
page_remove_rmap(page);
1315+
page_remove_rmap(page, true);
13161316
put_page(page);
13171317
}
13181318
ret |= VM_FAULT_WRITE;
@@ -1539,7 +1539,7 @@ int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
15391539
put_huge_zero_page();
15401540
} else {
15411541
struct page *page = pmd_page(orig_pmd);
1542-
page_remove_rmap(page);
1542+
page_remove_rmap(page, true);
15431543
VM_BUG_ON_PAGE(page_mapcount(page) < 0, page);
15441544
add_mm_counter(tlb->mm, MM_ANONPAGES, -HPAGE_PMD_NR);
15451545
VM_BUG_ON_PAGE(!PageHead(page), page);
@@ -2402,7 +2402,7 @@ static void __collapse_huge_page_copy(pte_t *pte, struct page *page,
24022402
* superfluous.
24032403
*/
24042404
pte_clear(vma->vm_mm, address, _pte);
2405-
page_remove_rmap(src_page);
2405+
page_remove_rmap(src_page, false);
24062406
spin_unlock(ptl);
24072407
free_page_and_swap_cache(src_page);
24082408
}
@@ -2753,7 +2753,7 @@ static void collapse_huge_page(struct mm_struct *mm,
27532753

27542754
spin_lock(pmd_ptl);
27552755
BUG_ON(!pmd_none(*pmd));
2756-
page_add_new_anon_rmap(new_page, vma, address);
2756+
page_add_new_anon_rmap(new_page, vma, address, true);
27572757
mem_cgroup_commit_charge(new_page, memcg, false);
27582758
lru_cache_add_active_or_unevictable(new_page, vma);
27592759
pgtable_trans_huge_deposit(mm, pmd, pgtable);

mm/hugetlb.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3186,7 +3186,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
31863186
set_page_dirty(page);
31873187

31883188
hugetlb_count_sub(pages_per_huge_page(h), mm);
3189-
page_remove_rmap(page);
3189+
page_remove_rmap(page, true);
31903190
force_flush = !__tlb_remove_page(tlb, page);
31913191
if (force_flush) {
31923192
address += sz;
@@ -3415,7 +3415,7 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
34153415
mmu_notifier_invalidate_range(mm, mmun_start, mmun_end);
34163416
set_huge_pte_at(mm, address, ptep,
34173417
make_huge_pte(vma, new_page, 1));
3418-
page_remove_rmap(old_page);
3418+
page_remove_rmap(old_page, true);
34193419
hugepage_add_new_anon_rmap(new_page, vma, address);
34203420
/* Make the old page be freed below */
34213421
new_page = old_page;

mm/ksm.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,13 +1110,13 @@ static int replace_page(struct vm_area_struct *vma, struct page *page,
11101110
}
11111111

11121112
get_page(kpage);
1113-
page_add_anon_rmap(kpage, vma, addr);
1113+
page_add_anon_rmap(kpage, vma, addr, false);
11141114

11151115
flush_cache_page(vma, addr, pte_pfn(*ptep));
11161116
ptep_clear_flush_notify(vma, addr, ptep);
11171117
set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot));
11181118

1119-
page_remove_rmap(page);
1119+
page_remove_rmap(page, false);
11201120
if (!page_mapped(page))
11211121
try_to_free_swap(page);
11221122
put_page(page);

mm/memory.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,7 +1118,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
11181118
mark_page_accessed(page);
11191119
}
11201120
rss[mm_counter(page)]--;
1121-
page_remove_rmap(page);
1121+
page_remove_rmap(page, false);
11221122
if (unlikely(page_mapcount(page) < 0))
11231123
print_bad_pte(vma, addr, ptent, page);
11241124
if (unlikely(!__tlb_remove_page(tlb, page))) {
@@ -2118,7 +2118,7 @@ static int wp_page_copy(struct mm_struct *mm, struct vm_area_struct *vma,
21182118
* thread doing COW.
21192119
*/
21202120
ptep_clear_flush_notify(vma, address, page_table);
2121-
page_add_new_anon_rmap(new_page, vma, address);
2121+
page_add_new_anon_rmap(new_page, vma, address, false);
21222122
mem_cgroup_commit_charge(new_page, memcg, false);
21232123
lru_cache_add_active_or_unevictable(new_page, vma);
21242124
/*
@@ -2151,7 +2151,7 @@ static int wp_page_copy(struct mm_struct *mm, struct vm_area_struct *vma,
21512151
* mapcount is visible. So transitively, TLBs to
21522152
* old page will be flushed before it can be reused.
21532153
*/
2154-
page_remove_rmap(old_page);
2154+
page_remove_rmap(old_page, false);
21552155
}
21562156

21572157
/* Free the old page.. */
@@ -2567,7 +2567,7 @@ int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
25672567
pte = maybe_mkwrite(pte_mkdirty(pte), vma);
25682568
flags &= ~FAULT_FLAG_WRITE;
25692569
ret |= VM_FAULT_WRITE;
2570-
exclusive = 1;
2570+
exclusive = RMAP_EXCLUSIVE;
25712571
}
25722572
flush_icache_page(vma, page);
25732573
if (pte_swp_soft_dirty(orig_pte))
@@ -2577,7 +2577,7 @@ int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
25772577
do_page_add_anon_rmap(page, vma, address, exclusive);
25782578
mem_cgroup_commit_charge(page, memcg, true);
25792579
} else { /* ksm created a completely new copy */
2580-
page_add_new_anon_rmap(page, vma, address);
2580+
page_add_new_anon_rmap(page, vma, address, false);
25812581
mem_cgroup_commit_charge(page, memcg, false);
25822582
lru_cache_add_active_or_unevictable(page, vma);
25832583
}
@@ -2735,7 +2735,7 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
27352735
}
27362736

27372737
inc_mm_counter_fast(mm, MM_ANONPAGES);
2738-
page_add_new_anon_rmap(page, vma, address);
2738+
page_add_new_anon_rmap(page, vma, address, false);
27392739
mem_cgroup_commit_charge(page, memcg, false);
27402740
lru_cache_add_active_or_unevictable(page, vma);
27412741
setpte:
@@ -2824,7 +2824,7 @@ void do_set_pte(struct vm_area_struct *vma, unsigned long address,
28242824
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
28252825
if (anon) {
28262826
inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
2827-
page_add_new_anon_rmap(page, vma, address);
2827+
page_add_new_anon_rmap(page, vma, address, false);
28282828
} else {
28292829
inc_mm_counter_fast(vma->vm_mm, mm_counter_file(page));
28302830
page_add_file_rmap(page);

mm/migrate.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ static int remove_migration_pte(struct page *new, struct vm_area_struct *vma,
167167
else
168168
page_dup_rmap(new);
169169
} else if (PageAnon(new))
170-
page_add_anon_rmap(new, vma, addr);
170+
page_add_anon_rmap(new, vma, addr, false);
171171
else
172172
page_add_file_rmap(new);
173173

@@ -1815,7 +1815,7 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
18151815
* guarantee the copy is visible before the pagetable update.
18161816
*/
18171817
flush_cache_range(vma, mmun_start, mmun_end);
1818-
page_add_anon_rmap(new_page, vma, mmun_start);
1818+
page_add_anon_rmap(new_page, vma, mmun_start, true);
18191819
pmdp_huge_clear_flush_notify(vma, mmun_start, pmd);
18201820
set_pmd_at(mm, mmun_start, pmd, entry);
18211821
flush_tlb_range(vma, mmun_start, mmun_end);
@@ -1826,14 +1826,14 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
18261826
flush_tlb_range(vma, mmun_start, mmun_end);
18271827
mmu_notifier_invalidate_range(mm, mmun_start, mmun_end);
18281828
update_mmu_cache_pmd(vma, address, &entry);
1829-
page_remove_rmap(new_page);
1829+
page_remove_rmap(new_page, true);
18301830
goto fail_putback;
18311831
}
18321832

18331833
mlock_migrate_page(new_page, page);
18341834
set_page_memcg(new_page, page_memcg(page));
18351835
set_page_memcg(page, NULL);
1836-
page_remove_rmap(page);
1836+
page_remove_rmap(page, true);
18371837

18381838
spin_unlock(ptl);
18391839
mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);

mm/rmap.c

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,16 +1133,17 @@ static void __page_check_anon_rmap(struct page *page,
11331133
* @page: the page to add the mapping to
11341134
* @vma: the vm area in which the mapping is added
11351135
* @address: the user virtual address mapped
1136+
* @compound: charge the page as compound or small page
11361137
*
11371138
* The caller needs to hold the pte lock, and the page must be locked in
11381139
* the anon_vma case: to serialize mapping,index checking after setting,
11391140
* and to ensure that PageAnon is not being upgraded racily to PageKsm
11401141
* (but PageKsm is never downgraded to PageAnon).
11411142
*/
11421143
void page_add_anon_rmap(struct page *page,
1143-
struct vm_area_struct *vma, unsigned long address)
1144+
struct vm_area_struct *vma, unsigned long address, bool compound)
11441145
{
1145-
do_page_add_anon_rmap(page, vma, address, 0);
1146+
do_page_add_anon_rmap(page, vma, address, compound ? RMAP_COMPOUND : 0);
11461147
}
11471148

11481149
/*
@@ -1151,29 +1152,33 @@ void page_add_anon_rmap(struct page *page,
11511152
* Everybody else should continue to use page_add_anon_rmap above.
11521153
*/
11531154
void do_page_add_anon_rmap(struct page *page,
1154-
struct vm_area_struct *vma, unsigned long address, int exclusive)
1155+
struct vm_area_struct *vma, unsigned long address, int flags)
11551156
{
11561157
int first = atomic_inc_and_test(&page->_mapcount);
11571158
if (first) {
1159+
bool compound = flags & RMAP_COMPOUND;
1160+
int nr = compound ? hpage_nr_pages(page) : 1;
11581161
/*
11591162
* We use the irq-unsafe __{inc|mod}_zone_page_stat because
11601163
* these counters are not modified in interrupt context, and
11611164
* pte lock(a spinlock) is held, which implies preemption
11621165
* disabled.
11631166
*/
1164-
if (PageTransHuge(page))
1167+
if (compound) {
1168+
VM_BUG_ON_PAGE(!PageTransHuge(page), page);
11651169
__inc_zone_page_state(page,
11661170
NR_ANON_TRANSPARENT_HUGEPAGES);
1167-
__mod_zone_page_state(page_zone(page), NR_ANON_PAGES,
1168-
hpage_nr_pages(page));
1171+
}
1172+
__mod_zone_page_state(page_zone(page), NR_ANON_PAGES, nr);
11691173
}
11701174
if (unlikely(PageKsm(page)))
11711175
return;
11721176

11731177
VM_BUG_ON_PAGE(!PageLocked(page), page);
11741178
/* address might be in next vma when migration races vma_adjust */
11751179
if (first)
1176-
__page_set_anon_rmap(page, vma, address, exclusive);
1180+
__page_set_anon_rmap(page, vma, address,
1181+
flags & RMAP_EXCLUSIVE);
11771182
else
11781183
__page_check_anon_rmap(page, vma, address);
11791184
}
@@ -1183,21 +1188,25 @@ void do_page_add_anon_rmap(struct page *page,
11831188
* @page: the page to add the mapping to
11841189
* @vma: the vm area in which the mapping is added
11851190
* @address: the user virtual address mapped
1191+
* @compound: charge the page as compound or small page
11861192
*
11871193
* Same as page_add_anon_rmap but must only be called on *new* pages.
11881194
* This means the inc-and-test can be bypassed.
11891195
* Page does not have to be locked.
11901196
*/
11911197
void page_add_new_anon_rmap(struct page *page,
1192-
struct vm_area_struct *vma, unsigned long address)
1198+
struct vm_area_struct *vma, unsigned long address, bool compound)
11931199
{
1200+
int nr = compound ? hpage_nr_pages(page) : 1;
1201+
11941202
VM_BUG_ON_VMA(address < vma->vm_start || address >= vma->vm_end, vma);
11951203
SetPageSwapBacked(page);
11961204
atomic_set(&page->_mapcount, 0); /* increment count (starts at -1) */
1197-
if (PageTransHuge(page))
1205+
if (compound) {
1206+
VM_BUG_ON_PAGE(!PageTransHuge(page), page);
11981207
__inc_zone_page_state(page, NR_ANON_TRANSPARENT_HUGEPAGES);
1199-
__mod_zone_page_state(page_zone(page), NR_ANON_PAGES,
1200-
hpage_nr_pages(page));
1208+
}
1209+
__mod_zone_page_state(page_zone(page), NR_ANON_PAGES, nr);
12011210
__page_set_anon_rmap(page, vma, address, 1);
12021211
}
12031212

@@ -1249,13 +1258,17 @@ static void page_remove_file_rmap(struct page *page)
12491258

12501259
/**
12511260
* page_remove_rmap - take down pte mapping from a page
1252-
* @page: page to remove mapping from
1261+
* @page: page to remove mapping from
1262+
* @compound: uncharge the page as compound or small page
12531263
*
12541264
* The caller needs to hold the pte lock.
12551265
*/
1256-
void page_remove_rmap(struct page *page)
1266+
void page_remove_rmap(struct page *page, bool compound)
12571267
{
1268+
int nr = compound ? hpage_nr_pages(page) : 1;
1269+
12581270
if (!PageAnon(page)) {
1271+
VM_BUG_ON_PAGE(compound && !PageHuge(page), page);
12591272
page_remove_file_rmap(page);
12601273
return;
12611274
}
@@ -1273,11 +1286,12 @@ void page_remove_rmap(struct page *page)
12731286
* these counters are not modified in interrupt context, and
12741287
* pte lock(a spinlock) is held, which implies preemption disabled.
12751288
*/
1276-
if (PageTransHuge(page))
1289+
if (compound) {
1290+
VM_BUG_ON_PAGE(!PageTransHuge(page), page);
12771291
__dec_zone_page_state(page, NR_ANON_TRANSPARENT_HUGEPAGES);
1292+
}
12781293

1279-
__mod_zone_page_state(page_zone(page), NR_ANON_PAGES,
1280-
-hpage_nr_pages(page));
1294+
__mod_zone_page_state(page_zone(page), NR_ANON_PAGES, -nr);
12811295

12821296
if (unlikely(PageMlocked(page)))
12831297
clear_page_mlock(page);
@@ -1416,7 +1430,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
14161430
} else
14171431
dec_mm_counter(mm, mm_counter_file(page));
14181432

1419-
page_remove_rmap(page);
1433+
page_remove_rmap(page, false);
14201434
page_cache_release(page);
14211435

14221436
out_unmap:

mm/swapfile.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,10 +1160,10 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
11601160
set_pte_at(vma->vm_mm, addr, pte,
11611161
pte_mkold(mk_pte(page, vma->vm_page_prot)));
11621162
if (page == swapcache) {
1163-
page_add_anon_rmap(page, vma, addr);
1163+
page_add_anon_rmap(page, vma, addr, false);
11641164
mem_cgroup_commit_charge(page, memcg, true);
11651165
} else { /* ksm created a completely new copy */
1166-
page_add_new_anon_rmap(page, vma, addr);
1166+
page_add_new_anon_rmap(page, vma, addr, false);
11671167
mem_cgroup_commit_charge(page, memcg, false);
11681168
lru_cache_add_active_or_unevictable(page, vma);
11691169
}

0 commit comments

Comments
 (0)