LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Compilation issue with 2.6.22 on ppc machine
From: Mani Kandan @ 2009-11-25  9:08 UTC (permalink / raw)
  To: linuxppc-dev@lists.ozlabs.org

[-- Attachment #1: Type: text/plain, Size: 3738 bytes --]

Hello All,

  I'm unable to compile my custom kernel version 2.6.22.5 after applying real time patch 2.6.22.1-rt9.  I get following compilation errors:

In file included from arch/ppc/include/asm/hw_irq.h:121,
                 from include/asm/system.h:9,
                 from include/linux/list.h:9,
                 from include/linux/signal.h:8,
                 from arch/ppc/kernel/asm-offsets.c:11:
include/linux/irqflags.h:92:1: warning: "raw_local_irq_save" redefined
In file included from include/asm/system.h:9,
                 from include/linux/list.h:9,
                 from include/linux/signal.h:8,
                 from arch/ppc/kernel/asm-offsets.c:11:
arch/ppc/include/asm/hw_irq.h:114:1: warning: this is the location of the previous definition
In file included from arch/ppc/include/asm/hw_irq.h:121,
                 from include/asm/system.h:9,
                 from include/linux/list.h:9,
                 from include/linux/signal.h:8,
                 from arch/ppc/kernel/asm-offsets.c:11:
include/linux/irqflags.h:97:1: warning: "raw_local_irq_restore" redefined
In file included from include/asm/system.h:9,
                 from include/linux/list.h:9,
                 from include/linux/signal.h:8,
                 from arch/ppc/kernel/asm-offsets.c:11:
arch/ppc/include/asm/hw_irq.h:73:1: warning: this is the location of the previous definition
In file included from include/linux/spinlock.h:108,
                 from include/linux/signal.h:9,
                 from arch/ppc/kernel/asm-offsets.c:11:
include/asm/spinlock.h: In function '__raw_spin_lock':
include/asm/spinlock.h:34: error: 'raw_spinlock_t' has no member named 'slock'
include/asm/spinlock.h: In function '__raw_spin_unlock':
include/asm/spinlock.h:41: error: 'raw_spinlock_t' has no member named 'slock'
include/asm/spinlock.h: In function '__raw_read_trylock':
include/asm/spinlock.h:74: error: 'raw_rwlock_t' has no member named 'lock'
include/asm/spinlock.h: In function '__raw_read_lock':
include/asm/spinlock.h:97: error: 'raw_rwlock_t' has no member named 'lock'
include/asm/spinlock.h: In function '__raw_read_unlock':
include/asm/spinlock.h:113: error: 'raw_rwlock_t' has no member named 'lock'
include/asm/spinlock.h: In function '__raw_write_trylock':
include/asm/spinlock.h:131: error: 'raw_rwlock_t' has no member named 'lock'
include/asm/spinlock.h: In function '__raw_write_lock':
include/asm/spinlock.h:154: error: 'raw_rwlock_t' has no member named 'lock'
include/asm/spinlock.h: In function '__raw_write_unlock':
include/asm/spinlock.h:161: error: 'raw_rwlock_t' has no member named 'lock'
arch/ppc/kernel/asm-offsets.c: In function 'main':
arch/ppc/kernel/asm-offsets.c:147: error: 'struct vdso_data' has no member named 'tb_orig_stamp'
arch/ppc/kernel/asm-offsets.c:148: error: 'struct vdso_data' has no member named 'tb_ticks_per_sec'
arch/ppc/kernel/asm-offsets.c:149: error: 'struct vdso_data' has no member named 'tb_to_xs'
arch/ppc/kernel/asm-offsets.c:150: error: 'struct vdso_data' has no member named 'stamp_xsec'
arch/ppc/kernel/asm-offsets.c:151: error: 'struct vdso_data' has no member named 'tb_update_count'
arch/ppc/kernel/asm-offsets.c:152: error: 'struct vdso_data' has no member named 'tz_minuteswest'
arch/ppc/kernel/asm-offsets.c:153: error: 'struct vdso_data' has no member named 'tz_dsttime'
arch/ppc/kernel/asm-offsets.c:155: error: 'struct vdso_data' has no member named 'wtom_clock_sec'
arch/ppc/kernel/asm-offsets.c:156: error: 'struct vdso_data' has no member named 'wtom_clock_nsec'
make[1]: *** [arch/ppc/kernel/asm-offsets.s] Error 1
make: *** [prepare0] Error 2
[root@dvl linux-rt]#

Is this a known issue ?

TIA,
Mani


[-- Attachment #2: Type: text/html, Size: 14085 bytes --]

^ permalink raw reply

* Re: [RFC] powerpc/mm: honor O_SYNC flag for memory map
From: Li Yang @ 2009-11-25  8:07 UTC (permalink / raw)
  To: Segher Boessenkool; +Cc: linuxppc-dev, Li Yang-R58472
In-Reply-To: <DFC7BA62-6CF0-4A08-8DD1-11825A638262@kernel.crashing.org>

On Sun, Nov 22, 2009 at 4:01 AM, Segher Boessenkool
<segher@kernel.crashing.org> wrote:
>>> You need to be a bit more careful tho. You must not allow RAM managed b=
y
>>> the kernel to be mapped non-cachable.
>>
>> Even if the user explicitly sets the O_SYNC flag? =C2=A0IMHO, it's a bug=
 of
>> the application if it uses O_SYNC on main memory to be mmap'ed later.
>> And we don't need to cover up the bug.
>
> Is that "embedded thinking"? =C2=A0Conflicts like this cause machine chec=
ks or
> checkstops on many PowerPC implementations, we do not normally allow such
> to be caused by userland.

So what you are saying is that if the kernel has mapped a physical
page as cacheable while user application is trying to map it as
non-cacheable, there will be machine checks and checkstops rather than
just performance drop?  This is new to me.  Could you elaborate a bit?
 Thanks.

- Leo

^ permalink raw reply

* [PATCH v2 3/3]CPU DLPAR handling
From: Nathan Fontenot @ 2009-11-25  7:13 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel; +Cc: gregkh, paul Mackerras
In-Reply-To: <4B0CD70F.6090600@austin.ibm.com>

This patch adds the specific routines to probe and release (add and remove)
cpu resource for the powerpc pseries platform and registers these handlers
with the ppc_md callout structure.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---
 arch/powerpc/platforms/pseries/dlpar.c |   88 +++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

Index: powerpc/arch/powerpc/platforms/pseries/dlpar.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pseries/dlpar.c	2009-11-25 04:54:13.000000000 -0600
+++ powerpc/arch/powerpc/platforms/pseries/dlpar.c	2009-11-25 04:55:00.000000000 -0600
@@ -341,4 +341,92 @@
 	return 0;
 }
 
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
 
+static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
+{
+	struct device_node *dn;
+	unsigned long drc_index;
+	char *cpu_name;
+	int rc;
+
+	rc = strict_strtoul(buf, 0, &drc_index);
+	if (rc)
+		return -EINVAL;
+
+	dn = dlpar_configure_connector(drc_index);
+	if (!dn)
+		return -EINVAL;
+
+	/* 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 = kzalloc(strlen(dn->full_name) + strlen("/cpus") + 1,
+			   GFP_KERNEL);
+	if (!cpu_name) {
+		dlpar_free_cc_nodes(dn);
+		return -ENOMEM;
+	}
+
+	sprintf(cpu_name, "/cpus%s", dn->full_name);
+	kfree(dn->full_name);
+	dn->full_name = cpu_name;
+
+	rc = dlpar_acquire_drc(drc_index);
+	if (rc) {
+		dlpar_free_cc_nodes(dn);
+		return -EINVAL;
+	}
+
+	rc = dlpar_attach_node(dn);
+	if (rc) {
+		dlpar_release_drc(drc_index);
+		dlpar_free_cc_nodes(dn);
+	}
+
+	return rc ? rc : count;
+}
+
+static ssize_t dlpar_cpu_release(const char *buf, size_t count)
+{
+	struct device_node *dn;
+	const u32 *drc_index;
+	int rc;
+
+	dn = of_find_node_by_path(buf);
+	if (!dn)
+		return -EINVAL;
+
+	drc_index = of_get_property(dn, "ibm,my-drc-index", NULL);
+	if (!drc_index) {
+		of_node_put(dn);
+		return -EINVAL;
+	}
+
+	rc = dlpar_release_drc(*drc_index);
+	if (rc) {
+		of_node_put(dn);
+		return -EINVAL;
+	}
+
+	rc = dlpar_detach_node(dn);
+	if (rc) {
+		dlpar_acquire_drc(*drc_index);
+		return rc;
+	}
+
+	of_node_put(dn);
+	return count;
+}
+
+static int __init pseries_dlpar_init(void)
+{
+	ppc_md.cpu_probe = dlpar_cpu_probe;
+	ppc_md.cpu_release = dlpar_cpu_release;
+
+	return 0;
+}
+machine_device_initcall(pseries, pseries_dlpar_init);
+
+#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */

^ permalink raw reply

* [PATCH v2 2/3] sysfs cpu probe/release files
From: Nathan Fontenot @ 2009-11-25  7:12 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel; +Cc: gregkh, paul Mackerras
In-Reply-To: <4B0CD70F.6090600@austin.ibm.com>

In order to support kernel DLPAR of CPU resources we need to provide an
interface to add (probe) and remove (release) the resource from the system.
This patch Creates new generic probe and release sysfs files to facilitate
cpu probe/release.  The probe/release interface provides for allowing each
arch to supply their own routines for implementing the backend of adding
and removing cpus to/from the system.

This also creates the powerpc specific stubs to handle the arch callouts 
from writes to the sysfs files.

The creation and use of these files is regulated by the 
CONFIG_ARCH_CPU_PROBE_RELEASE option so that only architectures that need the
capability will have the files created.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---

 arch/powerpc/Kconfig               |    4 ++++
 arch/powerpc/include/asm/machdep.h |    5 +++++
 arch/powerpc/kernel/sysfs.c        |   19 +++++++++++++++++++
 drivers/base/cpu.c                 |   32 ++++++++++++++++++++++++++++++++
 include/linux/cpu.h                |    2 ++
 5 files changed, 62 insertions(+)

Index: powerpc/drivers/base/cpu.c
===================================================================
--- powerpc.orig/drivers/base/cpu.c	2009-11-25 04:06:10.000000000 -0600
+++ powerpc/drivers/base/cpu.c	2009-11-25 04:11:01.000000000 -0600
@@ -72,6 +72,38 @@
 	per_cpu(cpu_sys_devices, logical_cpu) = NULL;
 	return;
 }
+
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+static ssize_t cpu_probe_store(struct class *class, const char *buf,
+			       size_t count)
+{
+	return arch_cpu_probe(buf, count);
+}
+
+static ssize_t cpu_release_store(struct class *class, const char *buf,
+				 size_t count)
+{
+	return arch_cpu_release(buf, count);
+}
+
+static CLASS_ATTR(probe, S_IWUSR, NULL, cpu_probe_store);
+static CLASS_ATTR(release, S_IWUSR, NULL, cpu_release_store);
+
+int __init cpu_probe_release_init(void)
+{
+	int rc;
+
+	rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
+			       &class_attr_probe.attr);
+	if (!rc)
+		rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
+				       &class_attr_release.attr);
+
+	return rc;
+}
+device_initcall(cpu_probe_release_init);
+#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
+
 #else /* ... !CONFIG_HOTPLUG_CPU */
 static inline void register_cpu_control(struct cpu *cpu)
 {
Index: powerpc/arch/powerpc/include/asm/machdep.h
===================================================================
--- powerpc.orig/arch/powerpc/include/asm/machdep.h	2009-11-25 04:06:10.000000000 -0600
+++ powerpc/arch/powerpc/include/asm/machdep.h	2009-11-25 04:11:01.000000000 -0600
@@ -266,6 +266,11 @@
 	void (*suspend_disable_irqs)(void);
 	void (*suspend_enable_irqs)(void);
 #endif
+
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+	ssize_t (*cpu_probe)(const char *, size_t);
+	ssize_t (*cpu_release)(const char *, size_t);
+#endif
 };
 
 extern void e500_idle(void);
