cpu/hotplug: Prevent overwriting of callbacks

Developers manage to overwrite states blindly without thought. That's fatal
and hard to debug. Add sanity checks to make it fail.

This requries to restructure the code so that the dynamic state allocation
happens in the same lock protected section as the actual store. Otherwise
the previous assignment of 'Reserved' to the name field would trigger the
overwrite check.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sebastian Siewior <bigeasy@linutronix.de>
Link: http://lkml.kernel.org/r/20161221192111.675234535@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

+50 -46
+50 -46
kernel/cpu.c
··· 1432 1432 return 0; 1433 1433 } 1434 1434 1435 - static void cpuhp_store_callbacks(enum cpuhp_state state, 1436 - const char *name, 1437 - int (*startup)(unsigned int cpu), 1438 - int (*teardown)(unsigned int cpu), 1439 - bool multi_instance) 1435 + /* 1436 + * Returns a free for dynamic slot assignment of the Online state. The states 1437 + * are protected by the cpuhp_slot_states mutex and an empty slot is identified 1438 + * by having no name assigned. 1439 + */ 1440 + static int cpuhp_reserve_state(enum cpuhp_state state) 1441 + { 1442 + enum cpuhp_state i; 1443 + 1444 + for (i = CPUHP_AP_ONLINE_DYN; i <= CPUHP_AP_ONLINE_DYN_END; i++) { 1445 + if (!cpuhp_ap_states[i].name) 1446 + return i; 1447 + } 1448 + WARN(1, "No more dynamic states available for CPU hotplug\n"); 1449 + return -ENOSPC; 1450 + } 1451 + 1452 + static int cpuhp_store_callbacks(enum cpuhp_state state, const char *name, 1453 + int (*startup)(unsigned int cpu), 1454 + int (*teardown)(unsigned int cpu), 1455 + bool multi_instance) 1440 1456 { 1441 1457 /* (Un)Install the callbacks for further cpu hotplug operations */ 1442 1458 struct cpuhp_step *sp; 1459 + int ret = 0; 1443 1460 1444 1461 mutex_lock(&cpuhp_state_mutex); 1462 + 1463 + if (state == CPUHP_AP_ONLINE_DYN) { 1464 + ret = cpuhp_reserve_state(state); 1465 + if (ret < 0) 1466 + goto out; 1467 + state = ret; 1468 + } 1445 1469 sp = cpuhp_get_step(state); 1470 + if (name && sp->name) { 1471 + ret = -EBUSY; 1472 + goto out; 1473 + } 1446 1474 sp->startup.single = startup; 1447 1475 sp->teardown.single = teardown; 1448 1476 sp->name = name; 1449 1477 sp->multi_instance = multi_instance; 1450 1478 INIT_HLIST_HEAD(&sp->list); 1479 + out: 1451 1480 mutex_unlock(&cpuhp_state_mutex); 1481 + return ret; 1452 1482 } 1453 1483 1454 1484 static void *cpuhp_get_teardown_cb(enum cpuhp_state state) ··· 1539 1509 } 1540 1510 } 1541 1511 1542 - /* 1543 - * Returns a free for dynamic slot assignment of the Online state. The states 1544 - * are protected by the cpuhp_slot_states mutex and an empty slot is identified 1545 - * by having no name assigned. 1546 - */ 1547 - static int cpuhp_reserve_state(enum cpuhp_state state) 1548 - { 1549 - enum cpuhp_state i; 1550 - 1551 - mutex_lock(&cpuhp_state_mutex); 1552 - for (i = CPUHP_AP_ONLINE_DYN; i <= CPUHP_AP_ONLINE_DYN_END; i++) { 1553 - if (cpuhp_ap_states[i].name) 1554 - continue; 1555 - 1556 - cpuhp_ap_states[i].name = "Reserved"; 1557 - mutex_unlock(&cpuhp_state_mutex); 1558 - return i; 1559 - } 1560 - mutex_unlock(&cpuhp_state_mutex); 1561 - WARN(1, "No more dynamic states available for CPU hotplug\n"); 1562 - return -ENOSPC; 1563 - } 1564 - 1565 1512 int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node, 1566 1513 bool invoke) 1567 1514 { ··· 1587 1580 1588 1581 /** 1589 1582 * __cpuhp_setup_state - Setup the callbacks for an hotplug machine state 1590 - * @state: The state to setup 1591 - * @invoke: If true, the startup function is invoked for cpus where 1592 - * cpu state >= @state 1593 - * @startup: startup callback function 1594 - * @teardown: teardown callback function 1583 + * @state: The state to setup 1584 + * @invoke: If true, the startup function is invoked for cpus where 1585 + * cpu state >= @state 1586 + * @startup: startup callback function 1587 + * @teardown: teardown callback function 1588 + * @multi_instance: State is set up for multiple instances which get 1589 + * added afterwards. 1595 1590 * 1596 1591 * Returns: 1597 1592 * On success: ··· 1608 1599 bool multi_instance) 1609 1600 { 1610 1601 int cpu, ret = 0; 1611 - int dyn_state = 0; 1612 1602 1613 1603 if (cpuhp_cb_check(state) || !name) 1614 1604 return -EINVAL; 1615 1605 1616 1606 get_online_cpus(); 1617 1607 1618 - /* currently assignments for the ONLINE state are possible */ 1619 - if (state == CPUHP_AP_ONLINE_DYN) { 1620 - dyn_state = 1; 1621 - ret = cpuhp_reserve_state(state); 1622 - if (ret < 0) 1623 - goto out; 1624 - state = ret; 1625 - } 1608 + ret = cpuhp_store_callbacks(state, name, startup, teardown, 1609 + multi_instance); 1626 1610 1627 - cpuhp_store_callbacks(state, name, startup, teardown, multi_instance); 1628 - 1629 - if (!invoke || !startup) 1611 + if (ret || !invoke || !startup) 1630 1612 goto out; 1631 1613 1632 1614 /* ··· 1641 1641 } 1642 1642 out: 1643 1643 put_online_cpus(); 1644 - if (!ret && dyn_state) 1644 + /* 1645 + * If the requested state is CPUHP_AP_ONLINE_DYN, return the 1646 + * dynamically allocated state in case of success. 1647 + */ 1648 + if (!ret && state == CPUHP_AP_ONLINE_DYN) 1645 1649 return state; 1646 1650 return ret; 1647 1651 }