llseek: automatically add .llseek fop
[linux-2.6.git] / arch / powerpc / platforms / pseries / reconfig.c
index 58c6121..1de2cbb 100644 (file)
 #include <linux/kref.h>
 #include <linux/notifier.h>
 #include <linux/proc_fs.h>
+#include <linux/slab.h>
 
 #include <asm/prom.h>
-#include <asm/pSeries_reconfig.h>
+#include <asm/machdep.h>
 #include <asm/uaccess.h>
+#include <asm/pSeries_reconfig.h>
+#include <asm/mmu.h>
 
 
 
@@ -94,16 +97,16 @@ static struct device_node *derive_parent(const char *path)
        return parent;
 }
 
-static struct notifier_block *pSeries_reconfig_chain;
+BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
 
 int pSeries_reconfig_notifier_register(struct notifier_block *nb)
 {
-       return notifier_chain_register(&pSeries_reconfig_chain, nb);
+       return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb);
 }
 
 void pSeries_reconfig_notifier_unregister(struct notifier_block *nb)
 {
-       notifier_chain_unregister(&pSeries_reconfig_chain, nb);
+       blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb);
 }
 
 static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
@@ -115,14 +118,12 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist
        if (!np)
                goto out_err;
 
-       np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL);
+       np->full_name = kstrdup(path, GFP_KERNEL);
        if (!np->full_name)
                goto out_err;
 
-       strcpy(np->full_name, path);
-
        np->properties = proplist;
-       OF_MARK_DYNAMIC(np);
+       of_node_set_flag(np, OF_DYNAMIC);
        kref_init(&np->kref);
 
        np->parent = derive_parent(path);
@@ -131,7 +132,7 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist
                goto out_err;
        }
 
-       err = notifier_call_chain(&pSeries_reconfig_chain,
+       err = blocking_notifier_call_chain(&pSeries_reconfig_chain,
                                  PSERIES_RECONFIG_ADD, np);
        if (err == NOTIFY_BAD) {
                printk(KERN_ERR "Failed to add device node %s\n", path);
@@ -166,12 +167,13 @@ static int pSeries_reconfig_remove_node(struct device_node *np)
 
        if ((child = of_get_next_child(np, NULL))) {
                of_node_put(child);
+               of_node_put(parent);
                return -EBUSY;
        }
 
        remove_node_proc_entries(np);
 
-       notifier_call_chain(&pSeries_reconfig_chain,
+       blocking_notifier_call_chain(&pSeries_reconfig_chain,
                            PSERIES_RECONFIG_REMOVE, np);
        of_detach_node(np);
 
@@ -181,7 +183,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np)
 }
 
 /*
- * /proc/ppc64/ofdt - yucky binary interface for adding and removing
+ * /proc/powerpc/ofdt - yucky binary interface for adding and removing
  * OF device nodes.  Should be deprecated as soon as we get an
  * in-kernel wrapper for the RTAS ibm,configure-connector call.
  */
@@ -220,14 +222,14 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length
        tmp = strchr(buf, ' ');
        if (!tmp) {
                printk(KERN_ERR "property parse failed in %s at line %d\n",
-                      __FUNCTION__, __LINE__);
+                      __func__, __LINE__);
                return NULL;
        }
        *tmp = '\0';
 
        if (++tmp >= end) {
                printk(KERN_ERR "property parse failed in %s at line %d\n",
-                      __FUNCTION__, __LINE__);
+                      __func__, __LINE__);
                return NULL;
        }
 
@@ -236,12 +238,12 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length
        *length = simple_strtoul(tmp, &tmp, 10);
        if (*length == -1) {
                printk(KERN_ERR "property parse failed in %s at line %d\n",
-                      __FUNCTION__, __LINE__);
+                      __func__, __LINE__);
                return NULL;
        }
        if (*tmp != ' ' || ++tmp >= end) {
                printk(KERN_ERR "property parse failed in %s at line %d\n",
-                      __FUNCTION__, __LINE__);
+                      __func__, __LINE__);
                return NULL;
        }
 
@@ -250,12 +252,12 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length
        tmp += *length;
        if (tmp > end) {
                printk(KERN_ERR "property parse failed in %s at line %d\n",
-                      __FUNCTION__, __LINE__);
+                      __func__, __LINE__);
                return NULL;
        }
        else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
                printk(KERN_ERR "property parse failed in %s at line %d\n",
-                      __FUNCTION__, __LINE__);
+                      __func__, __LINE__);
                return NULL;
        }
        tmp++;
@@ -267,11 +269,10 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length
 static struct property *new_property(const char *name, const int length,
                                     const unsigned char *value, struct property *last)
 {
-       struct property *new = kmalloc(sizeof(*new), GFP_KERNEL);
+       struct property *new = kzalloc(sizeof(*new), GFP_KERNEL);
 
        if (!new)
                return NULL;
-       memset(new, 0, sizeof(*new));
 
        if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL)))
                goto cleanup;
