linux/arch/powerpc/platforms/pseries/hotplug-memory.c
<<
>>
Prefs
   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
  27static bool rtas_hp_event;
  28
  29unsigned 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
  72static void dlpar_free_drconf_property(struct property *prop)
  73{
  74        kfree(prop->name);
  75        kfree(prop->value);
  76        kfree(prop);
  77}
  78
  79static 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
 119static struct memory_block *lmb_to_memblock(struct of_drconf_cell *lmb)
 120{
 121        unsigned long section_nr;
 122        struct mem_section *mem_sect;
 123        struct memory_block *mem_block;
 124
 125        section_nr = pfn_to_section_nr(PFN_DOWN(lmb->base_addr));
 126        mem_sect = __nr_to_section(section_nr);
 127
 128        mem_block = find_memory_block(mem_sect);
 129        return mem_block;
 130}
 131
 132#ifdef CONFIG_MEMORY_HOTREMOVE
 133static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size)
 134{
 135        unsigned long block_sz, start_pfn;
 136        int sections_per_block;
 137        int i, nid;
 138
 139        start_pfn = base >> PAGE_SHIFT;
 140
 141        lock_device_hotplug();
 142
 143        if (!pfn_valid(start_pfn))
 144                goto out;
 145
 146        block_sz = pseries_memory_block_size();
 147        sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
 148        nid = memory_add_physaddr_to_nid(base);
 149
 150        for (i = 0; i < sections_per_block; i++) {
 151                remove_memory(nid, base, MIN_MEMORY_BLOCK_SIZE);
 152                base += MIN_MEMORY_BLOCK_SIZE;
 153        }
 154
 155out:
 156        /* Update memory regions for memory remove */
 157        memblock_remove(base, memblock_size);
 158        unlock_device_hotplug();
 159        return 0;
 160}
 161
 162static int pseries_remove_mem_node(struct device_node *np)
 163{
 164        const char *type;
 165        const __be32 *regs;
 166        unsigned long base;
 167        unsigned int lmb_size;
 168        int ret = -EINVAL;
 169
 170        /*
 171         * Check to see if we are actually removing memory
 172         */
 173        type = of_get_property(np, "device_type", NULL);
 174        if (type == NULL || strcmp(type, "memory") != 0)
 175                return 0;
 176
 177        /*
 178         * Find the base address and size of the memblock
 179         */
 180        regs = of_get_property(np, "reg", NULL);
 181        if (!regs)
 182                return ret;
 183
 184        base = be64_to_cpu(*(unsigned long *)regs);
 185        lmb_size = be32_to_cpu(regs[3]);
 186
 187        pseries_remove_memblock(base, lmb_size);
 188        return 0;
 189}
 190
 191static bool lmb_is_removable(struct of_drconf_cell *lmb)
 192{
 193        int i, scns_per_block;
 194        int rc = 1;
 195        unsigned long pfn, block_sz;
 196        u64 phys_addr;
 197
 198        if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
 199                return false;
 200
 201        block_sz = memory_block_size_bytes();
 202        scns_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
 203        phys_addr = lmb->base_addr;
 204
 205        for (i = 0; i < scns_per_block; i++) {
 206                pfn = PFN_DOWN(phys_addr);
 207                if (!pfn_present(pfn))
 208                        continue;
 209
 210                rc &= is_mem_section_removable(pfn, PAGES_PER_SECTION);
 211                phys_addr += MIN_MEMORY_BLOCK_SIZE;
 212        }
 213
 214        return rc ? true : false;
 215}
 216
 217static int dlpar_add_lmb(struct of_drconf_cell *);
 218
 219static int dlpar_remove_lmb(struct of_drconf_cell *lmb)
 220{
 221        struct memory_block *mem_block;
 222        unsigned long block_sz;
 223        int nid, rc;
 224
 225        if (!lmb_is_removable(lmb))
 226                return -EINVAL;
 227
 228        mem_block = lmb_to_memblock(lmb);
 229        if (!mem_block)
 230                return -EINVAL;
 231
 232        rc = device_offline(&mem_block->dev);
 233        put_device(&mem_block->dev);
 234        if (rc)
 235                return rc;
 236
 237        block_sz = pseries_memory_block_size();
 238        nid = memory_add_physaddr_to_nid(lmb->base_addr);
 239
 240        remove_memory(nid, lmb->base_addr, block_sz);
 241
 242        /* Update memory regions for memory remove */
 243        memblock_remove(lmb->base_addr, block_sz);
 244
 245        dlpar_release_drc(lmb->drc_index);
 246
 247        lmb->flags &= ~DRCONF_MEM_ASSIGNED;
 248        return 0;
 249}
 250
 251static int dlpar_memory_remove_by_count(u32 lmbs_to_remove,
 252                                        struct property *prop)
 253{
 254        struct of_drconf_cell *lmbs;
 255        int lmbs_removed = 0;
 256        int lmbs_available = 0;
 257        u32 num_lmbs, *p;
 258        int i, rc;
 259
 260        pr_info("Attempting to hot-remove %d LMB(s)\n", lmbs_to_remove);
 261
 262        if (lmbs_to_remove == 0)
 263                return -EINVAL;
 264
 265        p = prop->value;
 266        num_lmbs = *p++;
 267        lmbs = (struct of_drconf_cell *)p;
 268
 269        /* Validate that there are enough LMBs to satisfy the request */
 270        for (i = 0; i < num_lmbs; i++) {
 271                if (lmbs[i].flags & DRCONF_MEM_ASSIGNED)
 272                        lmbs_available++;
 273        }
 274
 275        if (lmbs_available < lmbs_to_remove)
 276                return -EINVAL;
 277
 278        for (i = 0; i < num_lmbs && lmbs_removed < lmbs_to_remove; i++) {
 279                rc = dlpar_remove_lmb(&lmbs[i]);
 280                if (rc)
 281                        continue;
 282
 283                lmbs_removed++;
 284
 285                /* Mark this lmb so we can add it later if all of the
 286                 * requested LMBs cannot be removed.
 287                 */
 288                lmbs[i].reserved = 1;
 289        }
 290
 291        if (lmbs_removed != lmbs_to_remove) {
 292                pr_err("Memory hot-remove failed, adding LMB's back\n");
 293
 294                for (i = 0; i < num_lmbs; i++) {
 295                        if (!lmbs[i].reserved)
 296                                continue;
 297
 298                        rc = dlpar_add_lmb(&lmbs[i]);
 299                        if (rc)
 300                                pr_err("Failed to add LMB back, drc index %x\n",
 301                                       lmbs[i].drc_index);
 302
 303                        lmbs[i].reserved = 0;
 304                }
 305
 306                rc = -EINVAL;
 307        } else {
 308                for (i = 0; i < num_lmbs; i++) {
 309                        if (!lmbs[i].reserved)
 310                                continue;
 311
 312                        pr_info("Memory at %llx was hot-removed\n",
 313                                lmbs[i].base_addr);
 314
 315                        lmbs[i].reserved = 0;
 316                }
 317                rc = 0;
 318        }
 319
 320        return rc;
 321}
 322
 323static int dlpar_memory_remove_by_index(u32 drc_index, struct property *prop)
 324{
 325        struct of_drconf_cell *lmbs;
 326        u32 num_lmbs, *p;
 327        int lmb_found;
 328        int i, rc;
 329
 330        pr_info("Attempting to hot-remove LMB, drc index %x\n", drc_index);
 331
 332        p = prop->value;
 333        num_lmbs = *p++;
 334        lmbs = (struct of_drconf_cell *)p;
 335
 336        lmb_found = 0;
 337        for (i = 0; i < num_lmbs; i++) {
 338                if (lmbs[i].drc_index == drc_index) {
 339                        lmb_found = 1;
 340                        rc = dlpar_remove_lmb(&lmbs[i]);
 341                        break;
 342                }
 343        }
 344
 345        if (!lmb_found)
 346                rc = -EINVAL;
 347
 348        if (rc)
 349                pr_info("Failed to hot-remove memory at %llx\n",
 350                        lmbs[i].base_addr);
 351        else
 352                pr_info("Memory at %llx was hot-removed\n", lmbs[i].base_addr);
 353
 354        return rc;
 355}
 356
 357#else
 358static inline int pseries_remove_memblock(unsigned long base,
 359                                          unsigned int memblock_size)
 360{
 361        return -EOPNOTSUPP;
 362}
 363static inline int pseries_remove_mem_node(struct device_node *np)
 364{
 365        return 0;
 366}
 367static inline int dlpar_memory_remove(struct pseries_hp_errorlog *hp_elog)
 368{
 369        return -EOPNOTSUPP;
 370}
 371static int dlpar_remove_lmb(struct of_drconf_cell *lmb)
 372{
 373        return -EOPNOTSUPP;
 374}
 375static int dlpar_memory_remove_by_count(u32 lmbs_to_remove,
 376                                        struct property *prop)
 377{
 378        return -EOPNOTSUPP;
 379}
 380static int dlpar_memory_remove_by_index(u32 drc_index, struct property *prop)
 381{
 382        return -EOPNOTSUPP;
 383}
 384
 385#endif /* CONFIG_MEMORY_HOTREMOVE */
 386
 387static int dlpar_add_lmb(struct of_drconf_cell *lmb)
 388{
 389        struct memory_block *mem_block;
 390        unsigned long block_sz;
 391        int nid, rc;
 392
 393        if (lmb->flags & DRCONF_MEM_ASSIGNED)
 394                return -EINVAL;
 395
 396        block_sz = memory_block_size_bytes();
 397
 398        rc = dlpar_acquire_drc(lmb->drc_index);
 399        if (rc)
 400                return rc;
 401
 402        /* Find the node id for this address */
 403        nid = memory_add_physaddr_to_nid(lmb->base_addr);
 404
 405        /* Add the memory */
 406        rc = add_memory(nid, lmb->base_addr, block_sz);
 407        if (rc) {
 408                dlpar_release_drc(lmb->drc_index);
 409                return rc;
 410        }
 411
 412        /* Register this block of memory */
 413        rc = memblock_add(lmb->base_addr, block_sz);
 414        if (rc) {
 415                remove_memory(nid, lmb->base_addr, block_sz);
 416                dlpar_release_drc(lmb->drc_index);
 417                return rc;
 418        }
 419
 420        mem_block = lmb_to_memblock(lmb);
 421        if (!mem_block) {
 422                remove_memory(nid, lmb->base_addr, block_sz);
 423                dlpar_release_drc(lmb->drc_index);
 424                return -EINVAL;
 425        }
 426
 427        rc = device_online(&mem_block->dev);
 428        put_device(&mem_block->dev);
 429        if (rc) {
 430                remove_memory(nid, lmb->base_addr, block_sz);
 431                dlpar_release_drc(lmb->drc_index);
 432                return rc;
 433        }
 434
 435        lmb->flags |= DRCONF_MEM_ASSIGNED;
 436        return 0;
 437}
 438
 439static int dlpar_memory_add_by_count(u32 lmbs_to_add, struct property *prop)
 440{
 441        struct of_drconf_cell *lmbs;
 442        u32 num_lmbs, *p;
 443        int lmbs_available = 0;
 444        int lmbs_added = 0;
 445        int i, rc;
 446
 447        pr_info("Attempting to hot-add %d LMB(s)\n", lmbs_to_add);
 448
 449        if (lmbs_to_add == 0)
 450                return -EINVAL;
 451
 452        p = prop->value;
 453        num_lmbs = *p++;
 454        lmbs = (struct of_drconf_cell *)p;
 455
 456        /* Validate that there are enough LMBs to satisfy the request */
 457        for (i = 0; i < num_lmbs; i++) {
 458                if (!(lmbs[i].flags & DRCONF_MEM_ASSIGNED))
 459                        lmbs_available++;
 460        }
 461
 462        if (lmbs_available < lmbs_to_add)
 463                return -EINVAL;
 464
 465        for (i = 0; i < num_lmbs && lmbs_to_add != lmbs_added; i++) {
 466                rc = dlpar_add_lmb(&lmbs[i]);
 467                if (rc)
 468                        continue;
 469
 470                lmbs_added++;
 471
 472                /* Mark this lmb so we can remove it later if all of the
 473                 * requested LMBs cannot be added.
 474                 */
 475                lmbs[i].reserved = 1;
 476        }
 477
 478        if (lmbs_added != lmbs_to_add) {
 479                pr_err("Memory hot-add failed, removing any added LMBs\n");
 480
 481                for (i = 0; i < num_lmbs; i++) {
 482                        if (!lmbs[i].reserved)
 483                                continue;
 484
 485                        rc = dlpar_remove_lmb(&lmbs[i]);
 486                        if (rc)
 487                                pr_err("Failed to remove LMB, drc index %x\n",
 488                                       be32_to_cpu(lmbs[i].drc_index));
 489                }
 490                rc = -EINVAL;
 491        } else {
 492                for (i = 0; i < num_lmbs; i++) {
 493                        if (!lmbs[i].reserved)
 494                                continue;
 495
 496                        pr_info("Memory at %llx (drc index %x) was hot-added\n",
 497                                lmbs[i].base_addr, lmbs[i].drc_index);
 498                        lmbs[i].reserved = 0;
 499                }
 500        }
 501
 502        return rc;
 503}
 504
 505static int dlpar_memory_add_by_index(u32 drc_index, struct property *prop)
 506{
 507        struct of_drconf_cell *lmbs;
 508        u32 num_lmbs, *p;
 509        int i, lmb_found;
 510        int rc;
 511
 512        pr_info("Attempting to hot-add LMB, drc index %x\n", drc_index);
 513
 514        p = prop->value;
 515        num_lmbs = *p++;
 516        lmbs = (struct of_drconf_cell *)p;
 517
 518        lmb_found = 0;
 519        for (i = 0; i < num_lmbs; i++) {
 520                if (lmbs[i].drc_index == drc_index) {
 521                        lmb_found = 1;
 522                        rc = dlpar_add_lmb(&lmbs[i]);
 523                        break;
 524                }
 525        }
 526
 527        if (!lmb_found)
 528                rc = -EINVAL;
 529
 530        if (rc)
 531                pr_info("Failed to hot-add memory, drc index %x\n", drc_index);
 532        else
 533                pr_info("Memory at %llx (drc index %x) was hot-added\n",
 534                        lmbs[i].base_addr, drc_index);
 535
 536        return rc;
 537}
 538
 539static void dlpar_update_drconf_property(struct device_node *dn,
 540                                         struct property *prop)
 541{
 542        struct of_drconf_cell *lmbs;
 543        u32 num_lmbs, *p;
 544        int i;
 545
 546        /* Convert the property back to BE */
 547        p = prop->value;
 548        num_lmbs = *p;
 549        *p = cpu_to_be32(*p);
 550        p++;
 551
 552        lmbs = (struct of_drconf_cell *)p;
 553        for (i = 0; i < num_lmbs; i++) {
 554                lmbs[i].base_addr = cpu_to_be64(lmbs[i].base_addr);
 555                lmbs[i].drc_index = cpu_to_be32(lmbs[i].drc_index);
 556                lmbs[i].flags = cpu_to_be32(lmbs[i].flags);
 557        }
 558
 559        rtas_hp_event = true;
 560        of_update_property(dn, prop);
 561        rtas_hp_event = false;
 562}
 563
 564int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
 565{
 566        struct device_node *dn;
 567        struct property *prop;
 568        u32 count, drc_index;
 569        int rc;
 570
 571        count = hp_elog->_drc_u.drc_count;
 572        drc_index = hp_elog->_drc_u.drc_index;
 573
 574        lock_device_hotplug();
 575
 576        dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
 577        if (!dn) {
 578                rc = -EINVAL;
 579                goto dlpar_memory_out;
 580        }
 581
 582        prop = dlpar_clone_drconf_property(dn);
 583        if (!prop) {
 584                rc = -EINVAL;
 585                goto dlpar_memory_out;
 586        }
 587
 588        switch (hp_elog->action) {
 589        case PSERIES_HP_ELOG_ACTION_ADD:
 590                if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
 591                        rc = dlpar_memory_add_by_count(count, prop);
 592                else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
 593                        rc = dlpar_memory_add_by_index(drc_index, prop);
 594                else
 595                        rc = -EINVAL;
 596                break;
 597        case PSERIES_HP_ELOG_ACTION_REMOVE:
 598                if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
 599                        rc = dlpar_memory_remove_by_count(count, prop);
 600                else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
 601                        rc = dlpar_memory_remove_by_index(drc_index, prop);
 602                else
 603                        rc = -EINVAL;
 604                break;
 605        default:
 606                pr_err("Invalid action (%d) specified\n", hp_elog->action);
 607                rc = -EINVAL;
 608                break;
 609        }
 610
 611        if (rc)
 612                dlpar_free_drconf_property(prop);
 613        else
 614                dlpar_update_drconf_property(dn, prop);
 615
 616dlpar_memory_out:
 617        of_node_put(dn);
 618        unlock_device_hotplug();
 619        return rc;
 620}
 621
 622static int pseries_add_mem_node(struct device_node *np)
 623{
 624        const char *type;
 625        const __be32 *regs;
 626        unsigned long base;
 627        unsigned int lmb_size;
 628        int ret = -EINVAL;
 629
 630        /*
 631         * Check to see if we are actually adding memory
 632         */
 633        type = of_get_property(np, "device_type", NULL);
 634        if (type == NULL || strcmp(type, "memory") != 0)
 635                return 0;
 636
 637        /*
 638         * Find the base and size of the memblock
 639         */
 640        regs = of_get_property(np, "reg", NULL);
 641        if (!regs)
 642                return ret;
 643
 644        base = be64_to_cpu(*(unsigned long *)regs);
 645        lmb_size = be32_to_cpu(regs[3]);
 646
 647        /*
 648         * Update memory region to represent the memory add
 649         */
 650        ret = memblock_add(base, lmb_size);
 651        return (ret < 0) ? -EINVAL : 0;
 652}
 653
 654static int pseries_update_drconf_memory(struct of_reconfig_data *pr)
 655{
 656        struct of_drconf_cell *new_drmem, *old_drmem;
 657        unsigned long memblock_size;
 658        u32 entries;
 659        __be32 *p;
 660        int i, rc = -EINVAL;
 661
 662        if (rtas_hp_event)
 663                return 0;
 664
 665        memblock_size = pseries_memory_block_size();
 666        if (!memblock_size)
 667                return -EINVAL;
 668
 669        p = (__be32 *) pr->old_prop->value;
 670        if (!p)
 671                return -EINVAL;
 672
 673        /* The first int of the property is the number of lmb's described
 674         * by the property. This is followed by an array of of_drconf_cell
 675         * entries. Get the number of entries and skip to the array of
 676         * of_drconf_cell's.
 677         */
 678        entries = be32_to_cpu(*p++);
 679        old_drmem = (struct of_drconf_cell *)p;
 680
 681        p = (__be32 *)pr->prop->value;
 682        p++;
 683        new_drmem = (struct of_drconf_cell *)p;
 684
 685        for (i = 0; i < entries; i++) {
 686                if ((be32_to_cpu(old_drmem[i].flags) & DRCONF_MEM_ASSIGNED) &&
 687                    (!(be32_to_cpu(new_drmem[i].flags) & DRCONF_MEM_ASSIGNED))) {
 688                        rc = pseries_remove_memblock(
 689                                be64_to_cpu(old_drmem[i].base_addr),
 690                                                     memblock_size);
 691                        break;
 692                } else if ((!(be32_to_cpu(old_drmem[i].flags) &
 693                            DRCONF_MEM_ASSIGNED)) &&
 694                            (be32_to_cpu(new_drmem[i].flags) &
 695                            DRCONF_MEM_ASSIGNED)) {
 696                        rc = memblock_add(be64_to_cpu(old_drmem[i].base_addr),
 697                                          memblock_size);
 698                        rc = (rc < 0) ? -EINVAL : 0;
 699                        break;
 700                }
 701        }
 702        return rc;
 703}
 704
 705static int pseries_memory_notifier(struct notifier_block *nb,
 706                                   unsigned long action, void *data)
 707{
 708        struct of_reconfig_data *rd = data;
 709        int err = 0;
 710
 711        switch (action) {
 712        case OF_RECONFIG_ATTACH_NODE:
 713                err = pseries_add_mem_node(rd->dn);
 714                break;
 715        case OF_RECONFIG_DETACH_NODE:
 716                err = pseries_remove_mem_node(rd->dn);
 717                break;
 718        case OF_RECONFIG_UPDATE_PROPERTY:
 719                if (!strcmp(rd->prop->name, "ibm,dynamic-memory"))
 720                        err = pseries_update_drconf_memory(rd);
 721                break;
 722        }
 723        return notifier_from_errno(err);
 724}
 725
 726static struct notifier_block pseries_mem_nb = {
 727        .notifier_call = pseries_memory_notifier,
 728};
 729
 730static int __init pseries_memory_hotplug_init(void)
 731{
 732        if (firmware_has_feature(FW_FEATURE_LPAR))
 733                of_reconfig_notifier_register(&pseries_mem_nb);
 734
 735        return 0;
 736}
 737machine_device_initcall(pseries, pseries_memory_hotplug_init);
 738