Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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#define pr_fmt(fmt) "dlpar: " fmt
14
15#include <linux/kernel.h>
16#include <linux/notifier.h>
17#include <linux/spinlock.h>
18#include <linux/cpu.h>
19#include <linux/slab.h>
20#include <linux/of.h>
21
22#include "of_helpers.h"
23#include "pseries.h"
24
25#include <asm/prom.h>
26#include <asm/machdep.h>
27#include <asm/uaccess.h>
28#include <asm/rtas.h>
29
30struct cc_workarea {
31 __be32 drc_index;
32 __be32 zero;
33 __be32 name_offset;
34 __be32 prop_length;
35 __be32 prop_offset;
36};
37
38void dlpar_free_cc_property(struct property *prop)
39{
40 kfree(prop->name);
41 kfree(prop->value);
42 kfree(prop);
43}
44
45static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
46{
47 struct property *prop;
48 char *name;
49 char *value;
50
51 prop = kzalloc(sizeof(*prop), GFP_KERNEL);
52 if (!prop)
53 return NULL;
54
55 name = (char *)ccwa + be32_to_cpu(ccwa->name_offset);
56 prop->name = kstrdup(name, GFP_KERNEL);
57
58 prop->length = be32_to_cpu(ccwa->prop_length);
59 value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset);
60 prop->value = kmemdup(value, prop->length, GFP_KERNEL);
61 if (!prop->value) {
62 dlpar_free_cc_property(prop);
63 return NULL;
64 }
65
66 return prop;
67}
68
69static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa,
70 const char *path)
71{
72 struct device_node *dn;
73 char *name;
74
75 /* If parent node path is "/" advance path to NULL terminator to
76 * prevent double leading slashs in full_name.
77 */
78 if (!path[1])
79 path++;
80
81 dn = kzalloc(sizeof(*dn), GFP_KERNEL);
82 if (!dn)
83 return NULL;
84
85 name = (char *)ccwa + be32_to_cpu(ccwa->name_offset);
86 dn->full_name = kasprintf(GFP_KERNEL, "%s/%s", path, name);
87 if (!dn->full_name) {
88 kfree(dn);
89 return NULL;
90 }
91
92 of_node_set_flag(dn, OF_DYNAMIC);
93 of_node_init(dn);
94
95 return dn;
96}
97
98static void dlpar_free_one_cc_node(struct device_node *dn)
99{
100 struct property *prop;
101
102 while (dn->properties) {
103 prop = dn->properties;
104 dn->properties = prop->next;
105 dlpar_free_cc_property(prop);
106 }
107
108 kfree(dn->full_name);
109 kfree(dn);
110}
111
112void dlpar_free_cc_nodes(struct device_node *dn)
113{
114 if (dn->child)
115 dlpar_free_cc_nodes(dn->child);
116
117 if (dn->sibling)
118 dlpar_free_cc_nodes(dn->sibling);
119
120 dlpar_free_one_cc_node(dn);
121}
122
123#define COMPLETE 0
124#define NEXT_SIBLING 1
125#define NEXT_CHILD 2
126#define NEXT_PROPERTY 3
127#define PREV_PARENT 4
128#define MORE_MEMORY 5
129#define CALL_AGAIN -2
130#define ERR_CFG_USE -9003
131
132struct device_node *dlpar_configure_connector(__be32 drc_index,
133 struct device_node *parent)
134{
135 struct device_node *dn;
136 struct device_node *first_dn = NULL;
137 struct device_node *last_dn = NULL;
138 struct property *property;
139 struct property *last_property = NULL;
140 struct cc_workarea *ccwa;
141 char *data_buf;
142 const char *parent_path = parent->full_name;
143 int cc_token;
144 int rc = -1;
145
146 cc_token = rtas_token("ibm,configure-connector");
147 if (cc_token == RTAS_UNKNOWN_SERVICE)
148 return NULL;
149
150 data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
151 if (!data_buf)
152 return NULL;
153
154 ccwa = (struct cc_workarea *)&data_buf[0];
155 ccwa->drc_index = drc_index;
156 ccwa->zero = 0;
157
158 do {
159 /* Since we release the rtas_data_buf lock between configure
160 * connector calls we want to re-populate the rtas_data_buffer
161 * with the contents of the previous call.
162 */
163 spin_lock(&rtas_data_buf_lock);
164
165 memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
166 rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
167 memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
168
169 spin_unlock(&rtas_data_buf_lock);
170
171 switch (rc) {
172 case COMPLETE:
173 break;
174
175 case NEXT_SIBLING:
176 dn = dlpar_parse_cc_node(ccwa, parent_path);
177 if (!dn)
178 goto cc_error;
179
180 dn->parent = last_dn->parent;
181 last_dn->sibling = dn;
182 last_dn = dn;
183 break;
184
185 case NEXT_CHILD:
186 if (first_dn)
187 parent_path = last_dn->full_name;
188
189 dn = dlpar_parse_cc_node(ccwa, parent_path);
190 if (!dn)
191 goto cc_error;
192
193 if (!first_dn) {
194 dn->parent = parent;
195 first_dn = dn;
196 } else {
197 dn->parent = last_dn;
198 if (last_dn)
199 last_dn->child = dn;
200 }
201
202 last_dn = dn;
203 break;
204
205 case NEXT_PROPERTY:
206 property = dlpar_parse_cc_property(ccwa);
207 if (!property)
208 goto cc_error;
209
210 if (!last_dn->properties)
211 last_dn->properties = property;
212 else
213 last_property->next = property;
214
215 last_property = property;
216 break;
217
218 case PREV_PARENT:
219 last_dn = last_dn->parent;
220 parent_path = last_dn->parent->full_name;
221 break;
222
223 case CALL_AGAIN:
224 break;
225
226 case MORE_MEMORY:
227 case ERR_CFG_USE:
228 default:
229 printk(KERN_ERR "Unexpected Error (%d) "
230 "returned from configure-connector\n", rc);
231 goto cc_error;
232 }
233 } while (rc);
234
235cc_error:
236 kfree(data_buf);
237
238 if (rc) {
239 if (first_dn)
240 dlpar_free_cc_nodes(first_dn);
241
242 return NULL;
243 }
244
245 return first_dn;
246}
247
248int dlpar_attach_node(struct device_node *dn)
249{
250 int rc;
251
252 dn->parent = pseries_of_derive_parent(dn->full_name);
253 if (IS_ERR(dn->parent))
254 return PTR_ERR(dn->parent);
255
256 rc = of_attach_node(dn);
257 if (rc) {
258 printk(KERN_ERR "Failed to add device node %s\n",
259 dn->full_name);
260 return rc;
261 }
262
263 of_node_put(dn->parent);
264 return 0;
265}
266
267int dlpar_detach_node(struct device_node *dn)
268{
269 struct device_node *child;
270 int rc;
271
272 child = of_get_next_child(dn, NULL);
273 while (child) {
274 dlpar_detach_node(child);
275 child = of_get_next_child(dn, child);
276 }
277
278 rc = of_detach_node(dn);
279 if (rc)
280 return rc;
281
282 of_node_put(dn); /* Must decrement the refcount */
283 return 0;
284}
285
286#define DR_ENTITY_SENSE 9003
287#define DR_ENTITY_PRESENT 1
288#define DR_ENTITY_UNUSABLE 2
289#define ALLOCATION_STATE 9003
290#define ALLOC_UNUSABLE 0
291#define ALLOC_USABLE 1
292#define ISOLATION_STATE 9001
293#define ISOLATE 0
294#define UNISOLATE 1
295
296int dlpar_acquire_drc(u32 drc_index)
297{
298 int dr_status, rc;
299
300 rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
301 DR_ENTITY_SENSE, drc_index);
302 if (rc || dr_status != DR_ENTITY_UNUSABLE)
303 return -1;
304
305 rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
306 if (rc)
307 return rc;
308
309 rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
310 if (rc) {
311 rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
312 return rc;
313 }
314
315 return 0;
316}
317
318int dlpar_release_drc(u32 drc_index)
319{
320 int dr_status, rc;
321
322 rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
323 DR_ENTITY_SENSE, drc_index);
324 if (rc || dr_status != DR_ENTITY_PRESENT)
325 return -1;
326
327 rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
328 if (rc)
329 return rc;
330
331 rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
332 if (rc) {
333 rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
334 return rc;
335 }
336
337 return 0;
338}
339
340static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog)
341{
342 int rc;
343
344 /* pseries error logs are in BE format, convert to cpu type */
345 switch (hp_elog->id_type) {
346 case PSERIES_HP_ELOG_ID_DRC_COUNT:
347 hp_elog->_drc_u.drc_count =
348 be32_to_cpu(hp_elog->_drc_u.drc_count);
349 break;
350 case PSERIES_HP_ELOG_ID_DRC_INDEX:
351 hp_elog->_drc_u.drc_index =
352 be32_to_cpu(hp_elog->_drc_u.drc_index);
353 }
354
355 switch (hp_elog->resource) {
356 case PSERIES_HP_ELOG_RESOURCE_MEM:
357 rc = dlpar_memory(hp_elog);
358 break;
359 case PSERIES_HP_ELOG_RESOURCE_CPU:
360 rc = dlpar_cpu(hp_elog);
361 break;
362 default:
363 pr_warn_ratelimited("Invalid resource (%d) specified\n",
364 hp_elog->resource);
365 rc = -EINVAL;
366 }
367
368 return rc;
369}
370
371static ssize_t dlpar_store(struct class *class, struct class_attribute *attr,
372 const char *buf, size_t count)
373{
374 struct pseries_hp_errorlog *hp_elog;
375 const char *arg;
376 int rc;
377
378 hp_elog = kzalloc(sizeof(*hp_elog), GFP_KERNEL);
379 if (!hp_elog) {
380 rc = -ENOMEM;
381 goto dlpar_store_out;
382 }
383
384 /* Parse out the request from the user, this will be in the form
385 * <resource> <action> <id_type> <id>
386 */
387 arg = buf;
388 if (!strncmp(arg, "memory", 6)) {
389 hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM;
390 arg += strlen("memory ");
391 } else if (!strncmp(arg, "cpu", 3)) {
392 hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU;
393 arg += strlen("cpu ");
394 } else {
395 pr_err("Invalid resource specified: \"%s\"\n", buf);
396 rc = -EINVAL;
397 goto dlpar_store_out;
398 }
399
400 if (!strncmp(arg, "add", 3)) {
401 hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD;
402 arg += strlen("add ");
403 } else if (!strncmp(arg, "remove", 6)) {
404 hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE;
405 arg += strlen("remove ");
406 } else {
407 pr_err("Invalid action specified: \"%s\"\n", buf);
408 rc = -EINVAL;
409 goto dlpar_store_out;
410 }
411
412 if (!strncmp(arg, "index", 5)) {
413 u32 index;
414
415 hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX;
416 arg += strlen("index ");
417 if (kstrtou32(arg, 0, &index)) {
418 rc = -EINVAL;
419 pr_err("Invalid drc_index specified: \"%s\"\n", buf);
420 goto dlpar_store_out;
421 }
422
423 hp_elog->_drc_u.drc_index = cpu_to_be32(index);
424 } else if (!strncmp(arg, "count", 5)) {
425 u32 count;
426
427 hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT;
428 arg += strlen("count ");
429 if (kstrtou32(arg, 0, &count)) {
430 rc = -EINVAL;
431 pr_err("Invalid count specified: \"%s\"\n", buf);
432 goto dlpar_store_out;
433 }
434
435 hp_elog->_drc_u.drc_count = cpu_to_be32(count);
436 } else {
437 pr_err("Invalid id_type specified: \"%s\"\n", buf);
438 rc = -EINVAL;
439 goto dlpar_store_out;
440 }
441
442 rc = handle_dlpar_errorlog(hp_elog);
443
444dlpar_store_out:
445 kfree(hp_elog);
446 return rc ? rc : count;
447}
448
449static CLASS_ATTR(dlpar, S_IWUSR, NULL, dlpar_store);
450
451static int __init pseries_dlpar_init(void)
452{
453 return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
454}
455machine_device_initcall(pseries, pseries_dlpar_init);
456