Skip to content

Commit ba3c4ce

Browse files
yhuang-inteltorvalds
authored andcommitted
mm, THP, swap: make reuse_swap_page() works for THP swapped out
After supporting to delay THP (Transparent Huge Page) splitting after swapped out, it is possible that some page table mappings of the THP are turned into swap entries. So reuse_swap_page() need to check the swap count in addition to the map count as before. This patch done that. In the huge PMD write protect fault handler, in addition to the page map count, the swap count need to be checked too, so the page lock need to be acquired too when calling reuse_swap_page() in addition to the page table lock. [[email protected]: silence a compiler warning] Link: http://lkml.kernel.org/r/[email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: "Huang, Ying" <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Minchan Kim <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Shaohua Li <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: "Kirill A . Shutemov" <[email protected]> Cc: Dan Williams <[email protected]> Cc: Jens Axboe <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Ross Zwisler <[email protected]> [for brd.c, zram_drv.c, pmem.c] Cc: Vishal L Verma <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent e070982 commit ba3c4ce

File tree

4 files changed

+113
-15
lines changed

4 files changed

+113
-15
lines changed

include/linux/swap.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,8 @@ static inline int swp_swapcount(swp_entry_t entry)
510510
return 0;
511511
}
512512

513-
#define reuse_swap_page(page, total_mapcount) \
514-
(page_trans_huge_mapcount(page, total_mapcount) == 1)
513+
#define reuse_swap_page(page, total_map_swapcount) \
514+
(page_trans_huge_mapcount(page, total_map_swapcount) == 1)
515515

516516
static inline int try_to_free_swap(struct page *page)
517517
{

mm/huge_memory.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1240,15 +1240,29 @@ int do_huge_pmd_wp_page(struct vm_fault *vmf, pmd_t orig_pmd)
12401240
* We can only reuse the page if nobody else maps the huge page or it's
12411241
* part.
12421242
*/
1243-
if (page_trans_huge_mapcount(page, NULL) == 1) {
1243+
if (!trylock_page(page)) {
1244+
get_page(page);
1245+
spin_unlock(vmf->ptl);
1246+
lock_page(page);
1247+
spin_lock(vmf->ptl);
1248+
if (unlikely(!pmd_same(*vmf->pmd, orig_pmd))) {
1249+
unlock_page(page);
1250+
put_page(page);
1251+
goto out_unlock;
1252+
}
1253+
put_page(page);
1254+
}
1255+
if (reuse_swap_page(page, NULL)) {
12441256
pmd_t entry;
12451257
entry = pmd_mkyoung(orig_pmd);
12461258
entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
12471259
if (pmdp_set_access_flags(vma, haddr, vmf->pmd, entry, 1))
12481260
update_mmu_cache_pmd(vma, vmf->address, vmf->pmd);
12491261
ret |= VM_FAULT_WRITE;
1262+
unlock_page(page);
12501263
goto out_unlock;
12511264
}
1265+
unlock_page(page);
12521266
get_page(page);
12531267
spin_unlock(vmf->ptl);
12541268
alloc:

mm/memory.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2619,7 +2619,7 @@ static int do_wp_page(struct vm_fault *vmf)
26192619
* not dirty accountable.
26202620
*/
26212621
if (PageAnon(vmf->page) && !PageKsm(vmf->page)) {
2622-
int total_mapcount;
2622+
int total_map_swapcount;
26232623
if (!trylock_page(vmf->page)) {
26242624
get_page(vmf->page);
26252625
pte_unmap_unlock(vmf->pte, vmf->ptl);
@@ -2634,8 +2634,8 @@ static int do_wp_page(struct vm_fault *vmf)
26342634
}
26352635
put_page(vmf->page);
26362636
}
2637-
if (reuse_swap_page(vmf->page, &total_mapcount)) {
2638-
if (total_mapcount == 1) {
2637+
if (reuse_swap_page(vmf->page, &total_map_swapcount)) {
2638+
if (total_map_swapcount == 1) {
26392639
/*
26402640
* The page is all ours. Move it to
26412641
* our anon_vma so the rmap code will

mm/swapfile.c

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,9 +1405,89 @@ static bool page_swapped(struct page *page)
14051405
return swap_page_trans_huge_swapped(si, entry);
14061406
return false;
14071407
}
1408+
1409+
static int page_trans_huge_map_swapcount(struct page *page, int *total_mapcount,
1410+
int *total_swapcount)
1411+
{
1412+
int i, map_swapcount, _total_mapcount, _total_swapcount;
1413+
unsigned long offset = 0;
1414+
struct swap_info_struct *si;
1415+
struct swap_cluster_info *ci = NULL;
1416+
unsigned char *map = NULL;
1417+
int mapcount, swapcount = 0;
1418+
1419+
/* hugetlbfs shouldn't call it */
1420+
VM_BUG_ON_PAGE(PageHuge(page), page);
1421+
1422+
if (likely(!PageTransCompound(page))) {
1423+
mapcount = atomic_read(&page->_mapcount) + 1;
1424+
if (total_mapcount)
1425+
*total_mapcount = mapcount;
1426+
if (PageSwapCache(page))
1427+
swapcount = page_swapcount(page);
1428+
if (total_swapcount)
1429+
*total_swapcount = swapcount;
1430+
return mapcount + swapcount;
1431+
}
1432+
1433+
page = compound_head(page);
1434+
1435+
_total_mapcount = _total_swapcount = map_swapcount = 0;
1436+
if (PageSwapCache(page)) {
1437+
swp_entry_t entry;
1438+
1439+
entry.val = page_private(page);
1440+
si = _swap_info_get(entry);
1441+
if (si) {
1442+
map = si->swap_map;
1443+
offset = swp_offset(entry);
1444+
}
1445+
}
1446+
if (map)
1447+
ci = lock_cluster(si, offset);
1448+
for (i = 0; i < HPAGE_PMD_NR; i++) {
1449+
mapcount = atomic_read(&page[i]._mapcount) + 1;
1450+
_total_mapcount += mapcount;
1451+
if (map) {
1452+
swapcount = swap_count(map[offset + i]);
1453+
_total_swapcount += swapcount;
1454+
}
1455+
map_swapcount = max(map_swapcount, mapcount + swapcount);
1456+
}
1457+
unlock_cluster(ci);
1458+
if (PageDoubleMap(page)) {
1459+
map_swapcount -= 1;
1460+
_total_mapcount -= HPAGE_PMD_NR;
1461+
}
1462+
mapcount = compound_mapcount(page);
1463+
map_swapcount += mapcount;
1464+
_total_mapcount += mapcount;
1465+
if (total_mapcount)
1466+
*total_mapcount = _total_mapcount;
1467+
if (total_swapcount)
1468+
*total_swapcount = _total_swapcount;
1469+
1470+
return map_swapcount;
1471+
}
14081472
#else
14091473
#define swap_page_trans_huge_swapped(si, entry) swap_swapcount(si, entry)
14101474
#define page_swapped(page) (page_swapcount(page) != 0)
1475+
1476+
static int page_trans_huge_map_swapcount(struct page *page, int *total_mapcount,
1477+
int *total_swapcount)
1478+
{
1479+
int mapcount, swapcount = 0;
1480+
1481+
/* hugetlbfs shouldn't call it */
1482+
VM_BUG_ON_PAGE(PageHuge(page), page);
1483+
1484+
mapcount = page_trans_huge_mapcount(page, total_mapcount);
1485+
if (PageSwapCache(page))
1486+
swapcount = page_swapcount(page);
1487+
if (total_swapcount)
1488+
*total_swapcount = swapcount;
1489+
return mapcount + swapcount;
1490+
}
14111491
#endif
14121492

14131493
/*
@@ -1416,23 +1496,27 @@ static bool page_swapped(struct page *page)
14161496
* on disk will never be read, and seeking back there to write new content
14171497
* later would only waste time away from clustering.
14181498
*
1419-
* NOTE: total_mapcount should not be relied upon by the caller if
1499+
* NOTE: total_map_swapcount should not be relied upon by the caller if
14201500
* reuse_swap_page() returns false, but it may be always overwritten
14211501
* (see the other implementation for CONFIG_SWAP=n).
14221502
*/
1423-
bool reuse_swap_page(struct page *page, int *total_mapcount)
1503+
bool reuse_swap_page(struct page *page, int *total_map_swapcount)
14241504
{
1425-
int count;
1505+
int count, total_mapcount, total_swapcount;
14261506

14271507
VM_BUG_ON_PAGE(!PageLocked(page), page);
14281508
if (unlikely(PageKsm(page)))
14291509
return false;
1430-
count = page_trans_huge_mapcount(page, total_mapcount);
1431-
if (count <= 1 && PageSwapCache(page)) {
1432-
count += page_swapcount(page);
1433-
if (count != 1)
1434-
goto out;
1510+
count = page_trans_huge_map_swapcount(page, &total_mapcount,
1511+
&total_swapcount);
1512+
if (total_map_swapcount)
1513+
*total_map_swapcount = total_mapcount + total_swapcount;
1514+
if (count == 1 && PageSwapCache(page) &&
1515+
(likely(!PageTransCompound(page)) ||
1516+
/* The remaining swap count will be freed soon */
1517+
total_swapcount == page_swapcount(page))) {
14351518
if (!PageWriteback(page)) {
1519+
page = compound_head(page);
14361520
delete_from_swap_cache(page);
14371521
SetPageDirty(page);
14381522
} else {
@@ -1448,7 +1532,7 @@ bool reuse_swap_page(struct page *page, int *total_mapcount)
14481532
spin_unlock(&p->lock);
14491533
}
14501534
}
1451-
out:
1535+
14521536
return count <= 1;
14531537
}
14541538

0 commit comments

Comments
 (0)