diff options
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-memory.c | 43 | ||||
-rw-r--r-- | include/linux/lmb.h | 3 | ||||
-rw-r--r-- | lib/lmb.c | 66 |
3 files changed, 102 insertions, 10 deletions
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 2d3e9a4bd6a..3c5727dd5aa 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c | |||
@@ -10,6 +10,7 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/of.h> | 12 | #include <linux/of.h> |
13 | #include <linux/lmb.h> | ||
13 | #include <asm/firmware.h> | 14 | #include <asm/firmware.h> |
14 | #include <asm/machdep.h> | 15 | #include <asm/machdep.h> |
15 | #include <asm/pSeries_reconfig.h> | 16 | #include <asm/pSeries_reconfig.h> |
@@ -58,6 +59,11 @@ static int pseries_remove_memory(struct device_node *np) | |||
58 | return ret; | 59 | return ret; |
59 | 60 | ||
60 | /* | 61 | /* |
62 | * Update memory regions for memory remove | ||
63 | */ | ||
64 | lmb_remove(start_pfn << PAGE_SHIFT, regs[3]); | ||
65 | |||
66 | /* | ||
61 | * Remove htab bolted mappings for this section of memory | 67 | * Remove htab bolted mappings for this section of memory |
62 | */ | 68 | */ |
63 | start = (unsigned long)__va(start_pfn << PAGE_SHIFT); | 69 | start = (unsigned long)__va(start_pfn << PAGE_SHIFT); |
@@ -65,6 +71,41 @@ static int pseries_remove_memory(struct device_node *np) | |||
65 | return ret; | 71 | return ret; |
66 | } | 72 | } |
67 | 73 | ||
74 | static int pseries_add_memory(struct device_node *np) | ||
75 | { | ||
76 | const char *type; | ||
77 | const unsigned int *my_index; | ||
78 | const unsigned int *regs; | ||
79 | u64 start_pfn; | ||
80 | int ret = -EINVAL; | ||
81 | |||
82 | /* | ||
83 | * Check to see if we are actually adding memory | ||
84 | */ | ||
85 | type = of_get_property(np, "device_type", NULL); | ||
86 | if (type == NULL || strcmp(type, "memory") != 0) | ||
87 | return 0; | ||
88 | |||
89 | /* | ||
90 | * Find the memory index and size of the added section | ||
91 | */ | ||
92 | my_index = of_get_property(np, "ibm,my-drc-index", NULL); | ||
93 | if (!my_index) | ||
94 | return ret; | ||
95 | |||
96 | regs = of_get_property(np, "reg", NULL); | ||
97 | if (!regs) | ||
98 | return ret; | ||
99 | |||
100 | start_pfn = section_nr_to_pfn(*my_index & 0xffff); | ||
101 | |||
102 | /* | ||
103 | * Update memory region to represent the memory add | ||
104 | */ | ||
105 | lmb_add(start_pfn << PAGE_SHIFT, regs[3]); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
68 | static int pseries_memory_notifier(struct notifier_block *nb, | 109 | static int pseries_memory_notifier(struct notifier_block *nb, |
69 | unsigned long action, void *node) | 110 | unsigned long action, void *node) |
70 | { | 111 | { |
@@ -72,6 +113,8 @@ static int pseries_memory_notifier(struct notifier_block *nb, | |||
72 | 113 | ||
73 | switch (action) { | 114 | switch (action) { |
74 | case PSERIES_RECONFIG_ADD: | 115 | case PSERIES_RECONFIG_ADD: |
116 | if (pseries_add_memory(node)) | ||
117 | err = NOTIFY_BAD; | ||
75 | break; | 118 | break; |
76 | case PSERIES_RECONFIG_REMOVE: | 119 | case PSERIES_RECONFIG_REMOVE: |
77 | if (pseries_remove_memory(node)) | 120 | if (pseries_remove_memory(node)) |
diff --git a/include/linux/lmb.h b/include/linux/lmb.h index 271153d27fb..55d4b261a9e 100644 --- a/include/linux/lmb.h +++ b/include/linux/lmb.h | |||
@@ -40,7 +40,8 @@ extern struct lmb lmb; | |||
40 | 40 | ||
41 | extern void __init lmb_init(void); | 41 | extern void __init lmb_init(void); |
42 | extern void __init lmb_analyze(void); | 42 | extern void __init lmb_analyze(void); |
43 | extern long __init lmb_add(u64 base, u64 size); | 43 | extern long lmb_add(u64 base, u64 size); |
44 | extern long lmb_remove(u64 base, u64 size); | ||
44 | extern long __init lmb_reserve(u64 base, u64 size); | 45 | extern long __init lmb_reserve(u64 base, u64 size); |
45 | extern u64 __init lmb_alloc_nid(u64 size, u64 align, int nid, | 46 | extern u64 __init lmb_alloc_nid(u64 size, u64 align, int nid, |
46 | u64 (*nid_range)(u64, u64, int *)); | 47 | u64 (*nid_range)(u64, u64, int *)); |
@@ -46,14 +46,13 @@ void lmb_dump_all(void) | |||
46 | #endif /* DEBUG */ | 46 | #endif /* DEBUG */ |
47 | } | 47 | } |
48 | 48 | ||
49 | static unsigned long __init lmb_addrs_overlap(u64 base1, u64 size1, | 49 | static unsigned long lmb_addrs_overlap(u64 base1, u64 size1, u64 base2, |
50 | u64 base2, u64 size2) | 50 | u64 size2) |
51 | { | 51 | { |
52 | return ((base1 < (base2 + size2)) && (base2 < (base1 + size1))); | 52 | return ((base1 < (base2 + size2)) && (base2 < (base1 + size1))); |
53 | } | 53 | } |
54 | 54 | ||
55 | static long __init lmb_addrs_adjacent(u64 base1, u64 size1, | 55 | static long lmb_addrs_adjacent(u64 base1, u64 size1, u64 base2, u64 size2) |
56 | u64 base2, u64 size2) | ||
57 | { | 56 | { |
58 | if (base2 == base1 + size1) | 57 | if (base2 == base1 + size1) |
59 | return 1; | 58 | return 1; |
@@ -63,7 +62,7 @@ static long __init lmb_addrs_adjacent(u64 base1, u64 size1, | |||
63 | return 0; | 62 | return 0; |
64 | } | 63 | } |
65 | 64 | ||
66 | static long __init lmb_regions_adjacent(struct lmb_region *rgn, | 65 | static long lmb_regions_adjacent(struct lmb_region *rgn, |
67 | unsigned long r1, unsigned long r2) | 66 | unsigned long r1, unsigned long r2) |
68 | { | 67 | { |
69 | u64 base1 = rgn->region[r1].base; | 68 | u64 base1 = rgn->region[r1].base; |
@@ -74,7 +73,7 @@ static long __init lmb_regions_adjacent(struct lmb_region *rgn, | |||
74 | return lmb_addrs_adjacent(base1, size1, base2, size2); | 73 | return lmb_addrs_adjacent(base1, size1, base2, size2); |
75 | } | 74 | } |
76 | 75 | ||
77 | static void __init lmb_remove_region(struct lmb_region *rgn, unsigned long r) | 76 | static void lmb_remove_region(struct lmb_region *rgn, unsigned long r) |
78 | { | 77 | { |
79 | unsigned long i; | 78 | unsigned long i; |
80 | 79 | ||
@@ -86,7 +85,7 @@ static void __init lmb_remove_region(struct lmb_region *rgn, unsigned long r) | |||
86 | } | 85 | } |
87 | 86 | ||
88 | /* Assumption: base addr of region 1 < base addr of region 2 */ | 87 | /* Assumption: base addr of region 1 < base addr of region 2 */ |
89 | static void __init lmb_coalesce_regions(struct lmb_region *rgn, | 88 | static void lmb_coalesce_regions(struct lmb_region *rgn, |
90 | unsigned long r1, unsigned long r2) | 89 | unsigned long r1, unsigned long r2) |
91 | { | 90 | { |
92 | rgn->region[r1].size += rgn->region[r2].size; | 91 | rgn->region[r1].size += rgn->region[r2].size; |
@@ -118,7 +117,7 @@ void __init lmb_analyze(void) | |||
118 | lmb.memory.size += lmb.memory.region[i].size; | 117 | lmb.memory.size += lmb.memory.region[i].size; |
119 | } | 118 | } |
120 | 119 | ||
121 | static long __init lmb_add_region(struct lmb_region *rgn, u64 base, u64 size) | 120 | static long lmb_add_region(struct lmb_region *rgn, u64 base, u64 size) |
122 | { | 121 | { |
123 | unsigned long coalesced = 0; | 122 | unsigned long coalesced = 0; |
124 | long adjacent, i; | 123 | long adjacent, i; |
@@ -182,7 +181,7 @@ static long __init lmb_add_region(struct lmb_region *rgn, u64 base, u64 size) | |||
182 | return 0; | 181 | return 0; |
183 | } | 182 | } |
184 | 183 | ||
185 | long __init lmb_add(u64 base, u64 size) | 184 | long lmb_add(u64 base, u64 size) |
186 | { | 185 | { |
187 | struct lmb_region *_rgn = &lmb.memory; | 186 | struct lmb_region *_rgn = &lmb.memory; |
188 | 187 | ||
@@ -194,6 +193,55 @@ long __init lmb_add(u64 base, u64 size) | |||
194 | 193 | ||
195 | } | 194 | } |
196 | 195 | ||
196 | long lmb_remove(u64 base, u64 size) | ||
197 | { | ||
198 | struct lmb_region *rgn = &(lmb.memory); | ||
199 | u64 rgnbegin, rgnend; | ||
200 | u64 end = base + size; | ||
201 | int i; | ||
202 | |||
203 | rgnbegin = rgnend = 0; /* supress gcc warnings */ | ||
204 | |||
205 | /* Find the region where (base, size) belongs to */ | ||
206 | for (i=0; i < rgn->cnt; i++) { | ||
207 | rgnbegin = rgn->region[i].base; | ||
208 | rgnend = rgnbegin + rgn->region[i].size; | ||
209 | |||
210 | if ((rgnbegin <= base) && (end <= rgnend)) | ||
211 | break; | ||
212 | } | ||
213 | |||
214 | /* Didn't find the region */ | ||
215 | if (i == rgn->cnt) | ||
216 | return -1; | ||
217 | |||
218 | /* Check to see if we are removing entire region */ | ||
219 | if ((rgnbegin == base) && (rgnend == end)) { | ||
220 | lmb_remove_region(rgn, i); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | /* Check to see if region is matching at the front */ | ||
225 | if (rgnbegin == base) { | ||
226 | rgn->region[i].base = end; | ||
227 | rgn->region[i].size -= size; | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | /* Check to see if the region is matching at the end */ | ||
232 | if (rgnend == end) { | ||
233 | rgn->region[i].size -= size; | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * We need to split the entry - adjust the current one to the | ||
239 | * beginging of the hole and add the region after hole. | ||
240 | */ | ||
241 | rgn->region[i].size = base - rgn->region[i].base; | ||
242 | return lmb_add_region(rgn, end, rgnend - end); | ||
243 | } | ||
244 | |||
197 | long __init lmb_reserve(u64 base, u64 size) | 245 | long __init lmb_reserve(u64 base, u64 size) |
198 | { | 246 | { |
199 | struct lmb_region *_rgn = &lmb.reserved; | 247 | struct lmb_region *_rgn = &lmb.reserved; |