1410bccf9SNathan Fontenot /* 2410bccf9SNathan Fontenot * Support for Partition Mobility/Migration 3410bccf9SNathan Fontenot * 4410bccf9SNathan Fontenot * Copyright (C) 2010 Nathan Fontenot 5410bccf9SNathan Fontenot * Copyright (C) 2010 IBM Corporation 6410bccf9SNathan Fontenot * 7410bccf9SNathan Fontenot * This program is free software; you can redistribute it and/or 8410bccf9SNathan Fontenot * modify it under the terms of the GNU General Public License version 9410bccf9SNathan Fontenot * 2 as published by the Free Software Foundation. 10410bccf9SNathan Fontenot */ 11410bccf9SNathan Fontenot 12410bccf9SNathan Fontenot #include <linux/kernel.h> 13410bccf9SNathan Fontenot #include <linux/kobject.h> 14410bccf9SNathan Fontenot #include <linux/smp.h> 15b56eade5SPaul Gortmaker #include <linux/stat.h> 16410bccf9SNathan Fontenot #include <linux/completion.h> 17410bccf9SNathan Fontenot #include <linux/device.h> 18410bccf9SNathan Fontenot #include <linux/delay.h> 19410bccf9SNathan Fontenot #include <linux/slab.h> 20410bccf9SNathan Fontenot 218e83e905SMichael Ellerman #include <asm/machdep.h> 22410bccf9SNathan Fontenot #include <asm/rtas.h> 23410bccf9SNathan Fontenot #include "pseries.h" 24410bccf9SNathan Fontenot 25410bccf9SNathan Fontenot static struct kobject *mobility_kobj; 26410bccf9SNathan Fontenot 27410bccf9SNathan Fontenot struct update_props_workarea { 28f6ff0414STyrel Datwyler __be32 phandle; 29f6ff0414STyrel Datwyler __be32 state; 30f6ff0414STyrel Datwyler __be64 reserved; 31f6ff0414STyrel Datwyler __be32 nprops; 32d0ef4403STyrel Datwyler } __packed; 33410bccf9SNathan Fontenot 34410bccf9SNathan Fontenot #define NODE_ACTION_MASK 0xff000000 35410bccf9SNathan Fontenot #define NODE_COUNT_MASK 0x00ffffff 36410bccf9SNathan Fontenot 37410bccf9SNathan Fontenot #define DELETE_DT_NODE 0x01000000 38410bccf9SNathan Fontenot #define UPDATE_DT_NODE 0x02000000 39410bccf9SNathan Fontenot #define ADD_DT_NODE 0x03000000 40410bccf9SNathan Fontenot 41762ec157SNathan Fontenot #define MIGRATION_SCOPE (1) 42675d8ee6SJohn Allen #define PRRN_SCOPE -2 43762ec157SNathan Fontenot 44762ec157SNathan Fontenot static int mobility_rtas_call(int token, char *buf, s32 scope) 45410bccf9SNathan Fontenot { 46410bccf9SNathan Fontenot int rc; 47410bccf9SNathan Fontenot 48410bccf9SNathan Fontenot spin_lock(&rtas_data_buf_lock); 49410bccf9SNathan Fontenot 50410bccf9SNathan Fontenot memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE); 51762ec157SNathan Fontenot rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope); 52410bccf9SNathan Fontenot memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 53410bccf9SNathan Fontenot 54410bccf9SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 55410bccf9SNathan Fontenot return rc; 56410bccf9SNathan Fontenot } 57410bccf9SNathan Fontenot 58f6ff0414STyrel Datwyler static int delete_dt_node(__be32 phandle) 59410bccf9SNathan Fontenot { 60410bccf9SNathan Fontenot struct device_node *dn; 61410bccf9SNathan Fontenot 62f6ff0414STyrel Datwyler dn = of_find_node_by_phandle(be32_to_cpu(phandle)); 63410bccf9SNathan Fontenot if (!dn) 64410bccf9SNathan Fontenot return -ENOENT; 65410bccf9SNathan Fontenot 66410bccf9SNathan Fontenot dlpar_detach_node(dn); 6714cd820aSTyrel Datwyler of_node_put(dn); 68410bccf9SNathan Fontenot return 0; 69410bccf9SNathan Fontenot } 70410bccf9SNathan Fontenot 71410bccf9SNathan Fontenot static int update_dt_property(struct device_node *dn, struct property **prop, 72410bccf9SNathan Fontenot const char *name, u32 vd, char *value) 73410bccf9SNathan Fontenot { 74410bccf9SNathan Fontenot struct property *new_prop = *prop; 75410bccf9SNathan Fontenot int more = 0; 76410bccf9SNathan Fontenot 77410bccf9SNathan Fontenot /* A negative 'vd' value indicates that only part of the new property 78410bccf9SNathan Fontenot * value is contained in the buffer and we need to call 79410bccf9SNathan Fontenot * ibm,update-properties again to get the rest of the value. 80410bccf9SNathan Fontenot * 81410bccf9SNathan Fontenot * A negative value is also the two's compliment of the actual value. 82410bccf9SNathan Fontenot */ 83410bccf9SNathan Fontenot if (vd & 0x80000000) { 84410bccf9SNathan Fontenot vd = ~vd + 1; 85410bccf9SNathan Fontenot more = 1; 86410bccf9SNathan Fontenot } 87410bccf9SNathan Fontenot 88410bccf9SNathan Fontenot if (new_prop) { 89410bccf9SNathan Fontenot /* partial property fixup */ 90410bccf9SNathan Fontenot char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL); 91410bccf9SNathan Fontenot if (!new_data) 92410bccf9SNathan Fontenot return -ENOMEM; 93410bccf9SNathan Fontenot 94410bccf9SNathan Fontenot memcpy(new_data, new_prop->value, new_prop->length); 95410bccf9SNathan Fontenot memcpy(new_data + new_prop->length, value, vd); 96410bccf9SNathan Fontenot 97410bccf9SNathan Fontenot kfree(new_prop->value); 98410bccf9SNathan Fontenot new_prop->value = new_data; 99410bccf9SNathan Fontenot new_prop->length += vd; 100410bccf9SNathan Fontenot } else { 101410bccf9SNathan Fontenot new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL); 102410bccf9SNathan Fontenot if (!new_prop) 103410bccf9SNathan Fontenot return -ENOMEM; 104410bccf9SNathan Fontenot 105410bccf9SNathan Fontenot new_prop->name = kstrdup(name, GFP_KERNEL); 106410bccf9SNathan Fontenot if (!new_prop->name) { 107410bccf9SNathan Fontenot kfree(new_prop); 108410bccf9SNathan Fontenot return -ENOMEM; 109410bccf9SNathan Fontenot } 110410bccf9SNathan Fontenot 111410bccf9SNathan Fontenot new_prop->length = vd; 112410bccf9SNathan Fontenot new_prop->value = kzalloc(new_prop->length, GFP_KERNEL); 113410bccf9SNathan Fontenot if (!new_prop->value) { 114410bccf9SNathan Fontenot kfree(new_prop->name); 115410bccf9SNathan Fontenot kfree(new_prop); 116410bccf9SNathan Fontenot return -ENOMEM; 117410bccf9SNathan Fontenot } 118410bccf9SNathan Fontenot 119410bccf9SNathan Fontenot memcpy(new_prop->value, value, vd); 120410bccf9SNathan Fontenot *prop = new_prop; 121410bccf9SNathan Fontenot } 122410bccf9SNathan Fontenot 123410bccf9SNathan Fontenot if (!more) { 12479d1c712SNathan Fontenot of_update_property(dn, new_prop); 125d8e533b4STyrel Datwyler *prop = NULL; 126410bccf9SNathan Fontenot } 127410bccf9SNathan Fontenot 128410bccf9SNathan Fontenot return 0; 129410bccf9SNathan Fontenot } 130410bccf9SNathan Fontenot 131f6ff0414STyrel Datwyler static int update_dt_node(__be32 phandle, s32 scope) 132410bccf9SNathan Fontenot { 133410bccf9SNathan Fontenot struct update_props_workarea *upwa; 134410bccf9SNathan Fontenot struct device_node *dn; 135410bccf9SNathan Fontenot struct property *prop = NULL; 136638a405fSTyrel Datwyler int i, rc, rtas_rc; 137410bccf9SNathan Fontenot char *prop_data; 138410bccf9SNathan Fontenot char *rtas_buf; 139410bccf9SNathan Fontenot int update_properties_token; 140f6ff0414STyrel Datwyler u32 nprops; 1412e9b7b02SNathan Fontenot u32 vd; 142410bccf9SNathan Fontenot 143410bccf9SNathan Fontenot update_properties_token = rtas_token("ibm,update-properties"); 144410bccf9SNathan Fontenot if (update_properties_token == RTAS_UNKNOWN_SERVICE) 145410bccf9SNathan Fontenot return -EINVAL; 146410bccf9SNathan Fontenot 147410bccf9SNathan Fontenot rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 148410bccf9SNathan Fontenot if (!rtas_buf) 149410bccf9SNathan Fontenot return -ENOMEM; 150410bccf9SNathan Fontenot 151f6ff0414STyrel Datwyler dn = of_find_node_by_phandle(be32_to_cpu(phandle)); 152410bccf9SNathan Fontenot if (!dn) { 153410bccf9SNathan Fontenot kfree(rtas_buf); 154410bccf9SNathan Fontenot return -ENOENT; 155410bccf9SNathan Fontenot } 156410bccf9SNathan Fontenot 157410bccf9SNathan Fontenot upwa = (struct update_props_workarea *)&rtas_buf[0]; 158410bccf9SNathan Fontenot upwa->phandle = phandle; 159410bccf9SNathan Fontenot 160410bccf9SNathan Fontenot do { 161638a405fSTyrel Datwyler rtas_rc = mobility_rtas_call(update_properties_token, rtas_buf, 162762ec157SNathan Fontenot scope); 163638a405fSTyrel Datwyler if (rtas_rc < 0) 164410bccf9SNathan Fontenot break; 165410bccf9SNathan Fontenot 166410bccf9SNathan Fontenot prop_data = rtas_buf + sizeof(*upwa); 167f6ff0414STyrel Datwyler nprops = be32_to_cpu(upwa->nprops); 168410bccf9SNathan Fontenot 169c8f5a57cSTyrel Datwyler /* On the first call to ibm,update-properties for a node the 170c8f5a57cSTyrel Datwyler * the first property value descriptor contains an empty 171c8f5a57cSTyrel Datwyler * property name, the property value length encoded as u32, 172c8f5a57cSTyrel Datwyler * and the property value is the node path being updated. 1732e9b7b02SNathan Fontenot */ 174c8f5a57cSTyrel Datwyler if (*prop_data == 0) { 175c8f5a57cSTyrel Datwyler prop_data++; 176f6ff0414STyrel Datwyler vd = be32_to_cpu(*(__be32 *)prop_data); 177c8f5a57cSTyrel Datwyler prop_data += vd + sizeof(vd); 178f6ff0414STyrel Datwyler nprops--; 179c8f5a57cSTyrel Datwyler } 1802e9b7b02SNathan Fontenot 181f6ff0414STyrel Datwyler for (i = 0; i < nprops; i++) { 1822e9b7b02SNathan Fontenot char *prop_name; 1832e9b7b02SNathan Fontenot 1842e9b7b02SNathan Fontenot prop_name = prop_data; 1852e9b7b02SNathan Fontenot prop_data += strlen(prop_name) + 1; 186f6ff0414STyrel Datwyler vd = be32_to_cpu(*(__be32 *)prop_data); 1872e9b7b02SNathan Fontenot prop_data += sizeof(vd); 188410bccf9SNathan Fontenot 189410bccf9SNathan Fontenot switch (vd) { 190410bccf9SNathan Fontenot case 0x00000000: 191410bccf9SNathan Fontenot /* name only property, nothing to do */ 192410bccf9SNathan Fontenot break; 193410bccf9SNathan Fontenot 194410bccf9SNathan Fontenot case 0x80000000: 195925e2d1dSSuraj Jitindar Singh of_remove_property(dn, of_find_property(dn, 196925e2d1dSSuraj Jitindar Singh prop_name, NULL)); 197410bccf9SNathan Fontenot prop = NULL; 198410bccf9SNathan Fontenot break; 199410bccf9SNathan Fontenot 200410bccf9SNathan Fontenot default: 201410bccf9SNathan Fontenot rc = update_dt_property(dn, &prop, prop_name, 202410bccf9SNathan Fontenot vd, prop_data); 203410bccf9SNathan Fontenot if (rc) { 204410bccf9SNathan Fontenot printk(KERN_ERR "Could not update %s" 205410bccf9SNathan Fontenot " property\n", prop_name); 206410bccf9SNathan Fontenot } 207410bccf9SNathan Fontenot 208410bccf9SNathan Fontenot prop_data += vd; 209410bccf9SNathan Fontenot } 210410bccf9SNathan Fontenot } 211638a405fSTyrel Datwyler } while (rtas_rc == 1); 212410bccf9SNathan Fontenot 213410bccf9SNathan Fontenot of_node_put(dn); 214410bccf9SNathan Fontenot kfree(rtas_buf); 215410bccf9SNathan Fontenot return 0; 216410bccf9SNathan Fontenot } 217410bccf9SNathan Fontenot 218f6ff0414STyrel Datwyler static int add_dt_node(__be32 parent_phandle, __be32 drc_index) 219410bccf9SNathan Fontenot { 220410bccf9SNathan Fontenot struct device_node *dn; 221410bccf9SNathan Fontenot struct device_node *parent_dn; 222410bccf9SNathan Fontenot int rc; 223410bccf9SNathan Fontenot 224f6ff0414STyrel Datwyler parent_dn = of_find_node_by_phandle(be32_to_cpu(parent_phandle)); 2258d5ff320STyrel Datwyler if (!parent_dn) 2268d5ff320STyrel Datwyler return -ENOENT; 2278d5ff320STyrel Datwyler 2288d5ff320STyrel Datwyler dn = dlpar_configure_connector(drc_index, parent_dn); 229410bccf9SNathan Fontenot if (!dn) 230410bccf9SNathan Fontenot return -ENOENT; 231410bccf9SNathan Fontenot 232410bccf9SNathan Fontenot rc = dlpar_attach_node(dn); 233410bccf9SNathan Fontenot if (rc) 234410bccf9SNathan Fontenot dlpar_free_cc_nodes(dn); 235410bccf9SNathan Fontenot 236410bccf9SNathan Fontenot of_node_put(parent_dn); 237410bccf9SNathan Fontenot return rc; 238410bccf9SNathan Fontenot } 239410bccf9SNathan Fontenot 240675d8ee6SJohn Allen static void prrn_update_node(__be32 phandle) 241675d8ee6SJohn Allen { 242675d8ee6SJohn Allen struct pseries_hp_errorlog *hp_elog; 243675d8ee6SJohn Allen struct device_node *dn; 244675d8ee6SJohn Allen 245675d8ee6SJohn Allen /* 246675d8ee6SJohn Allen * If a node is found from a the given phandle, the phandle does not 247675d8ee6SJohn Allen * represent the drc index of an LMB and we can ignore. 248675d8ee6SJohn Allen */ 249675d8ee6SJohn Allen dn = of_find_node_by_phandle(be32_to_cpu(phandle)); 250675d8ee6SJohn Allen if (dn) { 251675d8ee6SJohn Allen of_node_put(dn); 252675d8ee6SJohn Allen return; 253675d8ee6SJohn Allen } 254675d8ee6SJohn Allen 255675d8ee6SJohn Allen hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL); 256675d8ee6SJohn Allen if(!hp_elog) 257675d8ee6SJohn Allen return; 258675d8ee6SJohn Allen 259675d8ee6SJohn Allen hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; 260675d8ee6SJohn Allen hp_elog->action = PSERIES_HP_ELOG_ACTION_READD; 261675d8ee6SJohn Allen hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; 262675d8ee6SJohn Allen hp_elog->_drc_u.drc_index = phandle; 263675d8ee6SJohn Allen 264675d8ee6SJohn Allen queue_hotplug_event(hp_elog, NULL, NULL); 265675d8ee6SJohn Allen 266675d8ee6SJohn Allen kfree(hp_elog); 267675d8ee6SJohn Allen } 268675d8ee6SJohn Allen 269762ec157SNathan Fontenot int pseries_devicetree_update(s32 scope) 270410bccf9SNathan Fontenot { 271410bccf9SNathan Fontenot char *rtas_buf; 272f6ff0414STyrel Datwyler __be32 *data; 273410bccf9SNathan Fontenot int update_nodes_token; 274410bccf9SNathan Fontenot int rc; 275410bccf9SNathan Fontenot 276410bccf9SNathan Fontenot update_nodes_token = rtas_token("ibm,update-nodes"); 277410bccf9SNathan Fontenot if (update_nodes_token == RTAS_UNKNOWN_SERVICE) 278410bccf9SNathan Fontenot return -EINVAL; 279410bccf9SNathan Fontenot 280410bccf9SNathan Fontenot rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 281410bccf9SNathan Fontenot if (!rtas_buf) 282410bccf9SNathan Fontenot return -ENOMEM; 283410bccf9SNathan Fontenot 284410bccf9SNathan Fontenot do { 285762ec157SNathan Fontenot rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope); 286410bccf9SNathan Fontenot if (rc && rc != 1) 287410bccf9SNathan Fontenot break; 288410bccf9SNathan Fontenot 289f6ff0414STyrel Datwyler data = (__be32 *)rtas_buf + 4; 290f6ff0414STyrel Datwyler while (be32_to_cpu(*data) & NODE_ACTION_MASK) { 291410bccf9SNathan Fontenot int i; 292f6ff0414STyrel Datwyler u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK; 293f6ff0414STyrel Datwyler u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK; 294410bccf9SNathan Fontenot 295410bccf9SNathan Fontenot data++; 296410bccf9SNathan Fontenot 297410bccf9SNathan Fontenot for (i = 0; i < node_count; i++) { 298f6ff0414STyrel Datwyler __be32 phandle = *data++; 299f6ff0414STyrel Datwyler __be32 drc_index; 300410bccf9SNathan Fontenot 301410bccf9SNathan Fontenot switch (action) { 302410bccf9SNathan Fontenot case DELETE_DT_NODE: 303410bccf9SNathan Fontenot delete_dt_node(phandle); 304410bccf9SNathan Fontenot break; 305410bccf9SNathan Fontenot case UPDATE_DT_NODE: 306762ec157SNathan Fontenot update_dt_node(phandle, scope); 307675d8ee6SJohn Allen 308675d8ee6SJohn Allen if (scope == PRRN_SCOPE) 309675d8ee6SJohn Allen prrn_update_node(phandle); 310675d8ee6SJohn Allen 311410bccf9SNathan Fontenot break; 312410bccf9SNathan Fontenot case ADD_DT_NODE: 313410bccf9SNathan Fontenot drc_index = *data++; 314410bccf9SNathan Fontenot add_dt_node(phandle, drc_index); 315410bccf9SNathan Fontenot break; 316410bccf9SNathan Fontenot } 317410bccf9SNathan Fontenot } 318410bccf9SNathan Fontenot } 319410bccf9SNathan Fontenot } while (rc == 1); 320410bccf9SNathan Fontenot 321410bccf9SNathan Fontenot kfree(rtas_buf); 322410bccf9SNathan Fontenot return rc; 323410bccf9SNathan Fontenot } 324410bccf9SNathan Fontenot 325410bccf9SNathan Fontenot void post_mobility_fixup(void) 326410bccf9SNathan Fontenot { 327410bccf9SNathan Fontenot int rc; 328410bccf9SNathan Fontenot int activate_fw_token; 329410bccf9SNathan Fontenot 330410bccf9SNathan Fontenot activate_fw_token = rtas_token("ibm,activate-firmware"); 331410bccf9SNathan Fontenot if (activate_fw_token == RTAS_UNKNOWN_SERVICE) { 332410bccf9SNathan Fontenot printk(KERN_ERR "Could not make post-mobility " 333410bccf9SNathan Fontenot "activate-fw call.\n"); 334410bccf9SNathan Fontenot return; 335410bccf9SNathan Fontenot } 336410bccf9SNathan Fontenot 33739a33b59SHaren Myneni do { 338410bccf9SNathan Fontenot rc = rtas_call(activate_fw_token, 0, 1, NULL); 33939a33b59SHaren Myneni } while (rtas_busy_delay(rc)); 34039a33b59SHaren Myneni 34139a33b59SHaren Myneni if (rc) 34239a33b59SHaren Myneni printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc); 34339a33b59SHaren Myneni 344762ec157SNathan Fontenot rc = pseries_devicetree_update(MIGRATION_SCOPE); 345410bccf9SNathan Fontenot if (rc) 34639a33b59SHaren Myneni printk(KERN_ERR "Post-mobility device tree update " 34739a33b59SHaren Myneni "failed: %d\n", rc); 348410bccf9SNathan Fontenot 349410bccf9SNathan Fontenot return; 350410bccf9SNathan Fontenot } 351410bccf9SNathan Fontenot 352*6f428096SGreg Kroah-Hartman static ssize_t migration_store(struct class *class, 353*6f428096SGreg Kroah-Hartman struct class_attribute *attr, const char *buf, 354*6f428096SGreg Kroah-Hartman size_t count) 355410bccf9SNathan Fontenot { 356410bccf9SNathan Fontenot u64 streamid; 357410bccf9SNathan Fontenot int rc; 358410bccf9SNathan Fontenot 3591618bd53SDaniel Walter rc = kstrtou64(buf, 0, &streamid); 360410bccf9SNathan Fontenot if (rc) 361410bccf9SNathan Fontenot return rc; 362410bccf9SNathan Fontenot 363410bccf9SNathan Fontenot do { 364c03e7374STyrel Datwyler rc = rtas_ibm_suspend_me(streamid); 365c03e7374STyrel Datwyler if (rc == -EAGAIN) 366410bccf9SNathan Fontenot ssleep(1); 367c03e7374STyrel Datwyler } while (rc == -EAGAIN); 368410bccf9SNathan Fontenot 369410bccf9SNathan Fontenot if (rc) 370410bccf9SNathan Fontenot return rc; 371410bccf9SNathan Fontenot 372410bccf9SNathan Fontenot post_mobility_fixup(); 373410bccf9SNathan Fontenot return count; 374410bccf9SNathan Fontenot } 375410bccf9SNathan Fontenot 376288a298cSTyrel Datwyler /* 377288a298cSTyrel Datwyler * Used by drmgr to determine the kernel behavior of the migration interface. 378288a298cSTyrel Datwyler * 379288a298cSTyrel Datwyler * Version 1: Performs all PAPR requirements for migration including 380288a298cSTyrel Datwyler * firmware activation and device tree update. 381288a298cSTyrel Datwyler */ 382288a298cSTyrel Datwyler #define MIGRATION_API_VERSION 1 383288a298cSTyrel Datwyler 384*6f428096SGreg Kroah-Hartman static CLASS_ATTR_WO(migration); 385288a298cSTyrel Datwyler static CLASS_ATTR_STRING(api_version, S_IRUGO, __stringify(MIGRATION_API_VERSION)); 386410bccf9SNathan Fontenot 387410bccf9SNathan Fontenot static int __init mobility_sysfs_init(void) 388410bccf9SNathan Fontenot { 389410bccf9SNathan Fontenot int rc; 390410bccf9SNathan Fontenot 391410bccf9SNathan Fontenot mobility_kobj = kobject_create_and_add("mobility", kernel_kobj); 392410bccf9SNathan Fontenot if (!mobility_kobj) 393410bccf9SNathan Fontenot return -ENOMEM; 394410bccf9SNathan Fontenot 395410bccf9SNathan Fontenot rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr); 396288a298cSTyrel Datwyler if (rc) 397288a298cSTyrel Datwyler pr_err("mobility: unable to create migration sysfs file (%d)\n", rc); 398410bccf9SNathan Fontenot 399288a298cSTyrel Datwyler rc = sysfs_create_file(mobility_kobj, &class_attr_api_version.attr.attr); 400288a298cSTyrel Datwyler if (rc) 401288a298cSTyrel Datwyler pr_err("mobility: unable to create api_version sysfs file (%d)\n", rc); 402288a298cSTyrel Datwyler 403288a298cSTyrel Datwyler return 0; 404410bccf9SNathan Fontenot } 4058e83e905SMichael Ellerman machine_device_initcall(pseries, mobility_sysfs_init); 406