Skip to content

Commit dc280d9

Browse files
committed
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 <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Sebastian Siewior <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 59fefd0 commit dc280d9

File tree

1 file changed

+50
-46
lines changed

1 file changed

+50
-46
lines changed

kernel/cpu.c

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,23 +1432,53 @@ static int cpuhp_cb_check(enum cpuhp_state state)
14321432
return 0;
14331433
}
14341434

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)
14401456
{
14411457
/* (Un)Install the callbacks for further cpu hotplug operations */
14421458
struct cpuhp_step *sp;
1459+
int ret = 0;
14431460

14441461
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+
}
14451469
sp = cpuhp_get_step(state);
1470+
if (name && sp->name) {
1471+
ret = -EBUSY;
1472+
goto out;
1473+
}
14461474
sp->startup.single = startup;
14471475
sp->teardown.single = teardown;
14481476
sp->name = name;
14491477
sp->multi_instance = multi_instance;
14501478
INIT_HLIST_HEAD(&sp->list);
1479+
out:
14511480
mutex_unlock(&cpuhp_state_mutex);
1481+
return ret;
14521482
}
14531483

14541484
static void *cpuhp_get_teardown_cb(enum cpuhp_state state)
@@ -1509,29 +1539,6 @@ static void cpuhp_rollback_install(int failedcpu, enum cpuhp_state state,
15091539
}
15101540
}
15111541

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-
*/
1517-
static 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-
15351542
int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,
15361543
bool invoke)
15371544
{
@@ -1580,11 +1587,13 @@ EXPORT_SYMBOL_GPL(__cpuhp_state_add_instance);
15801587

15811588
/**
15821589
* __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
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+
* @multi_instance: State is set up for multiple instances which get
1596+
* added afterwards.
15881597
*
15891598
* Returns:
15901599
* On success:
@@ -1599,25 +1608,16 @@ int __cpuhp_setup_state(enum cpuhp_state state,
15991608
bool multi_instance)
16001609
{
16011610
int cpu, ret = 0;
1602-
int dyn_state = 0;
16031611

16041612
if (cpuhp_cb_check(state) || !name)
16051613
return -EINVAL;
16061614

16071615
get_online_cpus();
16081616

1609-
/* currently assignments for the ONLINE state are possible */
1610-
if (state == CPUHP_AP_ONLINE_DYN) {
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);
1617+
ret = cpuhp_store_callbacks(state, name, startup, teardown,
1618+
multi_instance);
16191619

1620-
if (!invoke || !startup)
1620+
if (ret || !invoke || !startup)
16211621
goto out;
16221622

16231623
/*
@@ -1641,7 +1641,11 @@ int __cpuhp_setup_state(enum cpuhp_state state,
16411641
}
16421642
out:
16431643
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)
16451649
return state;
16461650
return ret;
16471651
}

0 commit comments

Comments
 (0)