| @@ -48,6 +48,7 @@ enum scan_result { · 47 unmodified lines |
| 48 | 48 | SCAN_CGROUP_CHARGE_FAIL, |
| 49 | 49 | SCAN_EXCEED_SWAP_PTE, |
| 50 | 50 | SCAN_TRUNCATED, |
| 51 | + SCAN_PAGE_HAS_PRIVATE, |
| 51 | 52 | }; |
| 52 | 53 | |
| 53 | 54 | #define CREATE_TRACE_POINTS |
| @@ -404,7 +405,11 @@ static bool hugepage_vma_check(struct vm_area_struct *vma, · 350 unmodified lines |
| 404 | 405 | (vm_flags & VM_NOHUGEPAGE) || |
| 405 | 406 | test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags)) |
| 406 | 407 | return false; |
| 407 | | − if (shmem_file(vma->vm_file)) { |
| 408 | + |
| 409 | + if (shmem_file(vma->vm_file) || |
| 410 | + (IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS) && |
| 411 | + vma->vm_file && |
| 412 | + (vm_flags & VM_DENYWRITE))) { |
| 408 | 413 | if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE)) |
| 409 | 414 | return false; |
| 410 | 415 | return IS_ALIGNED((vma->vm_start >> PAGE_SHIFT) - vma->vm_pgoff, |
| @@ -456,8 +461,9 @@ int khugepaged_enter_vma_merge(struct vm_area_struct *vma, · 45 unmodified lines |
| 456 | 461 | unsigned long hstart, hend; |
| 457 | 462 | |
| 458 | 463 | /* |
| 459 | | − * khugepaged does not yet work on non-shmem files or special |
| 460 | | − * mappings. And file-private shmem THP is not supported. |
| 464 | + * khugepaged only supports read-only files for non-shmem files. |
| 465 | + * khugepaged does not yet work on special mappings. And |
| 466 | + * file-private shmem THP is not supported. |
| 461 | 467 | */ |
| 462 | 468 | if (!hugepage_vma_check(vma, vm_flags)) |
| 463 | 469 | return 0; |
| @@ -1287,12 +1293,12 @@ static void retract_page_tables(struct address_space *mapping, pgoff_t pgoff) · 823 unmodified lines |
| 1287 | 1293 | } |
| 1288 | 1294 | |
| 1289 | 1295 | /** |
| 1290 | | − * collapse_file - collapse small tmpfs/shmem pages into huge one. |
| 1296 | + * collapse_file - collapse filemap/tmpfs/shmem pages into huge one. |
| 1291 | 1297 | * |
| 1292 | 1298 | * Basic scheme is simple, details are more complex: |
| 1293 | 1299 | * - allocate and lock a new huge page; |
| 1294 | 1300 | * - scan page cache replacing old pages with the new one |
| 1295 | | − * + swap in pages if necessary; |
| 1301 | + * + swap/gup in pages if necessary; |
| 1296 | 1302 | * + fill in gaps; |
| 1297 | 1303 | * + keep old pages around in case rollback is required; |
| 1298 | 1304 | * - if replacing succeeds: |
| @@ -1316,7 +1322,9 @@ static void collapse_file(struct mm_struct *mm, · 17 unmodified lines |
| 1316 | 1322 | LIST_HEAD(pagelist); |
| 1317 | 1323 | XA_STATE_ORDER(xas, &mapping->i_pages, start, HPAGE_PMD_ORDER); |
| 1318 | 1324 | int nr_none = 0, result = SCAN_SUCCEED; |
| 1325 | + bool is_shmem = shmem_file(file); |
| 1319 | 1326 | |
| 1327 | + VM_BUG_ON(!IS_ENABLED(CONFIG_READ_ONLY_THP_FOR_FS) && !is_shmem); |
| 1320 | 1328 | VM_BUG_ON(start & (HPAGE_PMD_NR - 1)); |
| 1321 | 1329 | |
| 1322 | 1330 | /* Only allocate from the target node */ |
| @@ -1348,7 +1356,8 @@ static void collapse_file(struct mm_struct *mm, · 25 unmodified lines |
| 1348 | 1356 | } while (1); |
| 1349 | 1357 | |
| 1350 | 1358 | __SetPageLocked(new_page); |
| 1351 | | − __SetPageSwapBacked(new_page); |
| 1359 | + if (is_shmem) |
| 1360 | + __SetPageSwapBacked(new_page); |
| 1352 | 1361 | new_page->index = start; |
| 1353 | 1362 | new_page->mapping = mapping; |
| 1354 | 1363 | |
| @@ -1363,41 +1372,75 @@ static void collapse_file(struct mm_struct *mm, · 8 unmodified lines |
| 1363 | 1372 | struct page *page = xas_next(&xas); |
| 1364 | 1373 | |
| 1365 | 1374 | VM_BUG_ON(index != xas.xa_index); |
| 1366 | | − if (!page) { |
| 1367 | | − /* |
| 1368 | | − * Stop if extent has been truncated or hole-punched, |
| 1369 | | − * and is now completely empty. |
| 1370 | | − */ |
| 1371 | | − if (index == start) { |
| 1372 | | − if (!xas_next_entry(&xas, end - 1)) { |
| 1373 | | − result = SCAN_TRUNCATED; |
| 1375 | + if (is_shmem) { |
| 1376 | + if (!page) { |
| 1377 | + /* |
| 1378 | + * Stop if extent has been truncated or |
| 1379 | + * hole-punched, and is now completely |
| 1380 | + * empty. |
| 1381 | + */ |
| 1382 | + if (index == start) { |
| 1383 | + if (!xas_next_entry(&xas, end - 1)) { |
| 1384 | + result = SCAN_TRUNCATED; |
| 1385 | + goto xa_locked; |
| 1386 | + } |
| 1387 | + xas_set(&xas, index); |
| 1388 | + } |
| 1389 | + if (!shmem_charge(mapping->host, 1)) { |
| 1390 | + result = SCAN_FAIL; |
| 1374 | 1391 | goto xa_locked; |
| 1375 | 1392 | } |
| 1376 | | − xas_set(&xas, index); |
| 1393 | + xas_store(&xas, new_page); |
| 1394 | + nr_none++; |
| 1395 | + continue; |
| 1377 | 1396 | } |
| 1378 | | − if (!shmem_charge(mapping->host, 1)) { |
| 1379 | | − result = SCAN_FAIL; |
| 1397 | + |
| 1398 | + if (xa_is_value(page) || !PageUptodate(page)) { |
| 1399 | + xas_unlock_irq(&xas); |
| 1400 | + /* swap in or instantiate fallocated page */ |
| 1401 | + if (shmem_getpage(mapping->host, index, &page, |
| 1402 | + SGP_NOHUGE)) { |
| 1403 | + result = SCAN_FAIL; |
| 1404 | + goto xa_unlocked; |
| 1405 | + } |
| 1406 | + } else if (trylock_page(page)) { |
| 1407 | + get_page(page); |
| 1408 | + xas_unlock_irq(&xas); |
| 1409 | + } else { |
| 1410 | + result = SCAN_PAGE_LOCK; |
| 1380 | 1411 | goto xa_locked; |
| 1381 | 1412 | } |
| 1382 | | − xas_store(&xas, new_page); |
| 1383 | | − nr_none++; |
| 1384 | | − continue; |
| 1385 | | − } |
| 1386 | | − |
| 1387 | | − if (xa_is_value(page) || !PageUptodate(page)) { |
| 1388 | | − xas_unlock_irq(&xas); |
| 1389 | | − /* swap in or instantiate fallocated page */ |
| 1390 | | − if (shmem_getpage(mapping->host, index, &page, |
| 1391 | | − SGP_NOHUGE)) { |
| 1413 | + } else { /* !is_shmem */ |
| 1414 | + if (!page || xa_is_value(page)) { |
| 1415 | + xas_unlock_irq(&xas); |
| 1416 | + page_cache_sync_readahead(mapping, &file->f_ra, |
| 1417 | + file, index, |
| 1418 | + PAGE_SIZE); |
| 1419 | + /* drain pagevecs to help isolate_lru_page() */ |
| 1420 | + lru_add_drain(); |
| 1421 | + page = find_lock_page(mapping, index); |
| 1422 | + if (unlikely(page == NULL)) { |
| 1423 | + result = SCAN_FAIL; |
| 1424 | + goto xa_unlocked; |
| 1425 | + } |
| 1426 | + } else if (!PageUptodate(page)) { |
| 1427 | + xas_unlock_irq(&xas); |
| 1428 | + wait_on_page_locked(page); |
| 1429 | + if (!trylock_page(page)) { |
| 1430 | + result = SCAN_PAGE_LOCK; |
| 1431 | + goto xa_unlocked; |
| 1432 | + } |
| 1433 | + get_page(page); |
| 1434 | + } else if (PageDirty(page)) { |
| 1392 | 1435 | result = SCAN_FAIL; |
| 1393 | | − goto xa_unlocked; |
| 1436 | + goto xa_locked; |
| 1437 | + } else if (trylock_page(page)) { |
| 1438 | + get_page(page); |
| 1439 | + xas_unlock_irq(&xas); |
| 1440 | + } else { |
| 1441 | + result = SCAN_PAGE_LOCK; |
| 1442 | + goto xa_locked; |
| 1394 | 1443 | } |
| 1395 | | − } else if (trylock_page(page)) { |
| 1396 | | − get_page(page); |
| 1397 | | − xas_unlock_irq(&xas); |
| 1398 | | − } else { |
| 1399 | | − result = SCAN_PAGE_LOCK; |
| 1400 | | − goto xa_locked; |
| 1401 | 1444 | } |
| 1402 | 1445 | |
| 1403 | 1446 | /* |
| @@ -1426,6 +1469,12 @@ static void collapse_file(struct mm_struct *mm, · 22 unmodified lines |
| 1426 | 1469 | goto out_unlock; |
| 1427 | 1470 | } |
| 1428 | 1471 | |
| 1472 | + if (page_has_private(page) && |
| 1473 | + !try_to_release_page(page, GFP_KERNEL)) { |
| 1474 | + result = SCAN_PAGE_HAS_PRIVATE; |
| 1475 | + goto out_unlock; |
| 1476 | + } |
| 1477 | + |
| 1429 | 1478 | if (page_mapped(page)) |
| 1430 | 1479 | unmap_mapping_pages(mapping, index, 1, false); |
| 1431 | 1480 | |
| @@ -1463,12 +1512,18 @@ out_unlock: · 31 unmodified lines |
| 1463 | 1512 | goto xa_unlocked; |
| 1464 | 1513 | } |
| 1465 | 1514 | |
| 1466 | | − __inc_node_page_state(new_page, NR_SHMEM_THPS); |
| 1515 | + if (is_shmem) |
| 1516 | + __inc_node_page_state(new_page, NR_SHMEM_THPS); |
| 1517 | + else |
| 1518 | + __inc_node_page_state(new_page, NR_FILE_THPS); |
| 1519 | + |
| 1467 | 1520 | if (nr_none) { |
| 1468 | 1521 | struct zone *zone = page_zone(new_page); |
| 1469 | 1522 | |
| 1470 | 1523 | __mod_node_page_state(zone->zone_pgdat, NR_FILE_PAGES, nr_none); |
| 1471 | | − __mod_node_page_state(zone->zone_pgdat, NR_SHMEM, nr_none); |
| 1524 | + if (is_shmem) |
| 1525 | + __mod_node_page_state(zone->zone_pgdat, |
| 1526 | + NR_SHMEM, nr_none); |
| 1472 | 1527 | } |
| 1473 | 1528 | |
| 1474 | 1529 | xa_locked: |
| @@ -1506,10 +1561,15 @@ xa_unlocked: · 31 unmodified lines |
| 1506 | 1561 | |
| 1507 | 1562 | SetPageUptodate(new_page); |
| 1508 | 1563 | page_ref_add(new_page, HPAGE_PMD_NR - 1); |
| 1509 | | − set_page_dirty(new_page); |
| 1510 | 1564 | mem_cgroup_commit_charge(new_page, memcg, false, true); |
| 1565 | + |
| 1566 | + if (is_shmem) { |
| 1567 | + set_page_dirty(new_page); |
| 1568 | + lru_cache_add_anon(new_page); |
| 1569 | + } else { |
| 1570 | + lru_cache_add_file(new_page); |
| 1571 | + } |
| 1511 | 1572 | count_memcg_events(memcg, THP_COLLAPSE_ALLOC, 1); |
| 1512 | | − lru_cache_add_anon(new_page); |
| 1513 | 1573 | |
| 1514 | 1574 | /* |
| 1515 | 1575 | * Remove pte page tables, so we can re-fault the page as huge. |
| @@ -1524,7 +1584,9 @@ xa_unlocked: · 8 unmodified lines |
| 1524 | 1584 | /* Something went wrong: roll back page cache changes */ |
| 1525 | 1585 | xas_lock_irq(&xas); |
| 1526 | 1586 | mapping->nrpages -= nr_none; |
| 1527 | | − shmem_uncharge(mapping->host, nr_none); |
| 1587 | + |
| 1588 | + if (is_shmem) |
| 1589 | + shmem_uncharge(mapping->host, nr_none); |
| 1528 | 1590 | |
| 1529 | 1591 | xas_set(&xas, start); |
| 1530 | 1592 | xas_for_each(&xas, page, end - 1) { |
| @@ -1607,7 +1669,8 @@ static void khugepaged_scan_file(struct mm_struct *mm, · 76 unmodified lines |
| 1607 | 1669 | break; |
| 1608 | 1670 | } |
| 1609 | 1671 | |
| 1610 | | − if (page_count(page) != 1 + page_mapcount(page)) { |
| 1672 | + if (page_count(page) != |
| 1673 | + 1 + page_mapcount(page) + page_has_private(page)) { |
| 1611 | 1674 | result = SCAN_PAGE_COUNT; |
| 1612 | 1675 | break; |
| 1613 | 1676 | } |
| @@ -1713,11 +1776,13 @@ skip: · 99 unmodified lines |
| 1713 | 1776 | VM_BUG_ON(khugepaged_scan.address < hstart || |
| 1714 | 1777 | khugepaged_scan.address + HPAGE_PMD_SIZE > |
| 1715 | 1778 | hend); |
| 1716 | | − if (shmem_file(vma->vm_file)) { |
| 1779 | + if (IS_ENABLED(CONFIG_SHMEM) && vma->vm_file) { |
| 1717 | 1780 | struct file *file; |
| 1718 | 1781 | pgoff_t pgoff = linear_page_index(vma, |
| 1719 | 1782 | khugepaged_scan.address); |
| 1720 | | − if (!shmem_huge_enabled(vma)) |
| 1783 | + |
| 1784 | + if (shmem_file(vma->vm_file) |
| 1785 | + && !shmem_huge_enabled(vma)) |
| 1721 | 1786 | goto skip; |
| 1722 | 1787 | file = get_file(vma->vm_file); |
| 1723 | 1788 | up_read(&mm->mmap_sem); |