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> 20*5c35a02cSChristophe Leroy #include <linux/stringify.h> 21410bccf9SNathan Fontenot 228e83e905SMichael Ellerman #include <asm/machdep.h> 23410bccf9SNathan Fontenot #include <asm/rtas.h> 24410bccf9SNathan Fontenot #include "pseries.h" 25410bccf9SNathan Fontenot 26410bccf9SNathan Fontenot static struct kobject *mobility_kobj; 27410bccf9SNathan Fontenot 28410bccf9SNathan Fontenot struct update_props_workarea { 29f6ff0414STyrel Datwyler __be32 phandle; 30f6ff0414STyrel Datwyler __be32 state; 31f6ff0414STyrel Datwyler __be64 reserved; 32f6ff0414STyrel Datwyler __be32 nprops; 33d0ef4403STyrel Datwyler } __packed; 34410bccf9SNathan Fontenot 35410bccf9SNathan Fontenot #define NODE_ACTION_MASK 0xff000000 36410bccf9SNathan Fontenot #define NODE_COUNT_MASK 0x00ffffff 37410bccf9SNathan Fontenot 38410bccf9SNathan Fontenot #define DELETE_DT_NODE 0x01000000 39410bccf9SNathan Fontenot #define UPDATE_DT_NODE 0x02000000 40410bccf9SNathan Fontenot #define ADD_DT_NODE 0x03000000 41410bccf9SNathan Fontenot 42762ec157SNathan Fontenot #define MIGRATION_SCOPE (1) 43675d8ee6SJohn Allen #define PRRN_SCOPE -2 44762ec157SNathan Fontenot 45762ec157SNathan Fontenot static int mobility_rtas_call(int token, char *buf, s32 scope) 46410bccf9SNathan Fontenot { 47410bccf9SNathan Fontenot int rc; 48410bccf9SNathan Fontenot 49410bccf9SNathan Fontenot spin_lock(&rtas_data_buf_lock); 50410bccf9SNathan Fontenot 51410bccf9SNathan Fontenot memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE); 52762ec157SNathan Fontenot rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope); 53410bccf9SNathan Fontenot memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 54410bccf9SNathan Fontenot 55410bccf9SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 56410bccf9SNathan Fontenot return rc; 57410bccf9SNathan Fontenot } 58410bccf9SNathan Fontenot 59f6ff0414STyrel Datwyler static int delete_dt_node(__be32 phandle) 60410bccf9SNathan Fontenot { 61410bccf9SNathan Fontenot struct device_node *dn; 62410bccf9SNathan Fontenot 63f6ff0414STyrel Datwyler dn = of_find_node_by_phandle(be32_to_cpu(phandle)); 64410bccf9SNathan Fontenot if (!dn) 65410bccf9SNathan Fontenot return -ENOENT; 66410bccf9SNathan Fontenot 67410bccf9SNathan Fontenot dlpar_detach_node(dn); 6814cd820aSTyrel Datwyler of_node_put(dn); 69410bccf9SNathan Fontenot return 0; 70410bccf9SNathan Fontenot } 71410bccf9SNathan Fontenot 72410bccf9SNathan Fontenot static int update_dt_property(struct device_node *dn, struct property **prop, 73410bccf9SNathan Fontenot const char *name, u32 vd, char *value) 74410bccf9SNathan Fontenot { 75410bccf9SNathan Fontenot struct property *new_prop = *prop; 76410bccf9SNathan Fontenot int more = 0; 77410bccf9SNathan Fontenot 78410bccf9SNathan Fontenot /* A negative 'vd' value indicates that only part of the new property 79410bccf9SNathan Fontenot * value is contained in the buffer and we need to call 80410bccf9SNathan Fontenot * ibm,update-properties again to get the rest of the value. 81410bccf9SNathan Fontenot * 82410bccf9SNathan Fontenot * A negative value is also the two's compliment of the actual value. 83410bccf9SNathan Fontenot */ 84410bccf9SNathan Fontenot if (vd & 0x80000000) { 85410bccf9SNathan Fontenot vd = ~vd + 1; 86410bccf9SNathan Fontenot more = 1; 87410bccf9SNathan Fontenot } 88410bccf9SNathan Fontenot 89410bccf9SNathan Fontenot if (new_prop) { 90410bccf9SNathan Fontenot /* partial property fixup */ 91410bccf9SNathan Fontenot char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL); 92410bccf9SNathan Fontenot if (!new_data) 93410bccf9SNathan Fontenot return -ENOMEM; 94410bccf9SNathan Fontenot 95410bccf9SNathan Fontenot memcpy(new_data, new_prop->value, new_prop->length); 96410bccf9SNathan Fontenot memcpy(new_data + new_prop->length, value, vd); 97410bccf9SNathan Fontenot 98410bccf9SNathan Fontenot kfree(new_prop->value); 99410bccf9SNathan Fontenot new_prop->value = new_data; 100410bccf9SNathan Fontenot new_prop->length += vd; 101410bccf9SNathan Fontenot } else { 102410bccf9SNathan Fontenot new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL); 103410bccf9SNathan Fontenot if (!new_prop) 104410bccf9SNathan Fontenot return -ENOMEM; 105410bccf9SNathan Fontenot 106410bccf9SNathan Fontenot new_prop->name = kstrdup(name, GFP_KERNEL); 107410bccf9SNathan Fontenot if (!new_prop->name) { 108410bccf9SNathan Fontenot kfree(new_prop); 109410bccf9SNathan Fontenot return -ENOMEM; 110410bccf9SNathan Fontenot } 111410bccf9SNathan Fontenot 112410bccf9SNathan Fontenot new_prop->length = vd; 113410bccf9SNathan Fontenot new_prop->value = kzalloc(new_prop->length, GFP_KERNEL); 114410bccf9SNathan Fontenot if (!new_prop->value) { 115410bccf9SNathan Fontenot kfree(new_prop->name); 116410bccf9SNathan Fontenot kfree(new_prop); 117410bccf9SNathan Fontenot return -ENOMEM; 118410bccf9SNathan Fontenot } 119410bccf9SNathan Fontenot 120410bccf9SNathan Fontenot memcpy(new_prop->value, value, vd); 121410bccf9SNathan Fontenot *prop = new_prop; 122410bccf9SNathan Fontenot } 123410bccf9SNathan Fontenot 124410bccf9SNathan Fontenot if (!more) { 12579d1c712SNathan Fontenot of_update_property(dn, new_prop); 126d8e533b4STyrel Datwyler *prop = NULL; 127410bccf9SNathan Fontenot } 128410bccf9SNathan Fontenot 129410bccf9SNathan Fontenot return 0; 130410bccf9SNathan Fontenot } 131410bccf9SNathan Fontenot 132f6ff0414STyrel Datwyler static int update_dt_node(__be32 phandle, s32 scope) 133410bccf9SNathan Fontenot { 134410bccf9SNathan Fontenot struct update_props_workarea *upwa; 135410bccf9SNathan Fontenot struct device_node *dn; 136410bccf9SNathan Fontenot struct property *prop = NULL; 137638a405fSTyrel Datwyler int i, rc, rtas_rc; 138410bccf9SNathan Fontenot char *prop_data; 139410bccf9SNathan Fontenot char *rtas_buf; 140410bccf9SNathan Fontenot int update_properties_token; 141f6ff0414STyrel Datwyler u32 nprops; 1422e9b7b02SNathan Fontenot u32 vd; 143410bccf9SNathan Fontenot 144410bccf9SNathan Fontenot update_properties_token = rtas_token("ibm,update-properties"); 145410bccf9SNathan Fontenot if (update_properties_token == RTAS_UNKNOWN_SERVICE) 146410bccf9SNathan Fontenot return -EINVAL; 147410bccf9SNathan Fontenot 148410bccf9SNathan Fontenot rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 149410bccf9SNathan Fontenot if (!rtas_buf) 150410bccf9SNathan Fontenot return -ENOMEM; 151410bccf9SNathan Fontenot 152f6ff0414STyrel Datwyler dn = of_find_node_by_phandle(be32_to_cpu(phandle)); 153410bccf9SNathan Fontenot if (!dn) { 154410bccf9SNathan Fontenot kfree(rtas_buf); 155410bccf9SNathan Fontenot return -ENOENT; 156410bccf9SNathan Fontenot } 157410bccf9SNathan Fontenot 158410bccf9SNathan Fontenot upwa = (struct update_props_workarea *)&rtas_buf[0]; 159410bccf9SNathan Fontenot upwa->phandle = phandle; 160410bccf9SNathan Fontenot 161410bccf9SNathan Fontenot do { 162638a405fSTyrel Datwyler rtas_rc = mobility_rtas_call(update_properties_token, rtas_buf, 163762ec157SNathan Fontenot scope); 164638a405fSTyrel Datwyler if (rtas_rc < 0) 165410bccf9SNathan Fontenot break; 166410bccf9SNathan Fontenot 167410bccf9SNathan Fontenot prop_data = rtas_buf + sizeof(*upwa); 168f6ff0414STyrel Datwyler nprops = be32_to_cpu(upwa->nprops); 169410bccf9SNathan Fontenot 170c8f5a57cSTyrel Datwyler /* On the first call to ibm,update-properties for a node the 171c8f5a57cSTyrel Datwyler * the first property value descriptor contains an empty 172c8f5a57cSTyrel Datwyler * property name, the property value length encoded as u32, 173c8f5a57cSTyrel Datwyler * and the property value is the node path being updated. 1742e9b7b02SNathan Fontenot */ 175c8f5a57cSTyrel Datwyler if (*prop_data == 0) { 176c8f5a57cSTyrel Datwyler prop_data++; 177f6ff0414STyrel Datwyler vd = be32_to_cpu(*(__be32 *)prop_data); 178c8f5a57cSTyrel Datwyler prop_data += vd + sizeof(vd); 179f6ff0414STyrel Datwyler nprops--; 180c8f5a57cSTyrel Datwyler } 1812e9b7b02SNathan Fontenot 182f6ff0414STyrel Datwyler for (i = 0; i < nprops; i++) { 1832e9b7b02SNathan Fontenot char *prop_name; 1842e9b7b02SNathan Fontenot 1852e9b7b02SNathan Fontenot prop_name = prop_data; 1862e9b7b02SNathan Fontenot prop_data += strlen(prop_name) + 1; 187f6ff0414STyrel Datwyler vd = be32_to_cpu(*(__be32 *)prop_data); 1882e9b7b02SNathan Fontenot prop_data += sizeof(vd); 189410bccf9SNathan Fontenot 190410bccf9SNathan Fontenot switch (vd) { 191410bccf9SNathan Fontenot case 0x00000000: 192410bccf9SNathan Fontenot /* name only property, nothing to do */ 193410bccf9SNathan Fontenot break; 194410bccf9SNathan Fontenot 195410bccf9SNathan Fontenot case 0x80000000: 196925e2d1dSSuraj Jitindar Singh of_remove_property(dn, of_find_property(dn, 197925e2d1dSSuraj Jitindar Singh prop_name, NULL)); 198410bccf9SNathan Fontenot prop = NULL; 199410bccf9SNathan Fontenot break; 200410bccf9SNathan Fontenot 201410bccf9SNathan Fontenot default: 202410bccf9SNathan Fontenot rc = update_dt_property(dn, &prop, prop_name, 203410bccf9SNathan Fontenot vd, prop_data); 204410bccf9SNathan Fontenot if (rc) { 205410bccf9SNathan Fontenot printk(KERN_ERR "Could not update %s" 206410bccf9SNathan Fontenot " property\n", prop_name); 207410bccf9SNathan Fontenot } 208410bccf9SNathan Fontenot 209410bccf9SNathan Fontenot prop_data += vd; 210410bccf9SNathan Fontenot } 211410bccf9SNathan Fontenot } 212638a405fSTyrel Datwyler } while (rtas_rc == 1); 213410bccf9SNathan Fontenot 214410bccf9SNathan Fontenot of_node_put(dn); 215410bccf9SNathan Fontenot kfree(rtas_buf); 216410bccf9SNathan Fontenot return 0; 217410bccf9SNathan Fontenot } 218410bccf9SNathan Fontenot 219f6ff0414STyrel Datwyler static int add_dt_node(__be32 parent_phandle, __be32 drc_index) 220410bccf9SNathan Fontenot { 221410bccf9SNathan Fontenot struct device_node *dn; 222410bccf9SNathan Fontenot struct device_node *parent_dn; 223410bccf9SNathan Fontenot int rc; 224410bccf9SNathan Fontenot 225f6ff0414STyrel Datwyler parent_dn = of_find_node_by_phandle(be32_to_cpu(parent_phandle)); 2268d5ff320STyrel Datwyler if (!parent_dn) 2278d5ff320STyrel Datwyler return -ENOENT; 2288d5ff320STyrel Datwyler 2298d5ff320STyrel Datwyler dn = dlpar_configure_connector(drc_index, parent_dn); 230b537ca6fSTyrel Datwyler if (!dn) { 231b537ca6fSTyrel Datwyler of_node_put(parent_dn); 232410bccf9SNathan Fontenot return -ENOENT; 233b537ca6fSTyrel Datwyler } 234410bccf9SNathan Fontenot 235215ee763SRob Herring rc = dlpar_attach_node(dn, parent_dn); 236410bccf9SNathan Fontenot if (rc) 237410bccf9SNathan Fontenot dlpar_free_cc_nodes(dn); 238410bccf9SNathan Fontenot 239410bccf9SNathan Fontenot of_node_put(parent_dn); 240410bccf9SNathan Fontenot return rc; 241410bccf9SNathan Fontenot } 242410bccf9SNathan Fontenot 243675d8ee6SJohn Allen static void prrn_update_node(__be32 phandle) 244675d8ee6SJohn Allen { 245675d8ee6SJohn Allen struct pseries_hp_errorlog *hp_elog; 246675d8ee6SJohn Allen struct device_node *dn; 247675d8ee6SJohn Allen 248675d8ee6SJohn Allen /* 249675d8ee6SJohn Allen * If a node is found from a the given phandle, the phandle does not 250675d8ee6SJohn Allen * represent the drc index of an LMB and we can ignore. 251675d8ee6SJohn Allen */ 252675d8ee6SJohn Allen dn = of_find_node_by_phandle(be32_to_cpu(phandle)); 253675d8ee6SJohn Allen if (dn) { 254675d8ee6SJohn Allen of_node_put(dn); 255675d8ee6SJohn Allen return; 256675d8ee6SJohn Allen } 257675d8ee6SJohn Allen 258675d8ee6SJohn Allen hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL); 259675d8ee6SJohn Allen if(!hp_elog) 260675d8ee6SJohn Allen return; 261675d8ee6SJohn Allen 262675d8ee6SJohn Allen hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; 263675d8ee6SJohn Allen hp_elog->action = PSERIES_HP_ELOG_ACTION_READD; 264675d8ee6SJohn Allen hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; 265675d8ee6SJohn Allen hp_elog->_drc_u.drc_index = phandle; 266675d8ee6SJohn Allen 267675d8ee6SJohn Allen queue_hotplug_event(hp_elog, NULL, NULL); 268675d8ee6SJohn Allen 269675d8ee6SJohn Allen kfree(hp_elog); 270675d8ee6SJohn Allen } 271675d8ee6SJohn Allen 272762ec157SNathan Fontenot int pseries_devicetree_update(s32 scope) 273410bccf9SNathan Fontenot { 274410bccf9SNathan Fontenot char *rtas_buf; 275f6ff0414STyrel Datwyler __be32 *data; 276410bccf9SNathan Fontenot int update_nodes_token; 277410bccf9SNathan Fontenot int rc; 278410bccf9SNathan Fontenot 279410bccf9SNathan Fontenot update_nodes_token = rtas_token("ibm,update-nodes"); 280410bccf9SNathan Fontenot if (update_nodes_token == RTAS_UNKNOWN_SERVICE) 281410bccf9SNathan Fontenot return -EINVAL; 282410bccf9SNathan Fontenot 283410bccf9SNathan Fontenot rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 284410bccf9SNathan Fontenot if (!rtas_buf) 285410bccf9SNathan Fontenot return -ENOMEM; 286410bccf9SNathan Fontenot 287410bccf9SNathan Fontenot do { 288762ec157SNathan Fontenot rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope); 289410bccf9SNathan Fontenot if (rc && rc != 1) 290410bccf9SNathan Fontenot break; 291410bccf9SNathan Fontenot 292f6ff0414STyrel Datwyler data = (__be32 *)rtas_buf + 4; 293f6ff0414STyrel Datwyler while (be32_to_cpu(*data) & NODE_ACTION_MASK) { 294410bccf9SNathan Fontenot int i; 295f6ff0414STyrel Datwyler u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK; 296f6ff0414STyrel Datwyler u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK; 297410bccf9SNathan Fontenot 298410bccf9SNathan Fontenot data++; 299410bccf9SNathan Fontenot 300410bccf9SNathan Fontenot for (i = 0; i < node_count; i++) { 301f6ff0414STyrel Datwyler __be32 phandle = *data++; 302f6ff0414STyrel Datwyler __be32 drc_index; 303410bccf9SNathan Fontenot 304410bccf9SNathan Fontenot switch (action) { 305410bccf9SNathan Fontenot case DELETE_DT_NODE: 306410bccf9SNathan Fontenot delete_dt_node(phandle); 307410bccf9SNathan Fontenot break; 308410bccf9SNathan Fontenot case UPDATE_DT_NODE: 309762ec157SNathan Fontenot update_dt_node(phandle, scope); 310675d8ee6SJohn Allen 311675d8ee6SJohn Allen if (scope == PRRN_SCOPE) 312675d8ee6SJohn Allen prrn_update_node(phandle); 313675d8ee6SJohn Allen 314410bccf9SNathan Fontenot break; 315410bccf9SNathan Fontenot case ADD_DT_NODE: 316410bccf9SNathan Fontenot drc_index = *data++; 317410bccf9SNathan Fontenot add_dt_node(phandle, drc_index); 318410bccf9SNathan Fontenot break; 319410bccf9SNathan Fontenot } 320410bccf9SNathan Fontenot } 321410bccf9SNathan Fontenot } 322410bccf9SNathan Fontenot } while (rc == 1); 323410bccf9SNathan Fontenot 324410bccf9SNathan Fontenot kfree(rtas_buf); 325410bccf9SNathan Fontenot return rc; 326410bccf9SNathan Fontenot } 327410bccf9SNathan Fontenot 328410bccf9SNathan Fontenot void post_mobility_fixup(void) 329410bccf9SNathan Fontenot { 330410bccf9SNathan Fontenot int rc; 331410bccf9SNathan Fontenot int activate_fw_token; 332410bccf9SNathan Fontenot 333410bccf9SNathan Fontenot activate_fw_token = rtas_token("ibm,activate-firmware"); 334410bccf9SNathan Fontenot if (activate_fw_token == RTAS_UNKNOWN_SERVICE) { 335410bccf9SNathan Fontenot printk(KERN_ERR "Could not make post-mobility " 336410bccf9SNathan Fontenot "activate-fw call.\n"); 337410bccf9SNathan Fontenot return; 338410bccf9SNathan Fontenot } 339410bccf9SNathan Fontenot 34039a33b59SHaren Myneni do { 341410bccf9SNathan Fontenot rc = rtas_call(activate_fw_token, 0, 1, NULL); 34239a33b59SHaren Myneni } while (rtas_busy_delay(rc)); 34339a33b59SHaren Myneni 34439a33b59SHaren Myneni if (rc) 34539a33b59SHaren Myneni printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc); 34639a33b59SHaren Myneni 347762ec157SNathan Fontenot rc = pseries_devicetree_update(MIGRATION_SCOPE); 348410bccf9SNathan Fontenot if (rc) 34939a33b59SHaren Myneni printk(KERN_ERR "Post-mobility device tree update " 35039a33b59SHaren Myneni "failed: %d\n", rc); 351410bccf9SNathan Fontenot 352921bc6cfSMichael Ellerman /* Possibly switch to a new RFI flush type */ 353921bc6cfSMichael Ellerman pseries_setup_rfi_flush(); 354921bc6cfSMichael Ellerman 355410bccf9SNathan Fontenot return; 356410bccf9SNathan Fontenot } 357410bccf9SNathan Fontenot 3586f428096SGreg Kroah-Hartman static ssize_t migration_store(struct class *class, 3596f428096SGreg Kroah-Hartman struct class_attribute *attr, const char *buf, 3606f428096SGreg Kroah-Hartman size_t count) 361410bccf9SNathan Fontenot { 362410bccf9SNathan Fontenot u64 streamid; 363410bccf9SNathan Fontenot int rc; 364410bccf9SNathan Fontenot 3651618bd53SDaniel Walter rc = kstrtou64(buf, 0, &streamid); 366410bccf9SNathan Fontenot if (rc) 367410bccf9SNathan Fontenot return rc; 368410bccf9SNathan Fontenot 369410bccf9SNathan Fontenot do { 370c03e7374STyrel Datwyler rc = rtas_ibm_suspend_me(streamid); 371c03e7374STyrel Datwyler if (rc == -EAGAIN) 372410bccf9SNathan Fontenot ssleep(1); 373c03e7374STyrel Datwyler } while (rc == -EAGAIN); 374410bccf9SNathan Fontenot 375410bccf9SNathan Fontenot if (rc) 376410bccf9SNathan Fontenot return rc; 377410bccf9SNathan Fontenot 378410bccf9SNathan Fontenot post_mobility_fixup(); 379410bccf9SNathan Fontenot return count; 380410bccf9SNathan Fontenot } 381410bccf9SNathan Fontenot 382288a298cSTyrel Datwyler /* 383288a298cSTyrel Datwyler * Used by drmgr to determine the kernel behavior of the migration interface. 384288a298cSTyrel Datwyler * 385288a298cSTyrel Datwyler * Version 1: Performs all PAPR requirements for migration including 386288a298cSTyrel Datwyler * firmware activation and device tree update. 387288a298cSTyrel Datwyler */ 388288a298cSTyrel Datwyler #define MIGRATION_API_VERSION 1 389288a298cSTyrel Datwyler 3906f428096SGreg Kroah-Hartman static CLASS_ATTR_WO(migration); 39157ad583fSRussell Currey static CLASS_ATTR_STRING(api_version, 0444, __stringify(MIGRATION_API_VERSION)); 392410bccf9SNathan Fontenot 393410bccf9SNathan Fontenot static int __init mobility_sysfs_init(void) 394410bccf9SNathan Fontenot { 395410bccf9SNathan Fontenot int rc; 396410bccf9SNathan Fontenot 397410bccf9SNathan Fontenot mobility_kobj = kobject_create_and_add("mobility", kernel_kobj); 398410bccf9SNathan Fontenot if (!mobility_kobj) 399410bccf9SNathan Fontenot return -ENOMEM; 400410bccf9SNathan Fontenot 401410bccf9SNathan Fontenot rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr); 402288a298cSTyrel Datwyler if (rc) 403288a298cSTyrel Datwyler pr_err("mobility: unable to create migration sysfs file (%d)\n", rc); 404410bccf9SNathan Fontenot 405288a298cSTyrel Datwyler rc = sysfs_create_file(mobility_kobj, &class_attr_api_version.attr.attr); 406288a298cSTyrel Datwyler if (rc) 407288a298cSTyrel Datwyler pr_err("mobility: unable to create api_version sysfs file (%d)\n", rc); 408288a298cSTyrel Datwyler 409288a298cSTyrel Datwyler return 0; 410410bccf9SNathan Fontenot } 4118e83e905SMichael Ellerman machine_device_initcall(pseries, mobility_sysfs_init); 412