diff options
-rw-r--r-- | drivers/base/memory.c | 9 | ||||
-rw-r--r-- | include/linux/memory.h | 27 | ||||
-rw-r--r-- | mm/memory_hotplug.c | 48 |
3 files changed, 61 insertions, 23 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index c41d0728efe2..7868707c7eda 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
@@ -137,7 +137,7 @@ static ssize_t show_mem_state(struct sys_device *dev, char *buf) | |||
137 | return len; | 137 | return len; |
138 | } | 138 | } |
139 | 139 | ||
140 | static inline int memory_notify(unsigned long val, void *v) | 140 | int memory_notify(unsigned long val, void *v) |
141 | { | 141 | { |
142 | return blocking_notifier_call_chain(&memory_chain, val, v); | 142 | return blocking_notifier_call_chain(&memory_chain, val, v); |
143 | } | 143 | } |
@@ -183,7 +183,6 @@ memory_block_action(struct memory_block *mem, unsigned long action) | |||
183 | break; | 183 | break; |
184 | case MEM_OFFLINE: | 184 | case MEM_OFFLINE: |
185 | mem->state = MEM_GOING_OFFLINE; | 185 | mem->state = MEM_GOING_OFFLINE; |
186 | memory_notify(MEM_GOING_OFFLINE, NULL); | ||
187 | start_paddr = page_to_pfn(first_page) << PAGE_SHIFT; | 186 | start_paddr = page_to_pfn(first_page) << PAGE_SHIFT; |
188 | ret = remove_memory(start_paddr, | 187 | ret = remove_memory(start_paddr, |
189 | PAGES_PER_SECTION << PAGE_SHIFT); | 188 | PAGES_PER_SECTION << PAGE_SHIFT); |
@@ -191,7 +190,6 @@ memory_block_action(struct memory_block *mem, unsigned long action) | |||
191 | mem->state = old_state; | 190 | mem->state = old_state; |
192 | break; | 191 | break; |
193 | } | 192 | } |
194 | memory_notify(MEM_MAPPING_INVALID, NULL); | ||
195 | break; | 193 | break; |
196 | default: | 194 | default: |
197 | printk(KERN_WARNING "%s(%p, %ld) unknown action: %ld\n", | 195 | printk(KERN_WARNING "%s(%p, %ld) unknown action: %ld\n", |
@@ -199,11 +197,6 @@ memory_block_action(struct memory_block *mem, unsigned long action) | |||
199 | WARN_ON(1); | 197 | WARN_ON(1); |
200 | ret = -EINVAL; | 198 | ret = -EINVAL; |
201 | } | 199 | } |
202 | /* | ||
203 | * For now, only notify on successful memory operations | ||
204 | */ | ||
205 | if (!ret) | ||
206 | memory_notify(action, NULL); | ||
207 | 200 | ||
208 | return ret; | 201 | return ret; |
209 | } | 202 | } |
diff --git a/include/linux/memory.h b/include/linux/memory.h index 654ef5544878..ec376e482abb 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h | |||
@@ -41,18 +41,15 @@ struct memory_block { | |||
41 | #define MEM_ONLINE (1<<0) /* exposed to userspace */ | 41 | #define MEM_ONLINE (1<<0) /* exposed to userspace */ |
42 | #define MEM_GOING_OFFLINE (1<<1) /* exposed to userspace */ | 42 | #define MEM_GOING_OFFLINE (1<<1) /* exposed to userspace */ |
43 | #define MEM_OFFLINE (1<<2) /* exposed to userspace */ | 43 | #define MEM_OFFLINE (1<<2) /* exposed to userspace */ |
44 | #define MEM_GOING_ONLINE (1<<3) | ||
45 | #define MEM_CANCEL_ONLINE (1<<4) | ||
46 | #define MEM_CANCEL_OFFLINE (1<<5) | ||
44 | 47 | ||
45 | /* | 48 | struct memory_notify { |
46 | * All of these states are currently kernel-internal for notifying | 49 | unsigned long start_pfn; |
47 | * kernel components and architectures. | 50 | unsigned long nr_pages; |
48 | * | 51 | int status_change_nid; |
49 | * For MEM_MAPPING_INVALID, all notifier chains with priority >0 | 52 | }; |
50 | * are called before pfn_to_page() becomes invalid. The priority=0 | ||
51 | * entry is reserved for the function that actually makes | ||
52 | * pfn_to_page() stop working. Any notifiers that want to be called | ||
53 | * after that should have priority <0. | ||
54 | */ | ||
55 | #define MEM_MAPPING_INVALID (1<<3) | ||
56 | 53 | ||
57 | struct notifier_block; | 54 | struct notifier_block; |
58 | struct mem_section; | 55 | struct mem_section; |
@@ -69,12 +66,18 @@ static inline int register_memory_notifier(struct notifier_block *nb) | |||
69 | static inline void unregister_memory_notifier(struct notifier_block *nb) | 66 | static inline void unregister_memory_notifier(struct notifier_block *nb) |
70 | { | 67 | { |
71 | } | 68 | } |
69 | static inline int memory_notify(unsigned long val, void *v) | ||
70 | { | ||
71 | return 0; | ||
72 | } | ||
72 | #else | 73 | #else |
74 | extern int register_memory_notifier(struct notifier_block *nb); | ||
75 | extern void unregister_memory_notifier(struct notifier_block *nb); | ||
73 | extern int register_new_memory(struct mem_section *); | 76 | extern int register_new_memory(struct mem_section *); |
74 | extern int unregister_memory_section(struct mem_section *); | 77 | extern int unregister_memory_section(struct mem_section *); |
75 | extern int memory_dev_init(void); | 78 | extern int memory_dev_init(void); |
76 | extern int remove_memory_block(unsigned long, struct mem_section *, int); | 79 | extern int remove_memory_block(unsigned long, struct mem_section *, int); |
77 | 80 | extern int memory_notify(unsigned long val, void *v); | |
78 | #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION<<PAGE_SHIFT) | 81 | #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION<<PAGE_SHIFT) |
79 | 82 | ||
80 | 83 | ||
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 1833879f8438..3a47871a29d9 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c | |||
@@ -187,7 +187,24 @@ int online_pages(unsigned long pfn, unsigned long nr_pages) | |||
187 | unsigned long onlined_pages = 0; | 187 | unsigned long onlined_pages = 0; |
188 | struct zone *zone; | 188 | struct zone *zone; |
189 | int need_zonelists_rebuild = 0; | 189 | int need_zonelists_rebuild = 0; |
190 | int nid; | ||
191 | int ret; | ||
192 | struct memory_notify arg; | ||
193 | |||
194 | arg.start_pfn = pfn; | ||
195 | arg.nr_pages = nr_pages; | ||
196 | arg.status_change_nid = -1; | ||
197 | |||
198 | nid = page_to_nid(pfn_to_page(pfn)); | ||
199 | if (node_present_pages(nid) == 0) | ||
200 | arg.status_change_nid = nid; | ||
190 | 201 | ||
202 | ret = memory_notify(MEM_GOING_ONLINE, &arg); | ||
203 | ret = notifier_to_errno(ret); | ||
204 | if (ret) { | ||
205 | memory_notify(MEM_CANCEL_ONLINE, &arg); | ||
206 | return ret; | ||
207 | } | ||
191 | /* | 208 | /* |
192 | * This doesn't need a lock to do pfn_to_page(). | 209 | * This doesn't need a lock to do pfn_to_page(). |
193 | * The section can't be removed here because of the | 210 | * The section can't be removed here because of the |
@@ -222,6 +239,10 @@ int online_pages(unsigned long pfn, unsigned long nr_pages) | |||
222 | build_all_zonelists(); | 239 | build_all_zonelists(); |
223 | vm_total_pages = nr_free_pagecache_pages(); | 240 | vm_total_pages = nr_free_pagecache_pages(); |
224 | writeback_set_ratelimit(); | 241 | writeback_set_ratelimit(); |
242 | |||
243 | if (onlined_pages) | ||
244 | memory_notify(MEM_ONLINE, &arg); | ||
245 | |||
225 | return 0; | 246 | return 0; |
226 | } | 247 | } |
227 | #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ | 248 | #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ |
@@ -467,8 +488,9 @@ int offline_pages(unsigned long start_pfn, | |||
467 | { | 488 | { |
468 | unsigned long pfn, nr_pages, expire; | 489 | unsigned long pfn, nr_pages, expire; |
469 | long offlined_pages; | 490 | long offlined_pages; |
470 | int ret, drain, retry_max; | 491 | int ret, drain, retry_max, node; |
471 | struct zone *zone; | 492 | struct zone *zone; |
493 | struct memory_notify arg; | ||
472 | 494 | ||
473 | BUG_ON(start_pfn >= end_pfn); | 495 | BUG_ON(start_pfn >= end_pfn); |
474 | /* at least, alignment against pageblock is necessary */ | 496 | /* at least, alignment against pageblock is necessary */ |
@@ -480,11 +502,27 @@ int offline_pages(unsigned long start_pfn, | |||
480 | we assume this for now. .*/ | 502 | we assume this for now. .*/ |
481 | if (!test_pages_in_a_zone(start_pfn, end_pfn)) | 503 | if (!test_pages_in_a_zone(start_pfn, end_pfn)) |
482 | return -EINVAL; | 504 | return -EINVAL; |
505 | |||
506 | zone = page_zone(pfn_to_page(start_pfn)); | ||
507 | node = zone_to_nid(zone); | ||
508 | nr_pages = end_pfn - start_pfn; | ||
509 | |||
483 | /* set above range as isolated */ | 510 | /* set above range as isolated */ |
484 | ret = start_isolate_page_range(start_pfn, end_pfn); | 511 | ret = start_isolate_page_range(start_pfn, end_pfn); |
485 | if (ret) | 512 | if (ret) |
486 | return ret; | 513 | return ret; |
487 | nr_pages = end_pfn - start_pfn; | 514 | |
515 | arg.start_pfn = start_pfn; | ||
516 | arg.nr_pages = nr_pages; | ||
517 | arg.status_change_nid = -1; | ||
518 | if (nr_pages >= node_present_pages(node)) | ||
519 | arg.status_change_nid = node; | ||
520 | |||
521 | ret = memory_notify(MEM_GOING_OFFLINE, &arg); | ||
522 | ret = notifier_to_errno(ret); | ||
523 | if (ret) | ||
524 | goto failed_removal; | ||
525 | |||
488 | pfn = start_pfn; | 526 | pfn = start_pfn; |
489 | expire = jiffies + timeout; | 527 | expire = jiffies + timeout; |
490 | drain = 0; | 528 | drain = 0; |
@@ -539,20 +577,24 @@ repeat: | |||
539 | /* reset pagetype flags */ | 577 | /* reset pagetype flags */ |
540 | start_isolate_page_range(start_pfn, end_pfn); | 578 | start_isolate_page_range(start_pfn, end_pfn); |
541 | /* removal success */ | 579 | /* removal success */ |
542 | zone = page_zone(pfn_to_page(start_pfn)); | ||
543 | zone->present_pages -= offlined_pages; | 580 | zone->present_pages -= offlined_pages; |
544 | zone->zone_pgdat->node_present_pages -= offlined_pages; | 581 | zone->zone_pgdat->node_present_pages -= offlined_pages; |
545 | totalram_pages -= offlined_pages; | 582 | totalram_pages -= offlined_pages; |
546 | num_physpages -= offlined_pages; | 583 | num_physpages -= offlined_pages; |
584 | |||
547 | vm_total_pages = nr_free_pagecache_pages(); | 585 | vm_total_pages = nr_free_pagecache_pages(); |
548 | writeback_set_ratelimit(); | 586 | writeback_set_ratelimit(); |
587 | |||
588 | memory_notify(MEM_OFFLINE, &arg); | ||
549 | return 0; | 589 | return 0; |
550 | 590 | ||
551 | failed_removal: | 591 | failed_removal: |
552 | printk(KERN_INFO "memory offlining %lx to %lx failed\n", | 592 | printk(KERN_INFO "memory offlining %lx to %lx failed\n", |
553 | start_pfn, end_pfn); | 593 | start_pfn, end_pfn); |
594 | memory_notify(MEM_CANCEL_OFFLINE, &arg); | ||
554 | /* pushback to free area */ | 595 | /* pushback to free area */ |
555 | undo_isolate_page_range(start_pfn, end_pfn); | 596 | undo_isolate_page_range(start_pfn, end_pfn); |
597 | |||
556 | return ret; | 598 | return ret; |
557 | } | 599 | } |
558 | #else | 600 | #else |