diff options
-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 | } | ||