]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - arch/powerpc/platforms/pseries/mobility.c
Merge tag 'modules-for-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu...
[mirror_ubuntu-hirsute-kernel.git] / arch / powerpc / platforms / pseries / mobility.c
CommitLineData
410bccf9
NF
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>
b56eade5 15#include <linux/stat.h>
410bccf9
NF
16#include <linux/completion.h>
17#include <linux/device.h>
18#include <linux/delay.h>
19#include <linux/slab.h>
5c35a02c 20#include <linux/stringify.h>
410bccf9 21
8e83e905 22#include <asm/machdep.h>
410bccf9
NF
23#include <asm/rtas.h>
24#include "pseries.h"
25
26static struct kobject *mobility_kobj;
27
28struct update_props_workarea {
f6ff0414
TD
29 __be32 phandle;
30 __be32 state;
31 __be64 reserved;
32 __be32 nprops;
d0ef4403 33} __packed;
410bccf9
NF
34
35#define NODE_ACTION_MASK 0xff000000
36#define NODE_COUNT_MASK 0x00ffffff
37
38#define DELETE_DT_NODE 0x01000000
39#define UPDATE_DT_NODE 0x02000000
40#define ADD_DT_NODE 0x03000000
41
762ec157 42#define MIGRATION_SCOPE (1)
675d8ee6 43#define PRRN_SCOPE -2
762ec157
NF
44
45static int mobility_rtas_call(int token, char *buf, s32 scope)
410bccf9
NF
46{
47 int rc;
48
49 spin_lock(&rtas_data_buf_lock);
50
51 memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
762ec157 52 rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope);
410bccf9
NF
53 memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
54
55 spin_unlock(&rtas_data_buf_lock);
56 return rc;
57}
58
f6ff0414 59static int delete_dt_node(__be32 phandle)
410bccf9
NF
60{
61 struct device_node *dn;
62
f6ff0414 63 dn = of_find_node_by_phandle(be32_to_cpu(phandle));
410bccf9
NF
64 if (!dn)
65 return -ENOENT;
66
67 dlpar_detach_node(dn);
14cd820a 68 of_node_put(dn);
410bccf9
NF
69 return 0;
70}
71
72static int update_dt_property(struct device_node *dn, struct property **prop,
73 const char *name, u32 vd, char *value)
74{
75 struct property *new_prop = *prop;
410bccf9
NF
76 int more = 0;
77
78 /* A negative 'vd' value indicates that only part of the new property
79 * value is contained in the buffer and we need to call
80 * ibm,update-properties again to get the rest of the value.
81 *
82 * A negative value is also the two's compliment of the actual value.
83 */
84 if (vd & 0x80000000) {
85 vd = ~vd + 1;
86 more = 1;
87 }
88
89 if (new_prop) {
90 /* partial property fixup */
91 char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL);
92 if (!new_data)
93 return -ENOMEM;
94
95 memcpy(new_data, new_prop->value, new_prop->length);
96 memcpy(new_data + new_prop->length, value, vd);
97
98 kfree(new_prop->value);
99 new_prop->value = new_data;
100 new_prop->length += vd;
101 } else {
102 new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
103 if (!new_prop)
104 return -ENOMEM;
105
106 new_prop->name = kstrdup(name, GFP_KERNEL);
107 if (!new_prop->name) {
108 kfree(new_prop);
109 return -ENOMEM;
110 }
111
112 new_prop->length = vd;
113 new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
114 if (!new_prop->value) {
115 kfree(new_prop->name);
116 kfree(new_prop);
117 return -ENOMEM;
118 }
119
120 memcpy(new_prop->value, value, vd);
121 *prop = new_prop;
122 }
123
124 if (!more) {
79d1c712 125 of_update_property(dn, new_prop);
d8e533b4 126 *prop = NULL;
410bccf9
NF
127 }
128
129 return 0;
130}
131
f6ff0414 132static int update_dt_node(__be32 phandle, s32 scope)
410bccf9
NF
133{
134 struct update_props_workarea *upwa;
135 struct device_node *dn;
136 struct property *prop = NULL;
638a405f 137 int i, rc, rtas_rc;
410bccf9
NF
138 char *prop_data;
139 char *rtas_buf;
140 int update_properties_token;
f6ff0414 141 u32 nprops;
2e9b7b02 142 u32 vd;
410bccf9
NF
143
144 update_properties_token = rtas_token("ibm,update-properties");
145 if (update_properties_token == RTAS_UNKNOWN_SERVICE)
146 return -EINVAL;
147
148 rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
149 if (!rtas_buf)
150 return -ENOMEM;
151
f6ff0414 152 dn = of_find_node_by_phandle(be32_to_cpu(phandle));
410bccf9
NF
153 if (!dn) {
154 kfree(rtas_buf);
155 return -ENOENT;
156 }
157
158 upwa = (struct update_props_workarea *)&rtas_buf[0];
159 upwa->phandle = phandle;
160
161 do {
638a405f 162 rtas_rc = mobility_rtas_call(update_properties_token, rtas_buf,
762ec157 163 scope);
638a405f 164 if (rtas_rc < 0)
410bccf9
NF
165 break;
166
167 prop_data = rtas_buf + sizeof(*upwa);
f6ff0414 168 nprops = be32_to_cpu(upwa->nprops);
410bccf9 169
c8f5a57c
TD
170 /* On the first call to ibm,update-properties for a node the
171 * the first property value descriptor contains an empty
172 * property name, the property value length encoded as u32,
173 * and the property value is the node path being updated.
2e9b7b02 174 */
c8f5a57c
TD
175 if (*prop_data == 0) {
176 prop_data++;
f6ff0414 177 vd = be32_to_cpu(*(__be32 *)prop_data);
c8f5a57c 178 prop_data += vd + sizeof(vd);
f6ff0414 179 nprops--;
c8f5a57c 180 }
2e9b7b02 181
f6ff0414 182 for (i = 0; i < nprops; i++) {
410bccf9 183 char *prop_name;
410bccf9 184
2e9b7b02 185 prop_name = prop_data;
410bccf9 186 prop_data += strlen(prop_name) + 1;
f6ff0414 187 vd = be32_to_cpu(*(__be32 *)prop_data);
2e9b7b02 188 prop_data += sizeof(vd);
410bccf9
NF
189
190 switch (vd) {
191 case 0x00000000:
192 /* name only property, nothing to do */
193 break;
194
195 case 0x80000000:
925e2d1d
SJS
196 of_remove_property(dn, of_find_property(dn,
197 prop_name, NULL));
410bccf9
NF
198 prop = NULL;
199 break;
200
201 default:
202 rc = update_dt_property(dn, &prop, prop_name,
203 vd, prop_data);
204 if (rc) {
205 printk(KERN_ERR "Could not update %s"
206 " property\n", prop_name);
207 }
208
209 prop_data += vd;
210 }
211 }
638a405f 212 } while (rtas_rc == 1);
410bccf9
NF
213
214 of_node_put(dn);
215 kfree(rtas_buf);
216 return 0;
217}
218
f6ff0414 219static int add_dt_node(__be32 parent_phandle, __be32 drc_index)
410bccf9
NF
220{
221 struct device_node *dn;
222 struct device_node *parent_dn;
223 int rc;
224
f6ff0414 225 parent_dn = of_find_node_by_phandle(be32_to_cpu(parent_phandle));
8d5ff320 226 if (!parent_dn)
410bccf9
NF
227 return -ENOENT;
228
8d5ff320 229 dn = dlpar_configure_connector(drc_index, parent_dn);
b537ca6f
TD
230 if (!dn) {
231 of_node_put(parent_dn);
410bccf9 232 return -ENOENT;
b537ca6f 233 }
410bccf9 234
215ee763 235 rc = dlpar_attach_node(dn, parent_dn);
410bccf9
NF
236 if (rc)
237 dlpar_free_cc_nodes(dn);
238
239 of_node_put(parent_dn);
240 return rc;
241}
242
675d8ee6
JA
243static void prrn_update_node(__be32 phandle)
244{
fd12527a 245 struct pseries_hp_errorlog hp_elog;
675d8ee6
JA
246 struct device_node *dn;
247
248 /*
249 * If a node is found from a the given phandle, the phandle does not
250 * represent the drc index of an LMB and we can ignore.
251 */
252 dn = of_find_node_by_phandle(be32_to_cpu(phandle));
253 if (dn) {
254 of_node_put(dn);
255 return;
256 }
257
fd12527a
NF
258 hp_elog.resource = PSERIES_HP_ELOG_RESOURCE_MEM;
259 hp_elog.action = PSERIES_HP_ELOG_ACTION_READD;
260 hp_elog.id_type = PSERIES_HP_ELOG_ID_DRC_INDEX;
261 hp_elog._drc_u.drc_index = phandle;
675d8ee6 262
fd12527a 263 handle_dlpar_errorlog(&hp_elog);
675d8ee6
JA
264}
265
762ec157 266int pseries_devicetree_update(s32 scope)
410bccf9
NF
267{
268 char *rtas_buf;
f6ff0414 269 __be32 *data;
410bccf9
NF
270 int update_nodes_token;
271 int rc;
272
273 update_nodes_token = rtas_token("ibm,update-nodes");
274 if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
275 return -EINVAL;
276
277 rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
278 if (!rtas_buf)
279 return -ENOMEM;
280
281 do {
762ec157 282 rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope);
410bccf9
NF
283 if (rc && rc != 1)
284 break;
285
f6ff0414
TD
286 data = (__be32 *)rtas_buf + 4;
287 while (be32_to_cpu(*data) & NODE_ACTION_MASK) {
410bccf9 288 int i;
f6ff0414
TD
289 u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK;
290 u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK;
410bccf9
NF
291
292 data++;
293
294 for (i = 0; i < node_count; i++) {
f6ff0414
TD
295 __be32 phandle = *data++;
296 __be32 drc_index;
410bccf9
NF
297
298 switch (action) {
299 case DELETE_DT_NODE:
300 delete_dt_node(phandle);
301 break;
302 case UPDATE_DT_NODE:
762ec157 303 update_dt_node(phandle, scope);
675d8ee6
JA
304
305 if (scope == PRRN_SCOPE)
306 prrn_update_node(phandle);
307
410bccf9
NF
308 break;
309 case ADD_DT_NODE:
310 drc_index = *data++;
311 add_dt_node(phandle, drc_index);
312 break;
313 }
314 }
315 }
316 } while (rc == 1);
317
318 kfree(rtas_buf);
319 return rc;
320}
321
322void post_mobility_fixup(void)
323{
324 int rc;
325 int activate_fw_token;
326
410bccf9
NF
327 activate_fw_token = rtas_token("ibm,activate-firmware");
328 if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
329 printk(KERN_ERR "Could not make post-mobility "
330 "activate-fw call.\n");
331 return;
332 }
333
39a33b59
HM
334 do {
335 rc = rtas_call(activate_fw_token, 0, 1, NULL);
336 } while (rtas_busy_delay(rc));
337
338 if (rc)
410bccf9 339 printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
39a33b59
HM
340
341 rc = pseries_devicetree_update(MIGRATION_SCOPE);
342 if (rc)
343 printk(KERN_ERR "Post-mobility device tree update "
344 "failed: %d\n", rc);
410bccf9 345
921bc6cf
ME
346 /* Possibly switch to a new RFI flush type */
347 pseries_setup_rfi_flush();
348
410bccf9
NF
349 return;
350}
351
6f428096
GKH
352static ssize_t migration_store(struct class *class,
353 struct class_attribute *attr, const char *buf,
354 size_t count)
410bccf9 355{
410bccf9
NF
356 u64 streamid;
357 int rc;
358
1618bd53 359 rc = kstrtou64(buf, 0, &streamid);
410bccf9
NF
360 if (rc)
361 return rc;
362
65b9fdad
MB
363 stop_topology_update();
364
410bccf9 365 do {
c03e7374
TD
366 rc = rtas_ibm_suspend_me(streamid);
367 if (rc == -EAGAIN)
410bccf9 368 ssleep(1);
c03e7374 369 } while (rc == -EAGAIN);
410bccf9
NF
370
371 if (rc)
372 return rc;
410bccf9
NF
373
374 post_mobility_fixup();
65b9fdad
MB
375
376 start_topology_update();
377
410bccf9
NF
378 return count;
379}
380
288a298c
TD
381/*
382 * Used by drmgr to determine the kernel behavior of the migration interface.
383 *
384 * Version 1: Performs all PAPR requirements for migration including
385 * firmware activation and device tree update.
386 */
387#define MIGRATION_API_VERSION 1
388
6f428096 389static CLASS_ATTR_WO(migration);
57ad583f 390static CLASS_ATTR_STRING(api_version, 0444, __stringify(MIGRATION_API_VERSION));
410bccf9
NF
391
392static int __init mobility_sysfs_init(void)
393{
394 int rc;
395
396 mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
397 if (!mobility_kobj)
398 return -ENOMEM;
399
400 rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
288a298c
TD
401 if (rc)
402 pr_err("mobility: unable to create migration sysfs file (%d)\n", rc);
410bccf9 403
288a298c
TD
404 rc = sysfs_create_file(mobility_kobj, &class_attr_api_version.attr.attr);
405 if (rc)
406 pr_err("mobility: unable to create api_version sysfs file (%d)\n", rc);
407
408 return 0;
410bccf9 409}
8e83e905 410machine_device_initcall(pseries, mobility_sysfs_init);