Skip to content

Commit 8422acd

Browse files
uarif1akpm00
authored andcommitted
mm: introduce a pageflag for partially mapped folios
Currently folio->_deferred_list is used to keep track of partially_mapped folios that are going to be split under memory pressure. In the next patch, all THPs that are faulted in and collapsed by khugepaged are also going to be tracked using _deferred_list. This patch introduces a pageflag to be able to distinguish between partially mapped folios and others in the deferred_list at split time in deferred_split_scan. Its needed as __folio_remove_rmap decrements _mapcount, _large_mapcount and _entire_mapcount, hence it won't be possible to distinguish between partially mapped folios and others in deferred_split_scan. Eventhough it introduces an extra flag to track if the folio is partially mapped, there is no functional change intended with this patch and the flag is not useful in this patch itself, it will become useful in the next patch when _deferred_list has non partially mapped folios. Link: https://lkml.kernel.org/r/20240830100438.3623486-5-usamaarif642@gmail.com Signed-off-by: Usama Arif <usamaarif642@gmail.com> Cc: Alexander Zhu <alexlzhu@fb.com> Cc: Barry Song <baohua@kernel.org> Cc: David Hildenbrand <david@redhat.com> Cc: Domenico Cerasuolo <cerasuolodomenico@gmail.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Kairui Song <ryncsn@gmail.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Mike Rapoport <rppt@kernel.org> Cc: Nico Pache <npache@redhat.com> Cc: Rik van Riel <riel@surriel.com> Cc: Roman Gushchin <roman.gushchin@linux.dev> Cc: Ryan Roberts <ryan.roberts@arm.com> Cc: Shakeel Butt <shakeel.butt@linux.dev> Cc: Shuang Zhai <zhais@google.com> Cc: Yu Zhao <yuzhao@google.com> Cc: Shuang Zhai <szhai2@cs.rochester.edu> Cc: Hugh Dickins <hughd@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 391e869 commit 8422acd

File tree

8 files changed

+56
-21
lines changed

8 files changed

+56
-21
lines changed

include/linux/huge_mm.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ static inline int split_huge_page(struct page *page)
333333
{
334334
return split_huge_page_to_list_to_order(page, NULL, 0);
335335
}
336-
void deferred_split_folio(struct folio *folio);
336+
void deferred_split_folio(struct folio *folio, bool partially_mapped);
337337

338338
void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
339339
unsigned long address, bool freeze, struct folio *folio);
@@ -507,7 +507,7 @@ static inline int split_huge_page(struct page *page)
507507
{
508508
return 0;
509509
}
510-
static inline void deferred_split_folio(struct folio *folio) {}
510+
static inline void deferred_split_folio(struct folio *folio, bool partially_mapped) {}
511511
#define split_huge_pmd(__vma, __pmd, __address) \
512512
do { } while (0)
513513

include/linux/page-flags.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ enum pageflags {
186186
/* At least one page in this folio has the hwpoison flag set */
187187
PG_has_hwpoisoned = PG_active,
188188
PG_large_rmappable = PG_workingset, /* anon or file-backed */
189+
PG_partially_mapped = PG_reclaim, /* was identified to be partially mapped */
189190
};
190191

191192
#define PAGEFLAGS_MASK ((1UL << NR_PAGEFLAGS) - 1)
@@ -859,8 +860,18 @@ static inline void ClearPageCompound(struct page *page)
859860
ClearPageHead(page);
860861
}
861862
FOLIO_FLAG(large_rmappable, FOLIO_SECOND_PAGE)
863+
FOLIO_TEST_FLAG(partially_mapped, FOLIO_SECOND_PAGE)
864+
/*
865+
* PG_partially_mapped is protected by deferred_split split_queue_lock,
866+
* so its safe to use non-atomic set/clear.
867+
*/
868+
__FOLIO_SET_FLAG(partially_mapped, FOLIO_SECOND_PAGE)
869+
__FOLIO_CLEAR_FLAG(partially_mapped, FOLIO_SECOND_PAGE)
862870
#else
863871
FOLIO_FLAG_FALSE(large_rmappable)
872+
FOLIO_TEST_FLAG_FALSE(partially_mapped)
873+
__FOLIO_SET_FLAG_NOOP(partially_mapped)
874+
__FOLIO_CLEAR_FLAG_NOOP(partially_mapped)
864875
#endif
865876

866877
#define PG_head_mask ((1UL << PG_head))
@@ -1171,7 +1182,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
11711182
*/
11721183
#define PAGE_FLAGS_SECOND \
11731184
(0xffUL /* order */ | 1UL << PG_has_hwpoisoned | \
1174-
1UL << PG_large_rmappable)
1185+
1UL << PG_large_rmappable | 1UL << PG_partially_mapped)
11751186

11761187
#define PAGE_FLAGS_PRIVATE \
11771188
(1UL << PG_private | 1UL << PG_private_2)

