Add memory hot add/remove notifier handlers for powerpc/pseries. This patch allows the powerpc/pseries platforms to perform memory DLPAR int the kernel. The handlers for add and remove do the work of acquiring/releasing the memory to firmware and updating the device tree. This is only used when memory is specified in the ibm,dynamic-reconfiguration-memory device tree node so the memory notifiers are registered contingent on its existence. Signed-off-by: Nathan Fontenot <nfont@xxxxxxxxxxxxxxxxxx> --- arch/powerpc/platforms/pseries/dlpar.c | 103 +++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) Index: linux/arch/powerpc/platforms/pseries/dlpar.c =================================================================== --- linux.orig/arch/powerpc/platforms/pseries/dlpar.c +++ linux/arch/powerpc/platforms/pseries/dlpar.c @@ -15,6 +15,7 @@ #include <linux/notifier.h> #include <linux/spinlock.h> #include <linux/cpu.h> +#include <linux/memory.h> #include <linux/slab.h> #include <linux/of.h> #include "offline_states.h" @@ -531,11 +532,113 @@ out: return rc ? rc : count; } +static struct of_drconf_cell *dlpar_get_drconf_cell(struct device_node *dn, + unsigned long phys_addr) +{ + struct of_drconf_cell *drmem; + u32 entries; + u32 *prop; + int i; + + prop = (u32 *)of_get_property(dn, "ibm,dynamic-memory", NULL); + of_node_put(dn); + if (!prop) + return NULL; + + entries = *prop++; + drmem = (struct of_drconf_cell *)prop; + + for (i = 0; i < entries; i++) { + if (drmem[i].base_addr == phys_addr) + return &drmem[i]; + } + + return NULL; +} + +static int dlpar_mem_probe(unsigned long phys_addr) +{ + struct device_node *dn; + struct of_drconf_cell *drmem; + int rc; + + dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); + if (!dn) + return -EINVAL; + + drmem = dlpar_get_drconf_cell(dn, phys_addr); + of_node_put(dn); + + if (!drmem) + return -EINVAL; + + if (drmem->flags & DRCONF_MEM_ASSIGNED) + return 0; + + drmem->flags |= DRCONF_MEM_ASSIGNED; + + rc = dlpar_acquire_drc(drmem->drc_index); + return rc; +} + +static int dlpar_mem_release(unsigned long phys_addr) +{ + struct device_node *dn; + struct of_drconf_cell *drmem; + int rc; + + dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); + if (!dn) + return -EINVAL; + + drmem = dlpar_get_drconf_cell(dn, phys_addr); + of_node_put(dn); + + if (!drmem) + return -EINVAL; + + if (!drmem->flags & DRCONF_MEM_ASSIGNED) + return 0; + + drmem->flags &= ~DRCONF_MEM_ASSIGNED; + + rc = dlpar_release_drc(drmem->drc_index); + return rc; +} + +static int pseries_dlpar_mem_callback(struct notifier_block *nb, + unsigned long action, void *hp_arg) +{ + struct memory_notify *arg = hp_arg; + unsigned long phys_addr = arg->start_pfn << PAGE_SHIFT; + int rc = 0; + + + switch (action) { + case MEM_BEING_HOT_ADDED: + rc = dlpar_mem_probe(phys_addr); + break; + case MEM_HOT_REMOVED: + rc = dlpar_mem_release(phys_addr); + break; + } + + return notifier_from_errno(rc); +} + static int __init pseries_dlpar_init(void) { + struct device_node *dn; + ppc_md.cpu_probe = dlpar_cpu_probe; ppc_md.cpu_release = dlpar_cpu_release; + dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); + if (dn) { + hotplug_memory_notifier(pseries_dlpar_mem_callback, 0); + of_node_put(dn); + } + return 0; } machine_device_initcall(pseries, pseries_dlpar_init); -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxx. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>