diff options
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-memory.c | 95 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/reconfig.c | 36 | ||||
-rw-r--r-- | include/asm-powerpc/pSeries_reconfig.h | 6 |
3 files changed, 104 insertions, 33 deletions
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 18a8138ef99f..a1a368dd2d99 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -15,32 +15,11 @@ #include <asm/machdep.h> #include <asm/pSeries_reconfig.h> -static int pseries_remove_memory(struct device_node *np) +static int pseries_remove_lmb(unsigned long base, unsigned int lmb_size) { - const char *type; - const unsigned int *regs; - unsigned long base; - unsigned int lmb_size; - u64 start_pfn, start; + unsigned long start, start_pfn; struct zone *zone; - int ret = -EINVAL; - - /* - * Check to see if we are actually removing memory - */ - type = of_get_property(np, "device_type", NULL); - if (type == NULL || strcmp(type, "memory") != 0) - return 0; - - /* - * Find the bae address and size of the lmb - */ - regs = of_get_property(np, "reg", NULL); - if (!regs) - return ret; - - base = *(unsigned long *)regs; - lmb_size = regs[3]; + int ret; start_pfn = base >> PFN_SECTION_SHIFT; zone = page_zone(pfn_to_page(start_pfn)); @@ -71,13 +50,41 @@ static int pseries_remove_memory(struct device_node *np) return ret; } +static int pseries_remove_memory(struct device_node *np) +{ + const char *type; + const unsigned int *regs; + unsigned long base; + unsigned int lmb_size; + int ret = -EINVAL; + + /* + * Check to see if we are actually removing memory + */ + type = of_get_property(np, "device_type", NULL); + if (type == NULL || strcmp(type, "memory") != 0) + return 0; + + /* + * Find the bae address and size of the lmb + */ + regs = of_get_property(np, "reg", NULL); + if (!regs) + return ret; + + base = *(unsigned long *)regs; + lmb_size = regs[3]; + + ret = pseries_remove_lmb(base, lmb_size); + return ret; +} + static int pseries_add_memory(struct device_node *np) { const char *type; const unsigned int *regs; unsigned long base; unsigned int lmb_size; - u64 start_pfn; int ret = -EINVAL; /* @@ -100,8 +107,37 @@ static int pseries_add_memory(struct device_node *np) /* * Update memory region to represent the memory add */ - lmb_add(base, lmb_size); - return 0; + ret = lmb_add(base, lmb_size); + return (ret < 0) ? -EINVAL : 0; +} + +static int pseries_drconf_memory(unsigned long *base, unsigned int action) +{ + struct device_node *np; + const unsigned long *lmb_size; + int rc; + + np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); + if (!np) + return -EINVAL; + + lmb_size = of_get_property(np, "ibm,lmb-size", NULL); + if (!lmb_size) { + of_node_put(np); + return -EINVAL; + } + + if (action == PSERIES_DRCONF_MEM_ADD) { + rc = lmb_add(*base, *lmb_size); + rc = (rc < 0) ? -EINVAL : 0; + } else if (action == PSERIES_DRCONF_MEM_REMOVE) { + rc = pseries_remove_lmb(*base, *lmb_size); + } else { + rc = -EINVAL; + } + + of_node_put(np); + return rc; } static int pseries_memory_notifier(struct notifier_block *nb, @@ -118,6 +154,11 @@ static int pseries_memory_notifier(struct notifier_block *nb, if (pseries_remove_memory(node)) err = NOTIFY_BAD; break; + case PSERIES_DRCONF_MEM_ADD: + case PSERIES_DRCONF_MEM_REMOVE: + if (pseries_drconf_memory(node, action)) + err = NOTIFY_BAD; + break; default: err = NOTIFY_DONE; break; diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index dfa2ebd2deb5..7637bd38c795 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -422,8 +422,8 @@ static int do_update_property(char *buf, size_t bufsize) { struct device_node *np; unsigned char *value; - char *name, *end; - int length; + char *name, *end, *next_prop; + int rc, length; struct property *newprop, *oldprop; buf = parse_node(buf, bufsize, &np); end = buf + bufsize; @@ -431,7 +431,8 @@ static int do_update_property(char *buf, size_t bufsize) if (!np) return -ENODEV; - if (parse_next_property(buf, end, &name, &length, &value) == NULL) + next_prop = parse_next_property(buf, end, &name, &length, &value); + if (!next_prop) return -EINVAL; newprop = new_property(name, length, value, NULL); @@ -442,7 +443,34 @@ static int do_update_property(char *buf, size_t bufsize) if (!oldprop) return -ENODEV; - return prom_update_property(np, newprop, oldprop); + rc = prom_update_property(np, newprop, oldprop); + if (rc) + return rc; + + /* For memory under the ibm,dynamic-reconfiguration-memory node + * of the device tree, adding and removing memory is just an update + * to the ibm,dynamic-memory property instead of adding/removing a + * memory node in the device tree. For these cases we still need to + * involve the notifier chain. + */ + if (!strcmp(name, "ibm,dynamic-memory")) { + int action; + + next_prop = parse_next_property(next_prop, end, &name, + &length, &value); + if (!next_prop) + return -EINVAL; + + if (!strcmp(name, "add")) + action = PSERIES_DRCONF_MEM_ADD; + else + action = PSERIES_DRCONF_MEM_REMOVE; + + blocking_notifier_call_chain(&pSeries_reconfig_chain, + action, value); + } + + return 0; } /** diff --git a/include/asm-powerpc/pSeries_reconfig.h b/include/asm-powerpc/pSeries_reconfig.h index ea6cfb8efb84..e482e5352e69 100644 --- a/include/asm-powerpc/pSeries_reconfig.h +++ b/include/asm-powerpc/pSeries_reconfig.h @@ -9,8 +9,10 @@ * added or removed on pSeries systems. */ -#define PSERIES_RECONFIG_ADD 0x0001 -#define PSERIES_RECONFIG_REMOVE 0x0002 +#define PSERIES_RECONFIG_ADD 0x0001 +#define PSERIES_RECONFIG_REMOVE 0x0002 +#define PSERIES_DRCONF_MEM_ADD 0x0003 +#define PSERIES_DRCONF_MEM_REMOVE 0x0004 #ifdef CONFIG_PPC_PSERIES extern int pSeries_reconfig_notifier_register(struct notifier_block *); |