lkml.org 
[lkml]   [2009]   [Nov]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH v2 1/3] Kernel DLPAR Infrastructure
    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)
    {

    \
     
     \ /
      Last update: 2009-11-25 08:13    [W:0.039 / U:30.260 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site