powerpc/pseries: Update the device tree correctly for drconf memory add/remove
authorNathan Fontenot <nfont@austin.ibm.com>
Thu, 3 Jul 2008 03:22:39 +0000 (13:22 +1000)
committerPaul Mackerras <paulus@samba.org>
Thu, 3 Jul 2008 06:58:16 +0000 (16:58 +1000)
This updates the device tree manipulation routines so that memory
add/remove of lmbs represented under the
ibm,dynamic-reconfiguration-memory node of the device tree invokes the
hotplug notifier chain.

This change is needed because of the change in the way memory is
represented under the ibm,dynamic-reconfiguration-memory node.  All lmbs
are described in the ibm,dynamic-memory property instead of having a
separate node for each lmb as in previous device tree layouts.  This
requires the update_node() routine to check for updates to the
ibm,dynamic-memory property and invoke the hotplug notifier chain.

This also updates the pseries hotplug notifier to be able to gather information
for lmbs represented under the ibm,dynamic-reconfiguration-memory node and
have the lmbs added/removed.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/platforms/pseries/hotplug-memory.c
arch/powerpc/platforms/pseries/reconfig.c
include/asm-powerpc/pSeries_reconfig.h

index 18a8138ef99f7f6137e2faf5b0b3cffaccbb4064..a1a368dd2d992adb57f4b99942ffdb1e6e55a87b 100644 (file)
 #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;
index dfa2ebd2deb55cb1305866cef9a29d30fb586271..7637bd38c7957d77d50d01c5f70a8597f3a4ddad 100644 (file)
@@ -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;
 }
 
 /**
index ea6cfb8efb84eb84e6a83cec13d2ca66619f3732..e482e5352e69fccf95d721d4775fb4068e10d720 100644 (file)
@@ -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 *);