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/machdep.h> 23 #include <linux/uaccess.h> 24 #include <asm/rtas.h> 25 #include <asm/rtas-work-area.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 if (!prop->name) { 62 dlpar_free_cc_property(prop); 63 return NULL; 64 } 65 66 prop->length = be32_to_cpu(ccwa->prop_length); 67 value = (char *)ccwa + be32_to_cpu(ccwa->prop_offset); 68 prop->value = kmemdup(value, prop->length, GFP_KERNEL); 69 if (!prop->value) { 70 dlpar_free_cc_property(prop); 71 return NULL; 72 } 73 74 return prop; 75 } 76 77 static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) 78 { 79 struct device_node *dn; 80 const char *name; 81 82 dn = kzalloc(sizeof(*dn), GFP_KERNEL); 83 if (!dn) 84 return NULL; 85 86 name = (const char *)ccwa + be32_to_cpu(ccwa->name_offset); 87 dn->full_name = kstrdup(name, GFP_KERNEL); 88 if (!dn->full_name) { 89 kfree(dn); 90 return NULL; 91 } 92 93 of_node_set_flag(dn, OF_DYNAMIC); 94 of_node_init(dn); 95 96 return dn; 97 } 98 99 static void dlpar_free_one_cc_node(struct device_node *dn) 100 { 101 struct property *prop; 102 103 while (dn->properties) { 104 prop = dn->properties; 105 dn->properties = prop->next; 106 dlpar_free_cc_property(prop); 107 } 108 109 kfree(dn->full_name); 110 kfree(dn); 111 } 112 113 void dlpar_free_cc_nodes(struct device_node *dn) 114 { 115 if (dn->child) 116 dlpar_free_cc_nodes(dn->child); 117 118 if (dn->sibling) 119 dlpar_free_cc_nodes(dn->sibling); 120 121 dlpar_free_one_cc_node(dn); 122 } 123 124 #define COMPLETE 0 125 #define NEXT_SIBLING 1 126 #define NEXT_CHILD 2 127 #define NEXT_PROPERTY 3 128 #define PREV_PARENT 4 129 #define MORE_MEMORY 5 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 struct rtas_work_area *work_area; 142 char *data_buf; 143 int cc_token; 144 int rc = -1; 145 146 cc_token = rtas_function_token(RTAS_FN_IBM_CONFIGURE_CONNECTOR); 147 if (cc_token == RTAS_UNKNOWN_SERVICE) 148 return NULL; 149 150 work_area = rtas_work_area_alloc(SZ_4K); 151 data_buf = rtas_work_area_raw_buf(work_area); 152 153 ccwa = (struct cc_workarea *)&data_buf[0]; 154 ccwa->drc_index = drc_index; 155 ccwa->zero = 0; 156 157 do { 158 do { 159 rc = rtas_call(cc_token, 2, 1, NULL, 160 rtas_work_area_phys(work_area), NULL); 161 } while (rtas_busy_delay(rc)); 162 163 switch (rc) { 164 case COMPLETE: 165 break; 166 167 case NEXT_SIBLING: 168 dn = dlpar_parse_cc_node(ccwa); 169 if (!dn) 170 goto cc_error; 171 172 dn->parent = last_dn->parent; 173 last_dn->sibling = dn; 174 last_dn = dn; 175 break; 176 177 case NEXT_CHILD: 178 dn = dlpar_parse_cc_node(ccwa); 179 if (!dn) 180 goto cc_error; 181 182 if (!first_dn) { 183 dn->parent = parent; 184 first_dn = dn; 185 } else { 186 dn->parent = last_dn; 187 if (last_dn) 188 last_dn->child = dn; 189 } 190 191 last_dn = dn; 192 break; 193 194 case NEXT_PROPERTY: 195 property = dlpar_parse_cc_property(ccwa); 196 if (!property) 197 goto cc_error; 198 199 if (!last_dn->properties) 200 last_dn->properties = property; 201 else 202 last_property->next = property; 203 204 last_property = property; 205 break; 206 207 case PREV_PARENT: 208 last_dn = last_dn->parent; 209 break; 210 211 case MORE_MEMORY: 212 case ERR_CFG_USE: 213 default: 214 printk(KERN_ERR "Unexpected Error (%d) " 215 "returned from configure-connector\n", rc); 216 goto cc_error; 217 } 218 } while (rc); 219 220 cc_error: 221 rtas_work_area_free(work_area); 222 223 if (rc) { 224 if (first_dn) 225 dlpar_free_cc_nodes(first_dn); 226 227 return NULL; 228 } 229 230 return first_dn; 231 } 232 233 int dlpar_attach_node(struct device_node *dn, struct device_node *parent) 234 { 235 int rc; 236 237 dn->parent = parent; 238 239 rc = of_attach_node(dn); 240 if (rc) { 241 printk(KERN_ERR "Failed to add device node %pOF\n", dn); 242 return rc; 243 } 244 245 return 0; 246 } 247 248 int dlpar_detach_node(struct device_node *dn) 249 { 250 struct device_node *child; 251 int rc; 252 253 for_each_child_of_node(dn, child) 254 dlpar_detach_node(child); 255 256 rc = of_detach_node(dn); 257 if (rc) 258 return rc; 259 260 of_node_put(dn); 261 262 return 0; 263 } 264 265 #define DR_ENTITY_SENSE 9003 266 #define DR_ENTITY_PRESENT 1 267 #define DR_ENTITY_UNUSABLE 2 268 #define ALLOCATION_STATE 9003 269 #define ALLOC_UNUSABLE 0 270 #define ALLOC_USABLE 1 271 #define ISOLATION_STATE 9001 272 #define ISOLATE 0 273 #define UNISOLATE 1 274 275 int dlpar_acquire_drc(u32 drc_index) 276 { 277 int dr_status, rc; 278 279 rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 280 if (rc || dr_status != DR_ENTITY_UNUSABLE) 281 return -1; 282 283 rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 284 if (rc) 285 return rc; 286 287 rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 288 if (rc) { 289 rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 290 return rc; 291 } 292 293 return 0; 294 } 295 296 int dlpar_release_drc(u32 drc_index) 297 { 298 int dr_status, rc; 299 300 rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 301 if (rc || dr_status != DR_ENTITY_PRESENT) 302 return -1; 303 304 rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 305 if (rc) 306 return rc; 307 308 rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 309 if (rc) { 310 rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 311 return rc; 312 } 313 314 return 0; 315 } 316 317 int dlpar_unisolate_drc(u32 drc_index) 318 { 319 int dr_status, rc; 320 321 rc = rtas_get_sensor(DR_ENTITY_SENSE, drc_index, &dr_status); 322 if (rc || dr_status != DR_ENTITY_PRESENT) 323 return -1; 324 325 rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 326 327 return 0; 328 } 329 330 static int changeset_detach_node_recursive(struct of_changeset *ocs, 331 struct device_node *node) 332 { 333 struct device_node *child; 334 int rc; 335 336 for_each_child_of_node(node, child) { 337 rc = changeset_detach_node_recursive(ocs, child); 338 if (rc) { 339 of_node_put(child); 340 return rc; 341 } 342 } 343 344 return of_changeset_detach_node(ocs, node); 345 } 346 347 static int dlpar_hp_dt_remove(u32 drc_index) 348 { 349 struct device_node *np; 350 struct of_changeset ocs; 351 u32 index; 352 int rc = 0; 353 354 /* 355 * Prune all nodes with a matching index. 356 */ 357 of_changeset_init(&ocs); 358 359 for_each_node_with_property(np, "ibm,my-drc-index") { 360 rc = of_property_read_u32(np, "ibm,my-drc-index", &index); 361 if (rc) { 362 pr_err("%s: %pOF: of_property_read_u32 %s: %d\n", 363 __func__, np, "ibm,my-drc-index", rc); 364 of_node_put(np); 365 goto out; 366 } 367 368 if (index == drc_index) { 369 rc = changeset_detach_node_recursive(&ocs, np); 370 if (rc) { 371 of_node_put(np); 372 goto out; 373 } 374 } 375 } 376 377 rc = of_changeset_apply(&ocs); 378 379 out: 380 of_changeset_destroy(&ocs); 381 return rc; 382 } 383 384 static int dlpar_hp_dt(struct pseries_hp_errorlog *phpe) 385 { 386 u32 drc_index; 387 int rc; 388 389 if (phpe->id_type != PSERIES_HP_ELOG_ID_DRC_INDEX) 390 return -EINVAL; 391 392 drc_index = be32_to_cpu(phpe->_drc_u.drc_index); 393 394 lock_device_hotplug(); 395 396 switch (phpe->action) { 397 case PSERIES_HP_ELOG_ACTION_REMOVE: 398 rc = dlpar_hp_dt_remove(drc_index); 399 break; 400 default: 401 pr_err("Invalid action (%d) specified\n", phpe->action); 402 rc = -EINVAL; 403 break; 404 } 405 406 unlock_device_hotplug(); 407 408 return rc; 409 } 410 411 int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) 412 { 413 int rc; 414 415 switch (hp_elog->resource) { 416 case PSERIES_HP_ELOG_RESOURCE_MEM: 417 rc = dlpar_memory(hp_elog); 418 break; 419 case PSERIES_HP_ELOG_RESOURCE_CPU: 420 rc = dlpar_cpu(hp_elog); 421 break; 422 case PSERIES_HP_ELOG_RESOURCE_PMEM: 423 rc = dlpar_hp_pmem(hp_elog); 424 break; 425 case PSERIES_HP_ELOG_RESOURCE_DT: 426 rc = dlpar_hp_dt(hp_elog); 427 break; 428 429 default: 430 pr_warn_ratelimited("Invalid resource (%d) specified\n", 431 hp_elog->resource); 432 rc = -EINVAL; 433 } 434 435 return rc; 436 } 437 438 static void pseries_hp_work_fn(struct work_struct *work) 439 { 440 struct pseries_hp_work *hp_work = 441 container_of(work, struct pseries_hp_work, work); 442 443 handle_dlpar_errorlog(hp_work->errlog); 444 445 kfree(hp_work->errlog); 446 kfree(work); 447 } 448 449 void queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog) 450 { 451 struct pseries_hp_work *work; 452 struct pseries_hp_errorlog *hp_errlog_copy; 453 454 hp_errlog_copy = kmemdup(hp_errlog, sizeof(*hp_errlog), GFP_ATOMIC); 455 if (!hp_errlog_copy) 456 return; 457 458 work = kmalloc(sizeof(struct pseries_hp_work), GFP_ATOMIC); 459 if (work) { 460 INIT_WORK((struct work_struct *)work, pseries_hp_work_fn); 461 work->errlog = hp_errlog_copy; 462 queue_work(pseries_hp_wq, (struct work_struct *)work); 463 } else { 464 kfree(hp_errlog_copy); 465 } 466 } 467 468 static int dlpar_parse_resource(char **cmd, struct pseries_hp_errorlog *hp_elog) 469 { 470 char *arg; 471 472 arg = strsep(cmd, " "); 473 if (!arg) 474 return -EINVAL; 475 476 if (sysfs_streq(arg, "memory")) { 477 hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_MEM; 478 } else if (sysfs_streq(arg, "cpu")) { 479 hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_CPU; 480 } else if (sysfs_streq(arg, "dt")) { 481 hp_elog->resource = PSERIES_HP_ELOG_RESOURCE_DT; 482 } else { 483 pr_err("Invalid resource specified.\n"); 484 return -EINVAL; 485 } 486 487 return 0; 488 } 489 490 static int dlpar_parse_action(char **cmd, struct pseries_hp_errorlog *hp_elog) 491 { 492 char *arg; 493 494 arg = strsep(cmd, " "); 495 if (!arg) 496 return -EINVAL; 497 498 if (sysfs_streq(arg, "add")) { 499 hp_elog->action = PSERIES_HP_ELOG_ACTION_ADD; 500 } else if (sysfs_streq(arg, "remove")) { 501 hp_elog->action = PSERIES_HP_ELOG_ACTION_REMOVE; 502 } else { 503 pr_err("Invalid action specified.\n"); 504 return -EINVAL; 505 } 506 507 return 0; 508 } 509 510 static int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog) 511 { 512 char *arg; 513 u32 count, index; 514 515 arg = strsep(cmd, " "); 516 if (!arg) 517 return -EINVAL; 518 519 if (sysfs_streq(arg, "indexed-count")) { 520 hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC; 521 arg = strsep(cmd, " "); 522 if (!arg) { 523 pr_err("No DRC count specified.\n"); 524 return -EINVAL; 525 } 526 527 if (kstrtou32(arg, 0, &count)) { 528 pr_err("Invalid DRC count specified.\n"); 529 return -EINVAL; 530 } 531 532 arg = strsep(cmd, " "); 533 if (!arg) { 534 pr_err("No DRC Index specified.\n"); 535 return -EINVAL; 536 } 537 538 if (kstrtou32(arg, 0, &index)) { 539 pr_err("Invalid DRC Index specified.\n"); 540 return -EINVAL; 541 } 542 543 hp_elog->_drc_u.ic.count = cpu_to_be32(count); 544 hp_elog->_drc_u.ic.index = cpu_to_be32(index); 545 } else if (sysfs_streq(arg, "index")) { 546 hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; 547 arg = strsep(cmd, " "); 548 if (!arg) { 549 pr_err("No DRC Index specified.\n"); 550 return -EINVAL; 551 } 552 553 if (kstrtou32(arg, 0, &index)) { 554 pr_err("Invalid DRC Index specified.\n"); 555 return -EINVAL; 556 } 557 558 hp_elog->_drc_u.drc_index = cpu_to_be32(index); 559 } else if (sysfs_streq(arg, "count")) { 560 hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_COUNT; 561 arg = strsep(cmd, " "); 562 if (!arg) { 563 pr_err("No DRC count specified.\n"); 564 return -EINVAL; 565 } 566 567 if (kstrtou32(arg, 0, &count)) { 568 pr_err("Invalid DRC count specified.\n"); 569 return -EINVAL; 570 } 571 572 hp_elog->_drc_u.drc_count = cpu_to_be32(count); 573 } else { 574 pr_err("Invalid id_type specified.\n"); 575 return -EINVAL; 576 } 577 578 return 0; 579 } 580 581 static ssize_t dlpar_store(const struct class *class, const struct class_attribute *attr, 582 const char *buf, size_t count) 583 { 584 struct pseries_hp_errorlog hp_elog; 585 char *argbuf; 586 char *args; 587 int rc; 588 589 args = argbuf = kstrdup(buf, GFP_KERNEL); 590 if (!argbuf) 591 return -ENOMEM; 592 593 /* 594 * Parse out the request from the user, this will be in the form: 595 * <resource> <action> <id_type> <id> 596 */ 597 rc = dlpar_parse_resource(&args, &hp_elog); 598 if (rc) 599 goto dlpar_store_out; 600 601 rc = dlpar_parse_action(&args, &hp_elog); 602 if (rc) 603 goto dlpar_store_out; 604 605 rc = dlpar_parse_id_type(&args, &hp_elog); 606 if (rc) 607 goto dlpar_store_out; 608 609 rc = handle_dlpar_errorlog(&hp_elog); 610 611 dlpar_store_out: 612 kfree(argbuf); 613 614 if (rc) 615 pr_err("Could not handle DLPAR request \"%s\"\n", buf); 616 617 return rc ? rc : count; 618 } 619 620 static ssize_t dlpar_show(const struct class *class, const struct class_attribute *attr, 621 char *buf) 622 { 623 return sprintf(buf, "%s\n", "memory,cpu,dt"); 624 } 625 626 static CLASS_ATTR_RW(dlpar); 627 628 int __init dlpar_workqueue_init(void) 629 { 630 if (pseries_hp_wq) 631 return 0; 632 633 pseries_hp_wq = alloc_ordered_workqueue("pseries hotplug workqueue", 0); 634 635 return pseries_hp_wq ? 0 : -ENOMEM; 636 } 637 638 static int __init dlpar_sysfs_init(void) 639 { 640 int rc; 641 642 rc = dlpar_workqueue_init(); 643 if (rc) 644 return rc; 645 646 return sysfs_create_file(kernel_kobj, &class_attr_dlpar.attr); 647 } 648 machine_device_initcall(pseries, dlpar_sysfs_init); 649 650