xref: /linux/arch/powerpc/platforms/pseries/dlpar.c (revision 0883c2c06fb5bcf5b9e008270827e63c09a88c1e)
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 
30 struct cc_workarea {
31 	__be32	drc_index;
32 	__be32	zero;
33 	__be32	name_offset;
34 	__be32	prop_length;
35 	__be32	prop_offset;
36 };
37 
38 void dlpar_free_cc_property(struct property *prop)
39 {
40 	kfree(prop->name);
41 	kfree(prop->value);
42 	kfree(prop);
43 }
44 
45 static 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 
69 static 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 
98 static 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 
112 void 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 
132 struct 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 
235 cc_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 
248 int 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 
267 int 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 
296 int 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 
318 int 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 
340 static 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 
371 static 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 
444 dlpar_store_out:
445 	kfree(hp_elog);
446 	return rc ? rc : count;
447 }
448 
449 static CLASS_ATTR(dlpar, S_IWUSR, NULL, dlpar_store);
450 
451 static int __init pseries_dlpar_init(void)
452 {
453 	return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
454 }
455 machine_device_initcall(pseries, pseries_dlpar_init);
456 
457