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