Index: powerpc/arch/powerpc/kernel/sysfs.c
===================================================================
--- powerpc.orig/arch/powerpc/kernel/sysfs.c	2009-11-25 04:06:10.000000000 -0600
+++ powerpc/arch/powerpc/kernel/sysfs.c	2009-11-25 04:11:01.000000000 -0600
@@ -461,6 +461,25 @@
 
 	cacheinfo_cpu_offline(cpu);
 }
+
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+ssize_t arch_cpu_probe(const char *buf, size_t count)
+{
+	if (ppc_md.cpu_probe)
+		return ppc_md.cpu_probe(buf, count);
+
+	return -EINVAL;
+}
+
+ssize_t arch_cpu_release(const char *buf, size_t count)
+{
+	if (ppc_md.cpu_release)
+		return ppc_md.cpu_release(buf, count);
+
+	return -EINVAL;
+}
+#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
+
 #endif /* CONFIG_HOTPLUG_CPU */
 
 static int __cpuinit sysfs_cpu_notify(struct notifier_block *self,
Index: powerpc/arch/powerpc/Kconfig
===================================================================
--- powerpc.orig/arch/powerpc/Kconfig	2009-11-25 04:06:10.000000000 -0600
+++ powerpc/arch/powerpc/Kconfig	2009-11-25 04:11:01.000000000 -0600
@@ -320,6 +320,10 @@
 
 	  Say N if you are unsure.
 
+config ARCH_CPU_PROBE_RELEASE
+	def_bool y
+	depends on HOTPLUG_CPU
+
 config ARCH_ENABLE_MEMORY_HOTPLUG
 	def_bool y
 
Index: powerpc/include/linux/cpu.h
===================================================================
--- powerpc.orig/include/linux/cpu.h	2009-11-25 04:06:10.000000000 -0600
+++ powerpc/include/linux/cpu.h	2009-11-25 04:11:56.000000000 -0600
@@ -43,6 +43,8 @@
 
 #ifdef CONFIG_HOTPLUG_CPU
 extern void unregister_cpu(struct cpu *cpu);
+extern ssize_t arch_cpu_probe(const char *, size_t);
+extern ssize_t arch_cpu_release(const char *, size_t);
 #endif
 struct notifier_block;
 

^ permalink raw reply

* [PATCH v2 1/3] Kernel DLPAR Infrastructure
From: Nathan Fontenot @ 2009-11-25  7:10 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel; +Cc: gregkh, paul Mackerras
In-Reply-To: <4B0CD70F.6090600@austin.ibm.com>

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 <nfont@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.000000000 +0000
+++ powerpc/arch/powerpc/platforms/pseries/dlpar.c	2009-11-25 04:54:13.000000000 -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_SIBLING    1
+#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 *ccwa;
+	int cc_token;
+	int rc;
+
+	cc_token = rtas_token("ibm,configure-connector");
+	if (cc_token == RTAS_UNKNOWN_SERVICE)
+		return NULL;
+
+	spin_lock(&rtas_data_buf_lock);
+	ccwa = (struct cc_workarea *)&rtas_data_buf[0];
+	ccwa->drc_index = drc_index;
+	ccwa->zero = 0;
+
+	rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
+	while (rc) {
+		switch (rc) {
+		case NEXT_SIBLING:
+			dn = dlpar_parse_cc_node(ccwa);
+			if (!dn)
+				goto cc_error;
+
+			dn->parent = last_dn->parent;
+			last_dn->sibling = dn;
+			last_dn = dn;
+			break;
+
+		case NEXT_CHILD:
+			dn = dlpar_parse_cc_node(ccwa);
+			if (!dn)
+				goto cc_error;
+
+			if (!first_dn)
+				first_dn = dn;
+			else {
+				dn->parent = last_dn;
+				if (last_dn)
+					last_dn->child = dn;
+			}
+
+			last_dn = dn;
+			break;
+
+		case NEXT_PROPERTY:
+			property = dlpar_parse_cc_property(ccwa);
+			if (!property)
+				goto cc_error;
+
+			if (!last_dn->properties)
+				last_dn->properties = property;
+			else
+				last_property->next = property;
+
+			last_property = property;
+			break;
+
+		case PREV_PARENT:
+			last_dn = last_dn->parent;
+			break;
+
+		case CALL_AGAIN:
+			break;
+
+		case MORE_MEMORY:
+		case ERR_CFG_USE:
+		default:
+			printk(KERN_ERR "Unexpected Error (%d) "
+			       "returned from configure-connector\n", rc);
+			goto cc_error;
+		}
+
+		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
+	}
+
+	spin_unlock(&rtas_data_buf_lock);
+	return first_dn;
+
+cc_error:
+	if (first_dn)
+		dlpar_free_cc_nodes(first_dn);
+	spin_unlock(&rtas_data_buf_lock);
+	return NULL;
+}
+
+static struct device_node *derive_parent(const char *path)
+{
+	struct device_node *parent;
+	char *last_slash;
+
+	last_slash = strrchr(path, '/');
+	if (last_slash == path) {
+		parent = of_find_node_by_path("/");
+	} else {
+		char *parent_path;
+		int parent_path_len = last_slash - path + 1;
+		parent_path = kmalloc(parent_path_len, GFP_KERNEL);
+		if (!parent_path)
+			return NULL;
+
+		strlcpy(parent_path, path, parent_path_len);
+		parent = of_find_node_by_path(parent_path);
+		kfree(parent_path);
+	}
+
+	return parent;
+}
+
+int dlpar_attach_node(struct device_node *dn)
+{
+	struct proc_dir_entry *ent;
+	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) {
+		printk(KERN_ERR "Failed to add device node %s\n",
+		       dn->full_name);
+		return -ENOMEM; /* For now, safe to assume kmalloc failure */
+	}
+
+	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)
+{
+	struct device_node *parent = dn->parent;
+	struct property *prop = dn->properties;
+
+#ifdef CONFIG_PROC_DEVICETREE
+	while (prop) {
+		remove_proc_entry(prop->name, dn->pde);
+		prop = prop->next;
+	}
+
+	if (dn->pde)
+		remove_proc_entry(dn->pde->name, parent->pde);
+#endif
+
+	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;
+}
+
+#define DR_ENTITY_SENSE		9003
+#define DR_ENTITY_PRESENT	1
+#define DR_ENTITY_UNUSABLE	2
+#define ALLOCATION_STATE	9003
+#define ALLOC_UNUSABLE		0
+#define ALLOC_USABLE		1
+#define ISOLATION_STATE		9001
+#define ISOLATE			0
+#define UNISOLATE		1
+
+int dlpar_acquire_drc(u32 drc_index)
+{
+	int dr_status, rc;
+
+	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
+		       DR_ENTITY_SENSE, drc_index);
+	if (rc || dr_status != DR_ENTITY_UNUSABLE)
+		return -1;
+
+	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
+	if (rc)
+		return rc;
+
+	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
+	if (rc) {
+		rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
+		return rc;
+	}
+
+	return 0;
+}
+
+int dlpar_release_drc(u32 drc_index)
+{
+	int dr_status, rc;
+
+	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
+		       DR_ENTITY_SENSE, drc_index);
+	if (rc || dr_status != DR_ENTITY_PRESENT)
+		return -1;
+
+	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
+	if (rc)
+		return rc;
+
+	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
+	if (rc) {
+		rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
+		return rc;
+	}
+
+	return 0;
+}
+
+
Index: powerpc/arch/powerpc/platforms/pseries/Makefile
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pseries/Makefile	2009-11-20 17:53:54.000000000 -0600
+++ powerpc/arch/powerpc/platforms/pseries/Makefile	2009-11-20 17:55:52.000000000 -0600
@@ -8,7 +8,7 @@
 
 obj-y			:= lpar.o hvCall.o nvram.o reconfig.o \
 			   setup.o iommu.o ras.o rtasd.o \