mm/huge_memory.c

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3459,7 +3459,11 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
34593459
if (folio_order(folio) > 1 &&
34603460
!list_empty(&folio->_deferred_list)) {
34613461
ds_queue->split_queue_len--;
3462-
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
3462+
if (folio_test_partially_mapped(folio)) {
3463+
__folio_clear_partially_mapped(folio);
3464+
mod_mthp_stat(folio_order(folio),
3465+
MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
3466+
}
34633467
/*
34643468
* Reinitialize page_deferred_list after removing the
34653469
* page from the split_queue, otherwise a subsequent
@@ -3526,13 +3530,18 @@ void __folio_undo_large_rmappable(struct folio *folio)
35263530
spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
35273531
if (!list_empty(&folio->_deferred_list)) {
35283532
ds_queue->split_queue_len--;
3529-
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
3533+
if (folio_test_partially_mapped(folio)) {
3534+
__folio_clear_partially_mapped(folio);
3535+
mod_mthp_stat(folio_order(folio),
3536+
MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
3537+
}
35303538
list_del_init(&folio->_deferred_list);
35313539
}
35323540
spin_unlock_irqrestore(&ds_queue->split_queue_lock, flags);
35333541
}
35343542

3535-
void deferred_split_folio(struct folio *folio)
3543+
/* partially_mapped=false won't clear PG_partially_mapped folio flag */
3544+
void deferred_split_folio(struct folio *folio, bool partially_mapped)
35363545
{
35373546
struct deferred_split *ds_queue = get_deferred_split_queue(folio);
35383547
#ifdef CONFIG_MEMCG
@@ -3560,15 +3569,21 @@ void deferred_split_folio(struct folio *folio)
35603569
if (folio_test_swapcache(folio))
35613570
return;
35623571

3563-
if (!list_empty(&folio->_deferred_list))
3564-
return;
3565-
35663572
spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
3573+
if (partially_mapped) {
3574+
if (!folio_test_partially_mapped(folio)) {
3575+
__folio_set_partially_mapped(folio);
3576+
if (folio_test_pmd_mappable(folio))
3577+
count_vm_event(THP_DEFERRED_SPLIT_PAGE);
3578+
count_mthp_stat(folio_order(folio), MTHP_STAT_SPLIT_DEFERRED);
3579+
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, 1);
3580+
3581+
}
3582+
} else {
3583+
/* partially mapped folios cannot become non-partially mapped */
3584+
VM_WARN_ON_FOLIO(folio_test_partially_mapped(folio), folio);
3585+
}
35673586
if (list_empty(&folio->_deferred_list)) {
3568-
if (folio_test_pmd_mappable(folio))
3569-
count_vm_event(THP_DEFERRED_SPLIT_PAGE);
3570-
count_mthp_stat(folio_order(folio), MTHP_STAT_SPLIT_DEFERRED);
3571-
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, 1);
35723587
list_add_tail(&folio->_deferred_list, &ds_queue->split_queue);
35733588
ds_queue->split_queue_len++;
35743589
#ifdef CONFIG_MEMCG
@@ -3616,7 +3631,11 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
36163631
list_move(&folio->_deferred_list, &list);
36173632
} else {
36183633
/* We lost race with folio_put() */
3619-
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
3634+
if (folio_test_partially_mapped(folio)) {
3635+
__folio_clear_partially_mapped(folio);
3636+
mod_mthp_stat(folio_order(folio),
3637+
MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
3638+
}
36203639
list_del_init(&folio->_deferred_list);
36213640
ds_queue->split_queue_len--;
36223641
}

mm/memcontrol.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4645,7 +4645,8 @@ static void uncharge_folio(struct folio *folio, struct uncharge_gather *ug)
46454645
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
46464646
VM_BUG_ON_FOLIO(folio_order(folio) > 1 &&
46474647
!folio_test_hugetlb(folio) &&
4648-
!list_empty(&folio->_deferred_list), folio);
4648+
!list_empty(&folio->_deferred_list) &&
4649+
folio_test_partially_mapped(folio), folio);
46494650

46504651
/*
46514652
* Nobody should be changing or seriously looking at

mm/migrate.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1766,7 +1766,8 @@ static int migrate_pages_batch(struct list_head *from,
17661766
* use _deferred_list.
17671767
*/
17681768
if (nr_pages > 2 &&
1769-
!list_empty(&folio->_deferred_list)) {
1769+
!list_empty(&folio->_deferred_list) &&
1770+
folio_test_partially_mapped(folio)) {
17701771
if (!try_split_folio(folio, split_folios, mode)) {
17711772
nr_failed++;
17721773
stats->nr_thp_failed += is_thp;

mm/page_alloc.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -962,8 +962,9 @@ static int free_tail_page_prepare(struct page *head_page, struct page *page)
962962
break;
963963
case 2:
964964
/* the second tail page: deferred_list overlaps ->mapping */
965-
if (unlikely(!list_empty(&folio->_deferred_list))) {
966-
bad_page(page, "on deferred list");
965+
if (unlikely(!list_empty(&folio->_deferred_list) &&
966+
folio_test_partially_mapped(folio))) {
967+
bad_page(page, "partially mapped folio on deferred list");
967968
goto out;
968969
}
969970
break;

mm/rmap.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,8 +1579,9 @@ static __always_inline void __folio_remove_rmap(struct folio *folio,
15791579
* Check partially_mapped first to ensure it is a large folio.
15801580
*/
15811581
if (partially_mapped && folio_test_anon(folio) &&
1582-
list_empty(&folio->_deferred_list))
1583-
deferred_split_folio(folio);
1582+
!folio_test_partially_mapped(folio))
1583+
deferred_split_folio(folio, true);
1584+
15841585
__folio_mod_stat(folio, -nr, -nr_pmdmapped);
15851586

15861587
/*

mm/vmscan.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1238,7 +1238,8 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
12381238
* Split partially mapped folios right away.
12391239
* We can free the unmapped pages without IO.
12401240
*/
1241-
if (data_race(!list_empty(&folio->_deferred_list)) &&
1241+
if (data_race(!list_empty(&folio->_deferred_list) &&
1242+
folio_test_partially_mapped(folio)) &&
12421243
split_folio_to_list(folio, folio_list))
12431244
goto activate_locked;
12441245
}

0 commit comments

Comments
 (0)