Skip to content

Commit 2799e77

Browse files
MiaoheLintorvalds
authored andcommitted
swap: fix do_swap_page() race with swapoff
When I was investigating the swap code, I found the below possible race window: CPU 1 CPU 2 ----- ----- do_swap_page if (data_race(si->flags & SWP_SYNCHRONOUS_IO) swap_readpage if (data_race(sis->flags & SWP_FS_OPS)) { swapoff .. p->swap_file = NULL; .. struct file *swap_file = sis->swap_file; struct address_space *mapping = swap_file->f_mapping;[oops!] Note that for the pages that are swapped in through swap cache, this isn't an issue. Because the page is locked, and the swap entry will be marked with SWAP_HAS_CACHE, so swapoff() can not proceed until the page has been unlocked. Fix this race by using get/put_swap_device() to guard against concurrent swapoff. Link: https://lkml.kernel.org/r/[email protected] Fixes: 0bcac06 ("mm,swap: skip swapcache for swapin of synchronous device") Signed-off-by: Miaohe Lin <[email protected]> Reviewed-by: "Huang, Ying" <[email protected]> Cc: Alex Shi <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Dennis Zhou <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: Joonsoo Kim <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Minchan Kim <[email protected]> Cc: Tim Chen <[email protected]> Cc: Wei Yang <[email protected]> Cc: Yang Shi <[email protected]> Cc: Yu Zhao <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 63d8620 commit 2799e77

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

include/linux/swap.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,15 @@ static inline struct swap_info_struct *swp_swap_info(swp_entry_t entry)
527527
return NULL;
528528
}
529529

530+
static inline struct swap_info_struct *get_swap_device(swp_entry_t entry)
531+
{
532+
return NULL;
533+
}
534+
535+
static inline void put_swap_device(struct swap_info_struct *si)
536+
{
537+
}
538+
530539
#define swap_address_space(entry) (NULL)
531540
#define get_nr_swap_pages() 0L
532541
#define total_swap_pages 0L

mm/memory.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3353,6 +3353,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
33533353
{
33543354
struct vm_area_struct *vma = vmf->vma;
33553355
struct page *page = NULL, *swapcache;
3356+
struct swap_info_struct *si = NULL;
33563357
swp_entry_t entry;
33573358
pte_t pte;
33583359
int locked;
@@ -3380,14 +3381,16 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
33803381
goto out;
33813382
}
33823383

3384+
/* Prevent swapoff from happening to us. */
3385+
si = get_swap_device(entry);
3386+
if (unlikely(!si))
3387+
goto out;
33833388

33843389
delayacct_set_flag(current, DELAYACCT_PF_SWAPIN);
33853390
page = lookup_swap_cache(entry, vma, vmf->address);
33863391
swapcache = page;
33873392

33883393
if (!page) {
3389-
struct swap_info_struct *si = swp_swap_info(entry);
3390-
33913394
if (data_race(si->flags & SWP_SYNCHRONOUS_IO) &&
33923395
__swap_count(entry) == 1) {
33933396
/* skip swapcache */
@@ -3556,6 +3559,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
35563559
unlock:
35573560
pte_unmap_unlock(vmf->pte, vmf->ptl);
35583561
out:
3562+
if (si)
3563+
put_swap_device(si);
35593564
return ret;
35603565
out_nomap:
35613566
pte_unmap_unlock(vmf->pte, vmf->ptl);
@@ -3567,6 +3572,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
35673572
unlock_page(swapcache);
35683573
put_page(swapcache);
35693574
}
3575+
if (si)
3576+
put_swap_device(si);
35703577
return ret;
35713578
}
35723579

0 commit comments

Comments
 (0)