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         * preceding '/', 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 COMPLETE        0
 116#define NEXT_SIBLING    1
 117#define NEXT_CHILD      2
 118#define NEXT_PROPERTY   3
 119#define PREV_PARENT     4
 120#define MORE_MEMORY     5
 121#define CALL_AGAIN      -2
 122#define ERR_CFG_USE     -9003
 123
 124struct device_node *dlpar_configure_connector(u32 drc_index)
 125{
 126        struct device_node *dn;
 127        struct device_node *first_dn = NULL;
 128        struct device_node *last_dn = NULL;
 129        struct property *property;
 130        struct property *last_property = NULL;
 131        struct cc_workarea *ccwa;
 132        char *data_buf;
 133        int cc_token;
 134        int rc = -1;
 135
 136        cc_token = rtas_token("ibm,configure-connector");
 137        if (cc_token == RTAS_UNKNOWN_SERVICE)
 138                return NULL;
 139
 140        data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
 141        if (!data_buf)
 142                return NULL;
 143
 144        ccwa = (struct cc_workarea *)&data_buf[0];
 145        ccwa->drc_index = drc_index;
 146        ccwa->zero = 0;
 147
 148        do {
 149                /* Since we release the rtas_data_buf lock between configure
 150                 * connector calls we want to re-populate the rtas_data_buffer
 151                 * with the contents of the previous call.
 152                 */
 153                spin_lock(&rtas_data_buf_lock);
 154
 155                memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
 156                rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
 157                memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
 158
 159                spin_unlock(&rtas_data_buf_lock);
 160
 161                switch (rc) {
 162                case COMPLETE:
 163                        break;
 164
 165                case NEXT_SIBLING:
 166                        dn = dlpar_parse_cc_node(ccwa);
 167                        if (!dn)
 168                                goto cc_error;
 169
 170                        dn->parent = last_dn->parent;
 171                        last_dn->sibling = dn;
 172                        last_dn = dn;
 173                        break;
 174
 175                case NEXT_CHILD:
 176                        dn = dlpar_parse_cc_node(ccwa);
 177                        if (!dn)
 178                                goto cc_error;
 179
 180                        if (!first_dn)
 181                                first_dn = dn;
 182                        else {
 183                                dn->parent = last_dn;
 184                                if (last_dn)
 185                                        last_dn->child = dn;
 186                        }
 187
 188                        last_dn = dn;
 189                        break;
 190
 191                case NEXT_PROPERTY:
 192                        property = dlpar_parse_cc_property(ccwa);
 193                        if (!property)
 194                                goto cc_error;
 195
 196                        if (!last_dn->properties)
 197                                last_dn->properties = property;
 198                        else
 199                                last_property->next = property;
 200
 201                        last_property = property;
 202                        break;
 203
 204                case PREV_PARENT:
 205                        last_dn = last_dn->parent;
 206                        break;
 207
 208                case CALL_AGAIN:
 209                        break;
 210
 211                case MORE_MEMORY:
 212                case ERR_CFG_USE:
 213                default:
 214                        printk(KERN_ERR "Unexpected Error (%d) "
 215                               "returned from configure-connector\n", rc);
 216                        goto cc_error;
 217                }
 218        } while (rc);
 219
 220cc_error:
 221        kfree(data_buf);
 222
 223        if (rc) {
 224                if (first_dn)
 225                        dlpar_free_cc_nodes(first_dn);
 226
 227                return NULL;
 228        }
 229
 230        return first_dn;
 231}
 232
 233static struct device_node *derive_parent(const char *path)
 234{
 235        struct device_node *parent;
 236        char *last_slash;
 237
 238        last_slash = strrchr(path, '/');
 239        if (last_slash == path) {
 240                parent = of_find_node_by_path("/");
 241        } else {
 242                char *parent_path;
 243                int parent_path_len = last_slash - path + 1;
 244                parent_path = kmalloc(parent_path_len, GFP_KERNEL);
 245                if (!parent_path)
 246                        return NULL;
 247
 248                strlcpy(parent_path, path, parent_path_len);
 249                parent = of_find_node_by_path(parent_path);
 250                kfree(parent_path);
 251        }
 252
 253        return parent;
 254}
 255
 256int dlpar_attach_node(struct device_node *dn)
 257{
 258#ifdef CONFIG_PROC_DEVICETREE
 259        struct proc_dir_entry *ent;
 260#endif
 261        int rc;
 262
 263        of_node_set_flag(dn, OF_DYNAMIC);
 264        kref_init(&dn->kref);
 265        dn->parent = derive_parent(dn->full_name);
 266        if (!dn->parent)
 267                return -ENOMEM;
 268
 269        rc = pSeries_reconfig_notify(PSERIES_RECONFIG_ADD, dn);
 270        if (rc) {
 271                printk(KERN_ERR "Failed to add device node %s\n",
 272                       dn->full_name);
 273                return rc;
 274        }
 275
 276        of_attach_node(dn);
 277
 278#ifdef CONFIG_PROC_DEVICETREE
 279        ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde);
 280        if (ent)
 281                proc_device_tree_add_node(dn, ent);
 282#endif
 283
 284        of_node_put(dn->parent);
 285        return 0;
 286}
 287
 288int dlpar_detach_node(struct device_node *dn)
 289{
 290#ifdef CONFIG_PROC_DEVICETREE
 291        struct device_node *parent = dn->parent;
 292        struct property *prop = dn->properties;
 293
 294        while (prop) {
 295                remove_proc_entry(prop->name, dn->pde);
 296                prop = prop->next;
 297        }
 298
 299        if (dn->pde)
 300                remove_proc_entry(dn->pde->name, parent->pde);
 301#endif
 302
 303        pSeries_reconfig_notify(PSERIES_RECONFIG_REMOVE, dn);
 304        of_detach_node(dn);
 305        of_node_put(dn); /* Must decrement the refcount */
 306
 307        return 0;
 308}
 309
 310#define DR_ENTITY_SENSE         9003
 311#define DR_ENTITY_PRESENT       1
 312#define DR_ENTITY_UNUSABLE      2
 313#define ALLOCATION_STATE        9003
 314#define ALLOC_UNUSABLE          0
 315#define ALLOC_USABLE            1
 316#define ISOLATION_STATE         9001
 317#define ISOLATE                 0
 318#define UNISOLATE               1
 319
 320int dlpar_acquire_drc(u32 drc_index)
 321{
 322        int dr_status, rc;
 323
 324        rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
 325                       DR_ENTITY_SENSE, drc_index);
 326        if (rc || dr_status != DR_ENTITY_UNUSABLE)
 327                return -1;
 328
 329        rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
 330        if (rc)
 331                return rc;
 332
 333        rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
 334        if (rc) {
 335                rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
 336                return rc;
 337        }
 338
 339        return 0;
 340}
 341
 342int dlpar_release_drc(u32 drc_index)
 343{
 344        int dr_status, rc;
 345
 346        rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
 347                       DR_ENTITY_SENSE, drc_index);
 348        if (rc || dr_status != DR_ENTITY_PRESENT)
 349                return -1;
 350
 351        rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
 352        if (rc)
 353                return rc;
 354
 355        rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
 356        if (rc) {
 357                rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
 358                return rc;
 359        }
 360
 361        return 0;
 362}
 363
 364#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
 365
 366static int dlpar_online_cpu(struct device_node *dn)
 367{
 368        int rc = 0;
 369        unsigned int cpu;
 370        int len, nthreads, i;
 371        const u32 *intserv;
 372
 373        intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
 374        if (!intserv)
 375                return -EINVAL;
 376
 377        nthreads = len / sizeof(u32);
 378
 379        cpu_maps_update_begin();
 380        for (i = 0; i < nthreads; i++) {
 381                for_each_present_cpu(cpu) {
 382                        if (get_hard_smp_processor_id(cpu) != intserv[i])
 383                                continue;
 384                        BUG_ON(get_cpu_current_state(cpu)
 385                                        != CPU_STATE_OFFLINE);
 386                        cpu_maps_update_done();
 387                        rc = cpu_up(cpu);
 388                        if (rc)
 389                                goto out;
 390                        cpu_maps_update_begin();
 391
 392                        break;
 393                }
 394                if (cpu == num_possible_cpus())
 395                        printk(KERN_WARNING "Could not find cpu to online "
 396                               "with physical id 0x%x\n", intserv[i]);
 397        }
 398        cpu_maps_update_done();
 399
 400out:
 401        return rc;
 402
 403}
 404
 405static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
 406{
 407        struct device_node *dn;
 408        unsigned long drc_index;
 409        char *cpu_name;
 410        int rc;
 411
 412        cpu_hotplug_driver_lock();
 413        rc = strict_strtoul(buf, 0, &drc_index);
 414        if (rc) {
 415                rc = -EINVAL;
 416                goto out;
 417        }
 418
 419        dn = dlpar_configure_connector(drc_index);
 420        if (!dn) {
 421                rc = -EINVAL;
 422                goto out;
 423        }
 424
 425        /* configure-connector reports cpus as living in the base
 426         * directory of the device tree.  CPUs actually live in the
 427         * cpus directory so we need to fixup the full_name.
 428         */
 429        cpu_name = kasprintf(GFP_KERNEL, "/cpus%s", dn->full_name);
 430        if (!cpu_name) {
 431                dlpar_free_cc_nodes(dn);
 432                rc = -ENOMEM;
 433                goto out;
 434        }
 435
 436        kfree(dn->full_name);
 437        dn->full_name = cpu_name;
 438
 439        rc = dlpar_acquire_drc(drc_index);
 440        if (rc) {
 441                dlpar_free_cc_nodes(dn);
 442                rc = -EINVAL;
 443                goto out;
 444        }
 445
 446        rc = dlpar_attach_node(dn);
 447        if (rc) {
 448                dlpar_release_drc(drc_index);
 449                dlpar_free_cc_nodes(dn);
 450                goto out;
 451        }
 452
 453        rc = dlpar_online_cpu(dn);
 454out:
 455        cpu_hotplug_driver_unlock();
 456
 457        return rc ? rc : count;
 458}
 459
 460static int dlpar_offline_cpu(struct device_node *dn)
 461{
 462        int rc = 0;
 463        unsigned int cpu;
 464        int len, nthreads, i;
 465        const u32 *intserv;
 466
 467        intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
 468        if (!intserv)
 469                return -EINVAL;
 470
 471        nthreads = len / sizeof(u32);
 472
 473        cpu_maps_update_begin();
 474        for (i = 0; i < nthreads; i++) {
 475                for_each_present_cpu(cpu) {
 476                        if (get_hard_smp_processor_id(cpu) != intserv[i])
 477                                continue;
 478
 479                        if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE)
 480                                break;
 481
 482                        if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) {
 483                                set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
 484                                cpu_maps_update_done();
 485                                rc = cpu_down(cpu);
 486                                if (rc)
 487                                        goto out;
 488                                cpu_maps_update_begin();
 489                                break;
 490
 491                        }
 492
 493                        /*
 494                         * The cpu is in CPU_STATE_INACTIVE.
 495                         * Upgrade it's state to CPU_STATE_OFFLINE.
 496                         */
 497                        set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
 498                        BUG_ON(plpar_hcall_norets(H_PROD, intserv[i])
 499                                                                != H_SUCCESS);
 500                        __cpu_die(cpu);
 501                        break;
 502                }
 503                if (cpu == num_possible_cpus())
 504                        printk(KERN_WARNING "Could not find cpu to offline "
 505                               "with physical id 0x%x\n", intserv[i]);
 506        }
 507        cpu_maps_update_done();
 508
 509out:
 510        return rc;
 511
 512}
 513
 514static ssize_t dlpar_cpu_release(const char *buf, size_t count)
 515{
 516        struct device_node *dn;
 517        const u32 *drc_index;
 518        int rc;
 519
 520        dn = of_find_node_by_path(buf);
 521        if (!dn)
 522                return -EINVAL;
 523
 524        drc_index = of_get_property(dn, "ibm,my-drc-index", NULL);
 525        if (!drc_index) {
 526                of_node_put(dn);
 527                return -EINVAL;
 528        }
 529
 530        cpu_hotplug_driver_lock();
 531        rc = dlpar_offline_cpu(dn);
 532        if (rc) {
 533                of_node_put(dn);
 534                rc = -EINVAL;
 535                goto out;
 536        }
 537
 538        rc = dlpar_release_drc(*drc_index);
 539        if (rc) {
 540                of_node_put(dn);
 541                goto out;
 542        }
 543
 544        rc = dlpar_detach_node(dn);
 545        if (rc) {
 546                dlpar_acquire_drc(*drc_index);
 547                goto out;
 548        }
 549
 550        of_node_put(dn);
 551out:
 552        cpu_hotplug_driver_unlock();
 553        return rc ? rc : count;
 554}
 555
 556static int __init pseries_dlpar_init(void)
 557{
 558        ppc_md.cpu_probe = dlpar_cpu_probe;
 559        ppc_md.cpu_release = dlpar_cpu_release;
 560
 561        return 0;
 562}
 563machine_device_initcall(pseries, pseries_dlpar_init);
 564
 565#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
 566