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