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#define pr_fmt(fmt)     "dlpar: " fmt
  14
  15#include <linux/kernel.h>
  16#include <linux/notifier.h>
  17#include <linux/spinlock.h>
  18#include <linux/cpu.h>
  19#include <linux/slab.h>
  20#include <linux/of.h>
  21
  22#include "of_helpers.h"
  23#include "pseries.h"
  24
  25#include <asm/prom.h>
  26#include <asm/machdep.h>
  27#include <linux/uaccess.h>
  28#include <asm/rtas.h>
  29
  30static struct workqueue_struct *pseries_hp_wq;
  31
  32struct pseries_hp_work {
  33        struct work_struct work;
  34        struct pseries_hp_errorlog *errlog;
  35        struct completion *hp_completion;
  36        int *rc;
  37};
  38
  39struct cc_workarea {
  40        __be32  drc_index;
  41        __be32  zero;
  42        __be32  name_offset;
  43        __be32  prop_length;
  44        __be32  prop_offset;
  45};
  46
  47void dlpar_free_cc_property(struct property *prop)
  48{
  49        kfree(prop->name);
  50        kfree(prop->value);
  51        kfree(prop);
  52}
  53
  54static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
  55{
  56        struct property *prop;
  57        char *name;
  58        char *value;
  59
  60        prop = kzalloc(sizeof(*prop), GFP_KERNEL);
  61        if (!prop)
  62                return NULL;
  63
  64        name = (char *)ccwa + be32_to_cpu(ccwa->name_offset);
  65        prop->name = kstrdup(name, GFP_KERNEL);
  66
  67        prop->length = be32_to_cpu(ccwa->prop_length);
  68        value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset);
  69        prop->value = kmemdup(value, prop->length, GFP_KERNEL);
  70        if (!prop->value) {
  71                dlpar_free_cc_property(prop);
  72                return NULL;
  73        }
  74
  75        return prop;
  76}
  77
  78static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa)
  79{
  80        struct device_node *dn;
  81        const char *name;
  82
  83        dn = kzalloc(sizeof(*dn), GFP_KERNEL);
  84        if (!dn)
  85                return NULL;
  86
  87        name = (const char *)ccwa + be32_to_cpu(ccwa->name_offset);
  88        dn->full_name = kstrdup(name, GFP_KERNEL);
  89        if (!dn->full_name) {
  90                kfree(dn);
  91                return NULL;
  92        }
  93
  94        of_node_set_flag(dn, OF_DYNAMIC);
  95        of_node_init(dn);
  96
  97        return dn;
  98}
  99
 100static void dlpar_free_one_cc_node(struct device_node *dn)
 101{
 102        struct property *prop;
 103
 104        while (dn->properties) {
 105                prop = dn->properties;
 106                dn->properties = prop->next;
 107                dlpar_free_cc_property(prop);
 108        }
 109
 110        kfree(dn->full_name);
 111        kfree(dn);
 112}
 113
 114void dlpar_free_cc_nodes(struct device_node *dn)
 115{
 116        if (dn->child)
 117                dlpar_free_cc_nodes(dn->child);
 118
 119        if (dn->sibling)
 120                dlpar_free_cc_nodes(dn->sibling);
 121
 122        dlpar_free_one_cc_node(dn);
 123}
 124
 125#define COMPLETE        0
 126#define NEXT_SIBLING    1
 127#define NEXT_CHILD      2
 128#define NEXT_PROPERTY   3
 129#define PREV_PARENT     4
 130#define MORE_MEMORY     5
 131#define CALL_AGAIN      -2
 132#define ERR_CFG_USE     -9003
 133
 134struct device_node *dlpar_configure_connector(__be32 drc_index,
 135                                              struct device_node *parent)
 136{
 137        struct device_node *dn;
 138        struct device_node *first_dn = NULL;
 139        struct device_node *last_dn = NULL;
 140        struct property *property;
 141        struct property *last_property = NULL;
 142        struct cc_workarea *ccwa;
 143        char *data_buf;
 144        int cc_token;
 145        int rc = -1;
 146
 147        cc_token = rtas_token("ibm,configure-connector");
 148        if (cc_token == RTAS_UNKNOWN_SERVICE)
 149                return NULL;
 150
 151        data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
 152        if (!data_buf)
 153                return NULL;
 154
 155        ccwa = (struct cc_workarea *)&data_buf[0];
 156        ccwa->drc_index = drc_index;
 157        ccwa->zero = 0;
 158
 159        do {
 160                /* Since we release the rtas_data_buf lock between configure
 161                 * connector calls we want to re-populate the rtas_data_buffer
 162                 * with the contents of the previous call.
 163                 */
 164                spin_lock(&rtas_data_buf_lock);
 165
 166                memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
 167                rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
 168                memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
 169
 170                spin_unlock(&rtas_data_buf_lock);
 171
 172                switch (rc) {
 173                case COMPLETE:
 174                        break;
 175
 176                case NEXT_SIBLING:
 177                        dn = dlpar_parse_cc_node(ccwa);
 178                        if (!dn)
 179                                goto cc_error;
 180
 181                        dn->parent = last_dn->parent;
 182                        last_dn->sibling = dn;
 183                        last_dn = dn;
 184                        break;
 185
 186                case NEXT_CHILD:
 187                        dn = dlpar_parse_cc_node(ccwa);
 188                        if (!dn)
 189                                goto cc_error;
 190
 191                        if (!first_dn) {
 192                                dn->parent = parent;
 193                                first_dn = dn;
 194                        } else {
 195                                dn->parent = last_dn;
 196                                if (last_dn)
 197                                        last_dn->child = dn;
 198                        }
 199
 200                        last_dn = dn;
 201                        break;
 202
 203                case NEXT_PROPERTY:
 204                        property = dlpar_parse_cc_property(ccwa);
 205                        if (!property)
 206                                goto cc_error;
 207
 208                        if (!last_dn->properties)
 209                                last_dn->properties = property;
 210                        else
 211                                last_property->next = property;
 212
 213                        last_property = property;
 214                        break;
 215
 216                case PREV_PARENT:
 217                        last_dn = last_dn->parent;
 218                        break;
 219
 220                case CALL_AGAIN:
 221                        break;
 222
 223                case MORE_MEMORY:
 224                case ERR_CFG_USE:
 225                default:
 226                        printk(KERN_ERR "Unexpected Error (%d) "
 227                               "returned from configure-connector\n", rc);
 228                        goto cc_error;
 229                }
 230        } while (rc);
 231
 232cc_error:
 233        kfree(data_buf);
 234
 235        if (rc) {
 236                if (first_dn)
 237                        dlpar_free_cc_nodes(first_dn);
 238
 239                return NULL;
 240        }
 241
 242        return first_dn;
 243}
 244
 245int dlpar_attach_node(struct device_node *dn, struct device_node *parent)
 246{
 247        int rc;
 248
 249        dn->parent = parent;
 250
 251        rc = of_attach_node(dn);
 252        if (rc) {
 253                printk(KERN_ERR "Failed to add device node %pOF\n", dn);
 254                return rc;
 255        }
 256
 257        return 0;
 258}
 259
 260int dlpar_detach_node(struct device_node *dn)
 261{
 262        struct device_node *child;
 263        int rc;
 264
 265        child = of_get_next_child(dn, NULL);
 266        while (child) {
 267                dlpar_detach_node(child);
 268                child = of_get_next_child(dn, child);
 269        }
 270
 271        rc = of_detach_node(dn);
 272        if (rc)
 273                return rc;
 274
 275        return 0;
 276}
 277
 278#define DR_ENTITY_SENSE         9003
 279#define DR_ENTITY_PRESENT       1
 280#define DR_ENTITY_UNUSABLE      2
 281#define ALLOCATION_STATE        9003
 282#define ALLOC_UNUSABLE          0
 283#define ALLOC_USABLE            1
 284#define ISOLATION_STATE         9001
 285#define ISOLATE                 0
 286#define UNISOLATE               1
 287
 288int dlpar_acquire_drc(u32 drc_index)
 289{
 290        int dr_status, rc;
 291
 292        rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
 293                       DR_ENTITY_SENSE, drc_index);
 294        if (rc || dr_status != DR_ENTITY_UNUSABLE)
 295                return -1;
 296
 297        rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
 298        if (rc)
 299                return rc;
 300
 301        rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
 302        if (rc) {
 303                rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
 304                return rc;
 305        }
 306
 307        return 0;
 308}
 309
 310int dlpar_release_drc(u32 drc_index)
 311{
 312        int dr_status, rc;
 313
 314        rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
 315                       DR_ENTITY_SENSE, drc_index);
 316        if (rc || dr_status != DR_ENTITY_PRESENT)
 317                return -1;
 318
 319        rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
 320        if (rc)
 321                return rc;
 322
 323        rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
 324        if (rc) {
 325                rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
 326                return rc;
 327        }
 328
 329        return 0;
 330}
 331
 332static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog)
 333{
 334        int rc;
 335
 336        /* pseries error logs are in BE format, convert to cpu type */
 337        switch (hp_elog->id_type) {
 338        case PSERIES_HP_ELOG_ID_DRC_COUNT:
 339                hp_elog->_drc_u.drc_count =
 340                                be32_to_cpu(hp_elog->_drc_u.drc_count);
 341                break;
 342        case PSERIES_HP_ELOG_ID_DRC_INDEX:
 343                hp_elog->_drc_u.drc_index =
 344                                be32_to_cpu(hp_elog->_drc_u.drc_index);
 345                break;
 346        case PSERIES_HP_ELOG_ID_DRC_IC:
 347                hp_elog->_drc_u.ic.count =
 348                                be32_to_cpu(hp_elog->_drc_u.ic.count);
 349                hp_elog->_drc_u.ic.index =
 350                                be32_to_cpu(hp_elog->_drc_u.ic.index);
 351        }
 352
 353        switch (hp_elog->resource) {
 354        case PSERIES_HP_ELOG_RESOURCE_MEM:
 355                rc = dlpar_memory(hp_elog);
 356                break;
 357        case PSERIES_HP_ELOG_RESOURCE_CPU:
 358                rc = dlpar_cpu(hp_elog);
 359                break;
 360        default:
 361                pr_warn_ratelimited("Invalid resource (%d) specified\n",
 362                                    hp_elog->resource);
 363                rc = -EINVAL;
 364        }
 365
 366        return rc;
 367}
 368
 369static void pseries_hp_work_fn(struct work_struct *work)
 370{
 371        struct pseries_hp_work *hp_work =
 372                        container_of(work, struct pseries_hp_work, work);
 373
 374        if (hp_work->rc)
 375                *(hp_work->rc) = handle_dlpar_errorlog(hp_work->errlog);
 376        else
 377                handle_dlpar_errorlog(hp_work->errlog);
 378
 379        if (hp_work->hp_completion)
 380                complete(hp_work->hp_completion);
 381
 382        kfree(hp_work->errlog);
 383        kfree((void *)work);
 384}
 385
 386void queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog,
 387                         struct completion *hotplug_done, int *rc)
 388{
 389        struct pseries_hp_work *work;
 390        struct pseries_hp_errorlog *hp_errlog_copy;
 391
 392        hp_errlog_copy = kmalloc(sizeof(struct pseries_hp_errorlog),
 393                                 GFP_KERNEL);
 394        memcpy(hp_errlog_copy, hp_errlog, sizeof(struct pseries_hp_errorlog));
 395
 396        work = kmalloc(sizeof(struct pseries_hp_work), GFP_KERNEL);
 397        if (work) {
 398                INIT_WORK((struct work_struct *)work, pseries_hp_work_fn);
 399                work->errlog = hp_errlog_copy;
 400                work->hp_completion = hotplug_done;
 401                work->rc = rc;
 402                queue_work(pseries_hp_wq, (struct work_struct *)work);
 403        } else {
 404                *rc = -ENOMEM;
 405                kfree(hp_errlog_copy);
 406                complete(hotplug_done);
 407        }
 408}
 409
 410static int dlpar_parse_resource(char **cmd, struct pseries_hp_errorlog *hp_elog)
 411{
 412        char *arg;
 413
 414        arg = strsep(cmd, " ");
 415        if (!arg)
 416                return -EINVAL;
 417
 418        if (sysfs_streq(arg, "memory")) {
 419                hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM;
 420        } else if (sysfs_streq(arg, "cpu")) {
 421                hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU;
 422        } else {
 423                pr_err("Invalid resource specified.\n");
 424                return -EINVAL;
 425        }
 426
 427        return 0;
 428}
 429
 430static int dlpar_parse_action(char **cmd, struct pseries_hp_errorlog *hp_elog)
 431{
 432        char *arg;
 433
 434        arg = strsep(cmd, " ");
 435        if (!arg)
 436                return -EINVAL;
 437
 438        if (sysfs_streq(arg, "add")) {
 439                hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD;
 440        } else if (sysfs_streq(arg, "remove")) {
 441                hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE;
 442        } else {
 443                pr_err("Invalid action specified.\n");
 444                return -EINVAL;
 445        }
 446
 447        return 0;
 448}
 449
 450static int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog)
 451{
 452        char *arg;
 453        u32 count, index;
 454
 455        arg = strsep(cmd, " ");
 456        if (!arg)
 457                return -EINVAL;
 458
 459        if (sysfs_streq(arg, "indexed-count")) {
 460                hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC;
 461                arg = strsep(cmd, " ");
 462                if (!arg) {
 463                        pr_err("No DRC count specified.\n");
 464                        return -EINVAL;
 465                }
 466
 467                if (kstrtou32(arg, 0, &count)) {
 468                        pr_err("Invalid DRC count specified.\n");
 469                        return -EINVAL;
 470                }
 471
 472                arg = strsep(cmd, " ");
 473                if (!arg) {
 474                        pr_err("No DRC Index specified.\n");
 475                        return -EINVAL;
 476                }
 477
 478                if (kstrtou32(arg, 0, &index)) {
 479                        pr_err("Invalid DRC Index specified.\n");
 480                        return -EINVAL;
 481                }
 482
 483                hp_elog->_drc_u.ic.count = cpu_to_be32(count);
 484                hp_elog->_drc_u.ic.index = cpu_to_be32(index);
 485        } else if (sysfs_streq(arg, "index")) {
 486                hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX;
 487                arg = strsep(cmd, " ");
 488                if (!arg) {
 489                        pr_err("No DRC Index specified.\n");
 490                        return -EINVAL;
 491                }
 492
 493                if (kstrtou32(arg, 0, &index)) {
 494                        pr_err("Invalid DRC Index specified.\n");
 495                        return -EINVAL;
 496                }
 497
 498                hp_elog->_drc_u.drc_index = cpu_to_be32(index);
 499        } else if (sysfs_streq(arg, "count")) {
 500                hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT;
 501                arg = strsep(cmd, " ");
 502                if (!arg) {
 503                        pr_err("No DRC count specified.\n");
 504                        return -EINVAL;
 505                }
 506
 507                if (kstrtou32(arg, 0, &count)) {
 508                        pr_err("Invalid DRC count specified.\n");
 509                        return -EINVAL;
 510                }
 511
 512                hp_elog->_drc_u.drc_count = cpu_to_be32(count);
 513        } else {
 514                pr_err("Invalid id_type specified.\n");
 515                return -EINVAL;
 516        }
 517
 518        return 0;
 519}
 520
 521static ssize_t dlpar_store(struct class *class, struct class_attribute *attr,
 522                           const char *buf, size_t count)
 523{
 524        struct pseries_hp_errorlog *hp_elog;
 525        struct completion hotplug_done;
 526        char *argbuf;
 527        char *args;
 528        int rc;
 529
 530        args = argbuf = kstrdup(buf, GFP_KERNEL);
 531        hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL);
 532        if (!hp_elog || !argbuf) {
 533                pr_info("Could not allocate resources for DLPAR operation\n");
 534                kfree(argbuf);
 535                kfree(hp_elog);
 536                return -ENOMEM;
 537        }
 538
 539        /*
 540         * Parse out the request from the user, this will be in the form:
 541         * <resource> <action> <id_type> <id>
 542         */
 543        rc = dlpar_parse_resource(&args, hp_elog);
 544        if (rc)
 545                goto dlpar_store_out;
 546
 547        rc = dlpar_parse_action(&args, hp_elog);
 548        if (rc)
 549                goto dlpar_store_out;
 550
 551        rc = dlpar_parse_id_type(&args, hp_elog);
 552        if (rc)
 553                goto dlpar_store_out;
 554
 555        init_completion(&hotplug_done);
 556        queue_hotplug_event(hp_elog, &hotplug_done, &rc);
 557        wait_for_completion(&hotplug_done);
 558
 559dlpar_store_out:
 560        kfree(argbuf);
 561        kfree(hp_elog);
 562
 563        if (rc)
 564                pr_err("Could not handle DLPAR request \"%s\"\n", buf);
 565
 566        return rc ? rc : count;
 567}
 568
 569static ssize_t dlpar_show(struct class *class, struct class_attribute *attr,
 570                          char *buf)
 571{
 572        return sprintf(buf, "%s\n", "memory,cpu");
 573}
 574
 575static CLASS_ATTR_RW(dlpar);
 576
 577int __init dlpar_workqueue_init(void)
 578{
 579        if (pseries_hp_wq)
 580                return 0;
 581
 582        pseries_hp_wq = alloc_workqueue("pseries hotplug workqueue",
 583                        WQ_UNBOUND, 1);
 584
 585        return pseries_hp_wq ? 0 : -ENOMEM;
 586}
 587
 588static int __init dlpar_sysfs_init(void)
 589{
 590        int rc;
 591
 592        rc = dlpar_workqueue_init();
 593        if (rc)
 594                return rc;
 595
 596        return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
 597}
 598machine_device_initcall(pseries, dlpar_sysfs_init);
 599
 600