summaryrefslogtreecommitdiffstats
path: root/kernel/cpu.c
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2017-03-14 11:06:45 -0400
committerThomas Gleixner <tglx@linutronix.de>2017-03-14 14:19:27 -0400
commitdc434e056fe1dada20df7ba07f32739d3a701adf (patch)
treef7756d35699b3a27b358a3d29e08220a18ef06c5 /kernel/cpu.c
parent4495c08e84729385774601b5146d51d9e5849f81 (diff)
cpu/hotplug: Serialize callback invocations proper
The setup/remove_state/instance() functions in the hotplug core code are serialized against concurrent CPU hotplug, but unfortunately not serialized against themself. As a consequence a concurrent invocation of these function results in corruption of the callback machinery because two instances try to invoke callbacks on remote cpus at the same time. This results in missing callback invocations and initiator threads waiting forever on the completion. The obvious solution to replace get_cpu_online() with cpu_hotplug_begin() is not possible because at least one callsite calls into these functions from a get_online_cpu() locked region. Extend the protection scope of the cpuhp_state_mutex from solely protecting the state arrays to cover the callback invocation machinery as well. Fixes: 5b7aa87e0482 ("cpu/hotplug: Implement setup/removal interface") Reported-and-tested-by: Bart Van Assche <Bart.VanAssche@sandisk.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Cc: hpa@zytor.com Cc: mingo@kernel.org Cc: akpm@linux-foundation.org Cc: torvalds@linux-foundation.org Link: http://lkml.kernel.org/r/20170314150645.g4tdyoszlcbajmna@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/cpu.c')
-rw-r--r--kernel/cpu.c28
1 files changed, 14 insertions, 14 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c
index f7c063239fa5..37b223e4fc05 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1335,26 +1335,21 @@ static int cpuhp_store_callbacks(enum cpuhp_state state, const char *name,
1335 struct cpuhp_step *sp; 1335 struct cpuhp_step *sp;
1336 int ret = 0; 1336 int ret = 0;
1337 1337
1338 mutex_lock(&cpuhp_state_mutex);
1339
1340 if (state == CPUHP_AP_ONLINE_DYN || state == CPUHP_BP_PREPARE_DYN) { 1338 if (state == CPUHP_AP_ONLINE_DYN || state == CPUHP_BP_PREPARE_DYN) {
1341 ret = cpuhp_reserve_state(state); 1339 ret = cpuhp_reserve_state(state);
1342 if (ret < 0) 1340 if (ret < 0)
1343 goto out; 1341 return ret;
1344 state = ret; 1342 state = ret;
1345 } 1343 }
1346 sp = cpuhp_get_step(state); 1344 sp = cpuhp_get_step(state);
1347 if (name && sp->name) { 1345 if (name && sp->name)
1348 ret = -EBUSY; 1346 return -EBUSY;
1349 goto out; 1347
1350 }
1351 sp->startup.single = startup; 1348 sp->startup.single = startup;
1352 sp->teardown.single = teardown; 1349 sp->teardown.single = teardown;
1353 sp->name = name; 1350 sp->name = name;
1354 sp->multi_instance = multi_instance; 1351 sp->multi_instance = multi_instance;
1355 INIT_HLIST_HEAD(&sp->list); 1352 INIT_HLIST_HEAD(&sp->list);
1356out:
1357 mutex_unlock(&cpuhp_state_mutex);
1358 return ret; 1353 return ret;
1359} 1354}
1360 1355
@@ -1428,6 +1423,7 @@ int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,
1428 return -EINVAL; 1423 return -EINVAL;
1429 1424
1430 get_online_cpus(); 1425 get_online_cpus();
1426 mutex_lock(&cpuhp_state_mutex);
1431 1427
1432 if (!invoke || !sp->startup.multi) 1428 if (!invoke || !sp->startup.multi)
1433 goto add_node; 1429 goto add_node;
@@ -1447,16 +1443,14 @@ int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,
1447 if (ret) { 1443 if (ret) {
1448 if (sp->teardown.multi) 1444 if (sp->teardown.multi)
1449 cpuhp_rollback_install(cpu, state, node); 1445 cpuhp_rollback_install(cpu, state, node);
1450 goto err; 1446 goto unlock;
1451 } 1447 }
1452 } 1448 }
1453add_node: 1449add_node:
1454 ret = 0; 1450 ret = 0;
1455 mutex_lock(&cpuhp_state_mutex);
1456 hlist_add_head(node, &sp->list); 1451 hlist_add_head(node, &sp->list);
1452unlock:
1457 mutex_unlock(&cpuhp_state_mutex); 1453 mutex_unlock(&cpuhp_state_mutex);
1458
1459err:
1460 put_online_cpus(); 1454 put_online_cpus();
1461 return ret; 1455 return ret;
1462} 1456}
@@ -1491,6 +1485,7 @@ int __cpuhp_setup_state(enum cpuhp_state state,
1491 return -EINVAL; 1485 return -EINVAL;
1492 1486
1493 get_online_cpus(); 1487 get_online_cpus();
1488 mutex_lock(&cpuhp_state_mutex);
1494 1489
1495 ret = cpuhp_store_callbacks(state, name, startup, teardown, 1490 ret = cpuhp_store_callbacks(state, name, startup, teardown,
1496 multi_instance); 1491 multi_instance);
@@ -1524,6 +1519,7 @@ int __cpuhp_setup_state(enum cpuhp_state state,
1524 } 1519 }
1525 } 1520 }
1526out: 1521out:
1522 mutex_unlock(&cpuhp_state_mutex);
1527 put_online_cpus(); 1523 put_online_cpus();
1528 /* 1524 /*
1529 * If the requested state is CPUHP_AP_ONLINE_DYN, return the 1525 * If the requested state is CPUHP_AP_ONLINE_DYN, return the
@@ -1547,6 +1543,8 @@ int __cpuhp_state_remove_instance(enum cpuhp_state state,
1547 return -EINVAL; 1543 return -EINVAL;
1548 1544
1549 get_online_cpus(); 1545 get_online_cpus();
1546 mutex_lock(&cpuhp_state_mutex);
1547
1550 if (!invoke || !cpuhp_get_teardown_cb(state)) 1548 if (!invoke || !cpuhp_get_teardown_cb(state))
1551 goto remove; 1549 goto remove;
1552 /* 1550 /*
@@ -1563,7 +1561,6 @@ int __cpuhp_state_remove_instance(enum cpuhp_state state,
1563 } 1561 }
1564 1562
1565remove: 1563remove:
1566 mutex_lock(&cpuhp_state_mutex);
1567 hlist_del(node); 1564 hlist_del(node);
1568 mutex_unlock(&cpuhp_state_mutex); 1565 mutex_unlock(&cpuhp_state_mutex);
1569 put_online_cpus(); 1566 put_online_cpus();
@@ -1571,6 +1568,7 @@ remove:
1571 return 0; 1568 return 0;
1572} 1569}
1573EXPORT_SYMBOL_GPL(__cpuhp_state_remove_instance); 1570EXPORT_SYMBOL_GPL(__cpuhp_state_remove_instance);
1571
1574/** 1572/**
1575 * __cpuhp_remove_state - Remove the callbacks for an hotplug machine state 1573 * __cpuhp_remove_state - Remove the callbacks for an hotplug machine state
1576 * @state: The state to remove 1574 * @state: The state to remove
@@ -1589,6 +1587,7 @@ void __cpuhp_remove_state(enum cpuhp_state state, bool invoke)
1589 1587
1590 get_online_cpus(); 1588 get_online_cpus();
1591 1589
1590 mutex_lock(&cpuhp_state_mutex);
1592 if (sp->multi_instance) { 1591 if (sp->multi_instance) {
1593 WARN(!hlist_empty(&sp->list), 1592 WARN(!hlist_empty(&sp->list),
1594 "Error: Removing state %d which has instances left.\n", 1593 "Error: Removing state %d which has instances left.\n",
@@ -1613,6 +1612,7 @@ void __cpuhp_remove_state(enum cpuhp_state state, bool invoke)
1613 } 1612 }
1614remove: 1613remove:
1615 cpuhp_store_callbacks(state, NULL, NULL, NULL, false); 1614 cpuhp_store_callbacks(state, NULL, NULL, NULL, false);
1615 mutex_unlock(&cpuhp_state_mutex);
1616 put_online_cpus(); 1616 put_online_cpus();
1617} 1617}
1618EXPORT_SYMBOL(__cpuhp_remove_state); 1618EXPORT_SYMBOL(__cpuhp_remove_state);