Skip to content

Commit 0bcac06

Browse files
minchanktorvalds
authored andcommitted
mm, swap: skip swapcache for swapin of synchronous device
With fast swap storage, the platforms want to use swap more aggressively and swap-in is crucial to application latency. The rw_page() based synchronous devices like zram, pmem and btt are such fast storage. When I profile swapin performance with zram lz4 decompress test, S/W overhead is more than 70%. Maybe, it would be bigger in nvdimm. This patch aims to reduce swap-in latency by skipping swapcache if the swap device is synchronous device like rw_page based device. It enhances 45% my swapin test(5G sequential swapin, no readahead, from 2.41sec to 1.64sec). Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Minchan Kim <[email protected]> Cc: Dan Williams <[email protected]> Cc: Ross Zwisler <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Ilya Dryomov <[email protected]> Cc: Jens Axboe <[email protected]> Cc: Sergey Senozhatsky <[email protected]> Cc: Huang Ying <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 539a6fe commit 0bcac06

File tree

4 files changed

+57
-23
lines changed

4 files changed

+57
-23
lines changed

include/linux/swap.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ extern int page_swapcount(struct page *);
466466
extern int __swp_swapcount(swp_entry_t entry);
467467
extern int swp_swapcount(swp_entry_t entry);
468468
extern struct swap_info_struct *page_swap_info(struct page *);
469+
extern struct swap_info_struct *swp_swap_info(swp_entry_t entry);
469470
extern bool reuse_swap_page(struct page *, int *);
470471
extern int try_to_free_swap(struct page *);
471472
struct backing_dev_info;
@@ -474,6 +475,16 @@ extern void exit_swap_address_space(unsigned int type);
474475

475476
#else /* CONFIG_SWAP */
476477

478+
static inline int swap_readpage(struct page *page, bool do_poll)
479+
{
480+
return 0;
481+
}
482+
483+
static inline struct swap_info_struct *swp_swap_info(swp_entry_t entry)
484+
{
485+
return NULL;
486+
}
487+
477488
#define swap_address_space(entry) (NULL)
478489
#define get_nr_swap_pages() 0L
479490
#define total_swap_pages 0L

