xref: /linux/arch/powerpc/platforms/pseries/dlpar.c (revision 31d166642c7c601c65eccf0ff2e0afe9a0538be2)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Support for dynamic reconfiguration for PCI, Memory, and CPU
4  * Hotplug and Dynamic Logical Partitioning on RPA platforms.
5  *
6  * Copyright (C) 2009 Nathan Fontenot
7  * Copyright (C) 2009 IBM Corporation
8  */
9 
10 #define pr_fmt(fmt)	"dlpar: " fmt
11 
12 #include <linux/kernel.h>
13 #include <linux/notifier.h>
14 #include <linux/spinlock.h>
15 #include <linux/cpu.h>
16 #include <linux/slab.h>
17 #include <linux/of.h>
18 
19 #include "of_helpers.h"
20 #include "pseries.h"
21 
22 #include <asm/prom.h>
23 #include <asm/machdep.h>
24 #include <linux/uaccess.h>
25 #include <asm/rtas.h>
26 
27 static struct workqueue_struct *pseries_hp_wq;
28 
29 struct pseries_hp_work {
30 	struct work_struct work;
31 	struct pseries_hp_errorlog *errlog;
32 };
33 
34 struct cc_workarea {
35 	__be32	drc_index;
36 	__be32	zero;
37 	__be32	name_offset;
38 	__be32	prop_length;
39 	__be32	prop_offset;
40 };
41 
42 void dlpar_free_cc_property(struct property *prop)
43 {
44 	kfree(prop->name);
45 	kfree(prop->value);
46 	kfree(prop);
47 }
48 
49 static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
50 {
51 	struct property *prop;
52 	char *name;
53 	char *value;
54 
55 	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
56 	if (!prop)
57 		return NULL;
58 
59 	name = (char *)ccwa + be32_to_cpu(ccwa->name_offset);
60 	prop->name = kstrdup(name, GFP_KERNEL);
61 
62 	prop->length = be32_to_cpu(ccwa->prop_length);
63 	value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset);
64 	prop->value = kmemdup(value, prop->length, GFP_KERNEL);
65 	if (!prop->value) {
66 		dlpar_free_cc_property(prop);
67 		return NULL;
68 	}
69 
70 	return prop;
71 }
72 
73 static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa)
74 {
75 	struct device_node *dn;
76 	const char *name;
77 
78 	dn = kzalloc(sizeof(*dn), GFP_KERNEL);
79 	if (!dn)
80 		return NULL;
81 
82 	name = (const char *)ccwa + be32_to_cpu(ccwa->name_offset);
83 	dn->full_name = kstrdup(name, GFP_KERNEL);
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(__be32 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 	int cc_token;
140 	int rc = -1;
141 
142 	cc_token = rtas_token("ibm,configure-connector");
143 	if (cc_token == RTAS_UNKNOWN_SERVICE)
144 		return NULL;
145 
146 	data_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
147 	if (!data_buf)
148 		return NULL;
149 
150 	ccwa = (struct cc_workarea *)&data_buf[0];
151 	ccwa->drc_index = drc_index;
152 	ccwa->zero = 0;
153 
154 	do {
155 		/* Since we release the rtas_data_buf lock between configure
156 		 * connector calls we want to re-populate the rtas_data_buffer
157 		 * with the contents of the previous call.
158 		 */
159 		spin_lock(&rtas_data_buf_lock);
160 
161 		memcpy(rtas_data_buf, data_buf, RTAS_DATA_BUF_SIZE);
162 		rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
163 		memcpy(data_buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
164 
165 		spin_unlock(&rtas_data_buf_lock);
166 
167 		switch (rc) {
168 		case COMPLETE:
169 			break;
170 
171 		case NEXT_SIBLING:
172 			dn = dlpar_parse_cc_node(ccwa);
173 			if (!dn)
174 				goto cc_error;
175 
176 			dn->parent = last_dn->parent;
177 			last_dn->sibling = dn;
178 			last_dn = dn;
179 			break;
180 
181 		case NEXT_CHILD:
182 			dn = dlpar_parse_cc_node(ccwa);
183 			if (!dn)
184 				goto cc_error;
185 
186 			if (!first_dn) {
187 				dn->parent = parent;
188 				first_dn = dn;
189 			} else {
190 				dn->parent = last_dn;
191 				if (last_dn)
192 					last_dn->child = dn;
193 			}
194 
195 			last_dn = dn;
196 			break;
197 
198 		case NEXT_PROPERTY:
199 			property = dlpar_parse_cc_property(ccwa);
200 			if (!property)
201 				goto cc_error;
202 
203 			if (!last_dn->properties)
204 				last_dn->properties = property;
205 			else
206 				last_property->next = property;
207 
208 			last_property = property;
209 			break;
210 
211 		case PREV_PARENT:
212 			last_dn = last_dn->parent;
213 			break;
214 
215 		case CALL_AGAIN:
216 			break;
217 
218 		case MORE_MEMORY:
219 		case ERR_CFG_USE:
220 		default:
221 			printk(KERN_ERR "Unexpected Error (%d) "
222 			       "returned from configure-connector\n", rc);
223 			goto cc_error;
224 		}
225 	} while (rc);
226 
227 cc_error:
228 	kfree(data_buf);
229 
230 	if (rc) {
231 		if (first_dn)
232 			dlpar_free_cc_nodes(first_dn);
233 
234 		return NULL;
235 	}
236 
237 	return first_dn;
238 }
239 
240 int dlpar_attach_node(struct device_node *dn, struct device_node *parent)
241 {
242 	int rc;
243 
244 	dn->parent = parent;
245 
246 	rc = of_attach_node(dn);
247 	if (rc) {
248 		printk(KERN_ERR "Failed to add device node %pOF\n", dn);
249 		return rc;
250 	}
251 
252 	return 0;
253 }
254 
255 int dlpar_detach_node(struct device_node *dn)
256 {
257 	struct device_node *child;
258 	int rc;
259 
260 	child = of_get_next_child(dn, NULL);
261 	while (child) {
262 		dlpar_detach_node(child);
263 		child = of_get_next_child(dn, child);
264 	}
265 
266 	rc = of_detach_node(dn);
267 	if (rc)
268 		return rc;
269 
270 	of_node_put(dn);
271 
272 	return 0;
273 }
274 
275 #define DR_ENTITY_SENSE		9003
276 #define DR_ENTITY_PRESENT	1
277 #define DR_ENTITY_UNUSABLE	2
278 #define ALLOCATION_STATE	9003
279 #define ALLOC_UNUSABLE		0
280 #define ALLOC_USABLE		1
281 #define ISOLATION_STATE		9001
282 #define ISOLATE			0
283 #define UNISOLATE		1
284 
285 int dlpar_acquire_drc(u32 drc_index)
286 {
287 	int dr_status, rc;
288 
289 	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
290 		       DR_ENTITY_SENSE, drc_index);
291 	if (rc || dr_status != DR_ENTITY_UNUSABLE)
292 		return -1;
293 
294 	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
295 	if (rc)
296 		return rc;
297 
298 	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
299 	if (rc) {
300 		rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
301 		return rc;
302 	}
303 
304 	return 0;
305 }
306 
307 int dlpar_release_drc(u32 drc_index)
308 {
309 	int dr_status, rc;
310 
311 	rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
312 		       DR_ENTITY_SENSE, drc_index);
313 	if (rc || dr_status != DR_ENTITY_PRESENT)
314 		return -1;
315 
316 	rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
317 	if (rc)
318 		return rc;
319 
320 	rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
321 	if (rc) {
322 		rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
323 		return rc;
324 	}
325 
326 	return 0;
327 }
328 
329 int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog)
330 {
331 	int rc;
332 
333 	/* pseries error logs are in BE format, convert to cpu type */
334 	switch (hp_elog->id_type) {
335 	case PSERIES_HP_ELOG_ID_DRC_COUNT:
336 		hp_elog->_drc_u.drc_count =
337 				be32_to_cpu(hp_elog->_drc_u.drc_count);
338 		break;
339 	case PSERIES_HP_ELOG_ID_DRC_INDEX:
340 		hp_elog->_drc_u.drc_index =
341 				be32_to_cpu(hp_elog->_drc_u.drc_index);
342 		break;
343 	case PSERIES_HP_ELOG_ID_DRC_IC:
344 		hp_elog->_drc_u.ic.count =
345 				be32_to_cpu(hp_elog->_drc_u.ic.count);
346 		hp_elog->_drc_u.ic.index =
347 				be32_to_cpu(hp_elog->_drc_u.ic.index);
348 	}
349 
350 	switch (hp_elog->resource) {
351 	case PSERIES_HP_ELOG_RESOURCE_MEM:
352 		rc = dlpar_memory(hp_elog);
353 		break;
354 	case PSERIES_HP_ELOG_RESOURCE_CPU:
355 		rc = dlpar_cpu(hp_elog);
356 		break;
357 	case PSERIES_HP_ELOG_RESOURCE_PMEM:
358 		rc = dlpar_hp_pmem(hp_elog);
359 		break;
360 
361 	default:
362 		pr_warn_ratelimited("Invalid resource (%d) specified\n",
363 				    hp_elog->resource);
364 		rc = -EINVAL;
365 	}
366 
367 	return rc;
368 }
369 
370 static void pseries_hp_work_fn(struct work_struct *work)
371 {
372 	struct pseries_hp_work *hp_work =
373 			container_of(work, struct pseries_hp_work, work);
374 
375 	handle_dlpar_errorlog(hp_work->errlog);
376 
377 	kfree(hp_work->errlog);
378 	kfree((void *)work);
379 }
380 
381 void queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog)
382 {
383 	struct pseries_hp_work *work;
384 	struct pseries_hp_errorlog *hp_errlog_copy;
385 
386 	hp_errlog_copy = kmalloc(sizeof(struct pseries_hp_errorlog),
387 				 GFP_KERNEL);
388 	memcpy(hp_errlog_copy, hp_errlog, sizeof(struct pseries_hp_errorlog));
389 
390 	work = kmalloc(sizeof(struct pseries_hp_work), GFP_KERNEL);
391 	if (work) {
392 		INIT_WORK((struct work_struct *)work, pseries_hp_work_fn);
393 		work->errlog = hp_errlog_copy;
394 		queue_work(pseries_hp_wq, (struct work_struct *)work);
395 	} else {
396 		kfree(hp_errlog_copy);
397 	}
398 }
399 
400 static int dlpar_parse_resource(char **cmd, struct pseries_hp_errorlog *hp_elog)
401 {
402 	char *arg;
403 
404 	arg = strsep(cmd, " ");
405 	if (!arg)
406 		return -EINVAL;
407 
408 	if (sysfs_streq(arg, "memory")) {
409 		hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM;
410 	} else if (sysfs_streq(arg, "cpu")) {
411 		hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU;
412 	} else {
413 		pr_err("Invalid resource specified.\n");
414 		return -EINVAL;
415 	}
416 
417 	return 0;
418 }
419 
420 static int dlpar_parse_action(char **cmd, struct pseries_hp_errorlog *hp_elog)
421 {
422 	char *arg;
423 
424 	arg = strsep(cmd, " ");
425 	if (!arg)
426 		return -EINVAL;
427 
428 	if (sysfs_streq(arg, "add")) {
429 		hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD;
430 	} else if (sysfs_streq(arg, "remove")) {
431 		hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE;
432 	} else {
433 		pr_err("Invalid action specified.\n");
434 		return -EINVAL;
435 	}
436 
437 	return 0;
438 }
439 
440 static int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog)
441 {
442 	char *arg;
443 	u32 count, index;
444 
445 	arg = strsep(cmd, " ");
446 	if (!arg)
447 		return -EINVAL;
448 
449 	if (sysfs_streq(arg, "indexed-count")) {
450 		hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC;
451 		arg = strsep(cmd, " ");
452 		if (!arg) {
453 			pr_err("No DRC count specified.\n");
454 			return -EINVAL;
455 		}
456 
457 		if (kstrtou32(arg, 0, &count)) {
458 			pr_err("Invalid DRC count specified.\n");
459 			return -EINVAL;
460 		}
461 
462 		arg = strsep(cmd, " ");
463 		if (!arg) {
464 			pr_err("No DRC Index specified.\n");
465 			return -EINVAL;
466 		}
467 
468 		if (kstrtou32(arg, 0, &index)) {
469 			pr_err("Invalid DRC Index specified.\n");
470 			return -EINVAL;
471 		}
472 
473 		hp_elog->_drc_u.ic.count = cpu_to_be32(count);
474 		hp_elog->_drc_u.ic.index = cpu_to_be32(index);
475 	} else if (sysfs_streq(arg, "index")) {
476 		hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX;
477 		arg = strsep(cmd, " ");
478 		if (!arg) {
479 			pr_err("No DRC Index specified.\n");
480 			return -EINVAL;
481 		}
482 
483 		if (kstrtou32(arg, 0, &index)) {
484 			pr_err("Invalid DRC Index specified.\n");
485 			return -EINVAL;
486 		}
487 
488 		hp_elog->_drc_u.drc_index = cpu_to_be32(index);
489 	} else if (sysfs_streq(arg, "count")) {
490 		hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT;
491 		arg = strsep(cmd, " ");
492 		if (!arg) {
493 			pr_err("No DRC count specified.\n");
494 			return -EINVAL;
495 		}
496 
497 		if (kstrtou32(arg, 0, &count)) {
498 			pr_err("Invalid DRC count specified.\n");
499 			return -EINVAL;
500 		}
501 
502 		hp_elog->_drc_u.drc_count = cpu_to_be32(count);
503 	} else {
504 		pr_err("Invalid id_type specified.\n");
505 		return -EINVAL;
506 	}
507 
508 	return 0;
509 }
510 
511 static ssize_t dlpar_store(struct class *class, struct class_attribute *attr,
512 			   const char *buf, size_t count)
513 {
514 	struct pseries_hp_errorlog hp_elog;
515 	char *argbuf;
516 	char *args;
517 	int rc;
518 
519 	args = argbuf = kstrdup(buf, GFP_KERNEL);
520 	if (!argbuf) {
521 		pr_info("Could not allocate resources for DLPAR operation\n");
522 		kfree(argbuf);
523 		return -ENOMEM;
524 	}
525 
526 	/*
527 	 * Parse out the request from the user, this will be in the form:
528 	 * <resource> <action> <id_type> <id>
529 	 */
530 	rc = dlpar_parse_resource(&args, &hp_elog);
531 	if (rc)
532 		goto dlpar_store_out;
533 
534 	rc = dlpar_parse_action(&args, &hp_elog);
535 	if (rc)
536 		goto dlpar_store_out;
537 
538 	rc = dlpar_parse_id_type(&args, &hp_elog);
539 	if (rc)
540 		goto dlpar_store_out;
541 
542 	rc = handle_dlpar_errorlog(&hp_elog);
543 
544 dlpar_store_out:
545 	kfree(argbuf);
546 
547 	if (rc)
548 		pr_err("Could not handle DLPAR request \"%s\"\n", buf);
549 
550 	return rc ? rc : count;
551 }
552 
553 static ssize_t dlpar_show(struct class *class, struct class_attribute *attr,
554 			  char *buf)
555 {
556 	return sprintf(buf, "%s\n", "memory,cpu");
557 }
558 
559 static CLASS_ATTR_RW(dlpar);
560 
561 int __init dlpar_workqueue_init(void)
562 {
563 	if (pseries_hp_wq)
564 		return 0;
565 
566 	pseries_hp_wq = alloc_workqueue("pseries hotplug workqueue",
567 			WQ_UNBOUND, 1);
568 
569 	return pseries_hp_wq ? 0 : -ENOMEM;
570 }
571 
572 static int __init dlpar_sysfs_init(void)
573 {
574 	int rc;
575 
576 	rc = dlpar_workqueue_init();
577 	if (rc)
578 		return rc;
579 
580 	return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr);
581 }
582 machine_device_initcall(pseries, dlpar_sysfs_init);
583 
584