aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-devices-memory24
-rw-r--r--drivers/base/memory.c19
-rw-r--r--include/linux/memory_hotplug.h12
-rw-r--r--mm/memory_hotplug.c60
4 files changed, 115 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-memory b/Documentation/ABI/testing/sysfs-devices-memory
new file mode 100644
index 000000000000..7a16fe1e2270
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-memory
@@ -0,0 +1,24 @@
1What: /sys/devices/system/memory
2Date: June 2008
3Contact: Badari Pulavarty <pbadari@us.ibm.com>
4Description:
5 The /sys/devices/system/memory contains a snapshot of the
6 internal state of the kernel memory blocks. Files could be
7 added or removed dynamically to represent hot-add/remove
8 operations.
9
10Users: hotplug memory add/remove tools
11 https://w3.opensource.ibm.com/projects/powerpc-utils/
12
13What: /sys/devices/system/memory/memoryX/removable
14Date: June 2008
15Contact: Badari Pulavarty <pbadari@us.ibm.com>
16Description:
17 The file /sys/devices/system/memory/memoryX/removable
18 indicates whether this memory block is removable or not.
19 This is useful for a user-level agent to determine
20 identify removable sections of the memory before attempting
21 potentially expensive hot-remove memory operation
22
23Users: hotplug memory remove tools
24 https://w3.opensource.ibm.com/projects/powerpc-utils/
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 4d4e0e7b6e92..855ed1a9f97b 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -101,6 +101,21 @@ static ssize_t show_mem_phys_index(struct sys_device *dev,
101} 101}
102 102
103/* 103/*
104 * Show whether the section of memory is likely to be hot-removable
105 */
106static ssize_t show_mem_removable(struct sys_device *dev, char *buf)
107{
108 unsigned long start_pfn;
109 int ret;
110 struct memory_block *mem =
111 container_of(dev, struct memory_block, sysdev);
112
113 start_pfn = section_nr_to_pfn(mem->phys_index);
114 ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
115 return sprintf(buf, "%d\n", ret);
116}
117
118/*
104 * online, offline, going offline, etc. 119 * online, offline, going offline, etc.
105 */ 120 */
106static ssize_t show_mem_state(struct sys_device *dev, 121static ssize_t show_mem_state(struct sys_device *dev,
@@ -262,6 +277,7 @@ static ssize_t show_phys_device(struct sys_device *dev,
262static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL); 277static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL);
263static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state); 278static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state);
264static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL); 279static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL);
280static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL);
265 281
266#define mem_create_simple_file(mem, attr_name) \ 282#define mem_create_simple_file(mem, attr_name) \
267 sysdev_create_file(&mem->sysdev, &attr_##attr_name) 283 sysdev_create_file(&mem->sysdev, &attr_##attr_name)
@@ -350,6 +366,8 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section,
350 ret = mem_create_simple_file(mem, state); 366 ret = mem_create_simple_file(mem, state);
351 if (!ret) 367 if (!ret)
352 ret = mem_create_simple_file(mem, phys_device); 368 ret = mem_create_simple_file(mem, phys_device);
369 if (!ret)
370 ret = mem_create_simple_file(mem, removable);
353 371
354 return ret; 372 return ret;
355} 373}
@@ -394,6 +412,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section,
394 mem_remove_simple_file(mem, phys_index); 412 mem_remove_simple_file(mem, phys_index);
395 mem_remove_simple_file(mem, state); 413 mem_remove_simple_file(mem, state);
396 mem_remove_simple_file(mem, phys_device); 414 mem_remove_simple_file(mem, phys_device);
415 mem_remove_simple_file(mem, removable);
397 unregister_memory(mem, section); 416 unregister_memory(mem, section);
398 417
399 return 0; 418 return 0;
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index 3628e5088f64..763ba81fc0f0 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -199,6 +199,18 @@ extern int walk_memory_resource(unsigned long start_pfn,
199 unsigned long nr_pages, void *arg, 199 unsigned long nr_pages, void *arg,
200 int (*func)(unsigned long, unsigned long, void *)); 200 int (*func)(unsigned long, unsigned long, void *));
201 201
202#ifdef CONFIG_MEMORY_HOTREMOVE
203
204extern int is_mem_section_removable(unsigned long pfn, unsigned long nr_pages);
205
206#else
207static inline int is_mem_section_removable(unsigned long pfn,
208 unsigned long nr_pages)
209{
210 return 0;
211}
212#endif /* CONFIG_MEMORY_HOTREMOVE */
213
202extern int add_memory(int nid, u64 start, u64 size); 214extern int add_memory(int nid, u64 start, u64 size);
203extern int arch_add_memory(int nid, u64 start, u64 size); 215extern int arch_add_memory(int nid, u64 start, u64 size);
204extern int remove_memory(u64 start, u64 size); 216extern int remove_memory(u64 start, u64 size);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 93aba78dc8b6..89fee2dcb039 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -523,6 +523,66 @@ EXPORT_SYMBOL_GPL(add_memory);
523 523
524#ifdef CONFIG_MEMORY_HOTREMOVE 524#ifdef CONFIG_MEMORY_HOTREMOVE
525/* 525/*
526 * A free page on the buddy free lists (not the per-cpu lists) has PageBuddy
527 * set and the size of the free page is given by page_order(). Using this,
528 * the function determines if the pageblock contains only free pages.
529 * Due to buddy contraints, a free page at least the size of a pageblock will
530 * be located at the start of the pageblock
531 */
532static inline int pageblock_free(struct page *page)
533{
534 return PageBuddy(page) && page_order(page) >= pageblock_order;
535}
536
537/* Return the start of the next active pageblock after a given page */
538static struct page *next_active_pageblock(struct page *page)
539{
540 int pageblocks_stride;
541
542 /* Ensure the starting page is pageblock-aligned */
543 BUG_ON(page_to_pfn(page) & (pageblock_nr_pages - 1));
544
545 /* Move forward by at least 1 * pageblock_nr_pages */
546 pageblocks_stride = 1;
547
548 /* If the entire pageblock is free, move to the end of free page */
549 if (pageblock_free(page))
550 pageblocks_stride += page_order(page) - pageblock_order;
551
552 return page + (pageblocks_stride * pageblock_nr_pages);
553}
554
555/* Checks if this range of memory is likely to be hot-removable. */
556int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
557{
558 int type;
559 struct page *page = pfn_to_page(start_pfn);
560 struct page *end_page = page + nr_pages;
561
562 /* Check the starting page of each pageblock within the range */
563 for (; page < end_page; page = next_active_pageblock(page)) {
564 type = get_pageblock_migratetype(page);
565
566 /*
567 * A pageblock containing MOVABLE or free pages is considered
568 * removable
569 */
570 if (type != MIGRATE_MOVABLE && !pageblock_free(page))
571 return 0;
572
573 /*
574 * A pageblock starting with a PageReserved page is not
575 * considered removable.
576 */
577 if (PageReserved(page))
578 return 0;
579 }
580
581 /* All pageblocks in the memory block are likely to be hot-removable */
582 return 1;
583}
584
585/*
526 * Confirm all pages in a range [start, end) is belongs to the same zone. 586 * Confirm all pages in a range [start, end) is belongs to the same zone.
527 */ 587 */
528static int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn) 588static int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)