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 #include <linux/kernel.h> 14 #include <linux/kref.h> 15 #include <linux/notifier.h> 16 #include <linux/proc_fs.h> 17 #include <linux/spinlock.h> 18 #include <linux/cpu.h> 19 #include <linux/slab.h> 20 #include "offline_states.h" 21 22 #include <asm/prom.h> 23 #include <asm/machdep.h> 24 #include <asm/uaccess.h> 25 #include <asm/rtas.h> 26 #include <asm/pSeries_reconfig.h> 27 28 struct cc_workarea { 29 u32 drc_index; 30 u32 zero; 31 u32 name_offset; 32 u32 prop_length; 33 u32 prop_offset; 34 }; 35 36 static void dlpar_free_cc_property(struct property *prop) 37 { 38 kfree(prop->name); 39 kfree(prop->value); 40 kfree(prop); 41 } 42 43 static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa) 44 { 45 struct property *prop; 46 char *name; 47 char *value; 48 49 prop = kzalloc(sizeof(*prop), GFP_KERNEL); 50 if (!prop) 51 return NULL; 52 53 name = (char *)ccwa + ccwa->name_offset; 54 prop->name = kstrdup(name, GFP_KERNEL); 55 56 prop->length = ccwa->prop_length; 57 value = (char *)ccwa + ccwa->prop_offset; 58 prop->value = kzalloc(prop->length, GFP_KERNEL); 59 if (!prop->value) { 60 dlpar_free_cc_property(prop); 61 return NULL; 62 } 63 64 memcpy(prop->value, value, prop->length); 65 return prop; 66 } 67 68 static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa) 69 { 70 struct device_node *dn; 71 char *name; 72 73 dn = kzalloc(sizeof(*dn), GFP_KERNEL); 74 if (!dn) 75 return NULL; 76 77 /* The configure connector reported name does not contain a 78 * preceeding '/', so we allocate a buffer large enough to 79 * prepend this to the full_name. 80 */ 81 name = (char *)ccwa + ccwa->name_offset; 82 dn->full_name = kasprintf(GFP_KERNEL, "/%s", name); 83 if (!dn->full_name) { 84 kfree(dn); 85 return NULL; 86 } 87 88 return dn; 89 } 90 91 static void dlpar_free_one_cc_node(struct device_node *dn) 92 { 93 struct property *prop; 94 95 while (dn->properties) { 96 prop = dn->properties; 97 dn->properties = prop->next; 98 dlpar_free_cc_property(prop); 99 } 100 101 kfree(dn->full_name); 102 kfree(dn); 103 } 104 105 static void dlpar_free_cc_nodes(struct device_node *dn) 106 { 107 if (dn->child) 108 dlpar_free_cc_nodes(dn->child); 109 110 if (dn->sibling) 111 dlpar_free_cc_nodes(dn->sibling); 112 113 dlpar_free_one_cc_node(dn); 114 } 115 116 #define NEXT_SIBLING 1 117 #define NEXT_CHILD 2 118 #define NEXT_PROPERTY 3 119 #define PREV_PARENT 4 120 #define MORE_MEMORY 5 121 #define CALL_AGAIN -2 122 #define ERR_CFG_USE -9003 123 124 struct device_node *dlpar_configure_connector(u32 drc_index) 125 { 126 struct device_node *dn; 127 struct device_node *first_dn = NULL; 128 struct device_node *last_dn = NULL; 129 struct property *property; 130 struct property *last_property = NULL; 131 struct cc_workarea *ccwa; 132 int cc_token; 133 int rc; 134 135 cc_token = rtas_token("ibm,configure-connector"); 136 if (cc_token == RTAS_UNKNOWN_SERVICE) 137 return NULL; 138 139 spin_lock(&rtas_data_buf_lock); 140 ccwa = (struct cc_workarea *)&rtas_data_buf[0]; 141 ccwa->drc_index = drc_index; 142 ccwa->zero = 0; 143 144 rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 145 while (rc) { 146 switch (rc) { 147 case NEXT_SIBLING: 148 dn = dlpar_parse_cc_node(ccwa); 149 if (!dn) 150 goto cc_error; 151 152 dn->parent = last_dn->parent; 153 last_dn->sibling = dn; 154 last_dn = dn; 155 break; 156 157 case NEXT_CHILD: 158 dn = dlpar_parse_cc_node(ccwa); 159 if (!dn) 160 goto cc_error; 161 162 if (!first_dn) 163 first_dn = dn; 164 else { 165 dn->parent = last_dn; 166 if (last_dn) 167 last_dn->child = dn; 168 } 169 170 last_dn = dn; 171 break; 172 173 case NEXT_PROPERTY: 174 property = dlpar_parse_cc_property(ccwa); 175 if (!property) 176 goto cc_error; 177 178 if (!last_dn->properties) 179 last_dn->properties = property; 180 else 181 last_property->next = property; 182 183 last_property = property; 184 break; 185 186 case PREV_PARENT: 187 last_dn = last_dn->parent; 188 break; 189 190 case CALL_AGAIN: 191 break; 192 193 case MORE_MEMORY: 194 case ERR_CFG_USE: 195 default: 196 printk(KERN_ERR "Unexpected Error (%d) " 197 "returned from configure-connector\n", rc); 198 goto cc_error; 199 } 200 201 rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL); 202 } 203 204 spin_unlock(&rtas_data_buf_lock); 205 return first_dn; 206 207 cc_error: 208 if (first_dn) 209 dlpar_free_cc_nodes(first_dn); 210 spin_unlock(&rtas_data_buf_lock); 211 return NULL; 212 } 213 214 static struct device_node *derive_parent(const char *path) 215 { 216 struct device_node *parent; 217 char *last_slash; 218 219 last_slash = strrchr(path, '/'); 220 if (last_slash == path) { 221 parent = of_find_node_by_path("/"); 222 } else { 223 char *parent_path; 224 int parent_path_len = last_slash - path + 1; 225 parent_path = kmalloc(parent_path_len, GFP_KERNEL); 226 if (!parent_path) 227 return NULL; 228 229 strlcpy(parent_path, path, parent_path_len); 230 parent = of_find_node_by_path(parent_path); 231 kfree(parent_path); 232 } 233 234 return parent; 235 } 236 237 int dlpar_attach_node(struct device_node *dn) 238 { 239 #ifdef CONFIG_PROC_DEVICETREE 240 struct proc_dir_entry *ent; 241 #endif 242 int rc; 243 244 of_node_set_flag(dn, OF_DYNAMIC); 245 kref_init(&dn->kref); 246 dn->parent = derive_parent(dn->full_name); 247 if (!dn->parent) 248 return -ENOMEM; 249 250 rc = blocking_notifier_call_chain(&pSeries_reconfig_chain, 251 PSERIES_RECONFIG_ADD, dn); 252 if (rc == NOTIFY_BAD) { 253 printk(KERN_ERR "Failed to add device node %s\n", 254 dn->full_name); 255 return -ENOMEM; /* For now, safe to assume kmalloc failure */ 256 } 257 258 of_attach_node(dn); 259 260 #ifdef CONFIG_PROC_DEVICETREE 261 ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde); 262 if (ent) 263 proc_device_tree_add_node(dn, ent); 264 #endif 265 266 of_node_put(dn->parent); 267 return 0; 268 } 269 270 int dlpar_detach_node(struct device_node *dn) 271 { 272 #ifdef CONFIG_PROC_DEVICETREE 273 struct device_node *parent = dn->parent; 274 struct property *prop = dn->properties; 275 276 while (prop) { 277 remove_proc_entry(prop->name, dn->pde); 278 prop = prop->next; 279 } 280 281 if (dn->pde) 282 remove_proc_entry(dn->pde->name, parent->pde); 283 #endif 284 285 blocking_notifier_call_chain(&pSeries_reconfig_chain, 286 PSERIES_RECONFIG_REMOVE, dn); 287 of_detach_node(dn); 288 of_node_put(dn); /* Must decrement the refcount */ 289 290 return 0; 291 } 292 293 #define DR_ENTITY_SENSE 9003 294 #define DR_ENTITY_PRESENT 1 295 #define DR_ENTITY_UNUSABLE 2 296 #define ALLOCATION_STATE 9003 297 #define ALLOC_UNUSABLE 0 298 #define ALLOC_USABLE 1 299 #define ISOLATION_STATE 9001 300 #define ISOLATE 0 301 #define UNISOLATE 1 302 303 int dlpar_acquire_drc(u32 drc_index) 304 { 305 int dr_status, rc; 306 307 rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 308 DR_ENTITY_SENSE, drc_index); 309 if (rc || dr_status != DR_ENTITY_UNUSABLE) 310 return -1; 311 312 rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE); 313 if (rc) 314 return rc; 315 316 rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 317 if (rc) { 318 rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 319 return rc; 320 } 321 322 return 0; 323 } 324 325 int dlpar_release_drc(u32 drc_index) 326 { 327 int dr_status, rc; 328 329 rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status, 330 DR_ENTITY_SENSE, drc_index); 331 if (rc || dr_status != DR_ENTITY_PRESENT) 332 return -1; 333 334 rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE); 335 if (rc) 336 return rc; 337 338 rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE); 339 if (rc) { 340 rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE); 341 return rc; 342 } 343 344 return 0; 345 } 346 347 #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE 348 349 static int dlpar_online_cpu(struct device_node *dn) 350 { 351 int rc = 0; 352 unsigned int cpu; 353 int len, nthreads, i; 354 const u32 *intserv; 355 356 intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 357 if (!intserv) 358 return -EINVAL; 359 360 nthreads = len / sizeof(u32); 361 362 cpu_maps_update_begin(); 363 for (i = 0; i < nthreads; i++) { 364 for_each_present_cpu(cpu) { 365 if (get_hard_smp_processor_id(cpu) != intserv[i]) 366 continue; 367 BUG_ON(get_cpu_current_state(cpu) 368 != CPU_STATE_OFFLINE); 369 cpu_maps_update_done(); 370 rc = cpu_up(cpu); 371 if (rc) 372 goto out; 373 cpu_maps_update_begin(); 374 375 break; 376 } 377 if (cpu == num_possible_cpus()) 378 printk(KERN_WARNING "Could not find cpu to online " 379 "with physical id 0x%x\n", intserv[i]); 380 } 381 cpu_maps_update_done(); 382 383 out: 384 return rc; 385 386 } 387 388 static ssize_t dlpar_cpu_probe(const char *buf, size_t count) 389 { 390 struct device_node *dn; 391 unsigned long drc_index; 392 char *cpu_name; 393 int rc; 394 395 cpu_hotplug_driver_lock(); 396 rc = strict_strtoul(buf, 0, &drc_index); 397 if (rc) { 398 rc = -EINVAL; 399 goto out; 400 } 401 402 dn = dlpar_configure_connector(drc_index); 403 if (!dn) { 404 rc = -EINVAL; 405 goto out; 406 } 407 408 /* configure-connector reports cpus as living in the base 409 * directory of the device tree. CPUs actually live in the 410 * cpus directory so we need to fixup the full_name. 411 */ 412 cpu_name = kasprintf(GFP_KERNEL, "/cpus%s", dn->full_name); 413 if (!cpu_name) { 414 dlpar_free_cc_nodes(dn); 415 rc = -ENOMEM; 416 goto out; 417 } 418 419 kfree(dn->full_name); 420 dn->full_name = cpu_name; 421 422 rc = dlpar_acquire_drc(drc_index); 423 if (rc) { 424 dlpar_free_cc_nodes(dn); 425 rc = -EINVAL; 426 goto out; 427 } 428 429 rc = dlpar_attach_node(dn); 430 if (rc) { 431 dlpar_release_drc(drc_index); 432 dlpar_free_cc_nodes(dn); 433 goto out; 434 } 435 436 rc = dlpar_online_cpu(dn); 437 out: 438 cpu_hotplug_driver_unlock(); 439 440 return rc ? rc : count; 441 } 442 443 static int dlpar_offline_cpu(struct device_node *dn) 444 { 445 int rc = 0; 446 unsigned int cpu; 447 int len, nthreads, i; 448 const u32 *intserv; 449 450 intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s", &len); 451 if (!intserv) 452 return -EINVAL; 453 454 nthreads = len / sizeof(u32); 455 456 cpu_maps_update_begin(); 457 for (i = 0; i < nthreads; i++) { 458 for_each_present_cpu(cpu) { 459 if (get_hard_smp_processor_id(cpu) != intserv[i]) 460 continue; 461 462 if (get_cpu_current_state(cpu) == CPU_STATE_OFFLINE) 463 break; 464 465 if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { 466 cpu_maps_update_done(); 467 rc = cpu_down(cpu); 468 if (rc) 469 goto out; 470 cpu_maps_update_begin(); 471 break; 472 473 } 474 475 /* 476 * The cpu is in CPU_STATE_INACTIVE. 477 * Upgrade it's state to CPU_STATE_OFFLINE. 478 */ 479 set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); 480 BUG_ON(plpar_hcall_norets(H_PROD, intserv[i]) 481 != H_SUCCESS); 482 __cpu_die(cpu); 483 break; 484 } 485 if (cpu == num_possible_cpus()) 486 printk(KERN_WARNING "Could not find cpu to offline " 487 "with physical id 0x%x\n", intserv[i]); 488 } 489 cpu_maps_update_done(); 490 491 out: 492 return rc; 493 494 } 495 496 static ssize_t dlpar_cpu_release(const char *buf, size_t count) 497 { 498 struct device_node *dn; 499 const u32 *drc_index; 500 int rc; 501 502 dn = of_find_node_by_path(buf); 503 if (!dn) 504 return -EINVAL; 505 506 drc_index = of_get_property(dn, "ibm,my-drc-index", NULL); 507 if (!drc_index) { 508 of_node_put(dn); 509 return -EINVAL; 510 } 511 512 cpu_hotplug_driver_lock(); 513 rc = dlpar_offline_cpu(dn); 514 if (rc) { 515 of_node_put(dn); 516 rc = -EINVAL; 517 goto out; 518 } 519 520 rc = dlpar_release_drc(*drc_index); 521 if (rc) { 522 of_node_put(dn); 523 goto out; 524 } 525 526 rc = dlpar_detach_node(dn); 527 if (rc) { 528 dlpar_acquire_drc(*drc_index); 529 goto out; 530 } 531 532 of_node_put(dn); 533 out: 534 cpu_hotplug_driver_unlock(); 535 return rc ? rc : count; 536 } 537 538 static int __init pseries_dlpar_init(void) 539 { 540 ppc_md.cpu_probe = dlpar_cpu_probe; 541 ppc_md.cpu_release = dlpar_cpu_release; 542 543 return 0; 544 } 545 machine_device_initcall(pseries, pseries_dlpar_init); 546 547 #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ 548