aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-memory.c43
-rw-r--r--include/linux/lmb.h3
-rw-r--r--lib/lmb.c66
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
74static 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
68static int pseries_memory_notifier(struct notifier_block *nb, 109static 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
41extern void __init lmb_init(void); 41extern void __init lmb_init(void);
42extern void __init lmb_analyze(void); 42extern void __init lmb_analyze(void);
43extern long __init lmb_add(u64 base, u64 size); 43extern long lmb_add(u64 base, u64 size);
44extern long lmb_remove(u64 base, u64 size);
44extern long __init lmb_reserve(u64 base, u64 size); 45extern long __init lmb_reserve(u64 base, u64 size);
45extern u64 __init lmb_alloc_nid(u64 size, u64 align, int nid, 46extern 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 *));
diff --git a/lib/lmb.c b/lib/lmb.c
index 207147ab25e..5b2a739bc3d 100644
--- a/lib/lmb.c
+++ b/lib/lmb.c
@@ -46,14 +46,13 @@ void lmb_dump_all(void)
46#endif /* DEBUG */ 46#endif /* DEBUG */
47} 47}
48 48
49static unsigned long __init lmb_addrs_overlap(u64 base1, u64 size1, 49static 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
55static long __init lmb_addrs_adjacent(u64 base1, u64 size1, 55static 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
66static long __init lmb_regions_adjacent(struct lmb_region *rgn, 65static 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
77static void __init lmb_remove_region(struct lmb_region *rgn, unsigned long r) 76static 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 */
89static void __init lmb_coalesce_regions(struct lmb_region *rgn, 88static 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
121static long __init lmb_add_region(struct lmb_region *rgn, u64 base, u64 size) 120static 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
185long __init lmb_add(u64 base, u64 size) 184long 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
196long 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
197long __init lmb_reserve(u64 base, u64 size) 245long __init lmb_reserve(u64 base, u64 size)
198{ 246{
199 struct lmb_region *_rgn = &lmb.reserved; 247 struct lmb_region *_rgn = &lmb.reserved;