From: Matthew Wilcox <mawilcox@xxxxxxxxxxxxx> If we have garbage in the PTE, we can call the radix tree code with a NULL radix tree head which leads to an OOPS. Detect the case where we've found a PTE that refers to a non-existent swap device and report the error correctly. Signed-off-by: Matthew Wilcox <mawilcox@xxxxxxxxxxxxx> --- include/linux/swap.h | 10 ++++------ mm/memory.c | 4 +--- mm/shmem.c | 2 +- mm/swap_state.c | 35 ++++++++++++++++++++++------------- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 7b6a59f722a3..045edb2ca8d0 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -415,9 +415,8 @@ extern void __delete_from_swap_cache(struct page *); extern void delete_from_swap_cache(struct page *); extern void free_page_and_swap_cache(struct page *); extern void free_pages_and_swap_cache(struct page **, int); -extern struct page *lookup_swap_cache(swp_entry_t entry, - struct vm_area_struct *vma, - unsigned long addr); +extern struct page *lookup_swap_cache(swp_entry_t entry, bool vma_ra, + struct vm_fault *vmf); extern struct page *read_swap_cache_async(swp_entry_t, gfp_t, struct vm_area_struct *vma, unsigned long addr, bool do_poll); @@ -568,9 +567,8 @@ static inline int swap_writepage(struct page *p, struct writeback_control *wbc) return 0; } -static inline struct page *lookup_swap_cache(swp_entry_t swp, - struct vm_area_struct *vma, - unsigned long addr) +static inline struct page *lookup_swap_cache(swp_entry_t swp, bool vma_ra, + struct vm_fault *vmf) { return NULL; } diff --git a/mm/memory.c b/mm/memory.c index 5fcfc24904d1..1cfc4699db42 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2926,11 +2926,9 @@ int do_swap_page(struct vm_fault *vmf) goto out; } - delayacct_set_flag(DELAYACCT_PF_SWAPIN); if (!page) { - page = lookup_swap_cache(entry, vma_readahead ? vma : NULL, - vmf->address); + page = lookup_swap_cache(entry, vma_readahead, vmf); swapcache = page; } diff --git a/mm/shmem.c b/mm/shmem.c index 1907688b75ee..8976f05823ba 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1650,7 +1650,7 @@ static int shmem_getpage_gfp(struct inode *inode, pgoff_t index, if (swap.val) { /* Look it up and read it in.. */ - page = lookup_swap_cache(swap, NULL, 0); + page = lookup_swap_cache(swap, false, NULL); if (!page) { /* Or update major stats only when swapin succeeds?? */ if (fault_type) { diff --git a/mm/swap_state.c b/mm/swap_state.c index 39ae7cfad90f..5a7755ecbb03 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -328,14 +328,22 @@ void free_pages_and_swap_cache(struct page **pages, int nr) * lock getting page table operations atomic even if we drop the page * lock before returning. */ -struct page *lookup_swap_cache(swp_entry_t entry, struct vm_area_struct *vma, - unsigned long addr) +struct page *lookup_swap_cache(swp_entry_t entry, bool vma_ra, + struct vm_fault *vmf) { struct page *page; - unsigned long ra_info; - int win, hits, readahead; + int readahead; + struct address_space *swapper_space = swap_address_space(entry); - page = find_get_page(swap_address_space(entry), swp_offset(entry)); + if (!swapper_space) { + if (vmf) + pte_ERROR(vmf->orig_pte); + else + pr_err("Bad swp_entry: %lx\n", entry.val); + return NULL; + } + + page = find_get_page(swapper_space, swp_offset(entry)); INC_CACHE_INFO(find_total); if (page) { @@ -343,18 +351,19 @@ struct page *lookup_swap_cache(swp_entry_t entry, struct vm_area_struct *vma, if (unlikely(PageTransCompound(page))) return page; readahead = TestClearPageReadahead(page); - if (vma) { - ra_info = GET_SWAP_RA_VAL(vma); - win = SWAP_RA_WIN(ra_info); - hits = SWAP_RA_HITS(ra_info); + if (vma_ra) { + unsigned long ra_info = GET_SWAP_RA_VAL(vmf->vma); + int win = SWAP_RA_WIN(ra_info); + int hits = SWAP_RA_HITS(ra_info); + if (readahead) hits = min_t(int, hits + 1, SWAP_RA_HITS_MAX); - atomic_long_set(&vma->swap_readahead_info, - SWAP_RA_VAL(addr, win, hits)); + atomic_long_set(&vmf->vma->swap_readahead_info, + SWAP_RA_VAL(vmf->address, win, hits)); } if (readahead) { count_vm_event(SWAP_RA_HIT); - if (!vma) + if (!vma_ra) atomic_inc(&swapin_readahead_hits); } } @@ -675,7 +684,7 @@ struct page *swap_readahead_detect(struct vm_fault *vmf, entry = pte_to_swp_entry(vmf->orig_pte); if ((unlikely(non_swap_entry(entry)))) return NULL; - page = lookup_swap_cache(entry, vma, faddr); + page = lookup_swap_cache(entry, true, vmf); if (page) return page; -- 2.16.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxx. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>