-			   firmware.o power.o
+			   firmware.o power.o dlpar.o
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_XICS)	+= xics.o
 obj-$(CONFIG_SCANLOG)	+= scanlog.o
Index: powerpc/arch/powerpc/include/asm/pSeries_reconfig.h
===================================================================
--- powerpc.orig/arch/powerpc/include/asm/pSeries_reconfig.h	2009-11-20 17:53:54.000000000 -0600
+++ powerpc/arch/powerpc/include/asm/pSeries_reconfig.h	2009-11-20 17:55:52.000000000 -0600
@@ -17,6 +17,7 @@
 #ifdef CONFIG_PPC_PSERIES
 extern int pSeries_reconfig_notifier_register(struct notifier_block *);
 extern void pSeries_reconfig_notifier_unregister(struct notifier_block *);
+extern struct blocking_notifier_head pSeries_reconfig_chain;
 #else /* !CONFIG_PPC_PSERIES */
 static inline int pSeries_reconfig_notifier_register(struct notifier_block *nb)
 {
Index: powerpc/arch/powerpc/platforms/pseries/reconfig.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pseries/reconfig.c	2009-11-20 17:53:54.000000000 -0600
+++ powerpc/arch/powerpc/platforms/pseries/reconfig.c	2009-11-20 17:55:52.000000000 -0600
@@ -96,7 +96,7 @@
 	return parent;
 }
 
-static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
+BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
 
 int pSeries_reconfig_notifier_register(struct notifier_block *nb)
 {

^ permalink raw reply

* Re: [PATCH 0/3] Kernel handling of Dynamic Logical Partitioning
From: Nathan Fontenot @ 2009-11-25  7:07 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, lkml
In-Reply-To: <19212.41144.253618.357013@cargo.ozlabs.ibm.com>

Paul Mackerras wrote:
> Nathan Fontenot writes:
> 
>> The Dynamic Logical Partitioning (DLPAR) capabilities of the powerpc pseries
>> platform allows for the addition and removal of resources (i.e. cpus,
>> memory, 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.
>>
>> The following set of patches implements the needed infrastructure to have the
>> kernel handle the DLPAR addition and removal of cpus (other DLPAR'able items 
>> to follow in future patches).  The framework for this is to create a set of 
>> probe/release sysfs files that will facilitate arch-specific call-outs to 
>> handle addition and removal of cpus to the system.
> 
> Nice explanation, but the 0/3 of the series doesn't go into git.
> Could you rework some/all of this into the commit message for patch
> 1/3 so it gets stored permanently with these patches in git?
> 
> Thanks,
> Paul.

Thanks for all the feedback.

Version two of the patches are on their way based on your comments.

-Nathan Fontenot

^ permalink raw reply

* [PATCH v2 0/3] Kernel handling of Dynamic Logical Partitioning
From: Nathan Fontenot @ 2009-11-25  7:04 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel; +Cc: gregkh, paul Mackerras

version 2 of the patch set with updates from comments.

The Dynamic Logical Partitioning (DLPAR) capabilities of the powerpc pseries
platform allows for the addition and removal of resources (i.e. cpus,
memory, 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.

The following set of patches implements the needed infrastructure to have the
kernel handle the DLPAR addition and removal of cpus (other DLPAR'able items to 
follow in future patches).  The framework for this is to create a set of probe/release 
sysfs files that will facilitate arch-specific call-outs to handle addition and 
removal of cpus to the system.

-Nathan Fontenot

1/3 - powerpc/pseries kernel DLPAR infrastructure
2/3 - Create probe/release sysfs files and the powerpc handlers
3/3 - powerpc/pseries CPU DLPAR handling 

 arch/powerpc/Kconfig                        |    4 
 arch/powerpc/include/asm/machdep.h          |    5 
 arch/powerpc/include/asm/pSeries_reconfig.h |    1 
 arch/powerpc/kernel/smp.c                   |   17 
 arch/powerpc/kernel/sysfs.c                 |   19 +
 arch/powerpc/platforms/pseries/Makefile     |    2 
 arch/powerpc/platforms/pseries/dlpar.c      |  500 +++++++++++++++++++++++++++-
 arch/powerpc/platforms/pseries/reconfig.c   |    2 
 drivers/base/cpu.c                          |   33 +
 include/linux/cpu.h                         |    4
 10 files changed, 581 insertions(+), 6 deletions(-)

^ permalink raw reply

* Re: [PATCH 3/3] CPU DLPAR  handling
From: Paul Mackerras @ 2009-11-25  3:22 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4B0C8E31.9040608@austin.ibm.com>

Nathan Fontenot writes:

> Register the pseries specific handlers for the powerpc architecture handlers
> for the cpu probe and release files.  This also implements the cpu DLPAR
> addition and removal of CPUS from the system.

...

> +	/* 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 = kzalloc(strlen(dn->full_name) + strlen("/cpus/") + 1,
> +			   GFP_KERNEL);
> +	if (!cpu_name) {
> +		dlpar_free_cc_nodes(dn);
> +		return -ENOMEM;
> +	}
> +
> +	sprintf(cpu_name, "/cpus/%s", dn->full_name);

I would have expected dn->full_name to start with a '/' already, in
which case this will end up with two consecutive slashes after
'cpus'.  I think that will probably cause problems.

Paul.

^ permalink raw reply

* Re: [PATCH 2/3] sysfs cpu probe/release files
From: Paul Mackerras @ 2009-11-25  3:20 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4B0C8D46.1030206@austin.ibm.com>

Nathan Fontenot writes:

> Create new probe and release sysfs files to facilitate adding and removing
> cpus from the system.  This also creates the powerpc specific stubs to handle
> the arch callouts from writes to the sysfs files.
> 
> The creation and use of these files is regulated by the 
> CONFIG_ARCH_CPU_PROBE_RELEASE option so that only architectures that need the
> capability will have the files created.
> 
> Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
> ---
>  arch/powerpc/Kconfig               |    4 ++++
>  arch/powerpc/include/asm/machdep.h |    5 +++++
>  arch/powerpc/kernel/sysfs.c        |   19 +++++++++++++++++++
>  drivers/base/cpu.c                 |   32 ++++++++++++++++++++++++++++++++

Since this touches drivers/base/cpu.c, you should cc Greg KH on this
patch and get an ack from him, assuming these patches are going in
through the powerpc tree.

> --- powerpc.orig/include/linux/cpu.h	2009-11-23 18:19:23.000000000 -0600
> +++ powerpc/include/linux/cpu.h	2009-11-24 14:30:07.000000000 -0600
> @@ -43,6 +43,10 @@
>  
>  #ifdef CONFIG_HOTPLUG_CPU
>  extern void unregister_cpu(struct cpu *cpu);
> +#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
> +extern ssize_t arch_cpu_probe(const char *, size_t);
> +extern ssize_t arch_cpu_release(const char *, size_t);
> +#endif

Since these are just declarations, they don't need the #ifdef around
them.

Paul.

^ permalink raw reply

* Re: [PATCH 1/3] Kernel DLPAR infrastructure
From: Paul Mackerras @ 2009-11-25  3:16 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4B0C8CE3.4010207@austin.ibm.com>

Nathan Fontenot writes:

> 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.

Mostly looks great.

> +static struct device_node *derive_parent(const char *path)
> +{
> +	struct device_node *parent;
> +	char parent_path[128];
> +	int parent_path_len;
> +
> +	parent_path_len = strrchr(path, '/') - path + 1;
> +	strlcpy(parent_path, path, parent_path_len);

This looks a bit fragile if path could possibly not contain any '/' or
if the '/' is more than 128 characters from the start of path.  Please
fix this to check if strrchr returns NULL and to cope in some
reasonable fashion if the path happens to be very long.

> +#ifdef CONFIG_PROC_DEVICETREE
> +	ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde);
> +	if (ent)
> +		proc_device_tree_add_node(dn, ent);

Also assumes that dn->full_name contains a '/'.  If for some reason it
couldn't possibly not contain a '/', put in a comment explaining that.

Thanks,
Paul.

^ permalink raw reply

* Re: [PATCH 0/3] Kernel handling of Dynamic Logical Partitioning
From: Paul Mackerras @ 2009-11-25  3:12 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, lkml
In-Reply-To: <4B0C8B45.6080804@austin.ibm.com>

Nathan Fontenot writes:

> The Dynamic Logical Partitioning (DLPAR) capabilities of the powerpc pseries
> platform allows for the addition and removal of resources (i.e. cpus,
> memory, 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.
> 
> The following set of patches implements the needed infrastructure to have the
> kernel handle the DLPAR addition and removal of cpus (other DLPAR'able items 
> to follow in future patches).  The framework for this is to create a set of 
> probe/release sysfs files that will facilitate arch-specific call-outs to 
> handle addition and removal of cpus to the system.

Nice explanation, but the 0/3 of the series doesn't go into git.
Could you rework some/all of this into the commit message for patch
1/3 so it gets stored permanently with these patches in git?

Thanks,
Paul.

^ permalink raw reply

* Re: [PATCH 1/3] Kernel DLPAR infrastructure
From: Michael Neuling @ 2009-11-25  3:03 UTC (permalink / raw)
  To: Nathan Fontenot, linuxppc-dev, linux-kernel
In-Reply-To: <31581.1259115310@neuling.org>



In message <31581.1259115310@neuling.org> you wrote:
> 
> 
> In message <4B0C8CE3.4010207@austin.ibm.com> you wrote:
> > This patch provides the kernel DLPAR infrastructure in a new filed named
> > dlpar.c.  The functionality provided is for acquiring and releasing a resou
rc
> e
> > 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 <nfont@austin.ibm.com> 
> 
> These look like they got white space munged somewhere...

Gah, sorry, they are fine

Mikey

^ permalink raw reply

* [PATCH] cpm2_pic: Allow correct flow_types for port C interrupts
From: Mark Ware @ 2009-11-25  2:15 UTC (permalink / raw)
  To: Linuxppc-dev Development

CPM2 Port C interrupts can be either falling edge, or either edge.
Other external interrupts are either falling edge or active low.

Signed-Off-By: Mark Ware <mware@elphinstone.net>
---
 arch/powerpc/sysdev/cpm2_pic.c |   29 ++++++++++++++++++++++-------
 1 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/arch/powerpc/sysdev/cpm2_pic.c b/arch/powerpc/sysdev/cpm2_pic.c
index 78f1f7c..179822c 100644
--- a/arch/powerpc/sysdev/cpm2_pic.c
+++ b/arch/powerpc/sysdev/cpm2_pic.c
@@ -141,13 +141,28 @@ static int cpm2_set_irq_type(unsigned int virq, unsigned int flow_type)
 	struct irq_desc *desc = get_irq_desc(virq);
 	unsigned int vold, vnew, edibit;
 
-	if (flow_type == IRQ_TYPE_NONE)
-		flow_type = IRQ_TYPE_LEVEL_LOW;
-
-	if (flow_type & IRQ_TYPE_EDGE_RISING) {
-		printk(KERN_ERR "CPM2 PIC: sense type 0x%x not supported\n",
-			flow_type);
-		return -EINVAL;
+	/* Port C interrupts are either IRQ_TYPE_EDGE_FALLING or
+	 * IRQ_TYPE_EDGE_BOTH (default).  All others are IRQ_TYPE_EDGE_FALLING
+	 * or IRQ_TYPE_LEVEL_LOW (default)
+	 */
+	if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) {
+		if (flow_type == IRQ_TYPE_NONE)
+			flow_type = IRQ_TYPE_EDGE_BOTH;
+
+		if (flow_type & ~IRQ_TYPE_EDGE_BOTH) {
+			printk(KERN_ERR "CPM2 PIC: sense type 0x%x not supported\n",
+				flow_type);
+			return -EINVAL;
+		}
+	} else {
+		if (flow_type == IRQ_TYPE_NONE)
+			flow_type = IRQ_TYPE_LEVEL_LOW;
+
+		if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH)) {
+			printk(KERN_ERR "CPM2 PIC: sense type 0x%x not supported\n",
+				flow_type);
+			return -EINVAL;
+		}
 	}
 
 	desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
