Path: blob/master/arch/powerpc/platforms/pseries/mobility.c
10818 views
/*1* Support for Partition Mobility/Migration2*3* Copyright (C) 2010 Nathan Fontenot4* Copyright (C) 2010 IBM Corporation5*6* This program is free software; you can redistribute it and/or7* modify it under the terms of the GNU General Public License version8* 2 as published by the Free Software Foundation.9*/1011#include <linux/kernel.h>12#include <linux/kobject.h>13#include <linux/smp.h>14#include <linux/completion.h>15#include <linux/device.h>16#include <linux/delay.h>17#include <linux/slab.h>1819#include <asm/rtas.h>20#include "pseries.h"2122static struct kobject *mobility_kobj;2324struct update_props_workarea {25u32 phandle;26u32 state;27u64 reserved;28u32 nprops;29};3031#define NODE_ACTION_MASK 0xff00000032#define NODE_COUNT_MASK 0x00ffffff3334#define DELETE_DT_NODE 0x0100000035#define UPDATE_DT_NODE 0x0200000036#define ADD_DT_NODE 0x030000003738static int mobility_rtas_call(int token, char *buf)39{40int rc;4142spin_lock(&rtas_data_buf_lock);4344memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);45rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, 1);46memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);4748spin_unlock(&rtas_data_buf_lock);49return rc;50}5152static int delete_dt_node(u32 phandle)53{54struct device_node *dn;5556dn = of_find_node_by_phandle(phandle);57if (!dn)58return -ENOENT;5960dlpar_detach_node(dn);61return 0;62}6364static int update_dt_property(struct device_node *dn, struct property **prop,65const char *name, u32 vd, char *value)66{67struct property *new_prop = *prop;68struct property *old_prop;69int more = 0;7071/* A negative 'vd' value indicates that only part of the new property72* value is contained in the buffer and we need to call73* ibm,update-properties again to get the rest of the value.74*75* A negative value is also the two's compliment of the actual value.76*/77if (vd & 0x80000000) {78vd = ~vd + 1;79more = 1;80}8182if (new_prop) {83/* partial property fixup */84char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL);85if (!new_data)86return -ENOMEM;8788memcpy(new_data, new_prop->value, new_prop->length);89memcpy(new_data + new_prop->length, value, vd);9091kfree(new_prop->value);92new_prop->value = new_data;93new_prop->length += vd;94} else {95new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);96if (!new_prop)97return -ENOMEM;9899new_prop->name = kstrdup(name, GFP_KERNEL);100if (!new_prop->name) {101kfree(new_prop);102return -ENOMEM;103}104105new_prop->length = vd;106new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);107if (!new_prop->value) {108kfree(new_prop->name);109kfree(new_prop);110return -ENOMEM;111}112113memcpy(new_prop->value, value, vd);114*prop = new_prop;115}116117if (!more) {118old_prop = of_find_property(dn, new_prop->name, NULL);119if (old_prop)120prom_update_property(dn, new_prop, old_prop);121else122prom_add_property(dn, new_prop);123124new_prop = NULL;125}126127return 0;128}129130static int update_dt_node(u32 phandle)131{132struct update_props_workarea *upwa;133struct device_node *dn;134struct property *prop = NULL;135int i, rc;136char *prop_data;137char *rtas_buf;138int update_properties_token;139140update_properties_token = rtas_token("ibm,update-properties");141if (update_properties_token == RTAS_UNKNOWN_SERVICE)142return -EINVAL;143144rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);145if (!rtas_buf)146return -ENOMEM;147148dn = of_find_node_by_phandle(phandle);149if (!dn) {150kfree(rtas_buf);151return -ENOENT;152}153154upwa = (struct update_props_workarea *)&rtas_buf[0];155upwa->phandle = phandle;156157do {158rc = mobility_rtas_call(update_properties_token, rtas_buf);159if (rc < 0)160break;161162prop_data = rtas_buf + sizeof(*upwa);163164for (i = 0; i < upwa->nprops; i++) {165char *prop_name;166u32 vd;167168prop_name = prop_data + 1;169prop_data += strlen(prop_name) + 1;170vd = *prop_data++;171172switch (vd) {173case 0x00000000:174/* name only property, nothing to do */175break;176177case 0x80000000:178prop = of_find_property(dn, prop_name, NULL);179prom_remove_property(dn, prop);180prop = NULL;181break;182183default:184rc = update_dt_property(dn, &prop, prop_name,185vd, prop_data);186if (rc) {187printk(KERN_ERR "Could not update %s"188" property\n", prop_name);189}190191prop_data += vd;192}193}194} while (rc == 1);195196of_node_put(dn);197kfree(rtas_buf);198return 0;199}200201static int add_dt_node(u32 parent_phandle, u32 drc_index)202{203struct device_node *dn;204struct device_node *parent_dn;205int rc;206207dn = dlpar_configure_connector(drc_index);208if (!dn)209return -ENOENT;210211parent_dn = of_find_node_by_phandle(parent_phandle);212if (!parent_dn) {213dlpar_free_cc_nodes(dn);214return -ENOENT;215}216217dn->parent = parent_dn;218rc = dlpar_attach_node(dn);219if (rc)220dlpar_free_cc_nodes(dn);221222of_node_put(parent_dn);223return rc;224}225226static int pseries_devicetree_update(void)227{228char *rtas_buf;229u32 *data;230int update_nodes_token;231int rc;232233update_nodes_token = rtas_token("ibm,update-nodes");234if (update_nodes_token == RTAS_UNKNOWN_SERVICE)235return -EINVAL;236237rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);238if (!rtas_buf)239return -ENOMEM;240241do {242rc = mobility_rtas_call(update_nodes_token, rtas_buf);243if (rc && rc != 1)244break;245246data = (u32 *)rtas_buf + 4;247while (*data & NODE_ACTION_MASK) {248int i;249u32 action = *data & NODE_ACTION_MASK;250int node_count = *data & NODE_COUNT_MASK;251252data++;253254for (i = 0; i < node_count; i++) {255u32 phandle = *data++;256u32 drc_index;257258switch (action) {259case DELETE_DT_NODE:260delete_dt_node(phandle);261break;262case UPDATE_DT_NODE:263update_dt_node(phandle);264break;265case ADD_DT_NODE:266drc_index = *data++;267add_dt_node(phandle, drc_index);268break;269}270}271}272} while (rc == 1);273274kfree(rtas_buf);275return rc;276}277278void post_mobility_fixup(void)279{280int rc;281int activate_fw_token;282283rc = pseries_devicetree_update();284if (rc) {285printk(KERN_ERR "Initial post-mobility device tree update "286"failed: %d\n", rc);287return;288}289290activate_fw_token = rtas_token("ibm,activate-firmware");291if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {292printk(KERN_ERR "Could not make post-mobility "293"activate-fw call.\n");294return;295}296297rc = rtas_call(activate_fw_token, 0, 1, NULL);298if (!rc) {299rc = pseries_devicetree_update();300if (rc)301printk(KERN_ERR "Secondary post-mobility device tree "302"update failed: %d\n", rc);303} else {304printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);305return;306}307308return;309}310311static ssize_t migrate_store(struct class *class, struct class_attribute *attr,312const char *buf, size_t count)313{314struct rtas_args args;315u64 streamid;316int rc;317318rc = strict_strtoull(buf, 0, &streamid);319if (rc)320return rc;321322memset(&args, 0, sizeof(args));323args.token = rtas_token("ibm,suspend-me");324args.nargs = 2;325args.nret = 1;326327args.args[0] = streamid >> 32 ;328args.args[1] = streamid & 0xffffffff;329args.rets = &args.args[args.nargs];330331do {332args.rets[0] = 0;333rc = rtas_ibm_suspend_me(&args);334if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE)335ssleep(1);336} while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE);337338if (rc)339return rc;340else if (args.rets[0])341return args.rets[0];342343post_mobility_fixup();344return count;345}346347static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store);348349static int __init mobility_sysfs_init(void)350{351int rc;352353mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);354if (!mobility_kobj)355return -ENOMEM;356357rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);358359return rc;360}361device_initcall(mobility_sysfs_init);362363364