diff options
author | Hugh Dickins <hugh.dickins@tiscali.co.uk> | 2009-06-23 15:36:58 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-23 15:50:05 -0400 |
commit | a5c9b696ec109bb54d547fdb437a7a0c2d514670 (patch) | |
tree | c97533e38359a2e731bcc2c65f063a27f4f629e6 /mm | |
parent | 626f380d0b264a1e40237f5a2a3dffc5d14f256e (diff) |
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>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memory.c | 2 | ||||
-rw-r--r-- | mm/thrash.c | 32 |
2 files changed, 16 insertions, 18 deletions
diff --git a/mm/memory.c b/mm/memory.c index 50da9511aa77..f46ac18ba231 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -2519,7 +2519,7 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, | |||
2519 | delayacct_set_flag(DELAYACCT_PF_SWAPIN); | 2519 | delayacct_set_flag(DELAYACCT_PF_SWAPIN); |
2520 | page = lookup_swap_cache(entry); | 2520 | page = lookup_swap_cache(entry); |
2521 | if (!page) { | 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 | page = swapin_readahead(entry, | 2523 | page = swapin_readahead(entry, |
2524 | GFP_HIGHUSER_MOVABLE, vma, address); | 2524 | GFP_HIGHUSER_MOVABLE, vma, address); |
2525 | if (!page) { | 2525 | if (!page) { |
diff --git a/mm/thrash.c b/mm/thrash.c index c4c5205a9c35..2372d4ed5dd8 100644 --- a/mm/thrash.c +++ b/mm/thrash.c | |||
@@ -26,47 +26,45 @@ static DEFINE_SPINLOCK(swap_token_lock); | |||
26 | struct mm_struct *swap_token_mm; | 26 | struct mm_struct *swap_token_mm; |
27 | static unsigned int global_faults; | 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 | int current_interval; | 31 | int current_interval; |
32 | 32 | ||
33 | global_faults++; | 33 | global_faults++; |
34 | 34 | ||
35 | current_interval = global_faults - current->mm->faultstamp; | 35 | current_interval = global_faults - mm->faultstamp; |
36 | 36 | ||
37 | if (!spin_trylock(&swap_token_lock)) | 37 | if (!spin_trylock(&swap_token_lock)) |
38 | return; | 38 | return; |
39 | 39 | ||
40 | /* First come first served */ | 40 | /* First come first served */ |
41 | if (swap_token_mm == NULL) { | 41 | if (swap_token_mm == NULL) { |
42 | current->mm->token_priority = current->mm->token_priority + 2; | 42 | mm->token_priority = mm->token_priority + 2; |
43 | swap_token_mm = current->mm; | 43 | swap_token_mm = mm; |
44 | goto out; | 44 | goto out; |
45 | } | 45 | } |
46 | 46 | ||
47 | if (current->mm != swap_token_mm) { | 47 | if (mm != swap_token_mm) { |
48 | if (current_interval < current->mm->last_interval) | 48 | if (current_interval < mm->last_interval) |
49 | current->mm->token_priority++; | 49 | mm->token_priority++; |
50 | else { | 50 | else { |
51 | if (likely(current->mm->token_priority > 0)) | 51 | if (likely(mm->token_priority > 0)) |
52 | current->mm->token_priority--; | 52 | mm->token_priority--; |
53 | } | 53 | } |
54 | /* Check if we deserve the token */ | 54 | /* Check if we deserve the token */ |
55 | if (current->mm->token_priority > | 55 | if (mm->token_priority > swap_token_mm->token_priority) { |
56 | swap_token_mm->token_priority) { | 56 | mm->token_priority += 2; |
57 | current->mm->token_priority += 2; | 57 | swap_token_mm = mm; |
58 | swap_token_mm = current->mm; | ||
59 | } | 58 | } |
60 | } else { | 59 | } else { |
61 | /* Token holder came in again! */ | 60 | /* Token holder came in again! */ |
62 | current->mm->token_priority += 2; | 61 | mm->token_priority += 2; |
63 | } | 62 | } |
64 | 63 | ||
65 | out: | 64 | out: |
66 | current->mm->faultstamp = global_faults; | 65 | mm->faultstamp = global_faults; |
67 | current->mm->last_interval = current_interval; | 66 | mm->last_interval = current_interval; |
68 | spin_unlock(&swap_token_lock); | 67 | spin_unlock(&swap_token_lock); |
69 | return; | ||
70 | } | 68 | } |
71 | 69 | ||
72 | /* Called on process exit. */ | 70 | /* Called on process exit. */ |