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