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