096337853dda3c3ed00f6cfd9491c20c793d85b4
[cascardo/linux.git] / arch / powerpc / platforms / pseries / dlpar.c
1 /*
2  * Support for dynamic reconfiguration for PCI, Memory, and CPU
3  * Hotplug and Dynamic Logical Partitioning on RPA platforms.
4  *
5  * Copyright (C) 2009 Nathan Fontenot
6  * Copyright (C) 2009 IBM Corporation
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version
10  * 2 as published by the Free Software Foundation.
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/notifier.h>
15 #include <linux/spinlock.h>
16 #include <linux/cpu.h>
17 #include <linux/slab.h>
18 #include <linux/of.h>
19 #include "offline_states.h"
20 #include "pseries.h"
21
22 #include <asm/prom.h>
23 #include <asm/machdep.h>
24 #include <asm/uaccess.h>
25 #include <asm/rtas.h>
26
27 struct cc_workarea {
28         u32     drc_index;
29         u32     zero;
30         u32     name_offset;
31         u32     prop_length;
32         u32     prop_offset;
33 };
34
35 void dlpar_free_cc_property(struct property *prop)
36 {
37         kfree(prop->name);
38         kfree(prop->value);
39         kfree(prop);
40 }
41
42 static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
43 {
44         struct property *prop;
45         char *name;
46         char *value;
47
48         prop = kzalloc(sizeof(*prop), GFP_KERNEL);
49         if (!prop)
50                 return NULL;
51
52         name = (char *)ccwa + ccwa->name_offset;
53         prop->name = kstrdup(name, GFP_KERNEL);
54
55         prop->length = ccwa->prop_length;
56         value = (char *)ccwa + ccwa->prop_offset;
57         prop->value = kmemdup(value, prop->length, GFP_KERNEL);
58         if (!prop->value) {
59                 dlpar_free_cc_property(prop);
60                 return NULL;
61         }
62
63         return prop;
64 }
65
66 static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa,
67                                                const char *path)
68 {
69         struct device_node *dn;
70         char *name;
71
72         /* If parent node path is "/" advance path to NULL terminator to
73          * prevent double leading slashs in full_name.
74          */
75         if (!path[1])
76                 path++;
77
78         dn = kzalloc(sizeof(*dn), GFP_KERNEL);
79         if (!dn)
80                 return NULL;
81
82         name = (char *)ccwa + ccwa->name_offset;
83         dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name);
84         if (!dn->full_name) {
85                 kfree(dn);
86                 return NULL;
87         }
88
89         of_node_set_flag(dn, OF_DYNAMIC);
90         of_node_init(dn);
91
92         return dn;
93 }
94
95 static void dlpar_free_one_cc_node(struct device_node *dn)
96 {
97         struct property *prop;
98
99         while (dn->properties) {
100                 prop = dn->properties;
101                 dn->properties = prop->next;
102                 dlpar_free_cc_property(prop);
103         }
104
105         kfree(dn->full_name);
106         kfree(dn);
107 }
108
109 void dlpar_free_cc_nodes(struct device_node *dn)
110 {
111         if (dn->child)
112                 dlpar_free_cc_nodes(dn->child);
113
114         if (dn->sibling)
115                 dlpar_free_cc_nodes(dn->sibling);
116
117         dlpar_free_one_cc_node(dn);
118 }
119
120 #define COMPLETE        0
121 #define NEXT_SIBLING    1
122 #define NEXT_CHILD      2
123 #define NEXT_PROPERTY   3
124 #define PREV_PARENT     4
125 #define MORE_MEMORY     5
126 #define CALL_AGAIN      -2
127 #define ERR_CFG_USE     -9003
128
129 struct device_node *dlpar_configure_connector(u32 drc_index,
130                                               struct device_node *parent)
131 {
132         struct device_node *dn;
133         struct device_node *first_dn = NULL;
134         struct device_node *last_dn = NULL;
135         struct property *property;
136         struct property *last_property = NULL;
137         struct cc_workarea *ccwa;
138         char *data_buf;
139         const char *parent_path = parent->full_name;
140         int cc_token;
141         int rc = -1;
142
143         cc_token = rtas_token("ibm,configure-connector");
144         if (cc_token == RTAS_UNKNOWN_SERVICE)
145                 return NULL;
146
147         data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
148         if (!data_buf)
149                 return NULL;
150
151         ccwa = (struct cc_workarea *)&data_buf[0];
152         ccwa->drc_index = drc_index;
153         ccwa->zero = 0;
154
155         do {
156                 /* Since we release the rtas_data_buf lock between configure
157                  * connector calls we want to re-populate the rtas_data_buffer
158                  * with the contents of the previous call.
159                  */
160                 spin_lock(&rtas_data_buf_lock);
161
162                 memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
163                 rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
164                 memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
165
166                 spin_unlock(&rtas_data_buf_lock);
167
168                 switch (rc) {
169                 case COMPLETE:
170                         break;
171
172                 case NEXT_SIBLING:
173                         dn = dlpar_parse_cc_node(ccwa, parent_path);
174                         if (!dn)
175                                 goto cc_error;
176
177                         dn->parent = last_dn->parent;
178                         last_dn->sibling = dn;
179                         last_dn = dn;
180                         break;
181
182                 case NEXT_CHILD:
183                         if (first_dn)
184                                 parent_path = last_dn->full_name;
185
186                         dn = dlpar_parse_cc_node(ccwa, parent_path);
187                         if (!dn)
188                                 goto cc_error;
189
190                         if (!first_dn) {
191                                 dn->parent = parent;
192                                 first_dn = dn;
193                         } else {
194                                 dn->parent = last_dn;
195                                 if (last_dn)
196                                         last_dn->child = dn;
197                         }
198
199                         last_dn = dn;
200                         break;
201
202                 case NEXT_PROPERTY:
203                         property = dlpar_parse_cc_property(ccwa);
204                         if (!property)
205                                 goto cc_error;
206
207                         if (!last_dn->properties)
208                                 last_dn->properties = property;
209                         else
210                                 last_property->next = property;
211
212                         last_property = property;
213                         break;
214
215                 case PREV_PARENT:
216                         last_dn = last_dn->parent;
217                         parent_path = last_dn->parent->full_name;
218                         break;
219
220                 case CALL_AGAIN:
221                         break;
222
223                 case MORE_MEMORY:
224                 case ERR_CFG_USE:
225                 default:
226                         printk(KERN_ERR "Unexpected Error (%d) "
227                                "returned from configure-connector\n", rc);
228                         goto cc_error;
229                 }
230         } while (rc);
231
232 cc_error:
233         kfree(data_buf);
234
235         if (rc) {
236                 if (first_dn)
237                         dlpar_free_cc_nodes(first_dn);
238
239                 return NULL;
240         }
241
242         return first_dn;
243 }
244
245 static struct device_node *derive_parent(const char *path)
246 {
247         struct device_node *parent;
248         char *last_slash;
249
250         last_slash = strrchr(path, '/');
251         if (last_slash == path) {
252                 parent = of_find_node_by_path("/");
253         } else {
254                 char *parent_path;
255                 int parent_path_len = last_slash - path + 1;
256                 parent_path = kmalloc(parent_path_len, GFP_KERNEL);
257                 if (!parent_path)
258                         return NULL;
259
260                 strlcpy(parent_path, path, parent_path_len);
261                 parent = of_find_node_by_path(parent_path);
262                 kfree(parent_path);
263         }
264
265         return parent;
266 }
267
268 int dlpar_attach_node(struct device_node *dn)
269 {
270         int rc;
271
272         dn->parent = derive_parent(dn->full_name);
273         if (!dn->parent)
274                 return -ENOMEM;
275
276         rc = of_attach_node(dn);
277         if (rc) {
278                 printk(KERN_ERR "Failed to add device node %s\n",
279                        dn->full_name);
280                 return rc;
281         }
282
283         of_node_put(dn->parent);
284         return 0;
285 }
286
287 int dlpar_detach_node(struct device_node *dn)
288 {
289         struct device_node *child;
290         int rc;
291
292         child = of_get_next_child(dn, NULL);
293         while (child) {
294                 dlpar_detach_node(child);
295                 child = of_get_next_child(dn, child);
296         }
297
298         rc = of_detach_node(dn);
299         if (rc)
300                 return rc;
301
302         of_node_put(dn); /* Must decrement the refcount */
303         return 0;
304 }
305
306 #define DR_ENTITY_SENSE         9003
307 #define DR_ENTITY_PRESENT       1
308 #define DR_ENTITY_UNUSABLE      2
309 #define ALLOCATION_STATE        9003
310 #define ALLOC_UNUSABLE          0
311 #define ALLOC_USABLE            1
312 #define ISOLATION_STATE         9001
313 #define ISOLATE                 0
314 #define UNISOLATE               1
315
316 int dlpar_acquire_drc(u32 drc_index)
317 {
318         int dr_status, rc;
319
320         rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
321                        DR_ENTITY_SENSE, drc_index);
322         if (rc || dr_status != DR_ENTITY_UNUSABLE)
323                 return -1;
324
325         rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
326         if (rc)
327                 return rc;
328
329         rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
330         if (rc) {
331                 rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
332                 return rc;
333         }
334
335         return 0;
336 }
337
338 int dlpar_release_drc(u32 drc_index)
339 {
340         int dr_status, rc;
341
342         rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
343                        DR_ENTITY_SENSE, drc_index);
344         if (rc || dr_status != DR_ENTITY_PRESENT)
345                 return -1;
346
347         rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
348         if (rc)
349                 return rc;
350
351         rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
352         if (rc) {
353                 rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
354                 return rc;
355         }
356
357         return 0;
358 }
359
360 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
361
362 static int dlpar_online_cpu(struct device_node *dn)
363 {
364         int rc = 0;
365         unsigned int cpu;
366         int len, nthreads, i;
367         const __be32 *intserv;
368         u32 thread;
369
370         intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
371         if (!intserv)
372                 return -EINVAL;
373
374         nthreads = len / sizeof(u32);
375
376         cpu_maps_update_begin();
377         for (i = 0; i < nthreads; i++) {
378                 thread = be32_to_cpu(intserv[i]);
379                 for_each_present_cpu(cpu) {
380                         if (get_hard_smp_processor_id(cpu) != thread)
381                                 continue;
382                         BUG_ON(get_cpu_current_state(cpu)
383                                         != CPU_STATE_OFFLINE);
384                         cpu_maps_update_done();
385                         rc = cpu_up(cpu);
386                         if (rc)
387                                 goto out;
388                         cpu_maps_update_begin();
389
390                         break;
391                 }
392                 if (cpu == num_possible_cpus())
393                         printk(KERN_WARNING "Could not find cpu to online "
394                                "with physical id 0x%x\n", thread);
395         }
396         cpu_maps_update_done();
397
398 out:
399         return rc;
400
401 }
402
403 static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
404 {
405         struct device_node *dn, *parent;
406         u32 drc_index;
407         int rc;
408
409         rc = kstrtou32(buf, 0, &drc_index);
410         if (rc)
411                 return -EINVAL;
412
413         parent = of_find_node_by_path("/cpus");
414         if (!parent)
415                 return -ENODEV;
416
417         dn = dlpar_configure_connector(drc_index, parent);
418         if (!dn)
419                 return -EINVAL;
420
421         of_node_put(parent);
422
423         rc = dlpar_acquire_drc(drc_index);
424         if (rc) {
425                 dlpar_free_cc_nodes(dn);
426                 return -EINVAL;
427         }
428
429         rc = dlpar_attach_node(dn);
430         if (rc) {
431                 dlpar_release_drc(drc_index);
432                 dlpar_free_cc_nodes(dn);
433                 return rc;
434         }
435
436         rc = dlpar_online_cpu(dn);
437         if (rc)
438                 return rc;
439
440         return count;
441 }
442
443 static int dlpar_offline_cpu(struct device_node *dn)
444 {
445         int rc = 0;
446         unsigned int cpu;
447         int len, nthreads, i;
448         const u32 *intserv;
449
450         intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len);
451         if (!intserv)
452                 return -EINVAL;
453
454         nthreads = len / sizeof(u32);
455
456         cpu_maps_update_begin();
457         for (i = 0; i < nthreads; i++) {
458                 for_each_present_cpu(cpu) {
459                         if (get_hard_smp_processor_id(cpu) != intserv[i])
460                                 continue;
461
462                         if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE)
463                                 break;
464
465                         if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) {
466                                 set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
467                                 cpu_maps_update_done();
468                                 rc = cpu_down(cpu);
469                                 if (rc)
470                                         goto out;
471                                 cpu_maps_update_begin();
472                                 break;
473
474                         }
475
476                         /*
477                          * The cpu is in CPU_STATE_INACTIVE.
478                          * Upgrade it's state to CPU_STATE_OFFLINE.
479                          */
480                         set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
481                         BUG_ON(plpar_hcall_norets(H_PROD, intserv[i])
482                                                                 != H_SUCCESS);
483                         __cpu_die(cpu);
484                         break;
485                 }
486                 if (cpu == num_possible_cpus())
487                         printk(KERN_WARNING "Could not find cpu to offline "
488                                "with physical id 0x%x\n", intserv[i]);
489         }
490         cpu_maps_update_done();
491
492 out:
493         return rc;
494
495 }
496
497 static ssize_t dlpar_cpu_release(const char *buf, size_t count)
498 {
499         struct device_node *dn;
500         const u32 *drc_index;
501         int rc;
502
503         dn = of_find_node_by_path(buf);
504         if (!dn)
505                 return -EINVAL;
506
507         drc_index = of_get_property(dn, "ibm,my-drc-index", NULL);
508         if (!drc_index) {
509                 of_node_put(dn);
510                 return -EINVAL;
511         }
512
513         rc = dlpar_offline_cpu(dn);
514         if (rc) {
515                 of_node_put(dn);
516                 return -EINVAL;
517         }
518
519         rc = dlpar_release_drc(*drc_index);
520         if (rc) {
521                 of_node_put(dn);
522                 return rc;
523         }
524
525         rc = dlpar_detach_node(dn);
526         if (rc) {
527                 dlpar_acquire_drc(*drc_index);
528                 return rc;
529         }
530
531         of_node_put(dn);
532
533         return count;
534 }
535
536 static int __init pseries_dlpar_init(void)
537 {
538         ppc_md.cpu_probe = dlpar_cpu_probe;
539         ppc_md.cpu_release = dlpar_cpu_release;
540
541         return 0;
542 }
543 machine_device_initcall(pseries, pseries_dlpar_init);
544
545 #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */