]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - arch/powerpc/platforms/pseries/dlpar.c
treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500
[mirror_ubuntu-jammy-kernel.git] / arch / powerpc / platforms / pseries / dlpar.c
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