diff options
| author | KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> | 2007-10-16 04:26:11 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-16 12:43:02 -0400 |
| commit | a5d76b54a3f3a40385d7f76069a2feac9f1bad63 (patch) | |
| tree | f58c432a4224b3be032bd4a4afa79dfa55d198a6 | |
| parent | 75884fb1c6388f3713ddcca662f3647b3129aaeb (diff) | |
memory unplug: page isolation
Implement generic chunk-of-pages isolation method by using page grouping ops.
This patch add MIGRATE_ISOLATE to MIGRATE_TYPES. By this
- MIGRATE_TYPES increases.
- bitmap for migratetype is enlarged.
pages of MIGRATE_ISOLATE migratetype will not be allocated even if it is free.
By this, you can isolated *freed* pages from users. How-to-free pages is not
a purpose of this patch. You may use reclaim and migrate codes to free pages.
If start_isolate_page_range(start,end) is called,
- migratetype of the range turns to be MIGRATE_ISOLATE if
its type is MIGRATE_MOVABLE. (*) this check can be updated if other
memory reclaiming works make progress.
- MIGRATE_ISOLATE is not on migratetype fallback list.
- All free pages and will-be-freed pages are isolated.
To check all pages in the range are isolated or not, use test_pages_isolated(),
To cancel isolation, use undo_isolate_page_range().
Changes V6 -> V7
- removed unnecessary #ifdef
There are HOLES_IN_ZONE handling codes...I'm glad if we can remove them..
Signed-off-by: Yasunori Goto <y-goto@jp.fujitsu.com>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | include/linux/mmzone.h | 3 | ||||
| -rw-r--r-- | include/linux/page-isolation.h | 37 | ||||
| -rw-r--r-- | include/linux/pageblock-flags.h | 2 | ||||
| -rw-r--r-- | mm/Makefile | 2 | ||||
| -rw-r--r-- | mm/page_alloc.c | 44 | ||||
| -rw-r--r-- | mm/page_isolation.c | 138 |
6 files changed, 223 insertions, 3 deletions
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 20ea42c45e4a..f4bfe824834f 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h | |||
| @@ -37,7 +37,8 @@ | |||
| 37 | #define MIGRATE_RECLAIMABLE 1 | 37 | #define MIGRATE_RECLAIMABLE 1 |
| 38 | #define MIGRATE_MOVABLE 2 | 38 | #define MIGRATE_MOVABLE 2 |
| 39 | #define MIGRATE_RESERVE 3 | 39 | #define MIGRATE_RESERVE 3 |
| 40 | #define MIGRATE_TYPES 4 | 40 | #define MIGRATE_ISOLATE 4 /* can't allocate from here */ |
| 41 | #define MIGRATE_TYPES 5 | ||
| 41 | 42 | ||
| 42 | #define for_each_migratetype_order(order, type) \ | 43 | #define for_each_migratetype_order(order, type) \ |
| 43 | for (order = 0; order < MAX_ORDER; order++) \ | 44 | for (order = 0; order < MAX_ORDER; order++) \ |
diff --git a/include/linux/page-isolation.h b/include/linux/page-isolation.h new file mode 100644 index 000000000000..051c1b1ede4e --- /dev/null +++ b/include/linux/page-isolation.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #ifndef __LINUX_PAGEISOLATION_H | ||
| 2 | #define __LINUX_PAGEISOLATION_H | ||
| 3 | |||
| 4 | /* | ||
| 5 | * Changes migrate type in [start_pfn, end_pfn) to be MIGRATE_ISOLATE. | ||
| 6 | * If specified range includes migrate types other than MOVABLE, | ||
| 7 | * this will fail with -EBUSY. | ||
| 8 | * | ||
| 9 | * For isolating all pages in the range finally, the caller have to | ||
| 10 | * free all pages in the range. test_page_isolated() can be used for | ||
| 11 | * test it. | ||
| 12 | */ | ||
| 13 | extern int | ||
| 14 | start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn); | ||
| 15 | |||
| 16 | /* | ||
| 17 | * Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE. | ||
| 18 | * target range is [start_pfn, end_pfn) | ||
| 19 | */ | ||
| 20 | extern int | ||
| 21 | undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn); | ||
| 22 | |||
| 23 | /* | ||
| 24 | * test all pages in [start_pfn, end_pfn)are isolated or not. | ||
| 25 | */ | ||
| 26 | extern int | ||
| 27 | test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn); | ||
| 28 | |||
| 29 | /* | ||
| 30 | * Internal funcs.Changes pageblock's migrate type. | ||
| 31 | * Please use make_pagetype_isolated()/make_pagetype_movable(). | ||
| 32 | */ | ||
| 33 | extern int set_migratetype_isolate(struct page *page); | ||
| 34 | extern void unset_migratetype_isolate(struct page *page); | ||
| 35 | |||
| 36 | |||
| 37 | #endif | ||
diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h index 9ea130829fb4..e875905f7b12 100644 --- a/include/linux/pageblock-flags.h +++ b/include/linux/pageblock-flags.h | |||
| @@ -31,7 +31,7 @@ | |||
| 31 | 31 | ||
| 32 | /* Bit indices that affect a whole block of pages */ | 32 | /* Bit indices that affect a whole block of pages */ |
| 33 | enum pageblock_bits { | 33 | enum pageblock_bits { |
| 34 | PB_range(PB_migrate, 2), /* 2 bits required for migrate types */ | 34 | PB_range(PB_migrate, 3), /* 3 bits required for migrate types */ |
| 35 | NR_PAGEBLOCK_BITS | 35 | NR_PAGEBLOCK_BITS |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
diff --git a/mm/Makefile b/mm/Makefile index d28f63e05b46..5c0b0ea7572d 100644 --- a/mm/Makefile +++ b/mm/Makefile | |||
| @@ -11,7 +11,7 @@ obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ | |||
| 11 | page_alloc.o page-writeback.o pdflush.o \ | 11 | page_alloc.o page-writeback.o pdflush.o \ |
| 12 | readahead.o swap.o truncate.o vmscan.o \ | 12 | readahead.o swap.o truncate.o vmscan.o \ |
| 13 | prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ | 13 | prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ |
| 14 | $(mmu-y) | 14 | page_isolation.o $(mmu-y) |
| 15 | 15 | ||
| 16 | obj-$(CONFIG_BOUNCE) += bounce.o | 16 | obj-$(CONFIG_BOUNCE) += bounce.o |
| 17 | obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o | 17 | obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o |
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 71ced519c31c..a44715e82058 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
| @@ -41,6 +41,7 @@ | |||
| 41 | #include <linux/pfn.h> | 41 | #include <linux/pfn.h> |
| 42 | #include <linux/backing-dev.h> | 42 | #include <linux/backing-dev.h> |
| 43 | #include <linux/fault-inject.h> | 43 | #include <linux/fault-inject.h> |
| 44 | #include <linux/page-isolation.h> | ||
| 44 | 45 | ||
| 45 | #include <asm/tlbflush.h> | 46 | #include <asm/tlbflush.h> |
| 46 | #include <asm/div64.h> | 47 | #include <asm/div64.h> |
| @@ -4433,3 +4434,46 @@ void set_pageblock_flags_group(struct page *page, unsigned long flags, | |||
| 4433 | else | 4434 | else |
| 4434 | __clear_bit(bitidx + start_bitidx, bitmap); | 4435 | __clear_bit(bitidx + start_bitidx, bitmap); |
| 4435 | } | 4436 | } |
| 4437 | |||
| 4438 | /* | ||
| 4439 | * This is designed as sub function...plz see page_isolation.c also. | ||
| 4440 | * set/clear page block's type to be ISOLATE. | ||
| 4441 | * page allocater never alloc memory from ISOLATE block. | ||
| 4442 | */ | ||
| 4443 | |||
| 4444 | int set_migratetype_isolate(struct page *page) | ||
| 4445 | { | ||
| 4446 | struct zone *zone; | ||
| 4447 | unsigned long flags; | ||
| 4448 | int ret = -EBUSY; | ||
| 4449 | |||
| 4450 | zone = page_zone(page); | ||
| 4451 | spin_lock_irqsave(&zone->lock, flags); | ||
| 4452 | /* | ||
| 4453 | * In future, more migrate types will be able to be isolation target. | ||
| 4454 | */ | ||
| 4455 | if (get_pageblock_migratetype(page) != MIGRATE_MOVABLE) | ||
| 4456 | goto out; | ||
| 4457 | set_pageblock_migratetype(page, MIGRATE_ISOLATE); | ||
| 4458 | move_freepages_block(zone, page, MIGRATE_ISOLATE); | ||
| 4459 | ret = 0; | ||
| 4460 | out: | ||
| 4461 | spin_unlock_irqrestore(&zone->lock, flags); | ||
| 4462 | if (!ret) | ||
| 4463 | drain_all_local_pages(); | ||
| 4464 | return ret; | ||
| 4465 | } | ||
| 4466 | |||
| 4467 | void unset_migratetype_isolate(struct page *page) | ||
| 4468 | { | ||
| 4469 | struct zone *zone; | ||
| 4470 | unsigned long flags; | ||
| 4471 | zone = page_zone(page); | ||
| 4472 | spin_lock_irqsave(&zone->lock, flags); | ||
| 4473 | if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) | ||
| 4474 | goto out; | ||
| 4475 | set_pageblock_migratetype(page, MIGRATE_MOVABLE); | ||
| 4476 | move_freepages_block(zone, page, MIGRATE_MOVABLE); | ||
| 4477 | out: | ||
| 4478 | spin_unlock_irqrestore(&zone->lock, flags); | ||
| 4479 | } | ||
diff --git a/mm/page_isolation.c b/mm/page_isolation.c new file mode 100644 index 000000000000..8f92a29695cc --- /dev/null +++ b/mm/page_isolation.c | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | /* | ||
| 2 | * linux/mm/page_isolation.c | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include <stddef.h> | ||
| 6 | #include <linux/mm.h> | ||
| 7 | #include <linux/page-isolation.h> | ||
| 8 | #include <linux/pageblock-flags.h> | ||
| 9 | #include "internal.h" | ||
| 10 | |||
| 11 | static inline struct page * | ||
| 12 | __first_valid_page(unsigned long pfn, unsigned long nr_pages) | ||
| 13 | { | ||
| 14 | int i; | ||
| 15 | for (i = 0; i < nr_pages; i++) | ||
| 16 | if (pfn_valid_within(pfn + i)) | ||
| 17 | break; | ||
| 18 | if (unlikely(i == nr_pages)) | ||
| 19 | return NULL; | ||
| 20 | return pfn_to_page(pfn + i); | ||
| 21 | } | ||
| 22 | |||
| 23 | /* | ||
| 24 | * start_isolate_page_range() -- make page-allocation-type of range of pages | ||
| 25 | * to be MIGRATE_ISOLATE. | ||
| 26 | * @start_pfn: The lower PFN of the range to be isolated. | ||
| 27 | * @end_pfn: The upper PFN of the range to be isolated. | ||
| 28 | * | ||
| 29 | * Making page-allocation-type to be MIGRATE_ISOLATE means free pages in | ||
| 30 | * the range will never be allocated. Any free pages and pages freed in the | ||
| 31 | * future will not be allocated again. | ||
| 32 | * | ||
| 33 | * start_pfn/end_pfn must be aligned to pageblock_order. | ||
| 34 | * Returns 0 on success and -EBUSY if any part of range cannot be isolated. | ||
| 35 | */ | ||
| 36 | int | ||
| 37 | start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn) | ||
| 38 | { | ||
| 39 | unsigned long pfn; | ||
| 40 | unsigned long undo_pfn; | ||
| 41 | struct page *page; | ||
| 42 | |||
| 43 | BUG_ON((start_pfn) & (pageblock_nr_pages - 1)); | ||
| 44 | BUG_ON((end_pfn) & (pageblock_nr_pages - 1)); | ||
| 45 | |||
| 46 | for (pfn = start_pfn; | ||
| 47 | pfn < end_pfn; | ||
| 48 | pfn += pageblock_nr_pages) { | ||
| 49 | page = __first_valid_page(pfn, pageblock_nr_pages); | ||
| 50 | if (page && set_migratetype_isolate(page)) { | ||
| 51 | undo_pfn = pfn; | ||
| 52 | goto undo; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | return 0; | ||
| 56 | undo: | ||
| 57 | for (pfn = start_pfn; | ||
| 58 | pfn <= undo_pfn; | ||
| 59 | pfn += pageblock_nr_pages) | ||
| 60 | unset_migratetype_isolate(pfn_to_page(pfn)); | ||
| 61 | |||
| 62 | return -EBUSY; | ||
| 63 | } | ||
| 64 | |||
| 65 | /* | ||
| 66 | * Make isolated pages available again. | ||
| 67 | */ | ||
| 68 | int | ||
| 69 | undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn) | ||
| 70 | { | ||
| 71 | unsigned long pfn; | ||
| 72 | struct page *page; | ||
| 73 | BUG_ON((start_pfn) & (pageblock_nr_pages - 1)); | ||
| 74 | BUG_ON((end_pfn) & (pageblock_nr_pages - 1)); | ||
| 75 | for (pfn = start_pfn; | ||
| 76 | pfn < end_pfn; | ||
| 77 | pfn += pageblock_nr_pages) { | ||
| 78 | page = __first_valid_page(pfn, pageblock_nr_pages); | ||
| 79 | if (!page || get_pageblock_flags(page) != MIGRATE_ISOLATE) | ||
| 80 | continue; | ||
| 81 | unset_migratetype_isolate(page); | ||
| 82 | } | ||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | /* | ||
| 86 | * Test all pages in the range is free(means isolated) or not. | ||
| 87 | * all pages in [start_pfn...end_pfn) must be in the same zone. | ||
| 88 | * zone->lock must be held before call this. | ||
| 89 | * | ||
| 90 | * Returns 0 if all pages in the range is isolated. | ||
| 91 | */ | ||
| 92 | static int | ||
| 93 | __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn) | ||
| 94 | { | ||
| 95 | struct page *page; | ||
| 96 | |||
| 97 | while (pfn < end_pfn) { | ||
| 98 | if (!pfn_valid_within(pfn)) { | ||
| 99 | pfn++; | ||
| 100 | continue; | ||
| 101 | } | ||
| 102 | page = pfn_to_page(pfn); | ||
| 103 | if (PageBuddy(page)) | ||
| 104 | pfn += 1 << page_order(page); | ||
| 105 | else if (page_count(page) == 0 && | ||
| 106 | page_private(page) == MIGRATE_ISOLATE) | ||
| 107 | pfn += 1; | ||
| 108 | else | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | if (pfn < end_pfn) | ||
| 112 | return 0; | ||
| 113 | return 1; | ||
| 114 | } | ||
| 115 | |||
| 116 | int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn) | ||
| 117 | { | ||
| 118 | unsigned long pfn; | ||
| 119 | struct page *page; | ||
| 120 | |||
| 121 | pfn = start_pfn; | ||
| 122 | /* | ||
| 123 | * Note: pageblock_nr_page != MAX_ORDER. Then, chunks of free page | ||
| 124 | * is not aligned to pageblock_nr_pages. | ||
| 125 | * Then we just check pagetype fist. | ||
| 126 | */ | ||
| 127 | for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) { | ||
| 128 | page = __first_valid_page(pfn, pageblock_nr_pages); | ||
| 129 | if (page && get_pageblock_flags(page) != MIGRATE_ISOLATE) | ||
| 130 | break; | ||
| 131 | } | ||
| 132 | if (pfn < end_pfn) | ||
| 133 | return -EBUSY; | ||
| 134 | /* Check all pages are free or Marked as ISOLATED */ | ||
| 135 | if (__test_page_isolated_in_pageblock(start_pfn, end_pfn)) | ||
| 136 | return 0; | ||
| 137 | return -EBUSY; | ||
| 138 | } | ||
