aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@ozlabs.org>2008-04-09 03:21:36 -0400
committerPaul Mackerras <paulus@samba.org>2008-04-18 01:37:16 -0400
commitf4ac7b5eb79ef15819c966b1f6b84bf443949123 (patch)
treecfc81caaa2d1d50f0986c99cf59764060993eb60
parentf13f4ca8036516ca1b99a41f95f7dea7e4dce104 (diff)
[POWERPC] Fix device-tree locking vs. interrupts
Lockdep found out that we can occasionally take the device-tree lock for reading from softirq time (from rtas_token called by the rtas real time clock code called by the NTP code), while we take it occasionally for writing without masking interrupts. The combination of those two can thus deadlock. While some of those cases of interrupt read lock could be fixed (such as caching the RTAS tokens) I figured that taking the lock for writing is so rare (device-tree modification) that we may as well penalize that case and allow reading from interrupts. Thus, this turns all the writers to take the lock with irqs masked to avoid the situation. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/kernel/prom.c28
1 files changed, 17 insertions, 11 deletions
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 8a9359ae4718..3bfe7837e820 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -1332,12 +1332,14 @@ EXPORT_SYMBOL(of_node_put);
1332 */ 1332 */
1333void of_attach_node(struct device_node *np) 1333void of_attach_node(struct device_node *np)
1334{ 1334{
1335 write_lock(&devtree_lock); 1335 unsigned long flags;
1336
1337 write_lock_irqsave(&devtree_lock, flags);
1336 np->sibling = np->parent->child; 1338 np->sibling = np->parent->child;
1337 np->allnext = allnodes; 1339 np->allnext = allnodes;
1338 np->parent->child = np; 1340 np->parent->child = np;
1339 allnodes = np; 1341 allnodes = np;
1340 write_unlock(&devtree_lock); 1342 write_unlock_irqrestore(&devtree_lock, flags);
1341} 1343}
1342 1344
1343/* 1345/*
@@ -1348,8 +1350,9 @@ void of_attach_node(struct device_node *np)
1348void of_detach_node(struct device_node *np) 1350void of_detach_node(struct device_node *np)
1349{ 1351{
1350 struct device_node *parent; 1352 struct device_node *parent;
1353 unsigned long flags;
1351 1354
1352 write_lock(&devtree_lock); 1355 write_lock_irqsave(&devtree_lock, flags);
1353 1356
1354 parent = np->parent; 1357 parent = np->parent;
1355 if (!parent) 1358 if (!parent)
@@ -1380,7 +1383,7 @@ void of_detach_node(struct device_node *np)
1380 of_node_set_flag(np, OF_DETACHED); 1383 of_node_set_flag(np, OF_DETACHED);
1381 1384
1382out_unlock: 1385out_unlock:
1383 write_unlock(&devtree_lock); 1386 write_unlock_irqrestore(&devtree_lock, flags);
1384} 1387}
1385 1388
1386#ifdef CONFIG_PPC_PSERIES 1389#ifdef CONFIG_PPC_PSERIES
@@ -1461,20 +1464,21 @@ __initcall(prom_reconfig_setup);
1461int prom_add_property(struct device_node* np, struct property* prop) 1464int prom_add_property(struct device_node* np, struct property* prop)
1462{ 1465{
1463 struct property **next; 1466 struct property **next;
1467 unsigned long flags;
1464 1468
1465 prop->next = NULL; 1469 prop->next = NULL;
1466 write_lock(&devtree_lock); 1470 write_lock_irqsave(&devtree_lock, flags);
1467 next = &np->properties; 1471 next = &np->properties;
1468 while (*next) { 1472 while (*next) {
1469 if (strcmp(prop->name, (*next)->name) == 0) { 1473 if (strcmp(prop->name, (*next)->name) == 0) {
1470 /* duplicate ! don't insert it */ 1474 /* duplicate ! don't insert it */
1471 write_unlock(&devtree_lock); 1475 write_unlock_irqrestore(&devtree_lock, flags);
1472 return -1; 1476 return -1;
1473 } 1477 }
1474 next = &(*next)->next; 1478 next = &(*next)->next;
1475 } 1479 }
1476 *next = prop; 1480 *next = prop;
1477 write_unlock(&devtree_lock); 1481 write_unlock_irqrestore(&devtree_lock, flags);
1478 1482
1479#ifdef CONFIG_PROC_DEVICETREE 1483#ifdef CONFIG_PROC_DEVICETREE
1480 /* try to add to proc as well if it was initialized */ 1484 /* try to add to proc as well if it was initialized */
@@ -1494,9 +1498,10 @@ int prom_add_property(struct device_node* np, struct property* prop)
1494int prom_remove_property(struct device_node *np, struct property *prop) 1498int prom_remove_property(struct device_node *np, struct property *prop)
1495{ 1499{
1496 struct property **next; 1500 struct property **next;
1501 unsigned long flags;
1497 int found = 0; 1502 int found = 0;
1498 1503
1499 write_lock(&devtree_lock); 1504 write_lock_irqsave(&devtree_lock, flags);
1500 next = &np->properties; 1505 next = &np->properties;
1501 while (*next) { 1506 while (*next) {
1502 if (*next == prop) { 1507 if (*next == prop) {
@@ -1509,7 +1514,7 @@ int prom_remove_property(struct device_node *np, struct property *prop)
1509 } 1514 }
1510 next = &(*next)->next; 1515 next = &(*next)->next;
1511 } 1516 }
1512 write_unlock(&devtree_lock); 1517 write_unlock_irqrestore(&devtree_lock, flags);
1513 1518
1514 if (!found) 1519 if (!found)
1515 return -ENODEV; 1520 return -ENODEV;
@@ -1535,9 +1540,10 @@ int prom_update_property(struct device_node *np,
1535 struct property *oldprop) 1540 struct property *oldprop)
1536{ 1541{
1537 struct property **next; 1542 struct property **next;
1543 unsigned long flags;
1538 int found = 0; 1544 int found = 0;
1539 1545
1540 write_lock(&devtree_lock); 1546 write_lock_irqsave(&devtree_lock, flags);
1541 next = &np->properties; 1547 next = &np->properties;
1542 while (*next) { 1548 while (*next) {
1543 if (*next == oldprop) { 1549 if (*next == oldprop) {
@@ -1551,7 +1557,7 @@ int prom_update_property(struct device_node *np,
1551 } 1557 }
1552 next = &(*next)->next; 1558 next = &(*next)->next;
1553 } 1559 }
1554 write_unlock(&devtree_lock); 1560 write_unlock_irqrestore(&devtree_lock, flags);
1555 1561
1556 if (!found) 1562 if (!found)
1557 return -ENODEV; 1563 return -ENODEV;