aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2017-03-14 11:06:45 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-05-08 01:47:55 -0400
commit7ad6de43deda083f1eab1e9a5cc48166e4ee52f5 (patch)
tree1f9cfb366025f6c98cd5a52b4809356abebd6cbb /kernel
parente99b0ea39354cf308423588df5daf69c42dbd735 (diff)
cpu/hotplug: Serialize callback invocations proper
commit dc434e056fe1dada20df7ba07f32739d3a701adf upstream. 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> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/cpu.c17
1 files changed, 9 insertions, 8 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 217fd2e7f435..99c6c568bc55 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1441,14 +1441,12 @@ static void cpuhp_store_callbacks(enum cpuhp_state state,
1441 /* (Un)Install the callbacks for further cpu hotplug operations */ 1441 /* (Un)Install the callbacks for further cpu hotplug operations */
1442 struct cpuhp_step *sp; 1442 struct cpuhp_step *sp;
1443 1443
1444 mutex_lock(&cpuhp_state_mutex);
1445 sp = cpuhp_get_step(state); 1444 sp = cpuhp_get_step(state);
1446 sp->startup.single = startup; 1445 sp->startup.single = startup;
1447 sp->teardown.single = teardown; 1446 sp->teardown.single = teardown;
1448 sp->name = name; 1447 sp->name = name;
1449 sp->multi_instance = multi_instance; 1448 sp->multi_instance = multi_instance;
1450 INIT_HLIST_HEAD(&sp->list); 1449 INIT_HLIST_HEAD(&sp->list);
1451 mutex_unlock(&cpuhp_state_mutex);
1452} 1450}
1453 1451
1454static void *cpuhp_get_teardown_cb(enum cpuhp_state state) 1452static void *cpuhp_get_teardown_cb(enum cpuhp_state state)
@@ -1518,16 +1516,13 @@ static int cpuhp_reserve_state(enum cpuhp_state state)
1518{ 1516{
1519 enum cpuhp_state i; 1517 enum cpuhp_state i;
1520 1518
1521 mutex_lock(&cpuhp_state_mutex);
1522 for (i = CPUHP_AP_ONLINE_DYN; i <= CPUHP_AP_ONLINE_DYN_END; i++) { 1519 for (i = CPUHP_AP_ONLINE_DYN; i <= CPUHP_AP_ONLINE_DYN_END; i++) {
1523 if (cpuhp_ap_states[i].name) 1520 if (cpuhp_ap_states[i].name)
1524 continue; 1521 continue;
1525 1522
1526 cpuhp_ap_states[i].name = "Reserved"; 1523 cpuhp_ap_states[i].name = "Reserved";
1527 mutex_unlock(&cpuhp_state_mutex);
1528 return i; 1524 return i;
1529 } 1525 }
1530 mutex_unlock(&cpuhp_state_mutex);
1531 WARN(1, "No more dynamic states available for CPU hotplug\n"); 1526 WARN(1, "No more dynamic states available for CPU hotplug\n");
1532 return -ENOSPC; 1527 return -ENOSPC;
1533} 1528}
@@ -1544,6 +1539,7 @@ int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,
1544 return -EINVAL; 1539 return -EINVAL;
1545 1540
1546 get_online_cpus(); 1541 get_online_cpus();
1542 mutex_lock(&cpuhp_state_mutex);
1547 1543
1548 if (!invoke || !sp->startup.multi) 1544 if (!invoke || !sp->startup.multi)
1549 goto add_node; 1545 goto add_node;
@@ -1568,11 +1564,10 @@ int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,
1568 } 1564 }
1569add_node: 1565add_node:
1570 ret = 0; 1566 ret = 0;
1571 mutex_lock(&cpuhp_state_mutex);
1572 hlist_add_head(node, &sp->list); 1567 hlist_add_head(node, &sp->list);
1573 mutex_unlock(&cpuhp_state_mutex);
1574 1568
1575err: 1569err:
1570 mutex_unlock(&cpuhp_state_mutex);
1576 put_online_cpus(); 1571 put_online_cpus();
1577 return ret; 1572 return ret;
1578} 1573}
@@ -1601,6 +1596,7 @@ int __cpuhp_setup_state(enum cpuhp_state state,
1601 return -EINVAL; 1596 return -EINVAL;
1602 1597
1603 get_online_cpus(); 1598 get_online_cpus();
1599 mutex_lock(&cpuhp_state_mutex);
1604 1600
1605 /* currently assignments for the ONLINE state are possible */ 1601 /* currently assignments for the ONLINE state are possible */
1606 if (state == CPUHP_AP_ONLINE_DYN) { 1602 if (state == CPUHP_AP_ONLINE_DYN) {
@@ -1636,6 +1632,8 @@ int __cpuhp_setup_state(enum cpuhp_state state,
1636 } 1632 }
1637 } 1633 }
1638out: 1634out:
1635 mutex_unlock(&cpuhp_state_mutex);
1636
1639 put_online_cpus(); 1637 put_online_cpus();
1640 if (!ret && dyn_state) 1638 if (!ret && dyn_state)
1641 return state; 1639 return state;
@@ -1655,6 +1653,8 @@ int __cpuhp_state_remove_instance(enum cpuhp_state state,
1655 return -EINVAL; 1653 return -EINVAL;
1656 1654
1657 get_online_cpus(); 1655 get_online_cpus();
1656 mutex_lock(&cpuhp_state_mutex);
1657
1658 if (!invoke || !cpuhp_get_teardown_cb(state)) 1658 if (!invoke || !cpuhp_get_teardown_cb(state))
1659 goto remove; 1659 goto remove;
1660 /* 1660 /*
@@ -1671,7 +1671,6 @@ int __cpuhp_state_remove_instance(enum cpuhp_state state,
1671 } 1671 }
1672 1672
1673remove: 1673remove:
1674 mutex_lock(&cpuhp_state_mutex);
1675 hlist_del(node); 1674 hlist_del(node);
1676 mutex_unlock(&cpuhp_state_mutex); 1675 mutex_unlock(&cpuhp_state_mutex);
1677 put_online_cpus(); 1676 put_online_cpus();
@@ -1696,6 +1695,7 @@ void __cpuhp_remove_state(enum cpuhp_state state, bool invoke)
1696 BUG_ON(cpuhp_cb_check(state)); 1695 BUG_ON(cpuhp_cb_check(state));
1697 1696
1698 get_online_cpus(); 1697 get_online_cpus();
1698 mutex_lock(&cpuhp_state_mutex);
1699 1699
1700 if (sp->multi_instance) { 1700 if (sp->multi_instance) {
1701 WARN(!hlist_empty(&sp->list), 1701 WARN(!hlist_empty(&sp->list),
@@ -1721,6 +1721,7 @@ void __cpuhp_remove_state(enum cpuhp_state state, bool invoke)
1721 } 1721 }
1722remove: 1722remove:
1723 cpuhp_store_callbacks(state, NULL, NULL, NULL, false); 1723 cpuhp_store_callbacks(state, NULL, NULL, NULL, false);
1724 mutex_unlock(&cpuhp_state_mutex);
1724 put_online_cpus(); 1725 put_online_cpus();
1725} 1726}
1726EXPORT_SYMBOL(__cpuhp_remove_state); 1727EXPORT_SYMBOL(__cpuhp_remove_state);