Skip to content

Commit 51925fb

Browse files
nfontozbenh
authored andcommitted
powerpc/pseries: Implement memory hotplug remove in the kernel
This patch adds the ability to do memory hotplug remove in the kernel. Currently the operation to hotplug remove memory is handled by the drmgr command which performs the operation by performing some work in user-space and making requests to the kernel to handle other pieces. By moving all of the work to the kernel we can do the remove faster, and provide a common code path to do memory hotplug for both the PowerVM and PowerKVM environments. Signed-off-by: Nathan Fontenot <[email protected]> Signed-off-by: Benjamin Herrenschmidt <[email protected]>
1 parent 5f97b2a commit 51925fb

File tree

1 file changed

+191
-1
lines changed

1 file changed

+191
-1
lines changed

arch/powerpc/platforms/pseries/hotplug-memory.c

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,173 @@ static int pseries_remove_mem_node(struct device_node *np)
188188
pseries_remove_memblock(base, lmb_size);
189189
return 0;
190190
}
191+
192+
static bool lmb_is_removable(struct of_drconf_cell *lmb)
193+
{
194+
int i, scns_per_block;
195+
int rc = 1;
196+
unsigned long pfn, block_sz;
197+
u64 phys_addr;
198+
199+
if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
200+
return false;
201+
202+
block_sz = memory_block_size_bytes();
203+
scns_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
204+
phys_addr = lmb->base_addr;
205+
206+
for (i = 0; i < scns_per_block; i++) {
207+
pfn = PFN_DOWN(phys_addr);
208+
if (!pfn_present(pfn))
209+
continue;
210+
211+
rc &= is_mem_section_removable(pfn, PAGES_PER_SECTION);
212+
phys_addr += MIN_MEMORY_BLOCK_SIZE;
213+
}
214+
215+
return rc ? true : false;
216+
}
217+
218+
static int dlpar_add_lmb(struct of_drconf_cell *);
219+
220+
static int dlpar_remove_lmb(struct of_drconf_cell *lmb)
221+
{
222+
struct memory_block *mem_block;
223+
unsigned long block_sz;
224+
int nid, rc;
225+
226+
if (!lmb_is_removable(lmb))
227+
return -EINVAL;
228+
229+
mem_block = lmb_to_memblock(lmb);
230+
if (!mem_block)
231+
return -EINVAL;
232+
233+
rc = device_offline(&mem_block->dev);
234+
put_device(&mem_block->dev);
235+
if (rc)
236+
return rc;
237+
238+
block_sz = pseries_memory_block_size();
239+
nid = memory_add_physaddr_to_nid(lmb->base_addr);
240+
241+
remove_memory(nid, lmb->base_addr, block_sz);
242+
243+
/* Update memory regions for memory remove */
244+
memblock_remove(lmb->base_addr, block_sz);
245+
246+
dlpar_release_drc(lmb->drc_index);
247+
248+
lmb->flags &= ~DRCONF_MEM_ASSIGNED;
249+
return 0;
250+
}
251+
252+
static int dlpar_memory_remove_by_count(u32 lmbs_to_remove,
253+
struct property *prop)
254+
{
255+
struct of_drconf_cell *lmbs;
256+
int lmbs_removed = 0;
257+
int lmbs_available = 0;
258+
u32 num_lmbs, *p;
259+
int i, rc;
260+
261+
pr_info("Attempting to hot-remove %d LMB(s)\n", lmbs_to_remove);
262+
263+
if (lmbs_to_remove == 0)
264+
return -EINVAL;
265+
266+
p = prop->value;
267+
num_lmbs = *p++;
268+
lmbs = (struct of_drconf_cell *)p;
269+
270+
/* Validate that there are enough LMBs to satisfy the request */
271+
for (i = 0; i < num_lmbs; i++) {
272+
if (lmbs[i].flags & DRCONF_MEM_ASSIGNED)
273+
lmbs_available++;
274+
}
275+
276+
if (lmbs_available < lmbs_to_remove)
277+
return -EINVAL;
278+
279+
for (i = 0; i < num_lmbs && lmbs_removed < lmbs_to_remove; i++) {
280+
rc = dlpar_remove_lmb(&lmbs[i]);
281+
if (rc)
282+
continue;
283+
284+
lmbs_removed++;
285+
286+
/* Mark this lmb so we can add it later if all of the
287+
* requested LMBs cannot be removed.
288+
*/
289+
lmbs[i].reserved = 1;
290+
}
291+
292+
if (lmbs_removed != lmbs_to_remove) {
293+
pr_err("Memory hot-remove failed, adding LMB's back\n");
294+
295+
for (i = 0; i < num_lmbs; i++) {
296+
if (!lmbs[i].reserved)
297+
continue;
298+
299+
rc = dlpar_add_lmb(&lmbs[i]);
300+
if (rc)
301+
pr_err("Failed to add LMB back, drc index %x\n",
302+
lmbs[i].drc_index);
303+
304+
lmbs[i].reserved = 0;
305+
}
306+
307+
rc = -EINVAL;
308+
} else {
309+
for (i = 0; i < num_lmbs; i++) {
310+
if (!lmbs[i].reserved)
311+
continue;
312+
313+
pr_info("Memory at %llx was hot-removed\n",
314+
lmbs[i].base_addr);
315+
316+
lmbs[i].reserved = 0;
317+
}
318+
rc = 0;
319+
}
320+
321+
return rc;
322+
}
323+
324+
static int dlpar_memory_remove_by_index(u32 drc_index, struct property *prop)
325+
{
326+
struct of_drconf_cell *lmbs;
327+
u32 num_lmbs, *p;
328+
int lmb_found;
329+
int i, rc;
330+
331+
pr_info("Attempting to hot-remove LMB, drc index %x\n", drc_index);
332+
333+
p = prop->value;
334+
num_lmbs = *p++;
335+
lmbs = (struct of_drconf_cell *)p;
336+
337+
lmb_found = 0;
338+
for (i = 0; i < num_lmbs; i++) {
339+
if (lmbs[i].drc_index == drc_index) {
340+
lmb_found = 1;
341+
rc = dlpar_remove_lmb(&lmbs[i]);
342+
break;
343+
}
344+
}
345+
346+
if (!lmb_found)
347+
rc = -EINVAL;
348+
349+
if (rc)
350+
pr_info("Failed to hot-remove memory at %llx\n",
351+
lmbs[i].base_addr);
352+
else
353+
pr_info("Memory at %llx was hot-removed\n", lmbs[i].base_addr);
354+
355+
return rc;
356+
}
357+
191358
#else
192359
static inline int pseries_remove_memblock(unsigned long base,
193360
unsigned int memblock_size)
@@ -198,6 +365,11 @@ static inline int pseries_remove_mem_node(struct device_node *np)
198365
{
199366
return 0;
200367
}
368+
static inline int dlpar_memory_remove(struct pseries_hp_errorlog *hp_elog)
369+
{
370+
return -EOPNOTSUPP;
371+
}
372+
201373
#endif /* CONFIG_MEMORY_HOTREMOVE */
202374

