diff options
author | Nick Piggin <piggin@cyberone.com.au> | 2006-04-09 21:21:48 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-10 13:16:37 -0400 |
commit | 676165a8af7167f488abdcce6851a9bc36e83254 (patch) | |
tree | a9b2b8dc155b48ce073b5ada31f2ac0694118e69 /include/linux | |
parent | c3a9d6541f84ac3ff566982d08389b87c1c36b4e (diff) |
[PATCH] Fix buddy list race that could lead to page lru list corruptions
Rohit found an obscure bug causing buddy list corruption.
page_is_buddy is using a non-atomic test (PagePrivate && page_count == 0)
to determine whether or not a free page's buddy is itself free and in the
buddy lists.
Each of the conjuncts may be true at different times due to unrelated
conditions, so the non-atomic page_is_buddy test may find each conjunct to
be true even if they were not both true at the same time (ie. the page was
not on the buddy lists).
Signed-off-by: Martin Bligh <mbligh@google.com>
Signed-off-by: Rohit Seth <rohitseth@google.com>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'include/linux')
-rw-r--r-- | include/linux/mm.h | 5 | ||||
-rw-r--r-- | include/linux/page-flags.h | 8 |
2 files changed, 9 insertions, 4 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index 6aa016f1d3ae..1154684209a4 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -229,10 +229,9 @@ struct page { | |||
229 | unsigned long private; /* Mapping-private opaque data: | 229 | unsigned long private; /* Mapping-private opaque data: |
230 | * usually used for buffer_heads | 230 | * usually used for buffer_heads |
231 | * if PagePrivate set; used for | 231 | * if PagePrivate set; used for |
232 | * swp_entry_t if PageSwapCache. | 232 | * swp_entry_t if PageSwapCache; |
233 | * When page is free, this | ||
234 | * indicates order in the buddy | 233 | * indicates order in the buddy |
235 | * system. | 234 | * system if PG_buddy is set. |
236 | */ | 235 | */ |
237 | struct address_space *mapping; /* If low bit clear, points to | 236 | struct address_space *mapping; /* If low bit clear, points to |
238 | * inode address_space, or NULL. | 237 | * inode address_space, or NULL. |
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 9ea629c02a4b..547aac7696cd 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h | |||
@@ -74,7 +74,9 @@ | |||
74 | #define PG_mappedtodisk 16 /* Has blocks allocated on-disk */ | 74 | #define PG_mappedtodisk 16 /* Has blocks allocated on-disk */ |
75 | #define PG_reclaim 17 /* To be reclaimed asap */ | 75 | #define PG_reclaim 17 /* To be reclaimed asap */ |
76 | #define PG_nosave_free 18 /* Free, should not be written */ | 76 | #define PG_nosave_free 18 /* Free, should not be written */ |
77 | #define PG_uncached 19 /* Page has been mapped as uncached */ | 77 | #define PG_buddy 19 /* Page is free, on buddy lists */ |
78 | |||
79 | #define PG_uncached 20 /* Page has been mapped as uncached */ | ||
78 | 80 | ||
79 | /* | 81 | /* |
80 | * Global page accounting. One instance per CPU. Only unsigned longs are | 82 | * Global page accounting. One instance per CPU. Only unsigned longs are |
@@ -317,6 +319,10 @@ extern void __mod_page_state_offset(unsigned long offset, unsigned long delta); | |||
317 | #define SetPageNosaveFree(page) set_bit(PG_nosave_free, &(page)->flags) | 319 | #define SetPageNosaveFree(page) set_bit(PG_nosave_free, &(page)->flags) |
318 | #define ClearPageNosaveFree(page) clear_bit(PG_nosave_free, &(page)->flags) | 320 | #define ClearPageNosaveFree(page) clear_bit(PG_nosave_free, &(page)->flags) |
319 | 321 | ||
322 | #define PageBuddy(page) test_bit(PG_buddy, &(page)->flags) | ||
323 | #define __SetPageBuddy(page) __set_bit(PG_buddy, &(page)->flags) | ||
324 | #define __ClearPageBuddy(page) __clear_bit(PG_buddy, &(page)->flags) | ||
325 | |||
320 | #define PageMappedToDisk(page) test_bit(PG_mappedtodisk, &(page)->flags) | 326 | #define PageMappedToDisk(page) test_bit(PG_mappedtodisk, &(page)->flags) |
321 | #define SetPageMappedToDisk(page) set_bit(PG_mappedtodisk, &(page)->flags) | 327 | #define SetPageMappedToDisk(page) set_bit(PG_mappedtodisk, &(page)->flags) |
322 | #define ClearPageMappedToDisk(page) clear_bit(PG_mappedtodisk, &(page)->flags) | 328 | #define ClearPageMappedToDisk(page) clear_bit(PG_mappedtodisk, &(page)->flags) |