-- 
1.5.6.5

^ permalink raw reply related

* Re: [PATCH 1/3] Kernel DLPAR infrastructure
From: Michael Neuling @ 2009-11-25  2:15 UTC (permalink / raw)
  To: Nathan Fontenot; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <4B0C8CE3.4010207@austin.ibm.com>



In message <4B0C8CE3.4010207@austin.ibm.com> you wrote:
> This patch provides the kernel DLPAR infrastructure in a new filed named
> dlpar.c.  The functionality provided is for acquiring and releasing a resourc
e
> 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 <nfont@austin.ibm.com> 

These look like they got white space munged somewhere...

Mikey

> ---
> 
> ---
>  arch/powerpc/include/asm/pSeries_reconfig.h |    1 
>  arch/powerpc/platforms/pseries/Makefile     |    2 
>  arch/powerpc/platforms/pseries/dlpar.c      |  324 +++++++++++++++++++++++++
+++
>  arch/powerpc/platforms/pseries/reconfig.c   |    2 
>  4 files changed, 327 insertions(+), 2 deletions(-)
> 
> Index: powerpc/arch/powerpc/platforms/pseries/dlpar.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ powerpc/arch/powerpc/platforms/pseries/dlpar.c	2009-11-24 23:31:28.000
000000 -0600
> @@ -0,0 +1,324 @@
> +/*
> + * 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;
> +
> +	name = (char *)ccwa + ccwa->name_offset;
> +	dn->full_name = kstrdup(name, GFP_KERNEL);
> +
> +	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_SIBLING    1
> +#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 *ccwa;
> +	int cc_token;
> +	int rc;
> +
> +	cc_token = rtas_token("ibm,configure-connector");
> +	if (cc_token == RTAS_UNKNOWN_SERVICE)
> +		return NULL;
> +
> +	spin_lock(&rtas_data_buf_lock);
> +	ccwa = (struct cc_workarea *)&rtas_data_buf[0];
> +	ccwa->drc_index = drc_index;
> +	ccwa->zero = 0;
> +
> +	rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
> +	while (rc) {
> +		switch (rc) {
> +		case NEXT_SIBLING:
> +			dn = dlpar_parse_cc_node(ccwa);
> +			if (!dn)
> +				goto cc_error;
> +
> +			dn->parent = last_dn->parent;
> +			last_dn->sibling = dn;
> +			last_dn = dn;
> +			break;
> +
> +		case NEXT_CHILD:
> +			dn = dlpar_parse_cc_node(ccwa);
> +			if (!dn)
> +				goto cc_error;
> +
> +			if (!first_dn)
> +				first_dn = dn;
> +			else {
> +				dn->parent = last_dn;
> +				if (last_dn)
> +					last_dn->child = dn;
> +			}
> +
> +			last_dn = dn;
> +			break;
> +
> +		case NEXT_PROPERTY:
> +			property = dlpar_parse_cc_property(ccwa);
> +			if (!property)
> +				goto cc_error;
> +
> +			if (!last_dn->properties)
> +				last_dn->properties = property;
> +			else
> +				last_property->next = property;
> +
> +			last_property = property;
> +			break;
> +
> +		case PREV_PARENT:
> +			last_dn = last_dn->parent;
> +			break;
> +
> +		case CALL_AGAIN:
> +			break;
> +
> +		case MORE_MEMORY:
> +		case ERR_CFG_USE:
> +		default:
> +			printk(KERN_ERR "Unexpected Error (%d) "
> +			       "returned from configure-connector\n", rc);
> +			goto cc_error;
> +		}
> +
> +		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
> +	}
> +
> +	spin_unlock(&rtas_data_buf_lock);
> +	return first_dn;
> +
> +cc_error:
> +	if (first_dn)
> +		dlpar_free_cc_nodes(first_dn);
> +	spin_unlock(&rtas_data_buf_lock);
> +	return NULL;
> +}
> +
> +static struct device_node *derive_parent(const char *path)
> +{
> +	struct device_node *parent;
> +	char parent_path[128];
> +	int parent_path_len;
> +
> +	parent_path_len = strrchr(path, '/') - path + 1;
> +	strlcpy(parent_path, path, parent_path_len);
> +
> +	parent = of_find_node_by_path(parent_path);
> +
> +	return parent;
> +}
> +
> +int dlpar_attach_node(struct device_node *dn)
> +{
> +	struct proc_dir_entry *ent;
> +	int rc;
> +
> +	of_node_set_flag(dn, OF_DYNAMIC);
> +	kref_init(&dn->kref);
> +	dn->parent = derive_parent(dn->full_name);
> +
> +	rc = blocking_notifier_call_chain(&pSeries_reconfig_chain,
> +					  PSERIES_RECONFIG_ADD, dn);
> +	if (rc == NOTIFY_BAD) {
> +		printk(KERN_ERR "Failed to add device node %s\n",
> +		       dn->full_name);
> +		return -ENOMEM; /* For now, safe to assume kmalloc failure */
> +	}
> +
> +	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)
> +{
> +	struct device_node *parent = dn->parent;
> +	struct property *prop = dn->properties;
> +
> +#ifdef CONFIG_PROC_DEVICETREE
> +	while (prop) {
> +		remove_proc_entry(prop->name, dn->pde);
> +		prop = prop->next;
> +	}
> +
> +	if (dn->pde)
> +		remove_proc_entry(dn->pde->name, parent->pde);
> +#endif
> +
> +	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;
> +}
> +
> +#define DR_ENTITY_SENSE		9003
> +#define DR_ENTITY_PRESENT	1
> +#define DR_ENTITY_UNUSABLE	2
> +#define ALLOCATION_STATE	9003
> +#define ALLOC_UNUSABLE		0
> +#define ALLOC_USABLE		1
> +#define ISOLATION_STATE		9001
> +#define ISOLATE			0
> +#define UNISOLATE		1
> +
> +int dlpar_acquire_drc(u32 drc_index)
> +{
> +	int dr_status, rc;
> +
> +	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
> +		       DR_ENTITY_SENSE, drc_index);
> +	if (rc || dr_status != DR_ENTITY_UNUSABLE)
> +		return -1;
> +
> +	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
> +	if (rc)
> +		return rc;
> +
> +	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
> +	if (rc) {
> +		rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE)
;
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +int dlpar_release_drc(u32 drc_index)
> +{
> +	int dr_status, rc;
> +
> +	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
> +		       DR_ENTITY_SENSE, drc_index);
> +	if (rc || dr_status != DR_ENTITY_PRESENT)
> +		return -1;
> +
> +	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
> +	if (rc)
> +		return rc;
> +
> +	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
> +	if (rc) {
> +		rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +
> Index: powerpc/arch/powerpc/platforms/pseries/Makefile
> ===================================================================
> --- powerpc.orig/arch/powerpc/platforms/pseries/Makefile	2009-11-20 17:5
3:54.000000000 -0600
> +++ powerpc/arch/powerpc/platforms/pseries/Makefile	2009-11-20 17:55:52.000
000000 -0600
> @@ -8,7 +8,7 @@
>  
>  obj-y			:= lpar.o hvCall.o nvram.o reconfig.o \
>  			   setup.o iommu.o ras.o rtasd.o \
> -			   firmware.o power.o
> +			   firmware.o power.o dlpar.o
>  obj-$(CONFIG_SMP)	+= smp.o
>  obj-$(CONFIG_XICS)	+= xics.o
>  obj-$(CONFIG_SCANLOG)	+= scanlog.o
> Index: powerpc/arch/powerpc/include/asm/pSeries_reconfig.h
> ===================================================================
> --- powerpc.orig/arch/powerpc/include/asm/pSeries_reconfig.h	2009-11-20 17:5
3:54.000000000 -0600
> +++ powerpc/arch/powerpc/include/asm/pSeries_reconfig.h	2009-11-20 17:5
5:52.000000000 -0600
> @@ -17,6 +17,7 @@
>  #ifdef CONFIG_PPC_PSERIES
>  extern int pSeries_reconfig_notifier_register(struct notifier_block *);
>  extern void pSeries_reconfig_notifier_unregister(struct notifier_block *);
> +extern struct blocking_notifier_head pSeries_reconfig_chain;
>  #else /* !CONFIG_PPC_PSERIES */
>  static inline int pSeries_reconfig_notifier_register(struct notifier_block *
nb)
>  {
> Index: powerpc/arch/powerpc/platforms/pseries/reconfig.c
> ===================================================================
> --- powerpc.orig/arch/powerpc/platforms/pseries/reconfig.c	2009-11-20 17:5
3:54.000000000 -0600
> +++ powerpc/arch/powerpc/platforms/pseries/reconfig.c	2009-11-20 17:55:52.000
000000 -0600
> @@ -96,7 +96,7 @@
>  	return parent;
>  }
>  
> -static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
> +BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
>  
>  int pSeries_reconfig_notifier_register(struct notifier_block *nb)
>  {
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
> 

^ permalink raw reply

* [PATCH 3/3] CPU DLPAR  handling
From: Nathan Fontenot @ 2009-11-25  1:53 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel
In-Reply-To: <4B0C8B45.6080804@austin.ibm.com>

Register the pseries specific handlers for the powerpc architecture handlers
for the cpu probe and release files.  This also implements the cpu DLPAR
addition and removal of CPUS from the system.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---

arch/powerpc/platforms/pseries/dlpar.c |   88 +++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

Index: powerpc/arch/powerpc/platforms/pseries/dlpar.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pseries/dlpar.c	2009-11-24 23:29:12.000000000 -0600
+++ powerpc/arch/powerpc/platforms/pseries/dlpar.c	2009-11-24 23:29:24.000000000 -0600
@@ -321,4 +321,92 @@
 	return 0;
 }
 
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
 
+static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
+{
+	struct device_node *dn;
+	unsigned long drc_index;
+	char *cpu_name;
+	int rc;
+
+	rc = strict_strtoul(buf, 0, &drc_index);
+	if (rc)
+		return -EINVAL;
+
+	dn = dlpar_configure_connector(drc_index);
+	if (!dn)
+		return -EINVAL;
+
+	/* 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 = kzalloc(strlen(dn->full_name) + strlen("/cpus/") + 1,
+			   GFP_KERNEL);
+	if (!cpu_name) {
+		dlpar_free_cc_nodes(dn);
+		return -ENOMEM;
+	}
+
+	sprintf(cpu_name, "/cpus/%s", dn->full_name);
+	kfree(dn->full_name);
+	dn->full_name = cpu_name;
+
+	rc = dlpar_acquire_drc(drc_index);
+	if (rc) {
+		dlpar_free_cc_nodes(dn);
+		return -EINVAL;
+	}
+
+	rc = dlpar_attach_node(dn);
+	if (rc) {
+		dlpar_release_drc(drc_index);
+		dlpar_free_cc_nodes(dn);
+	}
+
+	return rc ? rc : count;
+}
+
+static ssize_t dlpar_cpu_release(const char *buf, size_t count)
+{
+	struct device_node *dn;
+	const u32 *drc_index;
+	int rc;
+
+	dn = of_find_node_by_path(buf);
+	if (!dn)
+		return -EINVAL;
+
+	drc_index = of_get_property(dn, "ibm,my-drc-index", NULL);
+	if (!drc_index) {
+		of_node_put(dn);
+		return -EINVAL;
+	}
+
+	rc = dlpar_release_drc(*drc_index);
+	if (rc) {
+		of_node_put(dn);
+		return -EINVAL;
+	}
+
+	rc = dlpar_detach_node(dn);
+	if (rc) {
+		dlpar_acquire_drc(*drc_index);
+		return rc;
+	}
+
+	of_node_put(dn);
+	return count;
+}
+
+static int __init pseries_dlpar_init(void)
+{
+	ppc_md.cpu_probe = dlpar_cpu_probe;
+	ppc_md.cpu_release = dlpar_cpu_release;
+
+	return 0;
+}
+machine_device_initcall(pseries, pseries_dlpar_init);
+
+#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */

^ permalink raw reply

* [PATCH 2/3] sysfs cpu probe/release files
From: Nathan Fontenot @ 2009-11-25  1:49 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel
In-Reply-To: <4B0C8B45.6080804@austin.ibm.com>

Create new probe and release sysfs files to facilitate adding and removing
cpus from the system.  This also creates the powerpc specific stubs to handle
the arch callouts from writes to the sysfs files.

The creation and use of these files is regulated by the 
CONFIG_ARCH_CPU_PROBE_RELEASE option so that only architectures that need the
capability will have the files created.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---
 arch/powerpc/Kconfig               |    4 ++++
 arch/powerpc/include/asm/machdep.h |    5 +++++
 arch/powerpc/kernel/sysfs.c        |   19 +++++++++++++++++++
 drivers/base/cpu.c                 |   32 ++++++++++++++++++++++++++++++++
 include/linux/cpu.h                |    4 ++++
 5 files changed, 64 insertions(+)

Index: powerpc/drivers/base/cpu.c
===================================================================
--- powerpc.orig/drivers/base/cpu.c	2009-11-23 18:19:23.000000000 -0600
+++ powerpc/drivers/base/cpu.c	2009-11-24 14:30:07.000000000 -0600
@@ -72,6 +72,38 @@
 	per_cpu(cpu_sys_devices, logical_cpu) = NULL;
 	return;
 }
+
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+static ssize_t cpu_probe_store(struct class *class, const char *buf,
+			       size_t count)
+{
+	return arch_cpu_probe(buf, count);
+}
+
+static ssize_t cpu_release_store(struct class *class, const char *buf,
+				 size_t count)
+{
+	return arch_cpu_release(buf, count);
+}
+
+static CLASS_ATTR(probe, S_IWUSR, NULL, cpu_probe_store);
+static CLASS_ATTR(release, S_IWUSR, NULL, cpu_release_store);
+
+int __init cpu_probe_release_init(void)
+{
+	int rc;
+
+	rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
+			       &class_attr_probe.attr);
+	if (!rc)
+		rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
+				       &class_attr_release.attr);
+
+	return rc;
+}
+device_initcall(cpu_probe_release_init);
+#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
+
 #else /* ... !CONFIG_HOTPLUG_CPU */
 static inline void register_cpu_control(struct cpu *cpu)
 {
Index: powerpc/arch/powerpc/include/asm/machdep.h
===================================================================
--- powerpc.orig/arch/powerpc/include/asm/machdep.h	2009-11-23 18:19:23.000000000 -0600
+++ powerpc/arch/powerpc/include/asm/machdep.h	2009-11-24 14:30:07.000000000 -0600
@@ -266,6 +266,11 @@
 	void (*suspend_disable_irqs)(void);
 	void (*suspend_enable_irqs)(void);
 #endif
+
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+	ssize_t (*cpu_probe)(const char *, size_t);
+	ssize_t (*cpu_release)(const char *, size_t);
+#endif
 };
 
 extern void e500_idle(void);
