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