linux/arch/powerpc/platforms/pseries/dlpar.c
<<
>>
Prefs
   1/*
   2 * Support for dynamic reconfiguration for PCI, Memory, and CPU
   3 * Hotplug and Dynamic Logical Partitioning on RPA platforms.
   4 *
   5 * Copyright (C) 2009 Nathan Fontenot
   6 * Copyright (C) 2009 IBM Corporation
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License version
  10 * 2 as published by the Free Software Foundation.
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/kref.h>
  15#include <linux/notifier.h>
  16#include <linux/proc_fs.h>
  17#include <linux/spinlock.h>
  18#include <linux/cpu.h>
  19#include <linux/slab.h>
  20#include "offline_states.h"
  21
  22#include <asm/prom.h>
  23#include <asm/machdep.h>
  24#include <asm/uaccess.h>
  25#include <asm/rtas.h>
  26#include <asm/pSeries_reconfig.h>
  27
  28struct cc_workarea {
  29        u32     drc_index;
  30        u32     zero;
  31        u32     name_offset;
  32        u32     prop_length;
  33        u32     prop_offset;
  34};
  35
  36void dlpar_free_cc_property(struct property *prop)
  37{
  38        kfree(prop->name);
  39        kfree(prop->value);
  40        kfree(prop);
  41}
  42
  43static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
  44{
  45        struct property *prop;
  46        char *name;
  47        char *value;
  48
  49        prop = kzalloc(sizeof(*prop), GFP_KERNEL);
  50        if (!prop)
  51                return NULL;
  52
  53        name = (char *)ccwa + ccwa->name_offset;
  54        prop->name = kstrdup(name, GFP_KERNEL);
  55
  56        prop->length = ccwa->prop_length;
  57        value = (char *)ccwa + ccwa->prop_offset;
  58        prop->value = kmemdup(value, prop->length, GFP_KERNEL);
  59        if (!prop->value) {
  60                dlpar_free_cc_property(prop);
  61                return NULL;
  62        }
  63
  64        return prop;
  65}
  66
  67static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa)
  68{
  69        struct device_node *dn;
  70        char *name;
  71
  72        dn = kzalloc(sizeof(*dn), GFP_KERNEL);
  73        if (!dn)
  74                return NULL;
  75
  76        /* The configure connector reported name does not contain a
  77         * preceeding '/', so we allocate a buffer large enough to
  78         * prepend this to the full_name.
  79         */
  80        name = (char *)ccwa + ccwa->name_offset;
  81        dn->full_name = kasprintf(GFP_KERNEL, "/%s", name);
  82        if (!dn->full_name) {
  83                kfree(dn);
  84                return NULL;
  85        }
  86
  87        return dn;
  88}
  89
  90static void dlpar_free_one_cc_node(struct device_node *dn)
  91{
  92        struct property *prop;
  93
  94        while (dn->properties) {
  95                prop = dn->properties;
  96                dn->properties = prop->next;
  97                dlpar_free_cc_property(prop);
  98        }
  99
 100        kfree(dn->full_name);
 101        kfree(dn);
 102}
 103
 104void dlpar_free_cc_nodes(struct device_node *dn)
 105{
 106        if (dn->child)
 107                dlpar_free_cc_nodes(dn->child);
 108
 109        if (dn->sibling)
 110                dlpar_free_cc_nodes(dn->sibling);
 111
 112        dlpar_free_one_cc_node(dn);
 113}
 114
 115#define NEXT_SIBLING    1
 116#define NEXT_CHILD      2
 117#define NEXT_PROPERTY   3
 118#define PREV_PARENT     4
 119#define MORE_MEMORY     5
 120#define CALL_AGAIN      -2
 121#define ERR_CFG_USE     -9003
 122
 123struct device_node *dlpar_configure_connector(u32 drc_index)
 124{
 125        struct device_node *dn;
 126        struct device_node *first_dn = NULL;
 127        struct device_node *last_dn = NULL;
 128        struct property *property;
 129        struct property *last_property = NULL;
 130        struct cc_workarea *ccwa;
 131        char *data_buf;
 132        int cc_token;
 133        int rc = -1;
 134
 135        cc_token = rtas_token("ibm,configure-connector");
 136        if (cc_token == RTAS_UNKNOWN_SERVICE)
 137                return NULL;
 138
 139        data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
 140        if (!data_buf)
 141                return NULL;
 142
 143        ccwa = (struct cc_workarea *)&data_buf[0];
 144        ccwa->drc_index = drc_index;
 145        ccwa->zero = 0;
 146
 147        do {
 148                /* Since we release the rtas_data_buf lock between configure
 149                 * connector calls we want to re-populate the rtas_data_buffer
 150                 * with the contents of the previous call.
 151                 */
 152                spin_lock(&rtas_data_buf_lock);
 153
 154                memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
 155                rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
 156                memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
 157
 158                spin_unlock(&rtas_data_buf_lock);
 159
 160                switch (rc) {
 161                case NEXT_SIBLING:
 162                        dn = dlpar_parse_cc_node(ccwa);
 163                        if (!dn)
 164                                goto cc_error;
 165
 166                        dn->parent = last_dn->parent;
 167                        last_dn->sibling = dn;
 168                        last_dn = dn;
 169                        break;
 170
 171                case NEXT_CHILD:
 172                        dn = dlpar_parse_cc_node(ccwa);
 173                        if (!dn)
 174                                goto cc_error;
 175
 176                        if (!first_dn)
 177                                first_dn = dn;
 178                        else {
 179                                dn->parent = last_dn;
 180                                if (last_dn)
 181                                        last_dn->child = dn;
 182                        }
 183
 184                        last_dn = dn;
 185                        break;
 186
 187                case NEXT_PROPERTY:
 188                        property = dlpar_parse_cc_property(ccwa);
 189                        if (!property)
 190                                goto cc_error;
 191
 192                        if (!last_dn->properties)
 193                                last_dn->properties = property;
 194                        else
 195                                last_property->next = property;
 196
 197                        last_property = property;
 198                        break;
 199
 200                case PREV_PARENT:
 201                        last_dn = last_dn->parent;
 202                        break;
 203
 204                case CALL_AGAIN:
 205                        break;
 206
 207                case MORE_MEMORY:
 208                case ERR_CFG_USE:
 209                default:
 210                        printk(KERN_ERR "Unexpected Error (%d) "
 211                               "returned from configure-connector\n", rc);
 212                        goto cc_error;
 213                }
 214        } while (rc);
 215
 216cc_error:
 217        kfree(data_buf);
 218
 219        if (rc) {
 220                if (first_dn)
 221                        dlpar_free_cc_nodes(first_dn);
 222
 223                return NULL;
 224        }
 225
 226        return first_dn;
 227}
 228
 229static struct device_node *derive_parent(const char *path)
 230{
 231        struct device_node *parent;
 232        char *last_slash;
 233
 234        last_slash = strrchr(path, '/');
 235        if (last_slash == path) {
 236                parent = of_find_node_by_path("/");
 237        } else {
 238                char *parent_path;
 239                int parent_path_len = last_slash - path + 1;
 240                parent_path = kmalloc(parent_path_len, GFP_KERNEL);
 241                if (!parent_path)
 242                        return NULL;
 243
 244                strlcpy(parent_path, path, parent_path_len);
 245                parent = of_find_node_by_path(parent_path);
 246                kfree(parent_path);
 247        }
 248
 249        return parent;
 250}
 251
 252int dlpar_attach_node(struct device_node *dn)
 253{
 254#ifdef CONFIG_PROC_DEVICETREE
 255        struct proc_dir_entry *ent;
 256#endif
 257        int rc;
 258
 259        of_node_set_flag(dn, OF_DYNAMIC);
 260        kref_init(&dn->kref);
 261        dn->parent = derive_parent(dn->full_name);
 262        if (!dn->parent)
 263                return -ENOMEM;
 264
 265        rc = blocking_notifier_call_chain(&pSeries_reconfig_chain,
 266                                          PSERIES_RECONFIG_ADD, dn);
 267        if (rc == NOTIFY_BAD) {
 268                printk(KERN_ERR "Failed to add device node %s\n",
 269                       dn->full_name);
 270                return -ENOMEM; /* For now, safe to assume kmalloc failure */
 271        }
 272
 273        of_attach_node(dn);
 274
 275#ifdef CONFIG_PROC_DEVICETREE
 276        ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde);
 277        if (ent)
 278                proc_device_tree_add_node(dn, ent);
 279#endif
 280
 281        of_node_put(dn->parent);
 282        return 0;
 283}
 284
 285int dlpar_detach_node(struct device_node *dn)
 286{
 287#ifdef CONFIG_PROC_DEVICETREE
 288        struct device_node *parent = dn->parent;
 289        struct property *prop = dn->properties;
 290
 291        while (prop) {
 292                remove_proc_entry(prop->name, dn->pde);
 293                prop = prop->next;
 294        }
 295
 296        if (dn->pde)
 297                remove_proc_entry(dn->pde->name, parent->pde);
 298#endif
 299
 300        blocking_notifier_call_chain(&pSeries_reconfig_chain,
 301                            PSERIES_RECONFIG_REMOVE, dn);
 302        of_detach_node(dn);
 303        of_node_put(dn); /* Must decrement the refcount */
 304
 305        return 0;
 306}
 307
 308#define DR_ENTITY_SENSE         9003
 309#define DR_ENTITY_PRESENT       1
 310#define DR_ENTITY_UNUSABLE      2
 311#define ALLOCATION_STATE        9003
 312#define ALLOC_UNUSABLE          0
 313#define ALLOC_USABLE            1
 314#define ISOLATION_STATE         9001
 315#define ISOLATE                 0
 316#define UNISOLATE               1
 317
 318int dlpar_acquire_drc(u32 drc_index)
 319{
 320        int dr_status, rc;
 321
 322        rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
 323                       DR_ENTITY_SENSE, drc_index);
 324        if (rc || dr_status != DR_ENTITY_UNUSABLE)
 325                return -1;
 326
 327        rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
 328        if (rc)
 329                return rc;
 330
 331        rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
 332        if (rc) {
 333                rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
 334                return rc;
 335        }
 336
 337        return 0;
 338}
 339
 340int dlpar_release_drc(u32 drc_index)
 341{
 342        int dr_status, rc;
 343
 344        rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
 345                       DR_ENTITY_SENSE, drc_index);
 346        if (rc || dr_status != DR_ENTITY_PRESENT)
 347                return -1;
 348
 349        rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
 350        if (rc)
 351                return rc;
 352
 353        rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
 354        if (rc) {
 355                rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
 356                return rc;
 357        }
 358
 359        return 0;
 360}
 361
 362#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
 363
 364static int dlpar_online_cpu(struct device_node *dn)
 365{
 366        int rc = 0;
 367        unsigned int cpu;
 368        int len, nthreads, i;
 369        const u32 *intserv;
 370
 371        intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
 372        if (!intserv)
 373                return -EINVAL;
 374
 375        nthreads = len / sizeof(u32);
 376
 377        cpu_maps_update_begin();
 378        for (i = 0; i < nthreads; i++) {
 379                for_each_present_cpu(cpu) {
 380                        if (get_hard_smp_processor_id(cpu) != intserv[i])
 381                                continue;
 382                        BUG_ON(get_cpu_current_state(cpu)
 383                                        != CPU_STATE_OFFLINE);
 384                        cpu_maps_update_done();
 385                        rc = cpu_up(cpu);
 386                        if (rc)
 387                                goto out;
 388                        cpu_maps_update_begin();
 389
 390                        break;
 391                }
 392                if (cpu == num_possible_cpus())
 393                        printk(KERN_WARNING "Could not find cpu to online "
 394                               "with physical id 0x%x\n", intserv[i]);
 395        }
 396        cpu_maps_update_done();
 397
 398out:
 399        return rc;
 400
 401}
 402
 403static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
 404{
 405        struct device_node *dn;
 406        unsigned long drc_index;
 407        char *cpu_name;
 408        int rc;
 409
 410        cpu_hotplug_driver_lock();
 411        rc = strict_strtoul(buf, 0, &drc_index);
 412        if (rc) {
 413                rc = -EINVAL;
 414                goto out;
 415        }
 416
 417        dn = dlpar_configure_connector(drc_index);
 418        if (!dn) {
 419                rc = -EINVAL;
 420                goto out;
 421        }
 422
 423        /* configure-connector reports cpus as living in the base
 424         * directory of the device tree.  CPUs actually live in the
 425         * cpus directory so we need to fixup the full_name.
 426         */
 427        cpu_name = kasprintf(GFP_KERNEL, "/cpus%s", dn->full_name);
 428        if (!cpu_name) {
 429                dlpar_free_cc_nodes(dn);
 430                rc = -ENOMEM;
 431                goto out;
 432        }
 433
 434        kfree(dn->full_name);
 435        dn->full_name = cpu_name;
 436
 437        rc = dlpar_acquire_drc(drc_index);
 438        if (rc) {
 439                dlpar_free_cc_nodes(dn);
 440                rc = -EINVAL;
 441                goto out;
 442        }
 443
 444        rc = dlpar_attach_node(dn);
 445        if (rc) {
 446                dlpar_release_drc(drc_index);
 447                dlpar_free_cc_nodes(dn);
 448                goto out;
 449        }
 450
 451        rc = dlpar_online_cpu(dn);
 452out:
 453        cpu_hotplug_driver_unlock();
 454
 455        return rc ? rc : count;
 456}
 457
 458static int dlpar_offline_cpu(struct device_node *dn)
 459{
 460        int rc = 0;
 461        unsigned int cpu;
 462        int len, nthreads, i;
 463        const u32 *intserv;
 464
 465        intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
 466        if (!intserv)
 467                return -EINVAL;
 468
 469        nthreads = len / sizeof(u32);
 470
 471        cpu_maps_update_begin();
 472        for (i = 0; i < nthreads; i++) {
 473                for_each_present_cpu(cpu) {
 474                        if (get_hard_smp_processor_id(cpu) != intserv[i])
 475                                continue;
 476
 477                        if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE)
 478                                break;
 479
 480                        if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) {
 481                                set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
 482                                cpu_maps_update_done();
 483                                rc = cpu_down(cpu);
 484                                if (rc)
 485                                        goto out;
 486                                cpu_maps_update_begin();
 487                                break;
 488
 489                        }
 490
 491                        /*
 492                         * The cpu is in CPU_STATE_INACTIVE.
 493                         * Upgrade it's state to CPU_STATE_OFFLINE.
 494                         */
 495                        set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
 496                        BUG_ON(plpar_hcall_norets(H_PROD, intserv[i])
 497                                                                != H_SUCCESS);
 498                        __cpu_die(cpu);
 499                        break;
 500                }
 501                if (cpu == num_possible_cpus())
 502                        printk(KERN_WARNING "Could not find cpu to offline "
 503                               "with physical id 0x%x\n", intserv[i]);
 504        }
 505        cpu_maps_update_done();
 506
 507out:
 508        return rc;
 509
 510}
 511
 512static ssize_t dlpar_cpu_release(const char *buf, size_t count)
 513{
 514        struct device_node *dn;
 515        const u32 *drc_index;
 516        int rc;
 517
 518        dn = of_find_node_by_path(buf);
 519        if (!dn)
 520                return -EINVAL;
 521
 522        drc_index = of_get_property(dn, "ibm,my-drc-index", NULL);
 523        if (!drc_index) {
 524                of_node_put(dn);
 525                return -EINVAL;
 526        }
 527
 528        cpu_hotplug_driver_lock();
 529        rc = dlpar_offline_cpu(dn);
 530        if (rc) {
 531                of_node_put(dn);
 532                rc = -EINVAL;
 533                goto out;
 534        }
 535
 536        rc = dlpar_release_drc(*drc_index);
 537        if (rc) {
 538                of_node_put(dn);
 539                goto out;
 540        }
 541
 542        rc = dlpar_detach_node(dn);
 543        if (rc) {
 544                dlpar_acquire_drc(*drc_index);
 545                goto out;
 546        }
 547
 548        of_node_put(dn);
 549out:
 550        cpu_hotplug_driver_unlock();
 551        return rc ? rc : count;
 552}
 553
 554static int __init pseries_dlpar_init(void)
 555{
 556        ppc_md.cpu_probe = dlpar_cpu_probe;
 557        ppc_md.cpu_release = dlpar_cpu_release;
 558
 559        return 0;
 560}
 561machine_device_initcall(pseries, pseries_dlpar_init);
 562
 563#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
 564