Index: powerpc/arch/powerpc/kernel/sysfs.c
===================================================================
--- powerpc.orig/arch/powerpc/kernel/sysfs.c	2009-11-23 18:19:23.000000000 -0600
+++ powerpc/arch/powerpc/kernel/sysfs.c	2009-11-24 14:43:40.000000000 -0600
@@ -461,6 +461,25 @@
 
 	cacheinfo_cpu_offline(cpu);
 }
+
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+ssize_t arch_cpu_probe(const char *buf, size_t count)
+{
+	if (ppc_md.cpu_probe)
+		return ppc_md.cpu_probe(buf, count);
+
+	return -EINVAL;
+}
+
+ssize_t arch_cpu_release(const char *buf, size_t count)
+{
+	if (ppc_md.cpu_release)
+		return ppc_md.cpu_release(buf, count);
+
+	return -EINVAL;
+}
+#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
+
 #endif /* CONFIG_HOTPLUG_CPU */
 
 static int __cpuinit sysfs_cpu_notify(struct notifier_block *self,
Index: powerpc/arch/powerpc/Kconfig
===================================================================
--- powerpc.orig/arch/powerpc/Kconfig	2009-11-23 18:19:23.000000000 -0600
+++ powerpc/arch/powerpc/Kconfig	2009-11-24 14:30:07.000000000 -0600
@@ -320,6 +320,10 @@
 
 	  Say N if you are unsure.
 
+config ARCH_CPU_PROBE_RELEASE
+	def_bool y
+	depends on HOTPLUG_CPU
+
 config ARCH_ENABLE_MEMORY_HOTPLUG
 	def_bool y
 
Index: powerpc/include/linux/cpu.h
===================================================================
--- powerpc.orig/include/linux/cpu.h	2009-11-23 18:19:23.000000000 -0600
+++ powerpc/include/linux/cpu.h	2009-11-24 14:30:07.000000000 -0600
@@ -43,6 +43,10 @@
 
 #ifdef CONFIG_HOTPLUG_CPU
 extern void unregister_cpu(struct cpu *cpu);
+#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
+extern ssize_t arch_cpu_probe(const char *, size_t);
+extern ssize_t arch_cpu_release(const char *, size_t);
+#endif
 #endif
 struct notifier_block;
 

^ permalink raw reply

* [PATCH 1/3] Kernel DLPAR infrastructure
From: Nathan Fontenot @ 2009-11-25  1:48 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel
In-Reply-To: <4B0C8B45.6080804@austin.ibm.com>

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 <nfont@austin.ibm.com> 
---

---
 arch/powerpc/include/asm/pSeries_reconfig.h |    1 
 arch/powerpc/platforms/pseries/Makefile     |    2 
 arch/powerpc/platforms/pseries/dlpar.c      |  324 ++++++++++++++++++++++++++++
 arch/powerpc/platforms/pseries/reconfig.c   |    2 
 4 files changed, 327 insertions(+), 2 deletions(-)

Index: powerpc/arch/powerpc/platforms/pseries/dlpar.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ powerpc/arch/powerpc/platforms/pseries/dlpar.c	2009-11-24 23:31:28.000000000 -0600
@@ -0,0 +1,324 @@
+/*
+ * 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;
+
+	name = (char *)ccwa + ccwa->name_offset;
+	dn->full_name = kstrdup(name, GFP_KERNEL);
+
+	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_SIBLING    1
+#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 *ccwa;
+	int cc_token;
+	int rc;
+
+	cc_token = rtas_token("ibm,configure-connector");
+	if (cc_token == RTAS_UNKNOWN_SERVICE)
+		return NULL;
+
+	spin_lock(&rtas_data_buf_lock);
+	ccwa = (struct cc_workarea *)&rtas_data_buf[0];
+	ccwa->drc_index = drc_index;
+	ccwa->zero = 0;
+
+	rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
+	while (rc) {
+		switch (rc) {
+		case NEXT_SIBLING:
+			dn = dlpar_parse_cc_node(ccwa);
+			if (!dn)
+				goto cc_error;
+
+			dn->parent = last_dn->parent;
+			last_dn->sibling = dn;
+			last_dn = dn;
+			break;
+
+		case NEXT_CHILD:
+			dn = dlpar_parse_cc_node(ccwa);
+			if (!dn)
+				goto cc_error;
+
+			if (!first_dn)
+				first_dn = dn;
+			else {
+				dn->parent = last_dn;
+				if (last_dn)
+					last_dn->child = dn;
+			}
+
+			last_dn = dn;
+			break;
+
+		case NEXT_PROPERTY:
+			property = dlpar_parse_cc_property(ccwa);
+			if (!property)
+				goto cc_error;
+
+			if (!last_dn->properties)
+				last_dn->properties = property;
+			else
+				last_property->next = property;
+
+			last_property = property;
+			break;
+
+		case PREV_PARENT:
+			last_dn = last_dn->parent;
+			break;
+
+		case CALL_AGAIN:
+			break;
+
+		case MORE_MEMORY:
+		case ERR_CFG_USE:
+		default:
+			printk(KERN_ERR "Unexpected Error (%d) "
+			       "returned from configure-connector\n", rc);
+			goto cc_error;
+		}
+
+		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
+	}
+
+	spin_unlock(&rtas_data_buf_lock);
+	return first_dn;
+
+cc_error:
+	if (first_dn)
+		dlpar_free_cc_nodes(first_dn);
+	spin_unlock(&rtas_data_buf_lock);
+	return NULL;
+}
+
+static struct device_node *derive_parent(const char *path)
+{
+	struct device_node *parent;
+	char parent_path[128];
+	int parent_path_len;
+
+	parent_path_len = strrchr(path, '/') - path + 1;
+	strlcpy(parent_path, path, parent_path_len);
+
+	parent = of_find_node_by_path(parent_path);
+
+	return parent;
+}
+
+int dlpar_attach_node(struct device_node *dn)
+{
+	struct proc_dir_entry *ent;
+	int rc;
+
+	of_node_set_flag(dn, OF_DYNAMIC);
+	kref_init(&dn->kref);
+	dn->parent = derive_parent(dn->full_name);
+
+	rc = blocking_notifier_call_chain(&pSeries_reconfig_chain,
+					  PSERIES_RECONFIG_ADD, dn);
+	if (rc == NOTIFY_BAD) {
+		printk(KERN_ERR "Failed to add device node %s\n",
+		       dn->full_name);
+		return -ENOMEM; /* For now, safe to assume kmalloc failure */
+	}
+
+	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)
+{
+	struct device_node *parent = dn->parent;
+	struct property *prop = dn->properties;
+
+#ifdef CONFIG_PROC_DEVICETREE
+	while (prop) {
+		remove_proc_entry(prop->name, dn->pde);
+		prop = prop->next;
+	}
+
+	if (dn->pde)
+		remove_proc_entry(dn->pde->name, parent->pde);
+#endif
+
+	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;
+}
+
+#define DR_ENTITY_SENSE		9003
+#define DR_ENTITY_PRESENT	1
+#define DR_ENTITY_UNUSABLE	2
+#define ALLOCATION_STATE	9003
+#define ALLOC_UNUSABLE		0
+#define ALLOC_USABLE		1
+#define ISOLATION_STATE		9001
+#define ISOLATE			0
+#define UNISOLATE		1
+
+int dlpar_acquire_drc(u32 drc_index)
+{
+	int dr_status, rc;
+
+	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
+		       DR_ENTITY_SENSE, drc_index);
+	if (rc || dr_status != DR_ENTITY_UNUSABLE)
+		return -1;
+
+	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
+	if (rc)
+		return rc;
+
+	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
+	if (rc) {
+		rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
+		return rc;
+	}
+
+	return 0;
+}
+
+int dlpar_release_drc(u32 drc_index)
+{
+	int dr_status, rc;
+
+	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
+		       DR_ENTITY_SENSE, drc_index);
+	if (rc || dr_status != DR_ENTITY_PRESENT)
+		return -1;
+
+	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
+	if (rc)
+		return rc;
+
+	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
+	if (rc) {
+		rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
+		return rc;
+	}
+
+	return 0;
+}
+
+
Index: powerpc/arch/powerpc/platforms/pseries/Makefile
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pseries/Makefile	2009-11-20 17:53:54.000000000 -0600
+++ powerpc/arch/powerpc/platforms/pseries/Makefile	2009-11-20 17:55:52.000000000 -0600
@@ -8,7 +8,7 @@
 
 obj-y			:= lpar.o hvCall.o nvram.o reconfig.o \
 			   setup.o iommu.o ras.o rtasd.o \