203375
static int dlpar_add_lmb(struct of_drconf_cell *lmb)
@@ -292,7 +464,17 @@ static int dlpar_memory_add_by_count(u32 lmbs_to_add, struct property *prop)
292464
}
293465

294466
if (lmbs_added != lmbs_to_add) {
295-
/* TODO: remove added lmbs */
467+
pr_err("Memory hot-add failed, removing any added LMBs\n");
468+
469+
for (i = 0; i < num_lmbs; i++) {
470+
if (!lmbs[i].reserved)
471+
continue;
472+
473+
rc = dlpar_remove_lmb(&lmbs[i]);
474+
if (rc)
475+
pr_err("Failed to remove LMB, drc index %x\n",
476+
be32_to_cpu(lmbs[i].drc_index));
477+
}
296478
rc = -EINVAL;
297479
} else {
298480
for (i = 0; i < num_lmbs; i++) {
@@ -398,6 +580,14 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
398580
else
399581
rc = -EINVAL;
400582
break;
583+
case PSERIES_HP_ELOG_ACTION_REMOVE:
584+
if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
585+
rc = dlpar_memory_remove_by_count(count, prop);
586+
else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
587+
rc = dlpar_memory_remove_by_index(drc_index, prop);
588+
else
589+
rc = -EINVAL;
590+
break;
401591
default:
402592
pr_err("Invalid action (%d) specified\n", hp_elog->action);
403593
rc = -EINVAL;

0 commit comments

Comments
 (0)