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