-			   firmware.o power.o
+			   firmware.o power.o dlpar.o
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_XICS)	+= xics.o
 obj-$(CONFIG_SCANLOG)	+= scanlog.o
Index: powerpc/arch/powerpc/include/asm/pSeries_reconfig.h
===================================================================
--- powerpc.orig/arch/powerpc/include/asm/pSeries_reconfig.h	2009-11-20 17:53:54.000000000 -0600
+++ powerpc/arch/powerpc/include/asm/pSeries_reconfig.h	2009-11-20 17:55:52.000000000 -0600
@@ -17,6 +17,7 @@
 #ifdef CONFIG_PPC_PSERIES
 extern int pSeries_reconfig_notifier_register(struct notifier_block *);
 extern void pSeries_reconfig_notifier_unregister(struct notifier_block *);
+extern struct blocking_notifier_head pSeries_reconfig_chain;
 #else /* !CONFIG_PPC_PSERIES */
 static inline int pSeries_reconfig_notifier_register(struct notifier_block *nb)
 {
Index: powerpc/arch/powerpc/platforms/pseries/reconfig.c
===================================================================
--- powerpc.orig/arch/powerpc/platforms/pseries/reconfig.c	2009-11-20 17:53:54.000000000 -0600
+++ powerpc/arch/powerpc/platforms/pseries/reconfig.c	2009-11-20 17:55:52.000000000 -0600
@@ -96,7 +96,7 @@
 	return parent;
 }
 
