summaryrefslogtreecommitdiffstats
path: root/kernel/cpu.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2016-12-21 14:19:49 -0500
committerThomas Gleixner <tglx@linutronix.de>2016-12-25 04:47:42 -0500
commitdc280d93623927570da279e99393879dbbab39e7 (patch)
tree61223479925f5c46c38a7e0419e8e0058c39c84f /kernel/cpu.c
parent59fefd0890f12716b39de1d4e5482fd739316262 (diff)
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>
Diffstat (limited to 'kernel/cpu.c')
-rw-r--r--kernel/cpu.c96
1 files changed, 50 insertions, 46 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 5339aca811d2..3ff0ea5db371 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1432,23 +1432,53 @@ static int cpuhp_cb_check(enum cpuhp_state state)
1432 return 0; 1432 return 0;
1433} 1433}
1434 1434
1435static void cpuhp_store_callbacks(enum cpuhp_state state, 1435/*
1436 const char *name, 1436 * Returns a free for dynamic slot assignment of the Online state. The states
1437 int (*startup)(unsigned int cpu), 1437 * are protected by the cpuhp_slot_states mutex and an empty slot is identified
1438 int (*teardown)(unsigned int cpu), 1438 * by having no name assigned.
1439 bool multi_instance) 1439 */
1440static 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
1452static 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 /* (Un)Install the callbacks for further cpu hotplug operations */ 1457 /* (Un)Install the callbacks for further cpu hotplug operations */
1442 struct cpuhp_step *sp; 1458 struct cpuhp_step *sp;
1459 int ret = 0;
1443 1460
1444 mutex_lock(&cpuhp_state_mutex); 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 sp = cpuhp_get_step(state); 1469 sp = cpuhp_get_step(state);
1470 if (name && sp->name) {
1471 ret = -EBUSY;
1472 goto out;
1473 }
1446 sp->startup.single = startup; 1474 sp->startup.single = startup;
1447 sp->teardown.single = teardown; 1475 sp->teardown.single = teardown;
1448 sp->name = name; 1476 sp->name = name;
1449 sp->multi_instance = multi_instance; 1477 sp->multi_instance = multi_instance;
1450 INIT_HLIST_HEAD(&sp->list); 1478 INIT_HLIST_HEAD(&sp->list);
1479out:
1451 mutex_unlock(&cpuhp_state_mutex); 1480 mutex_unlock(&cpuhp_state_mutex);
1481 return ret;
1452} 1482}
1453 1483
1454static void *cpuhp_get_teardown_cb(enum cpuhp_state state) 1484static void *cpuhp_get_teardown_cb(enum cpuhp_state state)
@@ -1509,29 +1539,6 @@ static void cpuhp_rollback_install(int failedcpu, enum cpuhp_state state,
1509 } 1539 }
1510} 1540}
1511 1541
1512/*
1513 * Returns a free for dynamic slot assignment of the Online state. The states
1514 * are protected by the cpuhp_slot_states mutex and an empty slot is identified
1515 * by having no name assigned.
1516 */
1517static int cpuhp_reserve_state(enum cpuhp_state state)
1518{
1519 enum cpuhp_state i;
1520
1521 mutex_lock(&cpuhp_state_mutex);
1522 for (i = CPUHP_AP_ONLINE_DYN; i <= CPUHP_AP_ONLINE_DYN_END; i++) {
1523 if (cpuhp_ap_states[i].name)
1524 continue;
1525
1526 cpuhp_ap_states[i].name = "Reserved";
1527 mutex_unlock(&cpuhp_state_mutex);
1528 return i;
1529 }
1530 mutex_unlock(&cpuhp_state_mutex);
1531 WARN(1, "No more dynamic states available for CPU hotplug\n");
1532 return -ENOSPC;
1533}
1534
1535int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node, 1542int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,
1536 bool invoke) 1543 bool invoke)
1537{ 1544{
@@ -1580,11 +1587,13 @@ EXPORT_SYMBOL_GPL(__cpuhp_state_add_instance);
1580 1587
1581/** 1588/**
1582 * __cpuhp_setup_state - Setup the callbacks for an hotplug machine state 1589 * __cpuhp_setup_state - Setup the callbacks for an hotplug machine state
1583 * @state: The state to setup 1590 * @state: The state to setup
1584 * @invoke: If true, the startup function is invoked for cpus where 1591 * @invoke: If true, the startup function is invoked for cpus where
1585 * cpu state >= @state 1592 * cpu state >= @state
1586 * @startup: startup callback function 1593 * @startup: startup callback function
1587 * @teardown: teardown callback function 1594 * @teardown: teardown callback function
1595 * @multi_instance: State is set up for multiple instances which get
1596 * added afterwards.
1588 * 1597 *
1589 * Returns: 1598 * Returns:
1590 * On success: 1599 * On success:
@@ -1599,25 +1608,16 @@ int __cpuhp_setup_state(enum cpuhp_state state,
1599 bool multi_instance) 1608 bool multi_instance)
1600{ 1609{
1601 int cpu, ret = 0; 1610 int cpu, ret = 0;
1602 int dyn_state = 0;
1603 1611
1604 if (cpuhp_cb_check(state) || !name) 1612 if (cpuhp_cb_check(state) || !name)
1605 return -EINVAL; 1613 return -EINVAL;
1606 1614
1607 get_online_cpus(); 1615 get_online_cpus();
1608 1616
1609 /* currently assignments for the ONLINE state are possible */ 1617 ret = cpuhp_store_callbacks(state, name, startup, teardown,
1610 if (state == CPUHP_AP_ONLINE_DYN) { 1618 multi_instance);
1611 dyn_state = 1;
1612 ret = cpuhp_reserve_state(state);
1613 if (ret < 0)
1614 goto out;
1615 state = ret;
1616 }
1617
1618 cpuhp_store_callbacks(state, name, startup, teardown, multi_instance);
1619 1619
1620 if (!invoke || !startup) 1620 if (ret || !invoke || !startup)
1621 goto out; 1621 goto out;
1622 1622
1623 /* 1623 /*
@@ -1641,7 +1641,11 @@ int __cpuhp_setup_state(enum cpuhp_state state,
1641 } 1641 }
1642out: 1642out:
1643 put_online_cpus(); 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 return state; 1649 return state;
1646 return ret; 1650 return ret;
1647} 1651}