@@ -286,10 +287,8 @@ static struct property *new_property(const char *name, const int length,
        return new;
 
 cleanup:
-       if (new->name)
-               kfree(new->name);
-       if (new->value)
-               kfree(new->value);
+       kfree(new->name);
+       kfree(new->value);
        kfree(new);
        return NULL;
 }
@@ -352,6 +351,138 @@ static int do_remove_node(char *buf)
        return rv;
 }
 
+static char *parse_node(char *buf, size_t bufsize, struct device_node **npp)
+{
+       char *handle_str;
+       phandle handle;
+       *npp = NULL;
+
+       handle_str = buf;
+
+       buf = strchr(buf, ' ');
+       if (!buf)
+               return NULL;
+       *buf = '\0';
+       buf++;
+
+       handle = simple_strtoul(handle_str, NULL, 0);
+
+       *npp = of_find_node_by_phandle(handle);
+       return buf;
+}
+
+static int do_add_property(char *buf, size_t bufsize)
+{
+       struct property *prop = NULL;
+       struct device_node *np;
+       unsigned char *value;
+       char *name, *end;
+       int length;
+       end = buf + bufsize;
+       buf = parse_node(buf, bufsize, &np);
+
+       if (!np)
+               return -ENODEV;
+
+       if (parse_next_property(buf, end, &name, &length, &value) == NULL)
+               return -EINVAL;
+
+       prop = new_property(name, length, value, NULL);
+       if (!prop)
+               return -ENOMEM;
+
+       prom_add_property(np, prop);
+
+       return 0;
+}
+
+static int do_remove_property(char *buf, size_t bufsize)
+{
+       struct device_node *np;
+       char *tmp;
+       struct property *prop;
+       buf = parse_node(buf, bufsize, &np);
+
+       if (!np)
+               return -ENODEV;
+
+       tmp = strchr(buf,' ');
+       if (tmp)
+               *tmp = '\0';
+
+       if (strlen(buf) == 0)
+               return -EINVAL;
+
+       prop = of_find_property(np, buf, NULL);
+
+       return prom_remove_property(np, prop);
+}
+
+static int do_update_property(char *buf, size_t bufsize)
+{
+       struct device_node *np;
+       unsigned char *value;
+       char *name, *end, *next_prop;
+       int rc, length;
+       struct property *newprop, *oldprop;
+       buf = parse_node(buf, bufsize, &np);
+       end = buf + bufsize;
+
+       if (!np)
+               return -ENODEV;
+
+       next_prop = parse_next_property(buf, end, &name, &length, &value);
+       if (!next_prop)
+               return -EINVAL;
+
+       newprop = new_property(name, length, value, NULL);
+       if (!newprop)
+               return -ENOMEM;
+
+       if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
+               slb_set_size(*(int *)value);
+
+       oldprop = of_find_property(np, name,NULL);
+       if (!oldprop) {
+               if (strlen(name))
+                       return prom_add_property(np, newprop);
+               return -ENODEV;
+       }
+
+       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;
+
+               rc = blocking_notifier_call_chain(&pSeries_reconfig_chain,
+                                                 action, value);
+               if (rc == NOTIFY_BAD) {
+                       rc = prom_update_property(np, oldprop, newprop);
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+
 /**
  * ofdt_write - perform operations on the Open Firmware device tree
  *
@@ -394,6 +525,12 @@ static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t coun
                rv = do_add_node(tmp, count - (tmp - kbuf));
        else if (!strcmp(kbuf, "remove_node"))
                rv = do_remove_node(tmp);
+       else if (!strcmp(kbuf, "add_property"))
+               rv = do_add_property(tmp, count - (tmp - kbuf));
+       else if (!strcmp(kbuf, "remove_property"))
+               rv = do_remove_property(tmp, count - (tmp - kbuf));
+       else if (!strcmp(kbuf, "update_property"))
+               rv = do_update_property(tmp, count - (tmp - kbuf));
        else
                rv = -EINVAL;
 out:
@@ -401,25 +538,22 @@ out:
        return rv ? rv : count;
 }
 
-static struct file_operations ofdt_fops = {
-       .write = ofdt_write
+static const struct file_operations ofdt_fops = {
+       .write = ofdt_write,
+       .llseek = noop_llseek,
 };
 
-/* create /proc/ppc64/ofdt write-only by root */
+/* create /proc/powerpc/ofdt write-only by root */
 static int proc_ppc64_create_ofdt(void)
 {
        struct proc_dir_entry *ent;
 
-       if (!(systemcfg->platform & PLATFORM_PSERIES))
+       if (!machine_is(pseries))
                return 0;
 
-       ent = create_proc_entry("ppc64/ofdt", S_IWUSR, NULL);
-       if (ent) {
-               ent->nlink = 1;
-               ent->data = NULL;
+       ent = proc_create("powerpc/ofdt", S_IWUSR, NULL, &ofdt_fops);
+       if (ent)
                ent->size = 0;
-               ent->proc_fops = &ofdt_fops;
-       }
 
        return 0;
 }