-static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
+BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
 
 int pSeries_reconfig_notifier_register(struct notifier_block *nb)
 {

^ permalink raw reply

* [PATCH 0/3] Kernel handling of Dynamic Logical Partitioning
From: Nathan Fontenot @ 2009-11-25  1:41 UTC (permalink / raw)
  To: linuxppc-dev, lkml

The Dynamic Logical Partitioning (DLPAR) capabilities of the powerpc pseries
platform allows for the addition and removal of resources (i.e. cpus,
memory, 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.

The following set of patches implements the needed infrastructure to have the
kernel handle the DLPAR addition and removal of cpus (other DLPAR'able items 
to follow in future patches).  The framework for this is to create a set of 
probe/release sysfs files that will facilitate arch-specific call-outs to 
handle addition and removal of cpus to the system.

-Nathan Fontenot

1/3 - powerpc/pseries kernel DLPAR infrastructure
2/3 - Create probe/release sysfs files and the powerpc handlers
3/3 - powerpc/pseries CPU DLPAR handling

 arch/powerpc/Kconfig                        |    4 
 arch/powerpc/include/asm/machdep.h          |    5 
 arch/powerpc/include/asm/pSeries_reconfig.h |    1 
 arch/powerpc/kernel/sysfs.c                 |   19 +
 arch/powerpc/platforms/pseries/Makefile     |    2 
 arch/powerpc/platforms/pseries/dlpar.c      |  412 ++++++++++++++++++++++++++++
 arch/powerpc/platforms/pseries/reconfig.c   |    2 
 drivers/base/cpu.c                          |   32 ++
 include/linux/cpu.h                         |    4 
 9 files changed, 479 insertions(+), 2 deletions(-)

^ permalink raw reply

* Re: [PATCH v2 11/11] of: unify phandle name in struct device_node
From: Grant Likely @ 2009-11-25  1:33 UTC (permalink / raw)
  To: Segher Boessenkool
  Cc: sfr, monstr, microblaze-uclinux, devicetree-discuss, sparclinux,
	linuxppc-dev, davem
In-Reply-To: <AE8D50AE-03EE-45FC-80EF-376F0836B5CB@kernel.crashing.org>

On Tue, Nov 24, 2009 at 6:30 PM, Segher Boessenkool
<segher@kernel.crashing.org> wrote:
>> In struct device_node, the phandle is named 'linux_phandle' for PowerPC
>> and MicroBlaze, and 'node' for SPARC. =A0There is no good reason for the
>> difference, it is just an artifact of the code diverging over a couple
>> of years. =A0This patch renames both to simply .phandle.
>>
>> Note: the .node also existed in PowerPC/MicroBlaze, but the only user
>> seems to be arch/powerpc/platforms/powermac/pfunc_core.c. =A0It doesn't
>> look like the assignment between .linux_phandle and .node is
>> significantly different enough to warrant the separate code paths
>> unless ibm,phandle properties actually appear in Apple device trees.
>>
>> I think it is safe to eliminate the old .node property and use
>> linux_phandle everywhere.
>
> =A0^^^^^^^^^^^^ =A0You forgot to update one :-)

Heeheehee.  oops.

g.

--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* Re: [PATCH v2 11/11] of: unify phandle name in struct device_node
From: Segher Boessenkool @ 2009-11-25  1:30 UTC (permalink / raw)
  To: Grant Likely
  Cc: sfr, monstr, microblaze-uclinux, devicetree-discuss, sparclinux,
	linuxppc-dev, davem
In-Reply-To: <20091124215908.559.23998.stgit@angua>

> In struct device_node, the phandle is named 'linux_phandle' for  
> PowerPC
> and MicroBlaze, and 'node' for SPARC.  There is no good reason for the
> difference, it is just an artifact of the code diverging over a couple
> of years.  This patch renames both to simply .phandle.
>
> Note: the .node also existed in PowerPC/MicroBlaze, but the only user
> seems to be arch/powerpc/platforms/powermac/pfunc_core.c.  It doesn't
> look like the assignment between .linux_phandle and .node is
> significantly different enough to warrant the separate code paths
> unless ibm,phandle properties actually appear in Apple device trees.
>
> I think it is safe to eliminate the old .node property and use
> linux_phandle everywhere.

   ^^^^^^^^^^^^  You forgot to update one :-)


>
Segher

^ permalink raw reply

* Re: [RFC PATCH 14/19] powerpc: allow ioremap within reserved fake ram  regions
From: Michael Ellerman @ 2009-11-25  0:38 UTC (permalink / raw)
  To: Albert Herranz; +Cc: linuxppc-dev
In-Reply-To: <4B0C1337.6070309@yahoo.es>

