diff options
-rw-r--r-- | arch/powerpc/platforms/pseries/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-memory.c | 98 |
2 files changed, 99 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index bd2593ed28dd..554c6e42ef2a 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile | |||
@@ -18,6 +18,7 @@ obj-$(CONFIG_PCI) += pci.o pci_dlpar.o | |||
18 | obj-$(CONFIG_PCI_MSI) += msi.o | 18 | obj-$(CONFIG_PCI_MSI) += msi.o |
19 | 19 | ||
20 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o | 20 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o |
21 | obj-$(CONFIG_MEMORY_HOTPLUG) += hotplug-memory.o | ||
21 | 22 | ||
22 | obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o | 23 | obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o |
23 | obj-$(CONFIG_HVCS) += hvcserver.o | 24 | obj-$(CONFIG_HVCS) += hvcserver.o |
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c new file mode 100644 index 000000000000..2d3e9a4bd6ae --- /dev/null +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * pseries Memory Hotplug infrastructure. | ||
3 | * | ||
4 | * Copyright (C) 2008 Badari Pulavarty, IBM Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/of.h> | ||
13 | #include <asm/firmware.h> | ||
14 | #include <asm/machdep.h> | ||
15 | #include <asm/pSeries_reconfig.h> | ||
16 | |||
17 | static int pseries_remove_memory(struct device_node *np) | ||
18 | { | ||
19 | const char *type; | ||
20 | const unsigned int *my_index; | ||
21 | const unsigned int *regs; | ||
22 | u64 start_pfn, start; | ||
23 | struct zone *zone; | ||
24 | int ret = -EINVAL; | ||
25 | |||
26 | /* | ||
27 | * Check to see if we are actually removing memory | ||
28 | */ | ||
29 | type = of_get_property(np, "device_type", NULL); | ||
30 | if (type == NULL || strcmp(type, "memory") != 0) | ||
31 | return 0; | ||
32 | |||
33 | /* | ||
34 | * Find the memory index and size of the removing section | ||
35 | */ | ||
36 | my_index = of_get_property(np, "ibm,my-drc-index", NULL); | ||
37 | if (!my_index) | ||
38 | return ret; | ||
39 | |||
40 | regs = of_get_property(np, "reg", NULL); | ||
41 | if (!regs) | ||
42 | return ret; | ||
43 | |||
44 | start_pfn = section_nr_to_pfn(*my_index & 0xffff); | ||
45 | zone = page_zone(pfn_to_page(start_pfn)); | ||
46 | |||
47 | /* | ||
48 | * Remove section mappings and sysfs entries for the | ||
49 | * section of the memory we are removing. | ||
50 | * | ||
51 | * NOTE: Ideally, this should be done in generic code like | ||
52 | * remove_memory(). But remove_memory() gets called by writing | ||
53 | * to sysfs "state" file and we can't remove sysfs entries | ||
54 | * while writing to it. So we have to defer it to here. | ||
55 | */ | ||
56 | ret = __remove_pages(zone, start_pfn, regs[3] >> PAGE_SHIFT); | ||
57 | if (ret) | ||
58 | return ret; | ||
59 | |||
60 | /* | ||
61 | * Remove htab bolted mappings for this section of memory | ||
62 | */ | ||
63 | start = (unsigned long)__va(start_pfn << PAGE_SHIFT); | ||
64 | ret = remove_section_mapping(start, start + regs[3]); | ||
65 | return ret; | ||
66 | } | ||
67 | |||
68 | static int pseries_memory_notifier(struct notifier_block *nb, | ||
69 | unsigned long action, void *node) | ||
70 | { | ||
71 | int err = NOTIFY_OK; | ||
72 | |||
73 | switch (action) { | ||
74 | case PSERIES_RECONFIG_ADD: | ||
75 | break; | ||
76 | case PSERIES_RECONFIG_REMOVE: | ||
77 | if (pseries_remove_memory(node)) | ||
78 | err = NOTIFY_BAD; | ||
79 | break; | ||
80 | default: | ||
81 | err = NOTIFY_DONE; | ||
82 | break; | ||
83 | } | ||
84 | return err; | ||
85 | } | ||
86 | |||
87 | static struct notifier_block pseries_mem_nb = { | ||
88 | .notifier_call = pseries_memory_notifier, | ||
89 | }; | ||
90 | |||
91 | static int __init pseries_memory_hotplug_init(void) | ||
92 | { | ||
93 | if (firmware_has_feature(FW_FEATURE_LPAR)) | ||
94 | pSeries_reconfig_notifier_register(&pseries_mem_nb); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | machine_device_initcall(pseries, pseries_memory_hotplug_init); | ||