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 return 0; 1433 } 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) 1440 { 1441 /* (Un)Install the callbacks for further cpu hotplug operations */ 1442 struct cpuhp_step *sp; 1443 1444 mutex_lock(&cpuhp_state_mutex); 1445 sp = cpuhp_get_step(state); 1446 sp->startup.single = startup; 1447 sp->teardown.single = teardown; 1448 sp->name = name; 1449 sp->multi_instance = multi_instance; 1450 INIT_HLIST_HEAD(&sp->list); 1451 mutex_unlock(&cpuhp_state_mutex); 1452 } 1453 1454 static void *cpuhp_get_teardown_cb(enum cpuhp_state state) ··· 1539 } 1540 } 1541 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 int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node, 1566 bool invoke) 1567 { ··· 1587 1588 /** 1589 * __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 1595 * 1596 * Returns: 1597 * On success: ··· 1608 bool multi_instance) 1609 { 1610 int cpu, ret = 0; 1611 - int dyn_state = 0; 1612 1613 if (cpuhp_cb_check(state) || !name) 1614 return -EINVAL; 1615 1616 get_online_cpus(); 1617 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 - } 1626 1627 - cpuhp_store_callbacks(state, name, startup, teardown, multi_instance); 1628 - 1629 - if (!invoke || !startup) 1630 goto out; 1631 1632 /* ··· 1641 } 1642 out: 1643 put_online_cpus(); 1644 - if (!ret && dyn_state) 1645 return state; 1646 return ret; 1647 }
··· 1432 return 0; 1433 } 1434 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) 1456 { 1457 /* (Un)Install the callbacks for further cpu hotplug operations */ 1458 struct cpuhp_step *sp; 1459 + int ret = 0; 1460 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 + } 1469 sp = cpuhp_get_step(state); 1470 + if (name && sp->name) { 1471 + ret = -EBUSY; 1472 + goto out; 1473 + } 1474 sp->startup.single = startup; 1475 sp->teardown.single = teardown; 1476 sp->name = name; 1477 sp->multi_instance = multi_instance; 1478 INIT_HLIST_HEAD(&sp->list); 1479 + out: 1480 mutex_unlock(&cpuhp_state_mutex); 1481 + return ret; 1482 } 1483 1484 static void *cpuhp_get_teardown_cb(enum cpuhp_state state) ··· 1509 } 1510 } 1511 1512 int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node, 1513 bool invoke) 1514 { ··· 1580 1581 /** 1582 * __cpuhp_setup_state - Setup the callbacks for an hotplug machine state 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. 1590 * 1591 * Returns: 1592 * On success: ··· 1599 bool multi_instance) 1600 { 1601 int cpu, ret = 0; 1602 1603 if (cpuhp_cb_check(state) || !name) 1604 return -EINVAL; 1605 1606 get_online_cpus(); 1607 1608 + ret = cpuhp_store_callbacks(state, name, startup, teardown, 1609 + multi_instance); 1610 1611 + if (ret || !invoke || !startup) 1612 goto out; 1613 1614 /* ··· 1641 } 1642 out: 1643 put_online_cpus(); 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) 1649 return state; 1650 return ret; 1651 }