aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugh Dickins <hugh.dickins@tiscali.co.uk>2009-06-23 15:36:58 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-06-23 15:50:05 -0400
commita5c9b696ec109bb54d547fdb437a7a0c2d514670 (patch)
treec97533e38359a2e731bcc2c65f063a27f4f629e6
parent626f380d0b264a1e40237f5a2a3dffc5d14f256e (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>
-rw-r--r--include/linux/swap.h12
-rw-r--r--mm/memory.c2
-rw-r--r--mm/thrash.c32
3 files changed, 22 insertions, 24 deletions
diff --git a/include/linux/swap.h b/include/linux/swap.h
index c88b36665f79..7c15334f3ff2 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -298,8 +298,8 @@ extern int try_to_free_swap(struct page *);
298struct backing_dev_info; 298struct backing_dev_info;
299 299
300/* linux/mm/thrash.c */ 300/* linux/mm/thrash.c */
301extern struct mm_struct * swap_token_mm; 301extern struct mm_struct *swap_token_mm;
302extern void grab_swap_token(void); 302extern void grab_swap_token(struct mm_struct *);
303extern void __put_swap_token(struct mm_struct *); 303extern void __put_swap_token(struct mm_struct *);
304 304
305static inline int has_swap_token(struct mm_struct *mm) 305static inline int has_swap_token(struct mm_struct *mm)
@@ -419,10 +419,10 @@ static inline swp_entry_t get_swap_page(void)
419} 419}
420 420
421/* linux/mm/thrash.c */ 421/* linux/mm/thrash.c */
422#define put_swap_token(x) do { } while(0) 422#define put_swap_token(mm) do { } while (0)
423#define grab_swap_token() do { } while(0) 423#define grab_swap_token(mm) do { } while (0)
424#define has_swap_token(x) 0 424#define has_swap_token(mm) 0
425#define disable_swap_token() do { } while(0) 425#define disable_swap_token() do { } while (0)
426 426
427static inline void 427static inline void
428mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent) 428mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent)
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);
26struct mm_struct *swap_token_mm; 26struct mm_struct *swap_token_mm;
27static unsigned int global_faults; 27static unsigned int global_faults;
28 28
29void grab_swap_token(void) 29void 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
65out: 64out:
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);
69return;
70} 68}
71 69
72/* Called on process exit. */ 70/* Called on process exit. */