aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/pseries/dlpar.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/pseries/dlpar.c')
-rw-r--r--arch/powerpc/platforms/pseries/dlpar.c143
1 files changed, 61 insertions, 82 deletions
diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c
index b74a9230edc..2d0b4d68a40 100644
--- a/arch/powerpc/platforms/pseries/dlpar.c
+++ b/arch/powerpc/platforms/pseries/dlpar.c
@@ -11,19 +11,17 @@
*/
#include <linux/kernel.h>
-#include <linux/kref.h>
#include <linux/notifier.h>
-#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/cpu.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include "offline_states.h"
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/uaccess.h>
#include <asm/rtas.h>
-#include <asm/pSeries_reconfig.h>
struct cc_workarea {
u32 drc_index;
@@ -64,26 +62,32 @@ static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
return prop;
}
-static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa)
+static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa,
+ const char *path)
{
struct device_node *dn;
char *name;
+ /* If parent node path is "/" advance path to NULL terminator to
+ * prevent double leading slashs in full_name.
+ */
+ if (!path[1])
+ path++;
+
dn = kzalloc(sizeof(*dn), GFP_KERNEL);
if (!dn)
return NULL;
- /* The configure connector reported name does not contain a
- * preceeding '/', so we allocate a buffer large enough to
- * prepend this to the full_name.
- */
name = (char *)ccwa + ccwa->name_offset;
- dn->full_name = kasprintf(GFP_KERNEL, "/%s", name);
+ dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name);
if (!dn->full_name) {
kfree(dn);
return NULL;
}
+ of_node_set_flag(dn, OF_DYNAMIC);
+ of_node_init(dn);
+
return dn;
}
@@ -112,6 +116,7 @@ void dlpar_free_cc_nodes(struct device_node *dn)
dlpar_free_one_cc_node(dn);
}
+#define COMPLETE 0
#define NEXT_SIBLING 1
#define NEXT_CHILD 2
#define NEXT_PROPERTY 3
@@ -120,7 +125,8 @@ void dlpar_free_cc_nodes(struct device_node *dn)
#define CALL_AGAIN -2
#define ERR_CFG_USE -9003
-struct device_node *dlpar_configure_connector(u32 drc_index)
+struct device_node *dlpar_configure_connector(u32 drc_index,
+ struct device_node *parent)
{
struct device_node *dn;
struct device_node *first_dn = NULL;
@@ -129,6 +135,7 @@ struct device_node *dlpar_configure_connector(u32 drc_index)
struct property *last_property = NULL;
struct cc_workarea *ccwa;
char *data_buf;
+ const char *parent_path = parent->full_name;
int cc_token;
int rc = -1;
@@ -158,8 +165,11 @@ struct device_node *dlpar_configure_connector(u32 drc_index)
spin_unlock(&rtas_data_buf_lock);
switch (rc) {
+ case COMPLETE:
+ break;
+
case NEXT_SIBLING:
- dn = dlpar_parse_cc_node(ccwa);
+ dn = dlpar_parse_cc_node(ccwa, parent_path);
if (!dn)
goto cc_error;
@@ -169,13 +179,17 @@ struct device_node *dlpar_configure_connector(u32 drc_index)
break;
case NEXT_CHILD:
- dn = dlpar_parse_cc_node(ccwa);
+ if (first_dn)
+ parent_path = last_dn->full_name;
+
+ dn = dlpar_parse_cc_node(ccwa, parent_path);
if (!dn)
goto cc_error;
- if (!first_dn)
+ if (!first_dn) {
+ dn->parent = parent;
first_dn = dn;
- else {
+ } else {
dn->parent = last_dn;
if (last_dn)
last_dn->child = dn;
@@ -199,6 +213,7 @@ struct device_node *dlpar_configure_connector(u32 drc_index)
case PREV_PARENT:
last_dn = last_dn->parent;
+ parent_path = last_dn->parent->full_name;
break;
case CALL_AGAIN:
@@ -251,57 +266,39 @@ static struct device_node *derive_parent(const char *path)
int dlpar_attach_node(struct device_node *dn)
{
-#ifdef CONFIG_PROC_DEVICETREE
- struct proc_dir_entry *ent;
-#endif
int rc;
- of_node_set_flag(dn, OF_DYNAMIC);
- kref_init(&dn->kref);
dn->parent = derive_parent(dn->full_name);
if (!dn->parent)
return -ENOMEM;
- rc = blocking_notifier_call_chain(&pSeries_reconfig_chain,
- PSERIES_RECONFIG_ADD, dn);
- if (rc == NOTIFY_BAD) {
+ rc = of_attach_node(dn);
+ if (rc) {
printk(KERN_ERR "Failed to add device node %s\n",
dn->full_name);
- return -ENOMEM; /* For now, safe to assume kmalloc failure */
+ return rc;
}
- of_attach_node(dn);
-
-#ifdef CONFIG_PROC_DEVICETREE
- ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde);
- if (ent)
- proc_device_tree_add_node(dn, ent);
-#endif
-
of_node_put(dn->parent);
return 0;
}
int dlpar_detach_node(struct device_node *dn)
{
-#ifdef CONFIG_PROC_DEVICETREE
- struct device_node *parent = dn->parent;
- struct property *prop = dn->properties;
+ struct device_node *child;
+ int rc;
- while (prop) {
- remove_proc_entry(prop->name, dn->pde);
- prop = prop->next;
+ child = of_get_next_child(dn, NULL);
+ while (child) {
+ dlpar_detach_node(child);
+ child = of_get_next_child(dn, child);
}
- if (dn->pde)
- remove_proc_entry(dn->pde->name, parent->pde);
-#endif
+ rc = of_detach_node(dn);
+ if (rc)
+ return rc;
- blocking_notifier_call_chain(&pSeries_reconfig_chain,
- PSERIES_RECONFIG_REMOVE, dn);
- of_detach_node(dn);
of_node_put(dn); /* Must decrement the refcount */
-
return 0;
}
@@ -402,57 +399,42 @@ out:
static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
{
- struct device_node *dn;
+ struct device_node *dn, *parent;
unsigned long drc_index;
- char *cpu_name;
int rc;
- cpu_hotplug_driver_lock();
rc = strict_strtoul(buf, 0, &drc_index);
- if (rc) {
- rc = -EINVAL;
- goto out;
- }
+ if (rc)
+ return -EINVAL;
- dn = dlpar_configure_connector(drc_index);
- if (!dn) {
- rc = -EINVAL;
- goto out;
- }
+ parent = of_find_node_by_path("/cpus");
+ if (!parent)
+ return -ENODEV;
- /* configure-connector reports cpus as living in the base
- * directory of the device tree. CPUs actually live in the
- * cpus directory so we need to fixup the full_name.
- */
- cpu_name = kasprintf(GFP_KERNEL, "/cpus%s", dn->full_name);
- if (!cpu_name) {
- dlpar_free_cc_nodes(dn);
- rc = -ENOMEM;
- goto out;
- }
+ dn = dlpar_configure_connector(drc_index, parent);
+ if (!dn)
+ return -EINVAL;
- kfree(dn->full_name);
- dn->full_name = cpu_name;
+ of_node_put(parent);
rc = dlpar_acquire_drc(drc_index);
if (rc) {
dlpar_free_cc_nodes(dn);
- rc = -EINVAL;
- goto out;
+ return -EINVAL;
}
rc = dlpar_attach_node(dn);
if (rc) {
dlpar_release_drc(drc_index);
dlpar_free_cc_nodes(dn);
- goto out;
+ return rc;
}
rc = dlpar_online_cpu(dn);
-out:
- cpu_hotplug_driver_unlock();
+ if (rc)
+ return rc;
- return rc ? rc : count;
+ return count;
}
static int dlpar_offline_cpu(struct device_node *dn)
@@ -525,30 +507,27 @@ static ssize_t dlpar_cpu_release(const char *buf, size_t count)
return -EINVAL;
}
- cpu_hotplug_driver_lock();
rc = dlpar_offline_cpu(dn);
if (rc) {
of_node_put(dn);
- rc = -EINVAL;
- goto out;
+ return -EINVAL;
}
rc = dlpar_release_drc(*drc_index);
if (rc) {
of_node_put(dn);
- goto out;
+ return rc;
}
rc = dlpar_detach_node(dn);
if (rc) {
dlpar_acquire_drc(*drc_index);
- goto out;
+ return rc;
}
of_node_put(dn);
-out:
- cpu_hotplug_driver_unlock();
- return rc ? rc : count;
+
+ return count;
}
static int __init pseries_dlpar_init(void)