linux/arch/powerpc/platforms/pseries/mobility.c
<<
>>
Prefs
   1/*
   2 * Support for Partition Mobility/Migration
   3 *
   4 * Copyright (C) 2010 Nathan Fontenot
   5 * Copyright (C) 2010 IBM Corporation
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License version
   9 * 2 as published by the Free Software Foundation.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/kobject.h>
  14#include <linux/smp.h>
  15#include <linux/stat.h>
  16#include <linux/completion.h>
  17#include <linux/device.h>
  18#include <linux/delay.h>
  19#include <linux/slab.h>
  20
  21#include <asm/machdep.h>
  22#include <asm/rtas.h>
  23#include "pseries.h"
  24
  25static struct kobject *mobility_kobj;
  26
  27struct update_props_workarea {
  28        __be32 phandle;
  29        __be32 state;
  30        __be64 reserved;
  31        __be32 nprops;
  32} __packed;
  33
  34#define NODE_ACTION_MASK        0xff000000
  35#define NODE_COUNT_MASK         0x00ffffff
  36
  37#define DELETE_DT_NODE  0x01000000
  38#define UPDATE_DT_NODE  0x02000000
  39#define ADD_DT_NODE     0x03000000
  40
  41#define MIGRATION_SCOPE (1)
  42
  43static int mobility_rtas_call(int token, char *buf, s32 scope)
  44{
  45        int rc;
  46
  47        spin_lock(&rtas_data_buf_lock);
  48
  49        memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
  50        rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope);
  51        memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
  52
  53        spin_unlock(&rtas_data_buf_lock);
  54        return rc;
  55}
  56
  57static int delete_dt_node(__be32 phandle)
  58{
  59        struct device_node *dn;
  60
  61        dn = of_find_node_by_phandle(be32_to_cpu(phandle));
  62        if (!dn)
  63                return -ENOENT;
  64
  65        dlpar_detach_node(dn);
  66        of_node_put(dn);
  67        return 0;
  68}
  69
  70static int update_dt_property(struct device_node *dn, struct property **prop,
  71                              const char *name, u32 vd, char *value)
  72{
  73        struct property *new_prop = *prop;
  74        int more = 0;
  75
  76        /* A negative 'vd' value indicates that only part of the new property
  77         * value is contained in the buffer and we need to call
  78         * ibm,update-properties again to get the rest of the value.
  79         *
  80         * A negative value is also the two's compliment of the actual value.
  81         */
  82        if (vd & 0x80000000) {
  83                vd = ~vd + 1;
  84                more = 1;
  85        }
  86
  87        if (new_prop) {
  88                /* partial property fixup */
  89                char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL);
  90                if (!new_data)
  91                        return -ENOMEM;
  92
  93                memcpy(new_data, new_prop->value, new_prop->length);
  94                memcpy(new_data + new_prop->length, value, vd);
  95
  96                kfree(new_prop->value);
  97                new_prop->value = new_data;
  98                new_prop->length += vd;
  99        } else {
 100                new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
 101                if (!new_prop)
 102                        return -ENOMEM;
 103
 104                new_prop->name = kstrdup(name, GFP_KERNEL);
 105                if (!new_prop->name) {
 106                        kfree(new_prop);
 107                        return -ENOMEM;
 108                }
 109
 110                new_prop->length = vd;
 111                new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
 112                if (!new_prop->value) {
 113                        kfree(new_prop->name);
 114                        kfree(new_prop);
 115                        return -ENOMEM;
 116                }
 117
 118                memcpy(new_prop->value, value, vd);
 119                *prop = new_prop;
 120        }
 121
 122        if (!more) {
 123                of_update_property(dn, new_prop);
 124                *prop = NULL;
 125        }
 126
 127        return 0;
 128}
 129
 130static int update_dt_node(__be32 phandle, s32 scope)
 131{
 132        struct update_props_workarea *upwa;
 133        struct device_node *dn;
 134        struct property *prop = NULL;
 135        int i, rc, rtas_rc;
 136        char *prop_data;
 137        char *rtas_buf;
 138        int update_properties_token;
 139        u32 nprops;
 140        u32 vd;
 141
 142        update_properties_token = rtas_token("ibm,update-properties");
 143        if (update_properties_token == RTAS_UNKNOWN_SERVICE)
 144                return -EINVAL;
 145
 146        rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
 147        if (!rtas_buf)
 148                return -ENOMEM;
 149
 150        dn = of_find_node_by_phandle(be32_to_cpu(phandle));
 151        if (!dn) {
 152                kfree(rtas_buf);
 153                return -ENOENT;
 154        }
 155
 156        upwa = (struct update_props_workarea *)&rtas_buf[0];
 157        upwa->phandle = phandle;
 158
 159        do {
 160                rtas_rc = mobility_rtas_call(update_properties_token, rtas_buf,
 161                                        scope);
 162                if (rtas_rc < 0)
 163                        break;
 164
 165                prop_data = rtas_buf + sizeof(*upwa);
 166                nprops = be32_to_cpu(upwa->nprops);
 167
 168                /* On the first call to ibm,update-properties for a node the
 169                 * the first property value descriptor contains an empty
 170                 * property name, the property value length encoded as u32,
 171                 * and the property value is the node path being updated.
 172                 */
 173                if (*prop_data == 0) {
 174                        prop_data++;
 175                        vd = be32_to_cpu(*(__be32 *)prop_data);
 176                        prop_data += vd + sizeof(vd);
 177                        nprops--;
 178                }
 179
 180                for (i = 0; i < nprops; i++) {
 181                        char *prop_name;
 182
 183                        prop_name = prop_data;
 184                        prop_data += strlen(prop_name) + 1;
 185                        vd = be32_to_cpu(*(__be32 *)prop_data);
 186                        prop_data += sizeof(vd);
 187
 188                        switch (vd) {
 189                        case 0x00000000:
 190                                /* name only property, nothing to do */
 191                                break;
 192
 193                        case 0x80000000:
 194                                prop = of_find_property(dn, prop_name, NULL);
 195                                of_remove_property(dn, prop);
 196                                prop = NULL;
 197                                break;
 198
 199                        default:
 200                                rc = update_dt_property(dn, &prop, prop_name,
 201                                                        vd, prop_data);
 202                                if (rc) {
 203                                        printk(KERN_ERR "Could not update %s"
 204                                               " property\n", prop_name);
 205                                }
 206
 207                                prop_data += vd;
 208                        }
 209                }
 210        } while (rtas_rc == 1);
 211
 212        of_node_put(dn);
 213        kfree(rtas_buf);
 214        return 0;
 215}
 216
 217static int add_dt_node(__be32 parent_phandle, __be32 drc_index)
 218{
 219        struct device_node *dn;
 220        struct device_node *parent_dn;
 221        int rc;
 222
 223        parent_dn = of_find_node_by_phandle(be32_to_cpu(parent_phandle));
 224        if (!parent_dn)
 225                return -ENOENT;
 226
 227        dn = dlpar_configure_connector(drc_index, parent_dn);
 228        if (!dn)
 229                return -ENOENT;
 230
 231        rc = dlpar_attach_node(dn);
 232        if (rc)
 233                dlpar_free_cc_nodes(dn);
 234
 235        of_node_put(parent_dn);
 236        return rc;
 237}
 238
 239int pseries_devicetree_update(s32 scope)
 240{
 241        char *rtas_buf;
 242        __be32 *data;
 243        int update_nodes_token;
 244        int rc;
 245
 246        update_nodes_token = rtas_token("ibm,update-nodes");
 247        if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
 248                return -EINVAL;
 249
 250        rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
 251        if (!rtas_buf)
 252                return -ENOMEM;
 253
 254        do {
 255                rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope);
 256                if (rc && rc != 1)
 257                        break;
 258
 259                data = (__be32 *)rtas_buf + 4;
 260                while (be32_to_cpu(*data) & NODE_ACTION_MASK) {
 261                        int i;
 262                        u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK;
 263                        u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK;
 264
 265                        data++;
 266
 267                        for (i = 0; i < node_count; i++) {
 268                                __be32 phandle = *data++;
 269                                __be32 drc_index;
 270
 271                                switch (action) {
 272                                case DELETE_DT_NODE:
 273                                        delete_dt_node(phandle);
 274                                        break;
 275                                case UPDATE_DT_NODE:
 276                                        update_dt_node(phandle, scope);
 277                                        break;
 278                                case ADD_DT_NODE:
 279                                        drc_index = *data++;
 280                                        add_dt_node(phandle, drc_index);
 281                                        break;
 282                                }
 283                        }
 284                }
 285        } while (rc == 1);
 286
 287        kfree(rtas_buf);
 288        return rc;
 289}
 290
 291void post_mobility_fixup(void)
 292{
 293        int rc;
 294        int activate_fw_token;
 295
 296        activate_fw_token = rtas_token("ibm,activate-firmware");
 297        if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
 298                printk(KERN_ERR "Could not make post-mobility "
 299                       "activate-fw call.\n");
 300                return;
 301        }
 302
 303        do {
 304                rc = rtas_call(activate_fw_token, 0, 1, NULL);
 305        } while (rtas_busy_delay(rc));
 306
 307        if (rc)
 308                printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
 309
 310        rc = pseries_devicetree_update(MIGRATION_SCOPE);
 311        if (rc)
 312                printk(KERN_ERR "Post-mobility device tree update "
 313                        "failed: %d\n", rc);
 314
 315        return;
 316}
 317
 318static ssize_t migrate_store(struct class *class, struct class_attribute *attr,
 319                             const char *buf, size_t count)
 320{
 321        u64 streamid;
 322        int rc;
 323
 324        rc = kstrtou64(buf, 0, &streamid);
 325        if (rc)
 326                return rc;
 327
 328        do {
 329                rc = rtas_ibm_suspend_me(streamid);
 330                if (rc == -EAGAIN)
 331                        ssleep(1);
 332        } while (rc == -EAGAIN);
 333
 334        if (rc)
 335                return rc;
 336
 337        post_mobility_fixup();
 338        return count;
 339}
 340
 341/*
 342 * Used by drmgr to determine the kernel behavior of the migration interface.
 343 *
 344 * Version 1: Performs all PAPR requirements for migration including
 345 *      firmware activation and device tree update.
 346 */
 347#define MIGRATION_API_VERSION   1
 348
 349static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store);
 350static CLASS_ATTR_STRING(api_version, S_IRUGO, __stringify(MIGRATION_API_VERSION));
 351
 352static int __init mobility_sysfs_init(void)
 353{
 354        int rc;
 355
 356        mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
 357        if (!mobility_kobj)
 358                return -ENOMEM;
 359
 360        rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
 361        if (rc)
 362                pr_err("mobility: unable to create migration sysfs file (%d)\n", rc);
 363
 364        rc = sysfs_create_file(mobility_kobj, &class_attr_api_version.attr.attr);
 365        if (rc)
 366                pr_err("mobility: unable to create api_version sysfs file (%d)\n", rc);
 367
 368        return 0;
 369}
 370machine_device_initcall(pseries, mobility_sysfs_init);
 371