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