Skip to content

Commit 6287b7d

Browse files
Hugh Dickinsakpm00
authored andcommitted
mm,thp,rmap: fix races between updates of subpages_mapcount
Commit 4b51634, introducing the COMPOUND_MAPPED bit, paid attention to the impossibility of subpages_mapcount ever appearing negative; but did not attend to those races in which it can momentarily appear larger than thought possible. These arise from how page_remove_rmap() first decrements page->_mapcount or compound_mapcount, then, if that transition goes negative (logical 0), decrements subpages_mapcount. The initial decrement lets a racing page_add_*_rmap() reincrement _mapcount or compound_mapcount immediately, and then in rare cases its corresponding increment of subpages_mapcount may be completed before page_remove_rmap()'s decrement. There could even (with increasing unlikelihood) be a series of increments intermixed with the decrements. In practice, checking subpages_mapcount with a temporary WARN on range, has caught values of 0x1000000 (2*COMPOUND_MAPPED, when move_pages() was using remove_migration_pmd()) and 0x800201 (do_huge_pmd_wp_page() using __split_huge_pmd()): page_add_anon_rmap() racing page_remove_rmap(), as predicted. I certainly found it harder to reason about than when bit_spin_locked, but the easy case gives a clue to how to handle the harder case. The easy case being the three !(nr & COMPOUND_MAPPED) checks, which should obviously be replaced by (nr < COMPOUND_MAPPED) checks - to count a page as compound mapped, even while the bit in that position is 0. The harder case is when trying to decide how many subpages are newly covered or uncovered, when compound map is first added or last removed: not knowing all that racily happened between first and second atomic ops. But the easy way to handle that, is again to count the page as compound mapped all the while that its subpages_mapcount indicates so - ignoring the _mapcount or compound_mapcount transition while it is on the way to being reversed. Link: https://lkml.kernel.org/r/[email protected] Fixes: 4b51634 ("mm,thp,rmap: subpages_mapcount COMPOUND_MAPPED if PMD-mapped") Signed-off-by: Hugh Dickins <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: James Houghton <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: John Hubbard <[email protected]> Cc: "Kirill A . Shutemov" <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Miaohe Lin <[email protected]> Cc: Mike Kravetz <[email protected]> Cc: Mina Almasry <[email protected]> Cc: Muchun Song <[email protected]> Cc: Naoya Horiguchi <[email protected]> Cc: Peter Xu <[email protected]> Cc: Sidhartha Kumar <[email protected]> Cc: Vlastimil Babka <[email protected]> Cc: Yang Shi <[email protected]> Cc: Zach O'Keefe <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent c449deb commit 6287b7d

File tree

1 file changed

+33
-9
lines changed

1 file changed

+33
-9
lines changed

mm/rmap.c

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,7 @@ void page_add_anon_rmap(struct page *page,
12321232
if (first && PageCompound(page)) {
12331233
mapped = subpages_mapcount_ptr(compound_head(page));
12341234
nr = atomic_inc_return_relaxed(mapped);
1235-
nr = !(nr & COMPOUND_MAPPED);
1235+
nr = (nr < COMPOUND_MAPPED);
12361236
}
12371237
} else if (PageTransHuge(page)) {
12381238
/* That test is redundant: it's for safety or to optimize out */
@@ -1241,8 +1241,16 @@ void page_add_anon_rmap(struct page *page,
12411241
if (first) {
12421242
mapped = subpages_mapcount_ptr(page);
12431243
nr = atomic_add_return_relaxed(COMPOUND_MAPPED, mapped);
1244-
nr_pmdmapped = thp_nr_pages(page);
1245-
nr = nr_pmdmapped - (nr & SUBPAGES_MAPPED);
1244+
if (likely(nr < COMPOUND_MAPPED + COMPOUND_MAPPED)) {
1245+
nr_pmdmapped = thp_nr_pages(page);
1246+
nr = nr_pmdmapped - (nr & SUBPAGES_MAPPED);
1247+
/* Raced ahead of a remove and another add? */
1248+
if (unlikely(nr < 0))
1249+
nr = 0;
1250+
} else {
1251+
/* Raced ahead of a remove of COMPOUND_MAPPED */
1252+
nr = 0;
1253+
}
12461254
}
12471255
}
12481256

@@ -1330,7 +1338,7 @@ void page_add_file_rmap(struct page *page,
13301338
if (first && PageCompound(page)) {
13311339
mapped = subpages_mapcount_ptr(compound_head(page));
13321340
nr = atomic_inc_return_relaxed(mapped);
1333-
nr = !(nr & COMPOUND_MAPPED);
1341+
nr = (nr < COMPOUND_MAPPED);
13341342
}
13351343
} else if (PageTransHuge(page)) {
13361344
/* That test is redundant: it's for safety or to optimize out */
@@ -1339,8 +1347,16 @@ void page_add_file_rmap(struct page *page,
13391347
if (first) {
13401348
mapped = subpages_mapcount_ptr(page);
13411349
nr = atomic_add_return_relaxed(COMPOUND_MAPPED, mapped);
1342-
nr_pmdmapped = thp_nr_pages(page);
1343-
nr = nr_pmdmapped - (nr & SUBPAGES_MAPPED);
1350+
if (likely(nr < COMPOUND_MAPPED + COMPOUND_MAPPED)) {
1351+
nr_pmdmapped = thp_nr_pages(page);
1352+
nr = nr_pmdmapped - (nr & SUBPAGES_MAPPED);
1353+
/* Raced ahead of a remove and another add? */
1354+
if (unlikely(nr < 0))
1355+
nr = 0;
1356+
} else {
1357+
/* Raced ahead of a remove of COMPOUND_MAPPED */
1358+
nr = 0;
1359+
}
13441360
}
13451361
}
13461362

@@ -1387,7 +1403,7 @@ void page_remove_rmap(struct page *page,
13871403
if (last && PageCompound(page)) {
13881404
mapped = subpages_mapcount_ptr(compound_head(page));
13891405
nr = atomic_dec_return_relaxed(mapped);
1390-
nr = !(nr & COMPOUND_MAPPED);
1406+
nr = (nr < COMPOUND_MAPPED);
13911407
}
13921408
} else if (PageTransHuge(page)) {
13931409
/* That test is redundant: it's for safety or to optimize out */
@@ -1396,8 +1412,16 @@ void page_remove_rmap(struct page *page,
13961412
if (last) {
13971413
mapped = subpages_mapcount_ptr(page);
13981414
nr = atomic_sub_return_relaxed(COMPOUND_MAPPED, mapped);
1399-
nr_pmdmapped = thp_nr_pages(page);
1400-
nr = nr_pmdmapped - (nr & SUBPAGES_MAPPED);
1415+
if (likely(nr < COMPOUND_MAPPED)) {
1416+
nr_pmdmapped = thp_nr_pages(page);
1417+
nr = nr_pmdmapped - (nr & SUBPAGES_MAPPED);
1418+
/* Raced ahead of another remove and an add? */
1419+
if (unlikely(nr < 0))
1420+
nr = 0;
1421+
} else {
1422+
/* An add of COMPOUND_MAPPED raced ahead */
1423+
nr = 0;
1424+
}
14011425
}
14021426
}
14031427

0 commit comments

Comments
 (0)