[-- Attachment #1: Type: text/plain, Size: 1578 bytes --]

On Tue, 2009-11-24 at 18:09 +0100, Albert Herranz wrote:
> Michael Ellerman wrote:
> >> Would it be acceptable to create a global var __allow_ioremap_normal_ram that by default would have a value of 0 and would be set _only_ for those platforms needing it?
> >>
> >> The other solutions I see is:
> >> - add support for discontiguous memory to powerpc 32-bits (which is not something that I can look into now)
> >> - don't use the precious second 64MB area (which is a waste)
> > 
> > - Implement your own ppc_md.ioremap(), see iseries & cell for example.
> > 
> > Currently that's only called on 64-bit, but you could change that.
> > 
> > If I'm reading your patch right, you should be able to do that check in
> > your platform's ioremap() and then call the generic implementation to do
> > the actual work.
> > 
> > cheers
> 
> I could use ppc_md.ioremap to duplicate ioremap except for the ioremap ram check.
> But calling the stock ioremap without modifying it is not possible
> because it checks and bails out when ioremapping a region marked as
> ram (even if it's not real ram and it's memreserved).

OK, that wasn't clear in your patch. It looks like you're adding a check
(!__map_without_bats) - but whatever.

> Is the list of memreserved areas preserved once the kernel is running?
> If it is preserved another option would be to unban ioremapping ram if memreserved.

Yeah it is, so that might work, though it's still a bit of a hack :)
But if that's just a stop-gap until sparse mem gets going on 32-bit
maybe that's OK.

cheers

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: spi_mpc8xxx.c: chip select polarity problem
From: Grant Likely @ 2009-11-25  0:33 UTC (permalink / raw)
  To: Torsten Fleischer; +Cc: linuxppc-dev
In-Reply-To: <200911211708.47253.to-fleischer@t-online.de>

Thanks.  However, there needs to be a proper description of what this
patch does to go in the commit header.  Can you please write one?

Thanks,
g.

On Sat, Nov 21, 2009 at 9:08 AM, Torsten Fleischer
<to-fleischer@t-online.de> wrote:
> On Sat, Nov 21, 2009 at 09:45:50 Grant Likely wrote:
> [...]
>> Hey Torsten, =A0do you have an updated version of this change to address
>> the comments? =A0I'm collecting the last few things for some linux-next
>> exposure now.
>
> Hey Grant,
>
> here is the updated version of the patch containing the recommended
> changes.
>
> Best Regards
> Torsten
>
>
> Signed-off-by: Torsten Fleischer <to-fleischer@t-online.de>
> ---
>
> diff -u -r -N linux-2.6.31.6_orig//drivers/spi/spi_mpc8xxx.c linux-2.6.31=
.6/drivers/spi/spi_mpc8xxx.c
> --- linux-2.6.31.6_orig//drivers/spi/spi_mpc8xxx.c =A0 =A0 =A02009-11-10 =
01:32:31.000000000 +0100
> +++ linux-2.6.31.6/drivers/spi/spi_mpc8xxx.c =A0 =A02009-11-19 08:15:33.0=
00000000 +0100
> @@ -114,6 +114,7 @@
> =A0 =A0 =A0 =A0u32 rx_shift; =A0 =A0 =A0 =A0 =A0 /* RX data reg shift whe=
n in qe mode */
> =A0 =A0 =A0 =A0u32 tx_shift; =A0 =A0 =A0 =A0 =A0 /* TX data reg shift whe=
n in qe mode */
> =A0 =A0 =A0 =A0u32 hw_mode; =A0 =A0 =A0 =A0 =A0 =A0/* Holds HW mode regis=
ter settings */
> + =A0 =A0 =A0 int initialized;
> =A0};
>
> =A0static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
> @@ -503,15 +504,52 @@
>
> =A0 =A0 =A0 =A0return ret;
> =A0}
> +
> +
> +struct mpc8xxx_spi_probe_info {
> + =A0 =A0 =A0 struct fsl_spi_platform_data pdata;
> + =A0 =A0 =A0 int *gpios;
> + =A0 =A0 =A0 bool *alow_flags;
> +};
> +
> +static struct mpc8xxx_spi_probe_info *
> +to_of_pinfo(struct fsl_spi_platform_data *pdata)
> +{
> + =A0 =A0 =A0 return container_of(pdata, struct mpc8xxx_spi_probe_info, p=
data);
> +}
> +
> +static int mpc8xxx_spi_cs_init(struct spi_device *spi)
> +{
> + =A0 =A0 =A0 struct device *dev =3D spi->dev.parent;
> + =A0 =A0 =A0 struct mpc8xxx_spi_probe_info *pinfo =3D to_of_pinfo(dev->p=
latform_data);
> + =A0 =A0 =A0 u16 cs =3D spi->chip_select;
> + =A0 =A0 =A0 int gpio =3D pinfo->gpios[cs];
> + =A0 =A0 =A0 bool on =3D pinfo->alow_flags[cs] ^ !(spi->mode & SPI_CS_HI=
GH);
> +
> + =A0 =A0 =A0 return gpio_direction_output(gpio, on);
> +}
> +
> =A0static int mpc8xxx_spi_transfer(struct spi_device *spi,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct spi=
_message *m)
> =A0{
> =A0 =A0 =A0 =A0struct mpc8xxx_spi *mpc8xxx_spi =3D spi_master_get_devdata=
(spi->master);
> + =A0 =A0 =A0 struct spi_mpc8xxx_cs *cs =3D spi->controller_state;
> =A0 =A0 =A0 =A0unsigned long flags;
>
> =A0 =A0 =A0 =A0m->actual_length =3D 0;
> =A0 =A0 =A0 =A0m->status =3D -EINPROGRESS;
>
> + =A0 =A0 =A0 if (cs && !cs->initialized) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 int ret;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D mpc8xxx_spi_cs_init(spi);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_dbg(&spi->dev, "cs_init=
 failed: %d\n", ret);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 cs->initialized =3D 1;
> + =A0 =A0 =A0 }
> +
> =A0 =A0 =A0 =A0spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
> =A0 =A0 =A0 =A0list_add_tail(&m->queue, &mpc8xxx_spi->queue);
> =A0 =A0 =A0 =A0queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
> @@ -648,18 +686,6 @@
> =A0 =A0 =A0 =A0return 0;
> =A0}
>
> -struct mpc8xxx_spi_probe_info {
> - =A0 =A0 =A0 struct fsl_spi_platform_data pdata;
> - =A0 =A0 =A0 int *gpios;
> - =A0 =A0 =A0 bool *alow_flags;
> -};
> -
> -static struct mpc8xxx_spi_probe_info *
> -to_of_pinfo(struct fsl_spi_platform_data *pdata)
> -{
> - =A0 =A0 =A0 return container_of(pdata, struct mpc8xxx_spi_probe_info, p=
data);
> -}
> -
> =A0static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
> =A0{
> =A0 =A0 =A0 =A0struct device *dev =3D spi->dev.parent;
> @@ -720,14 +746,6 @@
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pinfo->gpios[i] =3D gpio;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pinfo->alow_flags[i] =3D flags & OF_GPIO_A=
CTIVE_LOW;
> -
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D gpio_direction_output(pinfo->gpios[=
i],
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 pinfo->alow_flags[i]);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (ret) {
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "can't set out=
put direction for gpio "
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "#%d: %d\n"=
, i, ret);
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto err_loop;
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> =A0 =A0 =A0 =A0}
>
> =A0 =A0 =A0 =A0pdata->max_chipselect =3D ngpios;
>



--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* Re: [RFC PATCH 03/19] powerpc: gamecube: bootwrapper bits
From: Albert Herranz @ 2009-11-24 23:45 UTC (permalink / raw)
  To: Segher Boessenkool; +Cc: linuxppc-dev
In-Reply-To: <CC1D5EB1-27C2-47A1-96D8-D007D86C4881@kernel.crashing.org>

Segher Boessenkool wrote:
>> So what's your proposal then? Placing it within a fake func?
>> That asm snippet is the entry point. I took as an example how
>> prpmc2800.c deals with that, providing an own version of the (weak)
>> _zImage_start.
> 
> Use an assembler source file.  You'll get much nicer syntax as well
> (none of that \n stuff).
> 

I found it cleaner to embed the entry point code in the .c file and avoid touching the wrapper script.
But I'm fine with that if it is the way to go (and I already finally touched the wrapper to increase the link address...).

>>> WIMG=0000, are you sure?  Not M=1?
>>
>> To be honest, I don't recall the details now.
>> But it was tested in the very early days, the result was not the
>> expected one and, in the end, manual cache coherency management was
>> still needed.
> 
> Sure, the memory controllers don't do coherency.  I'm slightly worried
> about two things:
> 1) Will the generic code use M=0 as well?  Is it a problem if it doesn't?
> 2) Do lwarx. etc. work in M=0?
> 
> And a question: does M=0 actually give better performance (lower bus
> utilisation, and maybe saves a few cycles)?
> 

I think that the generic code uses M=0 _except_ for SMP and some platforms (see comment in cputable.h).
And yes, the generic code works with these processors :)

M=0 should have a lower bus utilization, yes.
Also M=0 is a requirement if you use some Gekko/Broadway features like the locked (half-)cache.

Thanks,
Albert

^ permalink raw reply

* Re: [RFC PATCH 02/19] powerpc: gamecube: device tree
From: Segher Boessenkool @ 2009-11-24 22:53 UTC (permalink / raw)
  To: Grant Likely; +Cc: Albert Herranz, linuxppc-dev
In-Reply-To: <fa686aa40911231219k3a52b9fdn66b8cffb735e5c2f@mail.gmail.com>

>> The soc node here tries to represent the big multi-function chip  
>> that integrates most of the devices of the video game consoles  
>> ("Flipper" on the Nintendo GameCube and "Hollywood" on the Wii).
>
> Right.  Much like many other SoCs.

It isn't a SoC, it's really just a memory bridge / I/O bridge
like e.g. MPC10x.

All the device addresses are fixed on the chip, there is no IMMR
or similar.

You can either group all devices under a "flipper" node, or just
put the devices directly in the root node, and have a separate
node for the generic control regs.  Both are a good description
of both the physical and logical structure, so it just comes down
to taste.

Good drivers can handle either structure btw, they should normally
only look at "compatible" to find their devices, so it doesn't
matter much anyway.


Segher

^ permalink raw reply

* Re: [RFC PATCH 06/19] powerpc: gamecube/wii: introduce GAMECUBE_COMMON
From: Arnd Bergmann @ 2009-11-24 22:33 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Albert Herranz
In-Reply-To: <7129FD29-82CE-4731-90F2-0EA6775EF861@kernel.crashing.org>

On Tuesday 24 November 2009, Segher Boessenkool wrote:
> > This option should be used only for options applicable to both the  
> > GAMECUBE and WII, i.e. basically those options in the WII which  
> > where "inherited" from the GAMECUBE to make it retro-compatible.
> >
> > If GAMECUBE_COMMON is unacceptable, maybe GAMECUBE_LEGACY or  
> > GAMECUBE_COMPAT?
> 
> Maybe it's best to write GAMECUBE || WII in the places that use it,  
> then?

Or just use CONFIG_NINTENDO? If we ever want to port to a future
Nintendo system that is not compatible, we can still change it.

	Arnd <><

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox