projects/linux·blob master · refs

923 lines · 20.0 KiB

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