The Dynamic Logical Partitioning capabilities of the powerpc pseries platform
allows for the addition and removal of resources (i.e. CPU's, memory, and PCI
devices) from a partition. The removal of a resource involves
removing the resource's node from the device tree and then returning the
resource to firmware via the rtas set-indicator call. To add a resource, it
is first obtained from firmware via the rtas set-indicator call and then a
new device tree node is created using the ibm,configure-coinnector rtas call
and added to the device tree.
This patch provides the kernel DLPAR infrastructure in a new filed named
dlpar.c. The functionality provided is for acquiring and releasing a resource
from firmware and the parsing of information returned from the
ibm,configure-connector rtas call. Additionally this exports the pSeries
reconfiguration notifier chain so that it can be invoked when device tree
updates are made.
Signed-off-by: Nathan Fontenot nf...@austin.ibm.com
---
arch/powerpc/include/asm/pSeries_reconfig.h |1
arch/powerpc/platforms/pseries/Makefile |2
arch/powerpc/platforms/pseries/dlpar.c | 344
arch/powerpc/platforms/pseries/reconfig.c |2
4 files changed, 347 insertions(+), 2 deletions(-)
Index: powerpc/arch/powerpc/platforms/pseries/dlpar.c
===
--- /dev/null 1970-01-01 00:00:00.0 +
+++ powerpc/arch/powerpc/platforms/pseries/dlpar.c 2009-11-25
04:54:13.0 -0600
@@ -0,0 +1,344 @@
+/*
+ * Support for dynamic reconfiguration for PCI, Memory, and CPU
+ * Hotplug and Dynamic Logical Partitioning on RPA platforms.
+ *
+ * Copyright (C) 2009 Nathan Fontenot
+ * Copyright (C) 2009 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ */
+
+#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 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;
+ u32 zero;
+ u32 name_offset;
+ u32 prop_length;
+ u32 prop_offset;
+};
+
+static void dlpar_free_cc_property(struct property *prop)
+{
+ kfree(prop-name);
+ kfree(prop-value);
+ kfree(prop);
+}
+
+static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
+{
+ struct property *prop;
+ char *name;
+ char *value;
+
+ prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+ if (!prop)
+ return NULL;
+
+ name = (char *)ccwa + ccwa-name_offset;
+ prop-name = kstrdup(name, GFP_KERNEL);
+
+ prop-length = ccwa-prop_length;
+ value = (char *)ccwa + ccwa-prop_offset;
+ prop-value = kzalloc(prop-length, GFP_KERNEL);
+ if (!prop-value) {
+ dlpar_free_cc_property(prop);
+ return NULL;
+ }
+
+ memcpy(prop-value, value, prop-length);
+ return prop;
+}
+
+static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa)
+{
+ struct device_node *dn;
+ char *name;
+
+ 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 = kmalloc(strlen(name) + 2, GFP_KERNEL);
+ if (!dn-full_name) {
+ kfree(dn);
+ return NULL;
+ }
+
+ sprintf(dn-full_name, /%s, name);
+ return dn;
+}
+
+static void dlpar_free_one_cc_node(struct device_node *dn)
+{
+ struct property *prop;
+
+ while (dn-properties) {
+ prop = dn-properties;
+ dn-properties = prop-next;
+ dlpar_free_cc_property(prop);
+ }
+
+ kfree(dn-full_name);
+ kfree(dn);
+}
+
+static void dlpar_free_cc_nodes(struct device_node *dn)
+{
+ if (dn-child)
+ dlpar_free_cc_nodes(dn-child);
+
+ if (dn-sibling)
+ dlpar_free_cc_nodes(dn-sibling);
+
+ dlpar_free_one_cc_node(dn);
+}
+
+#define NEXT_SIBLING1
+#define NEXT_CHILD 2
+#define NEXT_PROPERTY 3
+#define PREV_PARENT 4
+#define MORE_MEMORY 5
+#define CALL_AGAIN -2
+#define ERR_CFG_USE -9003
+
+struct device_node *dlpar_configure_connector(u32 drc_index)
+{
+ struct device_node *dn;
+ struct device_node *first_dn = NULL;
+ struct device_node *last_dn = NULL;
+ struct property *property;
+ struct property *last_property = NULL;
+ struct cc_workarea