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