From ec9fe1cae51534c61dfaffad083284916042e7fe Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 3 Jun 2020 16:01:34 -0700 Subject: [PATCH 2332/2944] mm: shmem: remove rare optimization when swapin races with hole punching task #30476868 commit 14235ab36019d169f5eb5bf0c064c5b12ca1bf46 upstream Commit 215c02bc33bb ("tmpfs: fix shmem_getpage_gfp() VM_BUG_ON") recognized that hole punching can race with swapin and removed the BUG_ON() for a truncated entry from the swapin path. The patch also added a swapcache deletion to optimize this rare case: Since swapin has the page locked, and free_swap_and_cache() merely trylocks, this situation can leave the page stranded in swapcache. Usually, page reclaim picks up stale swapcache pages, and the race can happen at any other time when the page is locked. (The same happens for non-shmem swapin racing with page table zapping.) The thinking here was: we already observed the race and we have the page locked, we may as well do the cleanup instead of waiting for reclaim. However, this optimization complicates the next patch which moves the cgroup charging code around. As this is just a minor speedup for a race condition that is so rare that it required a fuzzer to trigger the original BUG_ON(), it's no longer worth the complications. In addition, shmem_add_to_page_cache add an parameter in order to in preparation for the anon workingset even though the current patch does not use it. Suggested-by: Hugh Dickins Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Acked-by: Hugh Dickins Cc: Alex Shi Cc: Joonsoo Kim Cc: Shakeel Butt Cc: "Kirill A. Shutemov" Cc: Michal Hocko Cc: Roman Gushchin Cc: Balbir Singh Link: http://lkml.kernel.org/r/20200511181056.GA339505@cmpxchg.org Signed-off-by: Linus Torvalds Signed-off-by: zhongjiang-ali Reviewed-by: Xunlei Pang --- mm/shmem.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index cc38871..35449f9 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -590,7 +590,7 @@ static inline bool is_huge_enabled(struct shmem_sb_info *sbinfo) */ static int shmem_add_to_page_cache(struct page *page, struct address_space *mapping, - pgoff_t index, void *expected) + pgoff_t index, void *expected, gfp_t gfp) { int error, nr = hpage_nr_pages(page); @@ -1204,7 +1204,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, */ if (!error) error = shmem_add_to_page_cache(*pagep, mapping, index, - radswap); + radswap, gfp); if (error != -ENOMEM) { /* * Truncation and eviction use free_swap_and_cache(), which @@ -1723,29 +1723,16 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index, } error = mem_cgroup_try_charge_delay(page, charge_mm, gfp, &memcg); - if (!error) { - error = shmem_add_to_page_cache(page, mapping, index, - swp_to_radix_entry(swap)); - /* - * We already confirmed swap under page lock, and make - * no memory allocation here, so usually no possibility - * of error; but free_swap_and_cache() only trylocks a - * page, so it is just possible that the entry has been - * truncated or holepunched since swap was confirmed. - * shmem_undo_range() will have done some of the - * unaccounting, now delete_from_swap_cache() will do - * the rest. - * Reset swap.val? No, leave it so "failed" goes back to - * "repeat": reading a hole and writing should succeed. - */ - if (error) { - mem_cgroup_cancel_charge(page, memcg); - delete_from_swap_cache(page); - } - } if (error) goto failed; + error = shmem_add_to_page_cache(page, mapping, index, + swp_to_radix_entry(swap), gfp); + if (error) { + mem_cgroup_cancel_charge(page, memcg); + goto failed; + } + mem_cgroup_commit_charge(page, memcg, true); spin_lock_irq(&info->lock); @@ -1834,7 +1821,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index, compound_order(page)); if (!error) { error = shmem_add_to_page_cache(page, mapping, hindex, - NULL); + NULL, gfp & GFP_RECLAIM_MASK); radix_tree_preload_end(); } if (error) { @@ -2312,7 +2299,8 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, ret = radix_tree_maybe_preload(gfp & GFP_RECLAIM_MASK); if (!ret) { - ret = shmem_add_to_page_cache(page, mapping, pgoff, NULL); + ret = shmem_add_to_page_cache(page, mapping, pgoff, NULL, + gfp & GFP_RECLAIM_MASK); radix_tree_preload_end(); } if (ret) -- 1.8.3.1