Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mobility.c
Go to the documentation of this file.
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/stat.h>
16 #include <linux/completion.h>
17 #include <linux/device.h>
18 #include <linux/delay.h>
19 #include <linux/slab.h>
20 
21 #include <asm/rtas.h>
22 #include "pseries.h"
23 
24 static struct kobject *mobility_kobj;
25 
31 };
32 
33 #define NODE_ACTION_MASK 0xff000000
34 #define NODE_COUNT_MASK 0x00ffffff
35 
36 #define DELETE_DT_NODE 0x01000000
37 #define UPDATE_DT_NODE 0x02000000
38 #define ADD_DT_NODE 0x03000000
39 
40 static int mobility_rtas_call(int token, char *buf)
41 {
42  int rc;
43 
44  spin_lock(&rtas_data_buf_lock);
45 
46  memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
47  rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, 1);
48  memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
49 
50  spin_unlock(&rtas_data_buf_lock);
51  return rc;
52 }
53 
54 static int delete_dt_node(u32 phandle)
55 {
56  struct device_node *dn;
57 
58  dn = of_find_node_by_phandle(phandle);
59  if (!dn)
60  return -ENOENT;
61 
63  return 0;
64 }
65 
66 static int update_dt_property(struct device_node *dn, struct property **prop,
67  const char *name, u32 vd, char *value)
68 {
69  struct property *new_prop = *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  prom_update_property(dn, new_prop);
120  new_prop = NULL;
121  }
122 
123  return 0;
124 }
125 
126 static int update_dt_node(u32 phandle)
127 {
128  struct update_props_workarea *upwa;
129  struct device_node *dn;
130  struct property *prop = NULL;
131  int i, rc;
132  char *prop_data;
133  char *rtas_buf;
134  int update_properties_token;
135 
136  update_properties_token = rtas_token("ibm,update-properties");
137  if (update_properties_token == RTAS_UNKNOWN_SERVICE)
138  return -EINVAL;
139 
140  rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
141  if (!rtas_buf)
142  return -ENOMEM;
143 
144  dn = of_find_node_by_phandle(phandle);
145  if (!dn) {
146  kfree(rtas_buf);
147  return -ENOENT;
148  }
149 
150  upwa = (struct update_props_workarea *)&rtas_buf[0];
151  upwa->phandle = phandle;
152 
153  do {
154  rc = mobility_rtas_call(update_properties_token, rtas_buf);
155  if (rc < 0)
156  break;
157 
158  prop_data = rtas_buf + sizeof(*upwa);
159 
160  for (i = 0; i < upwa->nprops; i++) {
161  char *prop_name;
162  u32 vd;
163 
164  prop_name = prop_data + 1;
165  prop_data += strlen(prop_name) + 1;
166  vd = *prop_data++;
167 
168  switch (vd) {
169  case 0x00000000:
170  /* name only property, nothing to do */
171  break;
172 
173  case 0x80000000:
174  prop = of_find_property(dn, prop_name, NULL);
175  prom_remove_property(dn, prop);
176  prop = NULL;
177  break;
178 
179  default:
180  rc = update_dt_property(dn, &prop, prop_name,
181  vd, prop_data);
182  if (rc) {
183  printk(KERN_ERR "Could not update %s"
184  " property\n", prop_name);
185  }
186 
187  prop_data += vd;
188  }
189  }
190  } while (rc == 1);
191 
192  of_node_put(dn);
193  kfree(rtas_buf);
194  return 0;
195 }
196 
197 static int add_dt_node(u32 parent_phandle, u32 drc_index)
198 {
199  struct device_node *dn;
200  struct device_node *parent_dn;
201  int rc;
202 
203  dn = dlpar_configure_connector(drc_index);
204  if (!dn)
205  return -ENOENT;
206 
207  parent_dn = of_find_node_by_phandle(parent_phandle);
208  if (!parent_dn) {
210  return -ENOENT;
211  }
212 
213  dn->parent = parent_dn;
214  rc = dlpar_attach_node(dn);
215  if (rc)
217 
218  of_node_put(parent_dn);
219  return rc;
220 }
221 
222 static int pseries_devicetree_update(void)
223 {
224  char *rtas_buf;
225  u32 *data;
226  int update_nodes_token;
227  int rc;
228 
229  update_nodes_token = rtas_token("ibm,update-nodes");
230  if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
231  return -EINVAL;
232 
233  rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
234  if (!rtas_buf)
235  return -ENOMEM;
236 
237  do {
238  rc = mobility_rtas_call(update_nodes_token, rtas_buf);
239  if (rc && rc != 1)
240  break;
241 
242  data = (u32 *)rtas_buf + 4;
243  while (*data & NODE_ACTION_MASK) {
244  int i;
245  u32 action = *data & NODE_ACTION_MASK;
246  int node_count = *data & NODE_COUNT_MASK;
247 
248  data++;
249 
250  for (i = 0; i < node_count; i++) {
251  u32 phandle = *data++;
252  u32 drc_index;
253 
254  switch (action) {
255  case DELETE_DT_NODE:
256  delete_dt_node(phandle);
257  break;
258  case UPDATE_DT_NODE:
259  update_dt_node(phandle);
260  break;
261  case ADD_DT_NODE:
262  drc_index = *data++;
263  add_dt_node(phandle, drc_index);
264  break;
265  }
266  }
267  }
268  } while (rc == 1);
269 
270  kfree(rtas_buf);
271  return rc;
272 }
273 
275 {
276  int rc;
277  int activate_fw_token;
278 
279  rc = pseries_devicetree_update();
280  if (rc) {
281  printk(KERN_ERR "Initial post-mobility device tree update "
282  "failed: %d\n", rc);
283  return;
284  }
285 
286  activate_fw_token = rtas_token("ibm,activate-firmware");
287  if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
288  printk(KERN_ERR "Could not make post-mobility "
289  "activate-fw call.\n");
290  return;
291  }
292 
293  rc = rtas_call(activate_fw_token, 0, 1, NULL);
294  if (!rc) {
295  rc = pseries_devicetree_update();
296  if (rc)
297  printk(KERN_ERR "Secondary post-mobility device tree "
298  "update failed: %d\n", rc);
299  } else {
300  printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
301  return;
302  }
303 
304  return;
305 }
306 
307 static ssize_t migrate_store(struct class *class, struct class_attribute *attr,
308  const char *buf, size_t count)
309 {
310  struct rtas_args args;
311  u64 streamid;
312  int rc;
313 
314  rc = strict_strtoull(buf, 0, &streamid);
315  if (rc)
316  return rc;
317 
318  memset(&args, 0, sizeof(args));
319  args.token = rtas_token("ibm,suspend-me");
320  args.nargs = 2;
321  args.nret = 1;
322 
323  args.args[0] = streamid >> 32 ;
324  args.args[1] = streamid & 0xffffffff;
325  args.rets = &args.args[args.nargs];
326 
327  do {
328  args.rets[0] = 0;
329  rc = rtas_ibm_suspend_me(&args);
330  if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE)
331  ssleep(1);
332  } while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE);
333 
334  if (rc)
335  return rc;
336  else if (args.rets[0])
337  return args.rets[0];
338 
340  return count;
341 }
342 
343 static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store);
344 
345 static int __init mobility_sysfs_init(void)
346 {
347  int rc;
348 
349  mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
350  if (!mobility_kobj)
351  return -ENOMEM;
352 
353  rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
354 
355  return rc;
356 }
357 device_initcall(mobility_sysfs_init);