mm/memory.c

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2842,7 +2842,7 @@ EXPORT_SYMBOL(unmap_mapping_range);
28422842
int do_swap_page(struct vm_fault *vmf)
28432843
{
28442844
struct vm_area_struct *vma = vmf->vma;
2845-
struct page *page = NULL, *swapcache;
2845+
struct page *page = NULL, *swapcache = NULL;
28462846
struct mem_cgroup *memcg;
28472847
struct vma_swap_readahead swap_ra;
28482848
swp_entry_t entry;
@@ -2881,17 +2881,35 @@ int do_swap_page(struct vm_fault *vmf)
28812881
}
28822882
goto out;
28832883
}
2884+
2885+
28842886
delayacct_set_flag(DELAYACCT_PF_SWAPIN);
28852887
if (!page)
28862888
page = lookup_swap_cache(entry, vma_readahead ? vma : NULL,
28872889
vmf->address);
28882890
if (!page) {
2889-
if (vma_readahead)
2890-
page = do_swap_page_readahead(entry,
2891-
GFP_HIGHUSER_MOVABLE, vmf, &swap_ra);
2892-
else
2893-
page = swapin_readahead(entry,
2894-
GFP_HIGHUSER_MOVABLE, vma, vmf->address);
2891+
struct swap_info_struct *si = swp_swap_info(entry);
2892+
2893+
if (!(si->flags & SWP_SYNCHRONOUS_IO)) {
2894+
if (vma_readahead)
2895+
page = do_swap_page_readahead(entry,
2896+
GFP_HIGHUSER_MOVABLE, vmf, &swap_ra);
2897+
else
2898+
page = swapin_readahead(entry,
2899+
GFP_HIGHUSER_MOVABLE, vma, vmf->address);
2900+
swapcache = page;
2901+
} else {
2902+
/* skip swapcache */
2903+
page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vmf->address);
2904+
if (page) {
2905+
__SetPageLocked(page);
2906+
__SetPageSwapBacked(page);
2907+
set_page_private(page, entry.val);
2908+
lru_cache_add_anon(page);
2909+
swap_readpage(page, true);
2910+
}
2911+
}
2912+
28952913
if (!page) {
28962914
/*
28972915
* Back out if somebody else faulted in this pte
@@ -2920,7 +2938,6 @@ int do_swap_page(struct vm_fault *vmf)
29202938
goto out_release;
29212939
}
29222940

2923-
swapcache = page;
29242941
locked = lock_page_or_retry(page, vma->vm_mm, vmf->flags);
29252942

29262943
delayacct_clear_flag(DELAYACCT_PF_SWAPIN);
@@ -2935,7 +2952,8 @@ int do_swap_page(struct vm_fault *vmf)
29352952
* test below, are not enough to exclude that. Even if it is still
29362953
* swapcache, we need to check that the page's swap has not changed.
29372954
*/
2938-
if (unlikely(!PageSwapCache(page) || page_private(page) != entry.val))
2955+
if (unlikely((!PageSwapCache(page) ||
2956+
page_private(page) != entry.val)) && swapcache)
29392957
goto out_page;
29402958

29412959
page = ksm_might_need_to_copy(page, vma, vmf->address);
@@ -2988,22 +3006,24 @@ int do_swap_page(struct vm_fault *vmf)
29883006
pte = pte_mksoft_dirty(pte);
29893007
set_pte_at(vma->vm_mm, vmf->address, vmf->pte, pte);
29903008
vmf->orig_pte = pte;
2991-
if (page == swapcache) {
2992-
do_page_add_anon_rmap(page, vma, vmf->address, exclusive);
2993-
mem_cgroup_commit_charge(page, memcg, true, false);
2994-
activate_page(page);
2995-
} else { /* ksm created a completely new copy */
3009+
3010+
/* ksm created a completely new copy */
3011+
if (unlikely(page != swapcache && swapcache)) {
29963012
page_add_new_anon_rmap(page, vma, vmf->address, false);
29973013
mem_cgroup_commit_charge(page, memcg, false, false);
29983014
lru_cache_add_active_or_unevictable(page, vma);
3015+
} else {
3016+
do_page_add_anon_rmap(page, vma, vmf->address, exclusive);
3017+
mem_cgroup_commit_charge(page, memcg, true, false);
3018+
activate_page(page);
29993019
}
30003020

30013021
swap_free(entry);
30023022
if (mem_cgroup_swap_full(page) ||
30033023
(vma->vm_flags & VM_LOCKED) || PageMlocked(page))
30043024
try_to_free_swap(page);
30053025
unlock_page(page);
3006-
if (page != swapcache) {
3026+
if (page != swapcache && swapcache) {
30073027
/*
30083028
* Hold the lock to avoid the swap entry to be reused
30093029
* until we take the PT lock for the pte_same() check
@@ -3036,7 +3056,7 @@ int do_swap_page(struct vm_fault *vmf)
30363056
unlock_page(page);
30373057
out_release:
30383058
put_page(page);
3039-
if (page != swapcache) {
3059+
if (page != swapcache && swapcache) {
30403060
unlock_page(swapcache);
30413061
put_page(swapcache);
30423062
}

mm/page_io.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -347,15 +347,15 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc,
347347
return ret;
348348
}
349349

350-
int swap_readpage(struct page *page, bool do_poll)
350+
int swap_readpage(struct page *page, bool synchronous)
351351
{
352352
struct bio *bio;
353353
int ret = 0;
354354
struct swap_info_struct *sis = page_swap_info(page);
355355
blk_qc_t qc;
356356
struct gendisk *disk;
357357

358-
VM_BUG_ON_PAGE(!PageSwapCache(page), page);
358+
VM_BUG_ON_PAGE(!PageSwapCache(page) && !synchronous, page);
359359
VM_BUG_ON_PAGE(!PageLocked(page), page);
360360
VM_BUG_ON_PAGE(PageUptodate(page), page);
361361
if (frontswap_load(page) == 0) {
@@ -403,7 +403,7 @@ int swap_readpage(struct page *page, bool do_poll)
403403
count_vm_event(PSWPIN);
404404
bio_get(bio);
405405
qc = submit_bio(bio);
406-
while (do_poll) {
406+
while (synchronous) {
407407
set_current_state(TASK_UNINTERRUPTIBLE);
408408
if (!READ_ONCE(bio->bi_private))
409409
break;

mm/swapfile.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3455,26 +3455,29 @@ int swapcache_prepare(swp_entry_t entry)
34553455
return __swap_duplicate(entry, SWAP_HAS_CACHE);
34563456
}
34573457

3458+
struct swap_info_struct *swp_swap_info(swp_entry_t entry)
3459+
{
3460+
return swap_info[swp_type(entry)];
3461+
}
3462+
34583463
struct swap_info_struct *page_swap_info(struct page *page)
34593464
{
3460-
swp_entry_t swap = { .val = page_private(page) };
3461-
return swap_info[swp_type(swap)];
3465+
swp_entry_t entry = { .val = page_private(page) };
3466+
return swp_swap_info(entry);
34623467
}
34633468

34643469
/*
34653470
* out-of-line __page_file_ methods to avoid include hell.
34663471
*/
34673472
struct address_space *__page_file_mapping(struct page *page)
34683473
{
3469-
VM_BUG_ON_PAGE(!PageSwapCache(page), page);
34703474
return page_swap_info(page)->swap_file->f_mapping;
34713475
}
34723476
EXPORT_SYMBOL_GPL(__page_file_mapping);
34733477

34743478
pgoff_t __page_file_index(struct page *page)
34753479
{
34763480
swp_entry_t swap = { .val = page_private(page) };
3477-
VM_BUG_ON_PAGE(!PageSwapCache(page), page);
34783481
return swp_offset(swap);
34793482
}
34803483
EXPORT_SYMBOL_GPL(__page_file_index);

0 commit comments

Comments
 (0)