mm: pass mm to grab_swap_token

If a kthread happens to use get_user_pages() on an mm (as KSM does),
there's a chance that it will end up trying to read in a swap page, then
oops in grab_swap_token() because the kthread has no mm: GUP passes down
the right mm, so grab_swap_token() ought to be using it.

We have not identified a stronger case than KSM's daemon (not yet in
mainline), but the issue must have come up before, since RHEL has included
a fix for this for years (though a different fix, they just back out of
grab_swap_token if current->mm is unset: which is what we first proposed,
but using the right mm here seems more correct).

Reported-by: Izik Eidus <ieidus@redhat.com>
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Signed-off-by: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Acked-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Hugh Dickins and committed by
Linus Torvalds
a5c9b696 626f380d

+22 -24
+6 -6
include/linux/swap.h
··· 298 298 struct backing_dev_info; 299 299 300 300 /* linux/mm/thrash.c */ 301 - extern struct mm_struct * swap_token_mm; 302 - extern void grab_swap_token(void); 301 + extern struct mm_struct *swap_token_mm; 302 + extern void grab_swap_token(struct mm_struct *); 303 303 extern void __put_swap_token(struct mm_struct *); 304 304 305 305 static inline int has_swap_token(struct mm_struct *mm) ··· 419 419 } 420 420 421 421 /* linux/mm/thrash.c */ 422 - #define put_swap_token(x) do { } while(0) 423 - #define grab_swap_token() do { } while(0) 424 - #define has_swap_token(x) 0 425 - #define disable_swap_token() do { } while(0) 422 + #define put_swap_token(mm) do { } while (0) 423 + #define grab_swap_token(mm) do { } while (0) 424 + #define has_swap_token(mm) 0 425 + #define disable_swap_token() do { } while (0) 426 426 427 427 static inline void 428 428 mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent)
+1 -1
mm/memory.c
··· 2519 2519 delayacct_set_flag(DELAYACCT_PF_SWAPIN); 2520 2520 page = lookup_swap_cache(entry); 2521 2521 if (!page) { 2522 - grab_swap_token(); /* Contend for token _before_ read-in */ 2522 + grab_swap_token(mm); /* Contend for token _before_ read-in */ 2523 2523 page = swapin_readahead(entry, 2524 2524 GFP_HIGHUSER_MOVABLE, vma, address); 2525 2525 if (!page) {
+15 -17
mm/thrash.c
··· 26 26 struct mm_struct *swap_token_mm; 27 27 static unsigned int global_faults; 28 28 29 - void grab_swap_token(void) 29 + void grab_swap_token(struct mm_struct *mm) 30 30 { 31 31 int current_interval; 32 32 33 33 global_faults++; 34 34 35 - current_interval = global_faults - current->mm->faultstamp; 35 + current_interval = global_faults - mm->faultstamp; 36 36 37 37 if (!spin_trylock(&swap_token_lock)) 38 38 return; 39 39 40 40 /* First come first served */ 41 41 if (swap_token_mm == NULL) { 42 - current->mm->token_priority = current->mm->token_priority + 2; 43 - swap_token_mm = current->mm; 42 + mm->token_priority = mm->token_priority + 2; 43 + swap_token_mm = mm; 44 44 goto out; 45 45 } 46 46 47 - if (current->mm != swap_token_mm) { 48 - if (current_interval < current->mm->last_interval) 49 - current->mm->token_priority++; 47 + if (mm != swap_token_mm) { 48 + if (current_interval < mm->last_interval) 49 + mm->token_priority++; 50 50 else { 51 - if (likely(current->mm->token_priority > 0)) 52 - current->mm->token_priority--; 51 + if (likely(mm->token_priority > 0)) 52 + mm->token_priority--; 53 53 } 54 54 /* Check if we deserve the token */ 55 - if (current->mm->token_priority > 56 - swap_token_mm->token_priority) { 57 - current->mm->token_priority += 2; 58 - swap_token_mm = current->mm; 55 + if (mm->token_priority > swap_token_mm->token_priority) { 56 + mm->token_priority += 2; 57 + swap_token_mm = mm; 59 58 } 60 59 } else { 61 60 /* Token holder came in again! */ 62 - current->mm->token_priority += 2; 61 + mm->token_priority += 2; 63 62 } 64 63 65 64 out: 66 - current->mm->faultstamp = global_faults; 67 - current->mm->last_interval = current_interval; 65 + mm->faultstamp = global_faults; 66 + mm->last_interval = current_interval; 68 67 spin_unlock(&swap_token_lock); 69 - return; 70 68 } 71 69 72 70 /* Called on process exit. */