Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * pseries Memory Hotplug infrastructure.
0004  *
0005  * Copyright (C) 2008 Badari Pulavarty, IBM Corporation
0006  */
0007 
0008 #define pr_fmt(fmt) "pseries-hotplug-mem: " fmt
0009 
0010 #include <linux/of.h>
0011 #include <linux/of_address.h>
0012 #include <linux/memblock.h>
0013 #include <linux/memory.h>
0014 #include <linux/memory_hotplug.h>
0015 #include <linux/slab.h>
0016 
0017 #include <asm/firmware.h>
0018 #include <asm/machdep.h>
0019 #include <asm/sparsemem.h>
0020 #include <asm/fadump.h>
0021 #include <asm/drmem.h>
0022 #include "pseries.h"
0023 
0024 unsigned long pseries_memory_block_size(void)
0025 {
0026     struct device_node *np;
0027     u64 memblock_size = MIN_MEMORY_BLOCK_SIZE;
0028     struct resource r;
0029 
0030     np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
0031     if (np) {
0032         int len;
0033         int size_cells;
0034         const __be32 *prop;
0035 
0036         size_cells = of_n_size_cells(np);
0037 
0038         prop = of_get_property(np, "ibm,lmb-size", &len);
0039         if (prop && len >= size_cells * sizeof(__be32))
0040             memblock_size = of_read_number(prop, size_cells);
0041         of_node_put(np);
0042 
0043     } else  if (machine_is(pseries)) {
0044         /* This fallback really only applies to pseries */
0045         unsigned int memzero_size = 0;
0046 
0047         np = of_find_node_by_path("/memory@0");
0048         if (np) {
0049             if (!of_address_to_resource(np, 0, &r))
0050                 memzero_size = resource_size(&r);
0051             of_node_put(np);
0052         }
0053 
0054         if (memzero_size) {
0055             /* We now know the size of memory@0, use this to find
0056              * the first memoryblock and get its size.
0057              */
0058             char buf[64];
0059 
0060             sprintf(buf, "/memory@%x", memzero_size);
0061             np = of_find_node_by_path(buf);
0062             if (np) {
0063                 if (!of_address_to_resource(np, 0, &r))
0064                     memblock_size = resource_size(&r);
0065                 of_node_put(np);
0066             }
0067         }
0068     }
0069     return memblock_size;
0070 }
0071 
0072 static void dlpar_free_property(struct property *prop)
0073 {
0074     kfree(prop->name);
0075     kfree(prop->value);
0076     kfree(prop);
0077 }
0078 
0079 static struct property *dlpar_clone_property(struct property *prop,
0080                          u32 prop_size)
0081 {
0082     struct property *new_prop;
0083 
0084     new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
0085     if (!new_prop)
0086         return NULL;
0087 
0088     new_prop->name = kstrdup(prop->name, GFP_KERNEL);
0089     new_prop->value = kzalloc(prop_size, GFP_KERNEL);
0090     if (!new_prop->name || !new_prop->value) {
0091         dlpar_free_property(new_prop);
0092         return NULL;
0093     }
0094 
0095     memcpy(new_prop->value, prop->value, prop->length);
0096     new_prop->length = prop_size;
0097 
0098     of_property_set_flag(new_prop, OF_DYNAMIC);
0099     return new_prop;
0100 }
0101 
0102 static bool find_aa_index(struct device_node *dr_node,
0103              struct property *ala_prop,
0104              const u32 *lmb_assoc, u32 *aa_index)
0105 {
0106     u32 *assoc_arrays, new_prop_size;
0107     struct property *new_prop;
0108     int aa_arrays, aa_array_entries, aa_array_sz;
0109     int i, index;
0110 
0111     /*
0112      * The ibm,associativity-lookup-arrays property is defined to be
0113      * a 32-bit value specifying the number of associativity arrays
0114      * followed by a 32-bitvalue specifying the number of entries per
0115      * array, followed by the associativity arrays.
0116      */
0117     assoc_arrays = ala_prop->value;
0118 
0119     aa_arrays = be32_to_cpu(assoc_arrays[0]);
0120     aa_array_entries = be32_to_cpu(assoc_arrays[1]);
0121     aa_array_sz = aa_array_entries * sizeof(u32);
0122 
0123     for (i = 0; i < aa_arrays; i++) {
0124         index = (i * aa_array_entries) + 2;
0125 
0126         if (memcmp(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz))
0127             continue;
0128 
0129         *aa_index = i;
0130         return true;
0131     }
0132 
0133     new_prop_size = ala_prop->length + aa_array_sz;
0134     new_prop = dlpar_clone_property(ala_prop, new_prop_size);
0135     if (!new_prop)
0136         return false;
0137 
0138     assoc_arrays = new_prop->value;
0139 
0140     /* increment the number of entries in the lookup array */
0141     assoc_arrays[0] = cpu_to_be32(aa_arrays + 1);
0142 
0143     /* copy the new associativity into the lookup array */
0144     index = aa_arrays * aa_array_entries + 2;
0145     memcpy(&assoc_arrays[index], &lmb_assoc[1], aa_array_sz);
0146 
0147     of_update_property(dr_node, new_prop);
0148 
0149     /*
0150      * The associativity lookup array index for this lmb is
0151      * number of entries - 1 since we added its associativity
0152      * to the end of the lookup array.
0153      */
0154     *aa_index = be32_to_cpu(assoc_arrays[0]) - 1;
0155     return true;
0156 }
0157 
0158 static int update_lmb_associativity_index(struct drmem_lmb *lmb)
0159 {
0160     struct device_node *parent, *lmb_node, *dr_node;
0161     struct property *ala_prop;
0162     const u32 *lmb_assoc;
0163     u32 aa_index;
0164     bool found;
0165 
0166     parent = of_find_node_by_path("/");
0167     if (!parent)
0168         return -ENODEV;
0169 
0170     lmb_node = dlpar_configure_connector(cpu_to_be32(lmb->drc_index),
0171                          parent);
0172     of_node_put(parent);
0173     if (!lmb_node)
0174         return -EINVAL;
0175 
0176     lmb_assoc = of_get_property(lmb_node, "ibm,associativity", NULL);
0177     if (!lmb_assoc) {
0178         dlpar_free_cc_nodes(lmb_node);
0179         return -ENODEV;
0180     }
0181 
0182     update_numa_distance(lmb_node);
0183 
0184     dr_node = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
0185     if (!dr_node) {
0186         dlpar_free_cc_nodes(lmb_node);
0187         return -ENODEV;
0188     }
0189 
0190     ala_prop = of_find_property(dr_node, "ibm,associativity-lookup-arrays",
0191                     NULL);
0192     if (!ala_prop) {
0193         of_node_put(dr_node);
0194         dlpar_free_cc_nodes(lmb_node);
0195         return -ENODEV;
0196     }
0197 
0198     found = find_aa_index(dr_node, ala_prop, lmb_assoc, &aa_index);
0199 
0200     of_node_put(dr_node);
0201     dlpar_free_cc_nodes(lmb_node);
0202 
0203     if (!found) {
0204         pr_err("Could not find LMB associativity\n");
0205         return -1;
0206     }
0207 
0208     lmb->aa_index = aa_index;
0209     return 0;
0210 }
0211 
0212 static struct memory_block *lmb_to_memblock(struct drmem_lmb *lmb)
0213 {
0214     unsigned long section_nr;
0215     struct memory_block *mem_block;
0216 
0217     section_nr = pfn_to_section_nr(PFN_DOWN(lmb->base_addr));
0218 
0219     mem_block = find_memory_block(section_nr);
0220     return mem_block;
0221 }
0222 
0223 static int get_lmb_range(u32 drc_index, int n_lmbs,
0224              struct drmem_lmb **start_lmb,
0225              struct drmem_lmb **end_lmb)
0226 {
0227     struct drmem_lmb *lmb, *start, *end;
0228     struct drmem_lmb *limit;
0229 
0230     start = NULL;
0231     for_each_drmem_lmb(lmb) {
0232         if (lmb->drc_index == drc_index) {
0233             start = lmb;
0234             break;
0235         }
0236     }
0237 
0238     if (!start)
0239         return -EINVAL;
0240 
0241     end = &start[n_lmbs];
0242 
0243     limit = &drmem_info->lmbs[drmem_info->n_lmbs];
0244     if (end > limit)
0245         return -EINVAL;
0246 
0247     *start_lmb = start;
0248     *end_lmb = end;
0249     return 0;
0250 }
0251 
0252 static int dlpar_change_lmb_state(struct drmem_lmb *lmb, bool online)
0253 {
0254     struct memory_block *mem_block;
0255     int rc;
0256 
0257     mem_block = lmb_to_memblock(lmb);
0258     if (!mem_block)
0259         return -EINVAL;
0260 
0261     if (online && mem_block->dev.offline)
0262         rc = device_online(&mem_block->dev);
0263     else if (!online && !mem_block->dev.offline)
0264         rc = device_offline(&mem_block->dev);
0265     else
0266         rc = 0;
0267 
0268     put_device(&mem_block->dev);
0269 
0270     return rc;
0271 }
0272 
0273 static int dlpar_online_lmb(struct drmem_lmb *lmb)
0274 {
0275     return dlpar_change_lmb_state(lmb, true);
0276 }
0277 
0278 #ifdef CONFIG_MEMORY_HOTREMOVE
0279 static int dlpar_offline_lmb(struct drmem_lmb *lmb)
0280 {
0281     return dlpar_change_lmb_state(lmb, false);
0282 }
0283 
0284 static int pseries_remove_memblock(unsigned long base, unsigned long memblock_size)
0285 {
0286     unsigned long block_sz, start_pfn;
0287     int sections_per_block;
0288     int i;
0289 
0290     start_pfn = base >> PAGE_SHIFT;
0291 
0292     lock_device_hotplug();
0293 
0294     if (!pfn_valid(start_pfn))
0295         goto out;
0296 
0297     block_sz = pseries_memory_block_size();
0298     sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
0299 
0300     for (i = 0; i < sections_per_block; i++) {
0301         __remove_memory(base, MIN_MEMORY_BLOCK_SIZE);
0302         base += MIN_MEMORY_BLOCK_SIZE;
0303     }
0304 
0305 out:
0306     /* Update memory regions for memory remove */
0307     memblock_remove(base, memblock_size);
0308     unlock_device_hotplug();
0309     return 0;
0310 }
0311 
0312 static int pseries_remove_mem_node(struct device_node *np)
0313 {
0314     const __be32 *prop;
0315     unsigned long base;
0316     unsigned long lmb_size;
0317     int ret = -EINVAL;
0318     int addr_cells, size_cells;
0319 
0320     /*
0321      * Check to see if we are actually removing memory
0322      */
0323     if (!of_node_is_type(np, "memory"))
0324         return 0;
0325 
0326     /*
0327      * Find the base address and size of the memblock
0328      */
0329     prop = of_get_property(np, "reg", NULL);
0330     if (!prop)
0331         return ret;
0332 
0333     addr_cells = of_n_addr_cells(np);
0334     size_cells = of_n_size_cells(np);
0335 
0336     /*
0337      * "reg" property represents (addr,size) tuple.
0338      */
0339     base = of_read_number(prop, addr_cells);
0340     prop += addr_cells;
0341     lmb_size = of_read_number(prop, size_cells);
0342 
0343     pseries_remove_memblock(base, lmb_size);
0344     return 0;
0345 }
0346 
0347 static bool lmb_is_removable(struct drmem_lmb *lmb)
0348 {
0349     if ((lmb->flags & DRCONF_MEM_RESERVED) ||
0350         !(lmb->flags & DRCONF_MEM_ASSIGNED))
0351         return false;
0352 
0353 #ifdef CONFIG_FA_DUMP
0354     /*
0355      * Don't hot-remove memory that falls in fadump boot memory area
0356      * and memory that is reserved for capturing old kernel memory.
0357      */
0358     if (is_fadump_memory_area(lmb->base_addr, memory_block_size_bytes()))
0359         return false;
0360 #endif
0361     /* device_offline() will determine if we can actually remove this lmb */
0362     return true;
0363 }
0364 
0365 static int dlpar_add_lmb(struct drmem_lmb *);
0366 
0367 static int dlpar_remove_lmb(struct drmem_lmb *lmb)
0368 {
0369     struct memory_block *mem_block;
0370     unsigned long block_sz;
0371     int rc;
0372 
0373     if (!lmb_is_removable(lmb))
0374         return -EINVAL;
0375 
0376     mem_block = lmb_to_memblock(lmb);
0377     if (mem_block == NULL)
0378         return -EINVAL;
0379 
0380     rc = dlpar_offline_lmb(lmb);
0381     if (rc) {
0382         put_device(&mem_block->dev);
0383         return rc;
0384     }
0385 
0386     block_sz = pseries_memory_block_size();
0387 
0388     __remove_memory(lmb->base_addr, block_sz);
0389     put_device(&mem_block->dev);
0390 
0391     /* Update memory regions for memory remove */
0392     memblock_remove(lmb->base_addr, block_sz);
0393 
0394     invalidate_lmb_associativity_index(lmb);
0395     lmb->flags &= ~DRCONF_MEM_ASSIGNED;
0396 
0397     return 0;
0398 }
0399 
0400 static int dlpar_memory_remove_by_count(u32 lmbs_to_remove)
0401 {
0402     struct drmem_lmb *lmb;
0403     int lmbs_reserved = 0;
0404     int lmbs_available = 0;
0405     int rc;
0406 
0407     pr_info("Attempting to hot-remove %d LMB(s)\n", lmbs_to_remove);
0408 
0409     if (lmbs_to_remove == 0)
0410         return -EINVAL;
0411 
0412     /* Validate that there are enough LMBs to satisfy the request */
0413     for_each_drmem_lmb(lmb) {
0414         if (lmb_is_removable(lmb))
0415             lmbs_available++;
0416 
0417         if (lmbs_available == lmbs_to_remove)
0418             break;
0419     }
0420 
0421     if (lmbs_available < lmbs_to_remove) {
0422         pr_info("Not enough LMBs available (%d of %d) to satisfy request\n",
0423             lmbs_available, lmbs_to_remove);
0424         return -EINVAL;
0425     }
0426 
0427     for_each_drmem_lmb(lmb) {
0428         rc = dlpar_remove_lmb(lmb);
0429         if (rc)
0430             continue;
0431 
0432         /* Mark this lmb so we can add it later if all of the
0433          * requested LMBs cannot be removed.
0434          */
0435         drmem_mark_lmb_reserved(lmb);
0436 
0437         lmbs_reserved++;
0438         if (lmbs_reserved == lmbs_to_remove)
0439             break;
0440     }
0441 
0442     if (lmbs_reserved != lmbs_to_remove) {
0443         pr_err("Memory hot-remove failed, adding LMB's back\n");
0444 
0445         for_each_drmem_lmb(lmb) {
0446             if (!drmem_lmb_reserved(lmb))
0447                 continue;
0448 
0449             rc = dlpar_add_lmb(lmb);
0450             if (rc)
0451                 pr_err("Failed to add LMB back, drc index %x\n",
0452                        lmb->drc_index);
0453 
0454             drmem_remove_lmb_reservation(lmb);
0455 
0456             lmbs_reserved--;
0457             if (lmbs_reserved == 0)
0458                 break;
0459         }
0460 
0461         rc = -EINVAL;
0462     } else {
0463         for_each_drmem_lmb(lmb) {
0464             if (!drmem_lmb_reserved(lmb))
0465                 continue;
0466 
0467             dlpar_release_drc(lmb->drc_index);
0468             pr_info("Memory at %llx was hot-removed\n",
0469                 lmb->base_addr);
0470 
0471             drmem_remove_lmb_reservation(lmb);
0472 
0473             lmbs_reserved--;
0474             if (lmbs_reserved == 0)
0475                 break;
0476         }
0477         rc = 0;
0478     }
0479 
0480     return rc;
0481 }
0482 
0483 static int dlpar_memory_remove_by_index(u32 drc_index)
0484 {
0485     struct drmem_lmb *lmb;
0486     int lmb_found;
0487     int rc;
0488 
0489     pr_debug("Attempting to hot-remove LMB, drc index %x\n", drc_index);
0490 
0491     lmb_found = 0;
0492     for_each_drmem_lmb(lmb) {
0493         if (lmb->drc_index == drc_index) {
0494             lmb_found = 1;
0495             rc = dlpar_remove_lmb(lmb);
0496             if (!rc)
0497                 dlpar_release_drc(lmb->drc_index);
0498 
0499             break;
0500         }
0501     }
0502 
0503     if (!lmb_found)
0504         rc = -EINVAL;
0505 
0506     if (rc)
0507         pr_debug("Failed to hot-remove memory at %llx\n",
0508              lmb->base_addr);
0509     else
0510         pr_debug("Memory at %llx was hot-removed\n", lmb->base_addr);
0511 
0512     return rc;
0513 }
0514 
0515 static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index)
0516 {
0517     struct drmem_lmb *lmb, *start_lmb, *end_lmb;
0518     int rc;
0519 
0520     pr_info("Attempting to hot-remove %u LMB(s) at %x\n",
0521         lmbs_to_remove, drc_index);
0522 
0523     if (lmbs_to_remove == 0)
0524         return -EINVAL;
0525 
0526     rc = get_lmb_range(drc_index, lmbs_to_remove, &start_lmb, &end_lmb);
0527     if (rc)
0528         return -EINVAL;
0529 
0530     /*
0531      * Validate that all LMBs in range are not reserved. Note that it
0532      * is ok if they are !ASSIGNED since our goal here is to remove the
0533      * LMB range, regardless of whether some LMBs were already removed
0534      * by any other reason.
0535      *
0536      * This is a contrast to what is done in remove_by_count() where we
0537      * check for both RESERVED and !ASSIGNED (via lmb_is_removable()),
0538      * because we want to remove a fixed amount of LMBs in that function.
0539      */
0540     for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
0541         if (lmb->flags & DRCONF_MEM_RESERVED) {
0542             pr_err("Memory at %llx (drc index %x) is reserved\n",
0543                 lmb->base_addr, lmb->drc_index);
0544             return -EINVAL;
0545         }
0546     }
0547 
0548     for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
0549         /*
0550          * dlpar_remove_lmb() will error out if the LMB is already
0551          * !ASSIGNED, but this case is a no-op for us.
0552          */
0553         if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
0554             continue;
0555 
0556         rc = dlpar_remove_lmb(lmb);
0557         if (rc)
0558             break;
0559 
0560         drmem_mark_lmb_reserved(lmb);
0561     }
0562 
0563     if (rc) {
0564         pr_err("Memory indexed-count-remove failed, adding any removed LMBs\n");
0565 
0566 
0567         for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
0568             if (!drmem_lmb_reserved(lmb))
0569                 continue;
0570 
0571             /*
0572              * Setting the isolation state of an UNISOLATED/CONFIGURED
0573              * device to UNISOLATE is a no-op, but the hypervisor can
0574              * use it as a hint that the LMB removal failed.
0575              */
0576             dlpar_unisolate_drc(lmb->drc_index);
0577 
0578             rc = dlpar_add_lmb(lmb);
0579             if (rc)
0580                 pr_err("Failed to add LMB, drc index %x\n",
0581                        lmb->drc_index);
0582 
0583             drmem_remove_lmb_reservation(lmb);
0584         }
0585         rc = -EINVAL;
0586     } else {
0587         for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
0588             if (!drmem_lmb_reserved(lmb))
0589                 continue;
0590 
0591             dlpar_release_drc(lmb->drc_index);
0592             pr_info("Memory at %llx (drc index %x) was hot-removed\n",
0593                 lmb->base_addr, lmb->drc_index);
0594 
0595             drmem_remove_lmb_reservation(lmb);
0596         }
0597     }
0598 
0599     return rc;
0600 }
0601 
0602 #else
0603 static inline int pseries_remove_memblock(unsigned long base,
0604                       unsigned long memblock_size)
0605 {
0606     return -EOPNOTSUPP;
0607 }
0608 static inline int pseries_remove_mem_node(struct device_node *np)
0609 {
0610     return 0;
0611 }
0612 static int dlpar_remove_lmb(struct drmem_lmb *lmb)
0613 {
0614     return -EOPNOTSUPP;
0615 }
0616 static int dlpar_memory_remove_by_count(u32 lmbs_to_remove)
0617 {
0618     return -EOPNOTSUPP;
0619 }
0620 static int dlpar_memory_remove_by_index(u32 drc_index)
0621 {
0622     return -EOPNOTSUPP;
0623 }
0624 
0625 static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index)
0626 {
0627     return -EOPNOTSUPP;
0628 }
0629 #endif /* CONFIG_MEMORY_HOTREMOVE */
0630 
0631 static int dlpar_add_lmb(struct drmem_lmb *lmb)
0632 {
0633     unsigned long block_sz;
0634     int nid, rc;
0635 
0636     if (lmb->flags & DRCONF_MEM_ASSIGNED)
0637         return -EINVAL;
0638 
0639     rc = update_lmb_associativity_index(lmb);
0640     if (rc) {
0641         dlpar_release_drc(lmb->drc_index);
0642         return rc;
0643     }
0644 
0645     block_sz = memory_block_size_bytes();
0646 
0647     /* Find the node id for this LMB.  Fake one if necessary. */
0648     nid = of_drconf_to_nid_single(lmb);
0649     if (nid < 0 || !node_possible(nid))
0650         nid = first_online_node;
0651 
0652     /* Add the memory */
0653     rc = __add_memory(nid, lmb->base_addr, block_sz, MHP_NONE);
0654     if (rc) {
0655         invalidate_lmb_associativity_index(lmb);
0656         return rc;
0657     }
0658 
0659     rc = dlpar_online_lmb(lmb);
0660     if (rc) {
0661         __remove_memory(lmb->base_addr, block_sz);
0662         invalidate_lmb_associativity_index(lmb);
0663     } else {
0664         lmb->flags |= DRCONF_MEM_ASSIGNED;
0665     }
0666 
0667     return rc;
0668 }
0669 
0670 static int dlpar_memory_add_by_count(u32 lmbs_to_add)
0671 {
0672     struct drmem_lmb *lmb;
0673     int lmbs_available = 0;
0674     int lmbs_reserved = 0;
0675     int rc;
0676 
0677     pr_info("Attempting to hot-add %d LMB(s)\n", lmbs_to_add);
0678 
0679     if (lmbs_to_add == 0)
0680         return -EINVAL;
0681 
0682     /* Validate that there are enough LMBs to satisfy the request */
0683     for_each_drmem_lmb(lmb) {
0684         if (lmb->flags & DRCONF_MEM_RESERVED)
0685             continue;
0686 
0687         if (!(lmb->flags & DRCONF_MEM_ASSIGNED))
0688             lmbs_available++;
0689 
0690         if (lmbs_available == lmbs_to_add)
0691             break;
0692     }
0693 
0694     if (lmbs_available < lmbs_to_add)
0695         return -EINVAL;
0696 
0697     for_each_drmem_lmb(lmb) {
0698         if (lmb->flags & DRCONF_MEM_ASSIGNED)
0699             continue;
0700 
0701         rc = dlpar_acquire_drc(lmb->drc_index);
0702         if (rc)
0703             continue;
0704 
0705         rc = dlpar_add_lmb(lmb);
0706         if (rc) {
0707             dlpar_release_drc(lmb->drc_index);
0708             continue;
0709         }
0710 
0711         /* Mark this lmb so we can remove it later if all of the
0712          * requested LMBs cannot be added.
0713          */
0714         drmem_mark_lmb_reserved(lmb);
0715         lmbs_reserved++;
0716         if (lmbs_reserved == lmbs_to_add)
0717             break;
0718     }
0719 
0720     if (lmbs_reserved != lmbs_to_add) {
0721         pr_err("Memory hot-add failed, removing any added LMBs\n");
0722 
0723         for_each_drmem_lmb(lmb) {
0724             if (!drmem_lmb_reserved(lmb))
0725                 continue;
0726 
0727             rc = dlpar_remove_lmb(lmb);
0728             if (rc)
0729                 pr_err("Failed to remove LMB, drc index %x\n",
0730                        lmb->drc_index);
0731             else
0732                 dlpar_release_drc(lmb->drc_index);
0733 
0734             drmem_remove_lmb_reservation(lmb);
0735             lmbs_reserved--;
0736 
0737             if (lmbs_reserved == 0)
0738                 break;
0739         }
0740         rc = -EINVAL;
0741     } else {
0742         for_each_drmem_lmb(lmb) {
0743             if (!drmem_lmb_reserved(lmb))
0744                 continue;
0745 
0746             pr_debug("Memory at %llx (drc index %x) was hot-added\n",
0747                  lmb->base_addr, lmb->drc_index);
0748             drmem_remove_lmb_reservation(lmb);
0749             lmbs_reserved--;
0750 
0751             if (lmbs_reserved == 0)
0752                 break;
0753         }
0754         rc = 0;
0755     }
0756 
0757     return rc;
0758 }
0759 
0760 static int dlpar_memory_add_by_index(u32 drc_index)
0761 {
0762     struct drmem_lmb *lmb;
0763     int rc, lmb_found;
0764 
0765     pr_info("Attempting to hot-add LMB, drc index %x\n", drc_index);
0766 
0767     lmb_found = 0;
0768     for_each_drmem_lmb(lmb) {
0769         if (lmb->drc_index == drc_index) {
0770             lmb_found = 1;
0771             rc = dlpar_acquire_drc(lmb->drc_index);
0772             if (!rc) {
0773                 rc = dlpar_add_lmb(lmb);
0774                 if (rc)
0775                     dlpar_release_drc(lmb->drc_index);
0776             }
0777 
0778             break;
0779         }
0780     }
0781 
0782     if (!lmb_found)
0783         rc = -EINVAL;
0784 
0785     if (rc)
0786         pr_info("Failed to hot-add memory, drc index %x\n", drc_index);
0787     else
0788         pr_info("Memory at %llx (drc index %x) was hot-added\n",
0789             lmb->base_addr, drc_index);
0790 
0791     return rc;
0792 }
0793 
0794 static int dlpar_memory_add_by_ic(u32 lmbs_to_add, u32 drc_index)
0795 {
0796     struct drmem_lmb *lmb, *start_lmb, *end_lmb;
0797     int rc;
0798 
0799     pr_info("Attempting to hot-add %u LMB(s) at index %x\n",
0800         lmbs_to_add, drc_index);
0801 
0802     if (lmbs_to_add == 0)
0803         return -EINVAL;
0804 
0805     rc = get_lmb_range(drc_index, lmbs_to_add, &start_lmb, &end_lmb);
0806     if (rc)
0807         return -EINVAL;
0808 
0809     /* Validate that the LMBs in this range are not reserved */
0810     for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
0811         /* Fail immediately if the whole range can't be hot-added */
0812         if (lmb->flags & DRCONF_MEM_RESERVED) {
0813             pr_err("Memory at %llx (drc index %x) is reserved\n",
0814                     lmb->base_addr, lmb->drc_index);
0815             return -EINVAL;
0816         }
0817     }
0818 
0819     for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
0820         if (lmb->flags & DRCONF_MEM_ASSIGNED)
0821             continue;
0822 
0823         rc = dlpar_acquire_drc(lmb->drc_index);
0824         if (rc)
0825             break;
0826 
0827         rc = dlpar_add_lmb(lmb);
0828         if (rc) {
0829             dlpar_release_drc(lmb->drc_index);
0830             break;
0831         }
0832 
0833         drmem_mark_lmb_reserved(lmb);
0834     }
0835 
0836     if (rc) {
0837         pr_err("Memory indexed-count-add failed, removing any added LMBs\n");
0838 
0839         for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
0840             if (!drmem_lmb_reserved(lmb))
0841                 continue;
0842 
0843             rc = dlpar_remove_lmb(lmb);
0844             if (rc)
0845                 pr_err("Failed to remove LMB, drc index %x\n",
0846                        lmb->drc_index);
0847             else
0848                 dlpar_release_drc(lmb->drc_index);
0849 
0850             drmem_remove_lmb_reservation(lmb);
0851         }
0852         rc = -EINVAL;
0853     } else {
0854         for_each_drmem_lmb_in_range(lmb, start_lmb, end_lmb) {
0855             if (!drmem_lmb_reserved(lmb))
0856                 continue;
0857 
0858             pr_info("Memory at %llx (drc index %x) was hot-added\n",
0859                 lmb->base_addr, lmb->drc_index);
0860             drmem_remove_lmb_reservation(lmb);
0861         }
0862     }
0863 
0864     return rc;
0865 }
0866 
0867 int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
0868 {
0869     u32 count, drc_index;
0870     int rc;
0871 
0872     lock_device_hotplug();
0873 
0874     switch (hp_elog->action) {
0875     case PSERIES_HP_ELOG_ACTION_ADD:
0876         switch (hp_elog->id_type) {
0877         case PSERIES_HP_ELOG_ID_DRC_COUNT:
0878             count = hp_elog->_drc_u.drc_count;
0879             rc = dlpar_memory_add_by_count(count);
0880             break;
0881         case PSERIES_HP_ELOG_ID_DRC_INDEX:
0882             drc_index = hp_elog->_drc_u.drc_index;
0883             rc = dlpar_memory_add_by_index(drc_index);
0884             break;
0885         case PSERIES_HP_ELOG_ID_DRC_IC:
0886             count = hp_elog->_drc_u.ic.count;
0887             drc_index = hp_elog->_drc_u.ic.index;
0888             rc = dlpar_memory_add_by_ic(count, drc_index);
0889             break;
0890         default:
0891             rc = -EINVAL;
0892             break;
0893         }
0894 
0895         break;
0896     case PSERIES_HP_ELOG_ACTION_REMOVE:
0897         switch (hp_elog->id_type) {
0898         case PSERIES_HP_ELOG_ID_DRC_COUNT:
0899             count = hp_elog->_drc_u.drc_count;
0900             rc = dlpar_memory_remove_by_count(count);
0901             break;
0902         case PSERIES_HP_ELOG_ID_DRC_INDEX:
0903             drc_index = hp_elog->_drc_u.drc_index;
0904             rc = dlpar_memory_remove_by_index(drc_index);
0905             break;
0906         case PSERIES_HP_ELOG_ID_DRC_IC:
0907             count = hp_elog->_drc_u.ic.count;
0908             drc_index = hp_elog->_drc_u.ic.index;
0909             rc = dlpar_memory_remove_by_ic(count, drc_index);
0910             break;
0911         default:
0912             rc = -EINVAL;
0913             break;
0914         }
0915 
0916         break;
0917     default:
0918         pr_err("Invalid action (%d) specified\n", hp_elog->action);
0919         rc = -EINVAL;
0920         break;
0921     }
0922 
0923     if (!rc)
0924         rc = drmem_update_dt();
0925 
0926     unlock_device_hotplug();
0927     return rc;
0928 }
0929 
0930 static int pseries_add_mem_node(struct device_node *np)
0931 {
0932     const __be32 *prop;
0933     unsigned long base;
0934     unsigned long lmb_size;
0935     int ret = -EINVAL;
0936     int addr_cells, size_cells;
0937 
0938     /*
0939      * Check to see if we are actually adding memory
0940      */
0941     if (!of_node_is_type(np, "memory"))
0942         return 0;
0943 
0944     /*
0945      * Find the base and size of the memblock
0946      */
0947     prop = of_get_property(np, "reg", NULL);
0948     if (!prop)
0949         return ret;
0950 
0951     addr_cells = of_n_addr_cells(np);
0952     size_cells = of_n_size_cells(np);
0953     /*
0954      * "reg" property represents (addr,size) tuple.
0955      */
0956     base = of_read_number(prop, addr_cells);
0957     prop += addr_cells;
0958     lmb_size = of_read_number(prop, size_cells);
0959 
0960     /*
0961      * Update memory region to represent the memory add
0962      */
0963     ret = memblock_add(base, lmb_size);
0964     return (ret < 0) ? -EINVAL : 0;
0965 }
0966 
0967 static int pseries_memory_notifier(struct notifier_block *nb,
0968                    unsigned long action, void *data)
0969 {
0970     struct of_reconfig_data *rd = data;
0971     int err = 0;
0972 
0973     switch (action) {
0974     case OF_RECONFIG_ATTACH_NODE:
0975         err = pseries_add_mem_node(rd->dn);
0976         break;
0977     case OF_RECONFIG_DETACH_NODE:
0978         err = pseries_remove_mem_node(rd->dn);
0979         break;
0980     case OF_RECONFIG_UPDATE_PROPERTY:
0981         if (!strcmp(rd->dn->name,
0982                 "ibm,dynamic-reconfiguration-memory"))
0983             drmem_update_lmbs(rd->prop);
0984     }
0985     return notifier_from_errno(err);
0986 }
0987 
0988 static struct notifier_block pseries_mem_nb = {
0989     .notifier_call = pseries_memory_notifier,
0990 };
0991 
0992 static int __init pseries_memory_hotplug_init(void)
0993 {
0994     if (firmware_has_feature(FW_FEATURE_LPAR))
0995         of_reconfig_notifier_register(&pseries_mem_nb);
0996 
0997     return 0;
0998 }
0999 machine_device_initcall(pseries, pseries_memory_hotplug_init);