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