Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
fork
Configure Feed
Select the types of activity you want to include in your feed.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * pseries Memory Hotplug infrastructure.
4 *
5 * Copyright (C) 2008 Badari Pulavarty, IBM Corporation
6 */
7
8#define pr_fmt(fmt) "pseries-hotplug-mem: " fmt
9
10#include <linux/of.h>
11#include <linux/of_address.h>
12#include <linux/memblock.h>
13#include <linux/memory.h>
14#include <linux/memory_hotplug.h>
15#include <linux/slab.h>
16
17#include <asm/firmware.h>
18#include <asm/machdep.h>
19#include <asm/prom.h>
20#include <asm/sparsemem.h>
21#include <asm/fadump.h>
22#include <asm/drmem.h>
23#include "pseries.h"
24
25unsigned long pseries_memory_block_size(void)
26{
27 struct device_node *np;
28 u64 memblock_size = MIN_MEMORY_BLOCK_SIZE;
29 struct resource r;
30
31 np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
32 if (np) {
33 const __be64 *size;
34
35 size = of_get_property(np, "ibm,lmb-size", NULL);
36 if (size)
37 memblock_size = be64_to_cpup(size);
38 of_node_put(np);
39 } else if (machine_is(pseries)) {
40 /* This fallback really only applies to pseries */
41 unsigned int memzero_size = 0;
42
43 np = of_find_node_by_path("/memory@0");
44 if (np) {
45 if (!of_address_to_resource(np, 0, &r))
46 memzero_size = resource_size(&r);
47 of_node_put(np);
48 }
49
50 if (memzero_size) {
51 /* We now know the size of memory@0, use this to find
52 * the first memoryblock and get its size.
53 */
54 char buf[64];
55
56 sprintf(buf, "/memory@%x", memzero_size);
57 np = of_find_node_by_path(buf);
58 if (np) {
59 if (!of_address_to_resource(np, 0, &r))
60 memblock_size = resource_size(&r);
61 of_node_put(np);
62 }
63 }
64 }
65 return memblock_size;
66}
67
68static void dlpar_free_property(struct property *prop)
69{
70 kfree(prop->name);
71 kfree(prop->value);
72 kfree(prop);
73}
74
75static struct property *dlpar_clone_property(struct property *prop,
76 u32 prop_size)
77{
78 struct property *new_prop;
79
80 new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
81 if (!new_prop)
82 return NULL;
83
84 new_prop->name = kstrdup(prop->name, GFP_KERNEL);
85 new_prop->value = kzalloc(prop_size, GFP_KERNEL);
86 if (!new_prop->name || !new_prop->value) {
87 dlpar_free_property(new_prop);
88 return NULL;
89 }
90
91 memcpy(new_prop->value, prop->value, prop->length);
92 new_prop->length = prop_size;
93
94 of_property_set_flag(new_prop, OF_DYNAMIC);
95 return new_prop;
96}
97
98static bool find_aa_index(struct device_node *dr_node,
99 struct property *ala_prop,
100 const u32 *lmb_assoc, u32 *aa_index)
101{
102 u32 *assoc_arrays, new_prop_size;
103 struct property *new_prop;
104 int aa_arrays, aa_array_entries, aa_array_sz;
105 int i, index;
106
107 /*
108 * The ibm,associativity-lookup-arrays property is defined to be
109 * a 32-bit value specifying the number of associativity arrays
110 * followed by a 32-bitvalue specifying the number of entries per
111 * array, followed by the associativity arrays.
112 */
113 assoc_arrays = ala_prop->value;
114
115 aa_arrays = be32_to_cpu(assoc_arrays[0]);
116 aa_array_entries = be32_to_cpu(assoc_arrays[1]);
117 aa_array_sz = aa_array_entries * sizeof(u32);
118
119 for (i = 0; i < aa_arrays; i++) {
120 index = (i * aa_array_entries) + 2;
121
122 if (memcmp(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz))
123 continue;
124
125 *aa_index = i;
126 return true;
127 }
128
129 new_prop_size = ala_prop->length + aa_array_sz;
130 new_prop = dlpar_clone_property(ala_prop, new_prop_size);
131 if (!new_prop)
132 return false;
133
134 assoc_arrays = new_prop->value;
135
136 /* increment the number of entries in the lookup array */
137 assoc_arrays[0] = cpu_to_be32(aa_arrays + 1);
138
139 /* copy the new associativity into the lookup array */
140 index = aa_arrays * aa_array_entries + 2;
141 memcpy(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz);
142
143 of_update_property(dr_node, new_prop);
144
145 /*
146 * The associativity lookup array index for this lmb is
147 * number of entries - 1 since we added its associativity
148 * to the end of the lookup array.
149 */
150 *aa_index = be32_to_cpu(assoc_arrays[0]) - 1;
151 return true;
152}
153
154static int update_lmb_associativity_index(struct drmem_lmb *lmb)
155{
156 struct device_node *parent, *lmb_node, *dr_node;
157 struct property *ala_prop;
158 const u32 *lmb_assoc;
159 u32 aa_index;
160 bool found;
161
162 parent = of_find_node_by_path("/");
163 if (!parent)
164 return -ENODEV;
165
166 lmb_node = dlpar_configure_connector(cpu_to_be32(lmb->drc_index),
167 parent);
168 of_node_put(parent);
169 if (!lmb_node)
170 return -EINVAL;
171
172 lmb_assoc = of_get_property(lmb_node, "ibm,associativity", NULL);
173 if (!lmb_assoc) {
174 dlpar_free_cc_nodes(lmb_node);
175 return -ENODEV;
176 }
177
178 dr_node = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
179 if (!dr_node) {
180 dlpar_free_cc_nodes(lmb_node);
181 return -ENODEV;
182 }
183
184 ala_prop = of_find_property(dr_node, "ibm,associativity-lookup-arrays",
185 NULL);
186 if (!ala_prop) {
187 of_node_put(dr_node);
188 dlpar_free_cc_nodes(lmb_node);
189 return -ENODEV;
190 }
191
192 found = find_aa_index(dr_node, ala_prop, lmb_assoc, &aa_index);
193
194 of_node_put(dr_node);
195 dlpar_free_cc_nodes(lmb_node);
196
197 if (!found) {
198 pr_err("Could not find LMB associativity\n");
199 return -1;
200 }
201
202 lmb->aa_index = aa_index;
203 return 0;
204}
205
206static struct memory_block *lmb_to_memblock(struct drmem_lmb *lmb)
207{
208 unsigned long section_nr;
209 struct mem_section *mem_sect;
210 struct memory_block *mem_block;
211
212 section_nr = pfn_to_section_nr(PFN_DOWN(lmb->base_addr));
213 mem_sect = __nr_to_section(section_nr);
214
215 mem_block = find_memory_block(mem_sect);
216 return mem_block;
217}
218
219static int get_lmb_range(u32 drc_index, int n_lmbs,
220 struct drmem_lmb **start_lmb,
221 struct drmem_lmb **end_lmb)
222{
223 struct drmem_lmb *lmb, *start, *end;
224 struct drmem_lmb *limit;
225
226 start = NULL;
227 for_each_drmem_lmb(lmb) {
228 if (lmb->drc_index == drc_index) {
229 start = lmb;
230 break;
231 }
232 }
233
234 if (!start)
235 return -EINVAL;
236
237 end = &start[n_lmbs];
238
239 limit = &drmem_info->lmbs[drmem_info->n_lmbs];
240 if (end > limit)
241 return -EINVAL;
242
243 *start_lmb = start;
244 *end_lmb = end;
245 return 0;
246}
247
248static int dlpar_change_lmb_state(struct drmem_lmb *lmb, bool online)
249{
250 struct memory_block *mem_block;
251 int rc;
252
253 mem_block = lmb_to_memblock(lmb);
254 if (!mem_block)
255 return -EINVAL;
256
257 if (online && mem_block->dev.offline)
258 rc = device_online(&mem_block->dev);
259 else if (!online && !mem_block->dev.offline)
260 rc = device_offline(&mem_block->dev);
261 else
262 rc = 0;
263
264 put_device(&mem_block->dev);
265
266 return rc;
267}
268
269static int dlpar_online_lmb(struct drmem_lmb *lmb)
270{
271 return dlpar_change_lmb_state(lmb, true);
272}
273
274#ifdef CONFIG_MEMORY_HOTREMOVE
275static int dlpar_offline_lmb(struct drmem_lmb *lmb)
276{
277 return dlpar_change_lmb_state(lmb, false);
278}
279
280static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size)
281{
282 unsigned long block_sz, start_pfn;
283 int sections_per_block;
284 int i, nid;
285
286 start_pfn = base >> PAGE_SHIFT;
287
288 lock_device_hotplug();
289
290 if (!pfn_valid(start_pfn))
291 goto out;
292
293 block_sz = pseries_memory_block_size();
294 sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
295 nid = memory_add_physaddr_to_nid(base);
296
297 for (i = 0; i < sections_per_block; i++) {
298 __remove_memory(nid, base, MIN_MEMORY_BLOCK_SIZE);
299 base += MIN_MEMORY_BLOCK_SIZE;
300 }
301
302out:
303 /* Update memory regions for memory remove */
304 memblock_remove(base, memblock_size);
305 unlock_device_hotplug();
306 return 0;
307}
308
309static int pseries_remove_mem_node(struct device_node *np)
310{
311 const __be32 *regs;
312 unsigned long base;
313 unsigned int lmb_size;
314 int ret = -EINVAL;
315
316 /*
317 * Check to see if we are actually removing memory
318 */
319 if (!of_node_is_type(np, "memory"))
320 return 0;
321
322 /*
323 * Find the base address and size of the memblock
324 */
325 regs = of_get_property(np, "reg", NULL);
326 if (!regs)
327 return ret;
328
329 base = be64_to_cpu(*(unsigned long *)regs);
330 lmb_size = be32_to_cpu(regs[3]);
331
332 pseries_remove_memblock(base, lmb_size);
333 return 0;
334}
335
336static bool lmb_is_removable(struct drmem_lmb *lmb)
337{
338 if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
339 return false;
340
341#ifdef CONFIG_FA_DUMP
342 /*
343 * Don't hot-remove memory that falls in fadump boot memory area
344 * and memory that is reserved for capturing old kernel memory.
345 */
346 if (is_fadump_memory_area(lmb->base_addr, memory_block_size_bytes()))
347 return false;
348#endif
349 /* device_offline() will determine if we can actually remove this lmb */
350 return true;
351}
352
353static int dlpar_add_lmb(struct drmem_lmb *);
354
355static int dlpar_remove_lmb(struct drmem_lmb *lmb)
356{
357 unsigned long block_sz;
358 int rc;
359
360 if (!lmb_is_removable(lmb))
361 return -EINVAL;
362
363 rc = dlpar_offline_lmb(lmb);
364 if (rc)
365 return rc;
366
367 block_sz = pseries_memory_block_size();
368
369 __remove_memory(lmb->nid, lmb->base_addr, block_sz);
370
371 /* Update memory regions for memory remove */
372 memblock_remove(lmb->base_addr, block_sz);
373
374 invalidate_lmb_associativity_index(lmb);
375 lmb_clear_nid(lmb);
376 lmb->flags &= ~DRCONF_MEM_ASSIGNED;
377
378 return 0;
379}
380
381static int dlpar_memory_remove_by_count(u32 lmbs_to_remove)
382{
383 struct drmem_lmb *lmb;
384 int lmbs_removed = 0;
385 int lmbs_available = 0;
386 int rc;
387
388 pr_info("Attempting to hot-remove %d LMB(s)\n", lmbs_to_remove);
389
390 if (lmbs_to_remove == 0)
391 return -EINVAL;
392
393 /* Validate that there are enough LMBs to satisfy the request */
394 for_each_drmem_lmb(lmb) {
395 if (lmb_is_removable(lmb))
396 lmbs_available++;
397
398 if (lmbs_available == lmbs_to_remove)
399 break;
400 }
401
402 if (lmbs_available < lmbs_to_remove) {
403 pr_info("Not enough LMBs available (%d of %d) to satisfy request\n",
404 lmbs_available, lmbs_to_remove);
405 return -EINVAL;
406 }
407
408 for_each_drmem_lmb(lmb) {
409 rc = dlpar_remove_lmb(lmb);
410 if (rc)
411 continue;
412
413 /* Mark this lmb so we can add it later if all of the
414 * requested LMBs cannot be removed.
415 */
416 drmem_mark_lmb_reserved(lmb);
417
418 lmbs_removed++;
419 if (lmbs_removed == lmbs_to_remove)
420 break;
421 }
422
423 if (lmbs_removed != lmbs_to_remove) {
424 pr_err("Memory hot-remove failed, adding LMB's back\n");
425
426 for_each_drmem_lmb(lmb) {
427 if (!drmem_lmb_reserved(lmb))
428 continue;
429
430 rc = dlpar_add_lmb(lmb);
431 if (rc)
432 pr_err("Failed to add LMB back, drc index %x\n",
433 lmb->drc_index);
434
435 drmem_remove_lmb_reservation(lmb);
436 }
437
438 rc = -EINVAL;
439 } else {
440 for_each_drmem_lmb(lmb) {
441 if (!drmem_lmb_reserved(lmb))
442 continue;
443
444 dlpar_release_drc(lmb->drc_index);
445 pr_info("Memory at %llx was hot-removed\n",
446 lmb->base_addr);
447
448 drmem_remove_lmb_reservation(lmb);
449 }
450 rc = 0;
451 }
452
453 return rc;
454}
455
456static int dlpar_memory_remove_by_index(u32 drc_index)
457{
458 struct drmem_lmb *lmb;
459 int lmb_found;
460 int rc;
461
462 pr_info("Attempting to hot-remove LMB, drc index %x\n", drc_index);
463
464 lmb_found = 0;
465 for_each_drmem_lmb(lmb) {
466 if (lmb->drc_index == drc_index) {
467 lmb_found = 1;
468 rc = dlpar_remove_lmb(lmb);
469 if (!rc)
470 dlpar_release_drc(lmb->drc_index);
471
472 break;
473 }
474 }
475
476 if (!lmb_found)
477 rc = -EINVAL;
478
479 if (rc)
480 pr_info("Failed to hot-remove memory at %llx\n",
481 lmb->base_addr);
482 else
483 pr_info("Memory at %llx was hot-removed\n", lmb->base_addr);
484
485 return rc;
486}
487
488static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index)
489{
490 struct drmem_lmb *lmb, *start_lmb, *end_lmb;
491 int lmbs_available = 0;
492 int rc;
493
494 pr_info("Attempting to hot-remove %u LMB(s) at %x\n",
495 lmbs_to_remove, drc_index);
496
497 if (lmbs_to_remove == 0)
498 return -EINVAL;
499
500 rc = get_lmb_range(drc_index, lmbs_to_remove, &start_lmb, &end_lmb);
501 if (rc)
502 return -EINVAL;
503
504 /* Validate that there are enough LMBs to satisfy the request */
505 for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
506 if (lmb->flags & DRCONF_MEM_RESERVED)
507 break;
508
509 lmbs_available++;
510 }
511
512 if (lmbs_available < lmbs_to_remove)
513 return -EINVAL;
514
515 for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
516 if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
517 continue;
518
519 rc = dlpar_remove_lmb(lmb);
520 if (rc)
521 break;
522
523 drmem_mark_lmb_reserved(lmb);
524 }
525
526 if (rc) {
527 pr_err("Memory indexed-count-remove failed, adding any removed LMBs\n");
528
529
530 for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
531 if (!drmem_lmb_reserved(lmb))
532 continue;
533
534 rc = dlpar_add_lmb(lmb);
535 if (rc)
536 pr_err("Failed to add LMB, drc index %x\n",
537 lmb->drc_index);
538
539 drmem_remove_lmb_reservation(lmb);
540 }
541 rc = -EINVAL;
542 } else {
543 for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
544 if (!drmem_lmb_reserved(lmb))
545 continue;
546
547 dlpar_release_drc(lmb->drc_index);
548 pr_info("Memory at %llx (drc index %x) was hot-removed\n",
549 lmb->base_addr, lmb->drc_index);
550
551 drmem_remove_lmb_reservation(lmb);
552 }
553 }
554
555 return rc;
556}
557
558#else
559static inline int pseries_remove_memblock(unsigned long base,
560 unsigned int memblock_size)
561{
562 return -EOPNOTSUPP;
563}
564static inline int pseries_remove_mem_node(struct device_node *np)
565{
566 return 0;
567}
568static inline int dlpar_memory_remove(struct pseries_hp_errorlog *hp_elog)
569{
570 return -EOPNOTSUPP;
571}
572static int dlpar_remove_lmb(struct drmem_lmb *lmb)
573{
574 return -EOPNOTSUPP;
575}
576static int dlpar_memory_remove_by_count(u32 lmbs_to_remove)
577{
578 return -EOPNOTSUPP;
579}
580static int dlpar_memory_remove_by_index(u32 drc_index)
581{
582 return -EOPNOTSUPP;
583}
584
585static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index)
586{
587 return -EOPNOTSUPP;
588}
589#endif /* CONFIG_MEMORY_HOTREMOVE */
590
591static int dlpar_add_lmb(struct drmem_lmb *lmb)
592{
593 unsigned long block_sz;
594 int rc;
595
596 if (lmb->flags & DRCONF_MEM_ASSIGNED)
597 return -EINVAL;
598
599 rc = update_lmb_associativity_index(lmb);
600 if (rc) {
601 dlpar_release_drc(lmb->drc_index);
602 return rc;
603 }
604
605 lmb_set_nid(lmb);
606 block_sz = memory_block_size_bytes();
607
608 /* Add the memory */
609 rc = __add_memory(lmb->nid, lmb->base_addr, block_sz);
610 if (rc) {
611 invalidate_lmb_associativity_index(lmb);
612 return rc;
613 }
614
615 rc = dlpar_online_lmb(lmb);
616 if (rc) {
617 __remove_memory(lmb->nid, lmb->base_addr, block_sz);
618 invalidate_lmb_associativity_index(lmb);
619 lmb_clear_nid(lmb);
620 } else {
621 lmb->flags |= DRCONF_MEM_ASSIGNED;
622 }
623
624 return rc;
625}
626
627static int dlpar_memory_add_by_count(u32 lmbs_to_add)
628{
629 struct drmem_lmb *lmb;
630 int lmbs_available = 0;
631 int lmbs_added = 0;
632 int rc;
633
634 pr_info("Attempting to hot-add %d LMB(s)\n", lmbs_to_add);
635
636 if (lmbs_to_add == 0)
637 return -EINVAL;
638
639 /* Validate that there are enough LMBs to satisfy the request */
640 for_each_drmem_lmb(lmb) {
641 if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
642 lmbs_available++;
643
644 if (lmbs_available == lmbs_to_add)
645 break;
646 }
647
648 if (lmbs_available < lmbs_to_add)
649 return -EINVAL;
650
651 for_each_drmem_lmb(lmb) {
652 if (lmb->flags & DRCONF_MEM_ASSIGNED)
653 continue;
654
655 rc = dlpar_acquire_drc(lmb->drc_index);
656 if (rc)
657 continue;
658
659 rc = dlpar_add_lmb(lmb);
660 if (rc) {
661 dlpar_release_drc(lmb->drc_index);
662 continue;
663 }
664
665 /* Mark this lmb so we can remove it later if all of the
666 * requested LMBs cannot be added.
667 */
668 drmem_mark_lmb_reserved(lmb);
669
670 lmbs_added++;
671 if (lmbs_added == lmbs_to_add)
672 break;
673 }
674
675 if (lmbs_added != lmbs_to_add) {
676 pr_err("Memory hot-add failed, removing any added LMBs\n");
677
678 for_each_drmem_lmb(lmb) {
679 if (!drmem_lmb_reserved(lmb))
680 continue;
681
682 rc = dlpar_remove_lmb(lmb);
683 if (rc)
684 pr_err("Failed to remove LMB, drc index %x\n",
685 lmb->drc_index);
686 else
687 dlpar_release_drc(lmb->drc_index);
688
689 drmem_remove_lmb_reservation(lmb);
690 }
691 rc = -EINVAL;
692 } else {
693 for_each_drmem_lmb(lmb) {
694 if (!drmem_lmb_reserved(lmb))
695 continue;
696
697 pr_info("Memory at %llx (drc index %x) was hot-added\n",
698 lmb->base_addr, lmb->drc_index);
699 drmem_remove_lmb_reservation(lmb);
700 }
701 rc = 0;
702 }
703
704 return rc;
705}
706
707static int dlpar_memory_add_by_index(u32 drc_index)
708{
709 struct drmem_lmb *lmb;
710 int rc, lmb_found;
711
712 pr_info("Attempting to hot-add LMB, drc index %x\n", drc_index);
713
714 lmb_found = 0;
715 for_each_drmem_lmb(lmb) {
716 if (lmb->drc_index == drc_index) {
717 lmb_found = 1;
718 rc = dlpar_acquire_drc(lmb->drc_index);
719 if (!rc) {
720 rc = dlpar_add_lmb(lmb);
721 if (rc)
722 dlpar_release_drc(lmb->drc_index);
723 }
724
725 break;
726 }
727 }
728
729 if (!lmb_found)
730 rc = -EINVAL;
731
732 if (rc)
733 pr_info("Failed to hot-add memory, drc index %x\n", drc_index);
734 else
735 pr_info("Memory at %llx (drc index %x) was hot-added\n",
736 lmb->base_addr, drc_index);
737
738 return rc;
739}
740
741static int dlpar_memory_add_by_ic(u32 lmbs_to_add, u32 drc_index)
742{
743 struct drmem_lmb *lmb, *start_lmb, *end_lmb;
744 int lmbs_available = 0;
745 int rc;
746
747 pr_info("Attempting to hot-add %u LMB(s) at index %x\n",
748 lmbs_to_add, drc_index);
749
750 if (lmbs_to_add == 0)
751 return -EINVAL;
752
753 rc = get_lmb_range(drc_index, lmbs_to_add, &start_lmb, &end_lmb);
754 if (rc)
755 return -EINVAL;
756
757 /* Validate that the LMBs in this range are not reserved */
758 for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
759 if (lmb->flags & DRCONF_MEM_RESERVED)
760 break;
761
762 lmbs_available++;
763 }
764
765 if (lmbs_available < lmbs_to_add)
766 return -EINVAL;
767
768 for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
769 if (lmb->flags & DRCONF_MEM_ASSIGNED)
770 continue;
771
772 rc = dlpar_acquire_drc(lmb->drc_index);
773 if (rc)
774 break;
775
776 rc = dlpar_add_lmb(lmb);
777 if (rc) {
778 dlpar_release_drc(lmb->drc_index);
779 break;
780 }
781
782 drmem_mark_lmb_reserved(lmb);
783 }
784
785 if (rc) {
786 pr_err("Memory indexed-count-add failed, removing any added LMBs\n");
787
788 for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
789 if (!drmem_lmb_reserved(lmb))
790 continue;
791
792 rc = dlpar_remove_lmb(lmb);
793 if (rc)
794 pr_err("Failed to remove LMB, drc index %x\n",
795 lmb->drc_index);
796 else
797 dlpar_release_drc(lmb->drc_index);
798
799 drmem_remove_lmb_reservation(lmb);
800 }
801 rc = -EINVAL;
802 } else {
803 for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
804 if (!drmem_lmb_reserved(lmb))
805 continue;
806
807 pr_info("Memory at %llx (drc index %x) was hot-added\n",
808 lmb->base_addr, lmb->drc_index);
809 drmem_remove_lmb_reservation(lmb);
810 }
811 }
812
813 return rc;
814}
815
816int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
817{
818 u32 count, drc_index;
819 int rc;
820
821 lock_device_hotplug();
822
823 switch (hp_elog->action) {
824 case PSERIES_HP_ELOG_ACTION_ADD:
825 switch (hp_elog->id_type) {
826 case PSERIES_HP_ELOG_ID_DRC_COUNT:
827 count = hp_elog->_drc_u.drc_count;
828 rc = dlpar_memory_add_by_count(count);
829 break;
830 case PSERIES_HP_ELOG_ID_DRC_INDEX:
831 drc_index = hp_elog->_drc_u.drc_index;
832 rc = dlpar_memory_add_by_index(drc_index);
833 break;
834 case PSERIES_HP_ELOG_ID_DRC_IC:
835 count = hp_elog->_drc_u.ic.count;
836 drc_index = hp_elog->_drc_u.ic.index;
837 rc = dlpar_memory_add_by_ic(count, drc_index);
838 break;
839 default:
840 rc = -EINVAL;
841 break;
842 }
843
844 break;
845 case PSERIES_HP_ELOG_ACTION_REMOVE:
846 switch (hp_elog->id_type) {
847 case PSERIES_HP_ELOG_ID_DRC_COUNT:
848 count = hp_elog->_drc_u.drc_count;
849 rc = dlpar_memory_remove_by_count(count);
850 break;
851 case PSERIES_HP_ELOG_ID_DRC_INDEX:
852 drc_index = hp_elog->_drc_u.drc_index;
853 rc = dlpar_memory_remove_by_index(drc_index);
854 break;
855 case PSERIES_HP_ELOG_ID_DRC_IC:
856 count = hp_elog->_drc_u.ic.count;
857 drc_index = hp_elog->_drc_u.ic.index;
858 rc = dlpar_memory_remove_by_ic(count, drc_index);
859 break;
860 default:
861 rc = -EINVAL;
862 break;
863 }
864
865 break;
866 default:
867 pr_err("Invalid action (%d) specified\n", hp_elog->action);
868 rc = -EINVAL;
869 break;
870 }
871
872 if (!rc)
873 rc = drmem_update_dt();
874
875 unlock_device_hotplug();
876 return rc;
877}
878
879static int pseries_add_mem_node(struct device_node *np)
880{
881 const __be32 *regs;
882 unsigned long base;
883 unsigned int lmb_size;
884 int ret = -EINVAL;
885
886 /*
887 * Check to see if we are actually adding memory
888 */
889 if (!of_node_is_type(np, "memory"))
890 return 0;
891
892 /*
893 * Find the base and size of the memblock
894 */
895 regs = of_get_property(np, "reg", NULL);
896 if (!regs)
897 return ret;
898
899 base = be64_to_cpu(*(unsigned long *)regs);
900 lmb_size = be32_to_cpu(regs[3]);
901
902 /*
903 * Update memory region to represent the memory add
904 */
905 ret = memblock_add(base, lmb_size);
906 return (ret < 0) ? -EINVAL : 0;
907}
908
909static int pseries_memory_notifier(struct notifier_block *nb,
910 unsigned long action, void *data)
911{
912 struct of_reconfig_data *rd = data;
913 int err = 0;
914
915 switch (action) {
916 case OF_RECONFIG_ATTACH_NODE:
917 err = pseries_add_mem_node(rd->dn);
918 break;
919 case OF_RECONFIG_DETACH_NODE:
920 err = pseries_remove_mem_node(rd->dn);
921 break;
922 }
923 return notifier_from_errno(err);
924}
925
926static struct notifier_block pseries_mem_nb = {
927 .notifier_call = pseries_memory_notifier,
928};
929
930static int __init pseries_memory_hotplug_init(void)
931{
932 if (firmware_has_feature(FW_FEATURE_LPAR))
933 of_reconfig_notifier_register(&pseries_mem_nb);
934
935 return 0;
936}
937machine_device_initcall(pseries, pseries_memory_hotplug_init);