aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-memory.c95
-rw-r--r--arch/powerpc/platforms/pseries/reconfig.c36
-rw-r--r--include/asm-powerpc/pSeries_reconfig.h6
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 18a8138ef99..a1a368dd2d9 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 dfa2ebd2deb..7637bd38c79 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 ea6cfb8efb8..e482e5352e6 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 *);