Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpu/hotplug: Add multi instance support

This patch adds the ability for a given state to have multiple
instances. Until now all states have a single instance and the startup /
teardown callback use global variables.
A few drivers need to perform a the same callbacks on multiple
"instances". Currently we have three drivers in tree which all have a
global list which they iterate over. With multi instance they support
don't need their private list and the functionality has been moved into
core code. Plus we hold the hotplug lock in core so no cpus comes/goes
while instances are registered and we do rollback in error case :)

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: rt@linutronix.de
Link: http://lkml.kernel.org/r/1471024183-12666-3-git-send-email-bigeasy@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

+317 -37
+107 -3
include/linux/cpuhotplug.h
··· 99 99 100 100 int __cpuhp_setup_state(enum cpuhp_state state, const char *name, bool invoke, 101 101 int (*startup)(unsigned int cpu), 102 - int (*teardown)(unsigned int cpu)); 102 + int (*teardown)(unsigned int cpu), bool multi_instance); 103 103 104 104 /** 105 105 * cpuhp_setup_state - Setup hotplug state callbacks with calling the callbacks ··· 116 116 int (*startup)(unsigned int cpu), 117 117 int (*teardown)(unsigned int cpu)) 118 118 { 119 - return __cpuhp_setup_state(state, name, true, startup, teardown); 119 + return __cpuhp_setup_state(state, name, true, startup, teardown, false); 120 120 } 121 121 122 122 /** ··· 135 135 int (*startup)(unsigned int cpu), 136 136 int (*teardown)(unsigned int cpu)) 137 137 { 138 - return __cpuhp_setup_state(state, name, false, startup, teardown); 138 + return __cpuhp_setup_state(state, name, false, startup, teardown, 139 + false); 140 + } 141 + 142 + /** 143 + * cpuhp_setup_state_multi - Add callbacks for multi state 144 + * @state: The state for which the calls are installed 145 + * @name: Name of the callback. 146 + * @startup: startup callback function 147 + * @teardown: teardown callback function 148 + * 149 + * Sets the internal multi_instance flag and prepares a state to work as a multi 150 + * instance callback. No callbacks are invoked at this point. The callbacks are 151 + * invoked once an instance for this state are registered via 152 + * @cpuhp_state_add_instance or @cpuhp_state_add_instance_nocalls. 153 + */ 154 + static inline int cpuhp_setup_state_multi(enum cpuhp_state state, 155 + const char *name, 156 + int (*startup)(unsigned int cpu, 157 + struct hlist_node *node), 158 + int (*teardown)(unsigned int cpu, 159 + struct hlist_node *node)) 160 + { 161 + return __cpuhp_setup_state(state, name, false, 162 + (void *) startup, 163 + (void *) teardown, true); 164 + } 165 + 166 + int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node, 167 + bool invoke); 168 + 169 + /** 170 + * cpuhp_state_add_instance - Add an instance for a state and invoke startup 171 + * callback. 172 + * @state: The state for which the instance is installed 173 + * @node: The node for this individual state. 174 + * 175 + * Installs the instance for the @state and invokes the startup callback on 176 + * the present cpus which have already reached the @state. The @state must have 177 + * been earlier marked as multi-instance by @cpuhp_setup_state_multi. 178 + */ 179 + static inline int cpuhp_state_add_instance(enum cpuhp_state state, 180 + struct hlist_node *node) 181 + { 182 + return __cpuhp_state_add_instance(state, node, true); 183 + } 184 + 185 + /** 186 + * cpuhp_state_add_instance_nocalls - Add an instance for a state without 187 + * invoking the startup callback. 188 + * @state: The state for which the instance is installed 189 + * @node: The node for this individual state. 190 + * 191 + * Installs the instance for the @state The @state must have been earlier 192 + * marked as multi-instance by @cpuhp_setup_state_multi. 193 + */ 194 + static inline int cpuhp_state_add_instance_nocalls(enum cpuhp_state state, 195 + struct hlist_node *node) 196 + { 197 + return __cpuhp_state_add_instance(state, node, false); 139 198 } 140 199 141 200 void __cpuhp_remove_state(enum cpuhp_state state, bool invoke); ··· 219 160 static inline void cpuhp_remove_state_nocalls(enum cpuhp_state state) 220 161 { 221 162 __cpuhp_remove_state(state, false); 163 + } 164 + 165 + /** 166 + * cpuhp_remove_multi_state - Remove hotplug multi state callback 167 + * @state: The state for which the calls are removed 168 + * 169 + * Removes the callback functions from a multi state. This is the reverse of 170 + * cpuhp_setup_state_multi(). All instances should have been removed before 171 + * invoking this function. 172 + */ 173 + static inline void cpuhp_remove_multi_state(enum cpuhp_state state) 174 + { 175 + __cpuhp_remove_state(state, false); 176 + } 177 + 178 + int __cpuhp_state_remove_instance(enum cpuhp_state state, 179 + struct hlist_node *node, bool invoke); 180 + 181 + /** 182 + * cpuhp_state_remove_instance - Remove hotplug instance from state and invoke 183 + * the teardown callback 184 + * @state: The state from which the instance is removed 185 + * @node: The node for this individual state. 186 + * 187 + * Removes the instance and invokes the teardown callback on the present cpus 188 + * which have already reached the @state. 189 + */ 190 + static inline int cpuhp_state_remove_instance(enum cpuhp_state state, 191 + struct hlist_node *node) 192 + { 193 + return __cpuhp_state_remove_instance(state, node, true); 194 + } 195 + 196 + /** 197 + * cpuhp_state_remove_instance_nocalls - Remove hotplug instance from state 198 + * without invoking the reatdown callback 199 + * @state: The state from which the instance is removed 200 + * @node: The node for this individual state. 201 + * 202 + * Removes the instance without invoking the teardown callback. 203 + */ 204 + static inline int cpuhp_state_remove_instance_nocalls(enum cpuhp_state state, 205 + struct hlist_node *node) 206 + { 207 + return __cpuhp_state_remove_instance(state, node, false); 222 208 } 223 209 224 210 #ifdef CONFIG_SMP
+28
include/trace/events/cpuhp.h
··· 33 33 __entry->cpu, __entry->target, __entry->idx, __entry->fun) 34 34 ); 35 35 36 + TRACE_EVENT(cpuhp_multi_enter, 37 + 38 + TP_PROTO(unsigned int cpu, 39 + int target, 40 + int idx, 41 + int (*fun)(unsigned int, struct hlist_node *), 42 + struct hlist_node *node), 43 + 44 + TP_ARGS(cpu, target, idx, fun, node), 45 + 46 + TP_STRUCT__entry( 47 + __field( unsigned int, cpu ) 48 + __field( int, target ) 49 + __field( int, idx ) 50 + __field( void *, fun ) 51 + ), 52 + 53 + TP_fast_assign( 54 + __entry->cpu = cpu; 55 + __entry->target = target; 56 + __entry->idx = idx; 57 + __entry->fun = fun; 58 + ), 59 + 60 + TP_printk("cpu: %04u target: %3d step: %3d (%pf)", 61 + __entry->cpu, __entry->target, __entry->idx, __entry->fun) 62 + ); 63 + 36 64 TRACE_EVENT(cpuhp_exit, 37 65 38 66 TP_PROTO(unsigned int cpu,
+182 -34
kernel/cpu.c
··· 52 52 bool rollback; 53 53 bool single; 54 54 bool bringup; 55 + struct hlist_node *node; 55 56 enum cpuhp_state cb_state; 56 57 int result; 57 58 struct completion done; ··· 71 70 * @cant_stop: Bringup/teardown can't be stopped at this step 72 71 */ 73 72 struct cpuhp_step { 74 - const char *name; 75 - int (*startup)(unsigned int cpu); 76 - int (*teardown)(unsigned int cpu); 77 - bool skip_onerr; 78 - bool cant_stop; 73 + const char *name; 74 + union { 75 + int (*startup)(unsigned int cpu); 76 + int (*startup_multi)(unsigned int cpu, 77 + struct hlist_node *node); 78 + }; 79 + union { 80 + int (*teardown)(unsigned int cpu); 81 + int (*teardown_multi)(unsigned int cpu, 82 + struct hlist_node *node); 83 + }; 84 + struct hlist_head list; 85 + bool skip_onerr; 86 + bool cant_stop; 87 + bool multi_instance; 79 88 }; 80 89 81 90 static DEFINE_MUTEX(cpuhp_state_mutex); ··· 115 104 * @step: The step in the state machine 116 105 * @bringup: True if the bringup callback should be invoked 117 106 * 118 - * Called from cpu hotplug and from the state register machinery 107 + * Called from cpu hotplug and from the state register machinery. 119 108 */ 120 109 static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, 121 - bool bringup) 110 + bool bringup, struct hlist_node *node) 122 111 { 123 112 struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); 124 113 struct cpuhp_step *step = cpuhp_get_step(state); 125 - int (*cb)(unsigned int cpu) = bringup ? step->startup : step->teardown; 126 - int ret = 0; 114 + int (*cbm)(unsigned int cpu, struct hlist_node *node); 115 + int (*cb)(unsigned int cpu); 116 + int ret, cnt; 127 117 128 - if (cb) { 118 + if (!step->multi_instance) { 119 + cb = bringup ? step->startup : step->teardown; 120 + if (!cb) 121 + return 0; 129 122 trace_cpuhp_enter(cpu, st->target, state, cb); 130 123 ret = cb(cpu); 131 124 trace_cpuhp_exit(cpu, st->state, state, ret); 125 + return ret; 126 + } 127 + cbm = bringup ? step->startup_multi : step->teardown_multi; 128 + if (!cbm) 129 + return 0; 130 + 131 + /* Single invocation for instance add/remove */ 132 + if (node) { 133 + trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); 134 + ret = cbm(cpu, node); 135 + trace_cpuhp_exit(cpu, st->state, state, ret); 136 + return ret; 137 + } 138 + 139 + /* State transition. Invoke on all instances */ 140 + cnt = 0; 141 + hlist_for_each(node, &step->list) { 142 + trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); 143 + ret = cbm(cpu, node); 144 + trace_cpuhp_exit(cpu, st->state, state, ret); 145 + if (ret) 146 + goto err; 147 + cnt++; 148 + } 149 + return 0; 150 + err: 151 + /* Rollback the instances if one failed */ 152 + cbm = !bringup ? step->startup_multi : step->teardown_multi; 153 + if (!cbm) 154 + return ret; 155 + 156 + hlist_for_each(node, &step->list) { 157 + if (!cnt--) 158 + break; 159 + cbm(cpu, node); 132 160 } 133 161 return ret; 134 162 } ··· 448 398 struct cpuhp_step *step = cpuhp_get_step(st->state); 449 399 450 400 if (!step->skip_onerr) 451 - cpuhp_invoke_callback(cpu, st->state, true); 401 + cpuhp_invoke_callback(cpu, st->state, true, NULL); 452 402 } 453 403 } 454 404 ··· 459 409 int ret = 0; 460 410 461 411 for (; st->state > target; st->state--) { 462 - ret = cpuhp_invoke_callback(cpu, st->state, false); 412 + ret = cpuhp_invoke_callback(cpu, st->state, false, NULL); 463 413 if (ret) { 464 414 st->target = prev_state; 465 415 undo_cpu_down(cpu, st); ··· 475 425 struct cpuhp_step *step = cpuhp_get_step(st->state); 476 426 477 427 if (!step->skip_onerr) 478 - cpuhp_invoke_callback(cpu, st->state, false); 428 + cpuhp_invoke_callback(cpu, st->state, false, NULL); 479 429 } 480 430 } 481 431 ··· 487 437 488 438 while (st->state < target) { 489 439 st->state++; 490 - ret = cpuhp_invoke_callback(cpu, st->state, true); 440 + ret = cpuhp_invoke_callback(cpu, st->state, true, NULL); 491 441 if (ret) { 492 442 st->target = prev_state; 493 443 undo_cpu_up(cpu, st); ··· 552 502 if (st->cb_state < CPUHP_AP_ONLINE) { 553 503 local_irq_disable(); 554 504 ret = cpuhp_invoke_callback(cpu, st->cb_state, 555 - st->bringup); 505 + st->bringup, st->node); 556 506 local_irq_enable(); 557 507 } else { 558 508 ret = cpuhp_invoke_callback(cpu, st->cb_state, 559 - st->bringup); 509 + st->bringup, st->node); 560 510 } 561 511 } else if (st->rollback) { 562 512 BUG_ON(st->state < CPUHP_AP_ONLINE_IDLE); ··· 584 534 585 535 /* Invoke a single callback on a remote cpu */ 586 536 static int 587 - cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, bool bringup) 537 + cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, bool bringup, 538 + struct hlist_node *node) 588 539 { 589 540 struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); 590 541 ··· 597 546 * we invoke the thread function directly. 598 547 */ 599 548 if (!st->thread) 600 - return cpuhp_invoke_callback(cpu, state, bringup); 549 + return cpuhp_invoke_callback(cpu, state, bringup, node); 601 550 602 551 st->cb_state = state; 603 552 st->single = true; 604 553 st->bringup = bringup; 554 + st->node = node; 605 555 606 556 /* 607 557 * Make sure the above stores are visible before should_run becomes ··· 778 726 st->state--; 779 727 /* Invoke the former CPU_DYING callbacks */ 780 728 for (; st->state > target; st->state--) 781 - cpuhp_invoke_callback(cpu, st->state, false); 729 + cpuhp_invoke_callback(cpu, st->state, false, NULL); 782 730 783 731 /* Give up timekeeping duties */ 784 732 tick_handover_do_timer(); ··· 973 921 974 922 while (st->state < target) { 975 923 st->state++; 976 - cpuhp_invoke_callback(cpu, st->state, true); 924 + cpuhp_invoke_callback(cpu, st->state, true, NULL); 977 925 } 978 926 } 979 927 ··· 1438 1386 static void cpuhp_store_callbacks(enum cpuhp_state state, 1439 1387 const char *name, 1440 1388 int (*startup)(unsigned int cpu), 1441 - int (*teardown)(unsigned int cpu)) 1389 + int (*teardown)(unsigned int cpu), 1390 + bool multi_instance) 1442 1391 { 1443 1392 /* (Un)Install the callbacks for further cpu hotplug operations */ 1444 1393 struct cpuhp_step *sp; ··· 1449 1396 sp->startup = startup; 1450 1397 sp->teardown = teardown; 1451 1398 sp->name = name; 1399 + sp->multi_instance = multi_instance; 1400 + INIT_HLIST_HEAD(&sp->list); 1452 1401 mutex_unlock(&cpuhp_state_mutex); 1453 1402 } 1454 1403 ··· 1463 1408 * Call the startup/teardown function for a step either on the AP or 1464 1409 * on the current CPU. 1465 1410 */ 1466 - static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup) 1411 + static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup, 1412 + struct hlist_node *node) 1467 1413 { 1468 1414 struct cpuhp_step *sp = cpuhp_get_step(state); 1469 1415 int ret; ··· 1477 1421 */ 1478 1422 #ifdef CONFIG_SMP 1479 1423 if (cpuhp_is_ap_state(state)) 1480 - ret = cpuhp_invoke_ap_callback(cpu, state, bringup); 1424 + ret = cpuhp_invoke_ap_callback(cpu, state, bringup, node); 1481 1425 else 1482 - ret = cpuhp_invoke_callback(cpu, state, bringup); 1426 + ret = cpuhp_invoke_callback(cpu, state, bringup, node); 1483 1427 #else 1484 - ret = cpuhp_invoke_callback(cpu, state, bringup); 1428 + ret = cpuhp_invoke_callback(cpu, state, bringup, node); 1485 1429 #endif 1486 1430 BUG_ON(ret && !bringup); 1487 1431 return ret; ··· 1492 1436 * 1493 1437 * Note: The teardown callbacks for rollback are not allowed to fail! 1494 1438 */ 1495 - static void cpuhp_rollback_install(int failedcpu, enum cpuhp_state state) 1439 + static void cpuhp_rollback_install(int failedcpu, enum cpuhp_state state, 1440 + struct hlist_node *node) 1496 1441 { 1497 1442 int cpu; 1498 1443 ··· 1507 1450 1508 1451 /* Did we invoke the startup call on that cpu ? */ 1509 1452 if (cpustate >= state) 1510 - cpuhp_issue_call(cpu, state, false); 1453 + cpuhp_issue_call(cpu, state, false, node); 1511 1454 } 1512 1455 } 1513 1456 ··· 1534 1477 return -ENOSPC; 1535 1478 } 1536 1479 1480 + int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node, 1481 + bool invoke) 1482 + { 1483 + struct cpuhp_step *sp; 1484 + int cpu; 1485 + int ret; 1486 + 1487 + sp = cpuhp_get_step(state); 1488 + if (sp->multi_instance == false) 1489 + return -EINVAL; 1490 + 1491 + get_online_cpus(); 1492 + 1493 + if (!invoke || !sp->startup_multi) 1494 + goto add_node; 1495 + 1496 + /* 1497 + * Try to call the startup callback for each present cpu 1498 + * depending on the hotplug state of the cpu. 1499 + */ 1500 + for_each_present_cpu(cpu) { 1501 + struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); 1502 + int cpustate = st->state; 1503 + 1504 + if (cpustate < state) 1505 + continue; 1506 + 1507 + ret = cpuhp_issue_call(cpu, state, true, node); 1508 + if (ret) { 1509 + if (sp->teardown_multi) 1510 + cpuhp_rollback_install(cpu, state, node); 1511 + goto err; 1512 + } 1513 + } 1514 + add_node: 1515 + ret = 0; 1516 + mutex_lock(&cpuhp_state_mutex); 1517 + hlist_add_head(node, &sp->list); 1518 + mutex_unlock(&cpuhp_state_mutex); 1519 + 1520 + err: 1521 + put_online_cpus(); 1522 + return ret; 1523 + } 1524 + EXPORT_SYMBOL_GPL(__cpuhp_state_add_instance); 1525 + 1537 1526 /** 1538 1527 * __cpuhp_setup_state - Setup the callbacks for an hotplug machine state 1539 1528 * @state: The state to setup ··· 1593 1490 int __cpuhp_setup_state(enum cpuhp_state state, 1594 1491 const char *name, bool invoke, 1595 1492 int (*startup)(unsigned int cpu), 1596 - int (*teardown)(unsigned int cpu)) 1493 + int (*teardown)(unsigned int cpu), 1494 + bool multi_instance) 1597 1495 { 1598 1496 int cpu, ret = 0; 1599 1497 int dyn_state = 0; ··· 1613 1509 state = ret; 1614 1510 } 1615 1511 1616 - cpuhp_store_callbacks(state, name, startup, teardown); 1512 + cpuhp_store_callbacks(state, name, startup, teardown, multi_instance); 1617 1513 1618 1514 if (!invoke || !startup) 1619 1515 goto out; ··· 1629 1525 if (cpustate < state) 1630 1526 continue; 1631 1527 1632 - ret = cpuhp_issue_call(cpu, state, true); 1528 + ret = cpuhp_issue_call(cpu, state, true, NULL); 1633 1529 if (ret) { 1634 1530 if (teardown) 1635 - cpuhp_rollback_install(cpu, state); 1636 - cpuhp_store_callbacks(state, NULL, NULL, NULL); 1531 + cpuhp_rollback_install(cpu, state, NULL); 1532 + cpuhp_store_callbacks(state, NULL, NULL, NULL, false); 1637 1533 goto out; 1638 1534 } 1639 1535 } ··· 1645 1541 } 1646 1542 EXPORT_SYMBOL(__cpuhp_setup_state); 1647 1543 1544 + int __cpuhp_state_remove_instance(enum cpuhp_state state, 1545 + struct hlist_node *node, bool invoke) 1546 + { 1547 + struct cpuhp_step *sp = cpuhp_get_step(state); 1548 + int cpu; 1549 + 1550 + BUG_ON(cpuhp_cb_check(state)); 1551 + 1552 + if (!sp->multi_instance) 1553 + return -EINVAL; 1554 + 1555 + get_online_cpus(); 1556 + if (!invoke || !cpuhp_get_teardown_cb(state)) 1557 + goto remove; 1558 + /* 1559 + * Call the teardown callback for each present cpu depending 1560 + * on the hotplug state of the cpu. This function is not 1561 + * allowed to fail currently! 1562 + */ 1563 + for_each_present_cpu(cpu) { 1564 + struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); 1565 + int cpustate = st->state; 1566 + 1567 + if (cpustate >= state) 1568 + cpuhp_issue_call(cpu, state, false, node); 1569 + } 1570 + 1571 + remove: 1572 + mutex_lock(&cpuhp_state_mutex); 1573 + hlist_del(node); 1574 + mutex_unlock(&cpuhp_state_mutex); 1575 + put_online_cpus(); 1576 + 1577 + return 0; 1578 + } 1579 + EXPORT_SYMBOL_GPL(__cpuhp_state_remove_instance); 1648 1580 /** 1649 1581 * __cpuhp_remove_state - Remove the callbacks for an hotplug machine state 1650 1582 * @state: The state to remove ··· 1692 1552 */ 1693 1553 void __cpuhp_remove_state(enum cpuhp_state state, bool invoke) 1694 1554 { 1555 + struct cpuhp_step *sp = cpuhp_get_step(state); 1695 1556 int cpu; 1696 1557 1697 1558 BUG_ON(cpuhp_cb_check(state)); 1698 1559 1699 1560 get_online_cpus(); 1561 + 1562 + if (sp->multi_instance) { 1563 + WARN(!hlist_empty(&sp->list), 1564 + "Error: Removing state %d which has instances left.\n", 1565 + state); 1566 + goto remove; 1567 + } 1700 1568 1701 1569 if (!invoke || !cpuhp_get_teardown_cb(state)) 1702 1570 goto remove; ··· 1719 1571 int cpustate = st->state; 1720 1572 1721 1573 if (cpustate >= state) 1722 - cpuhp_issue_call(cpu, state, false); 1574 + cpuhp_issue_call(cpu, state, false, NULL); 1723 1575 } 1724 1576 remove: 1725 - cpuhp_store_callbacks(state, NULL, NULL, NULL); 1577 + cpuhp_store_callbacks(state, NULL, NULL, NULL, false); 1726 1578 put_online_cpus(); 1727 1579 } 1728 1580 EXPORT_SYMBOL(__cpuhp_remove_state);