aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/pseries/iommu.c
diff options
context:
space:
mode:
authorGavin Shan <gwshan@linux.vnet.ibm.com>2014-08-11 05:16:20 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-08-13 01:13:47 -0400
commit5efbabe09d986f25c02d19954660238fcd7f008a (patch)
tree9ed3958ab9e81e79d17362c6867e6aa35b91b259 /arch/powerpc/platforms/pseries/iommu.c
parentf1b3929c232784580e5d8ee324b6bc634e709575 (diff)
powerpc/pseries: Avoid deadlock on removing ddw
Function remove_ddw() could be called in of_reconfig_notifier and we potentially remove the dynamic DMA window property, which invokes of_reconfig_notifier again. Eventually, it leads to the deadlock as following backtrace shows. The patch fixes the above issue by deferring releasing the dynamic DMA window property while releasing the device node. ============================================= [ INFO: possible recursive locking detected ] 3.16.0+ #428 Tainted: G W --------------------------------------------- drmgr/2273 is trying to acquire lock: ((of_reconfig_chain).rwsem){.+.+..}, at: [<c000000000091890>] \ .__blocking_notifier_call_chain+0x40/0x78 but task is already holding lock: ((of_reconfig_chain).rwsem){.+.+..}, at: [<c000000000091890>] \ .__blocking_notifier_call_chain+0x40/0x78 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock((of_reconfig_chain).rwsem); lock((of_reconfig_chain).rwsem); *** DEADLOCK *** May be due to missing lock nesting notation 2 locks held by drmgr/2273: #0: (sb_writers#4){.+.+.+}, at: [<c0000000001cbe70>] \ .vfs_write+0xb0/0x1f8 #1: ((of_reconfig_chain).rwsem){.+.+..}, at: [<c000000000091890>] \ .__blocking_notifier_call_chain+0x40/0x78 stack backtrace: CPU: 17 PID: 2273 Comm: drmgr Tainted: G W 3.16.0+ #428 Call Trace: [c0000000137e7000] [c000000000013d9c] .show_stack+0x88/0x148 (unreliable) [c0000000137e70b0] [c00000000083cd34] .dump_stack+0x7c/0x9c [c0000000137e7130] [c0000000000b8afc] .__lock_acquire+0x128c/0x1c68 [c0000000137e7280] [c0000000000b9a4c] .lock_acquire+0xe8/0x104 [c0000000137e7350] [c00000000083588c] .down_read+0x4c/0x90 [c0000000137e73e0] [c000000000091890] .__blocking_notifier_call_chain+0x40/0x78 [c0000000137e7490] [c000000000091900] .blocking_notifier_call_chain+0x38/0x48 [c0000000137e7520] [c000000000682a28] .of_reconfig_notify+0x34/0x5c [c0000000137e75b0] [c000000000682a9c] .of_property_notify+0x4c/0x54 [c0000000137e7650] [c000000000682bf0] .of_remove_property+0x30/0xd4 [c0000000137e76f0] [c000000000052a44] .remove_ddw+0x144/0x168 [c0000000137e7790] [c000000000053204] .iommu_reconfig_notifier+0x30/0xe0 [c0000000137e7820] [c00000000009137c] .notifier_call_chain+0x6c/0xb4 [c0000000137e78c0] [c0000000000918ac] .__blocking_notifier_call_chain+0x5c/0x78 [c0000000137e7970] [c000000000091900] .blocking_notifier_call_chain+0x38/0x48 [c0000000137e7a00] [c000000000682a28] .of_reconfig_notify+0x34/0x5c [c0000000137e7a90] [c000000000682e14] .of_detach_node+0x44/0x1fc [c0000000137e7b40] [c0000000000518e4] .ofdt_write+0x3ac/0x688 [c0000000137e7c20] [c000000000238430] .proc_reg_write+0xb8/0xd4 [c0000000137e7cd0] [c0000000001cbeac] .vfs_write+0xec/0x1f8 [c0000000137e7d70] [c0000000001cc3b0] .SyS_write+0x58/0xa0 [c0000000137e7e30] [c00000000000a064] syscall_exit+0x0/0x98 Cc: stable@vger.kernel.org Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms/pseries/iommu.c')
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 33b552ffbe57..4642d6a4d356 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -721,13 +721,13 @@ static int __init disable_ddw_setup(char *str)
721 721
722early_param("disable_ddw", disable_ddw_setup); 722early_param("disable_ddw", disable_ddw_setup);
723 723
724static void remove_ddw(struct device_node *np) 724static void remove_ddw(struct device_node *np, bool remove_prop)
725{ 725{
726 struct dynamic_dma_window_prop *dwp; 726 struct dynamic_dma_window_prop *dwp;
727 struct property *win64; 727 struct property *win64;
728 const u32 *ddw_avail; 728 const u32 *ddw_avail;
729 u64 liobn; 729 u64 liobn;
730 int len, ret; 730 int len, ret = 0;
731 731
732 ddw_avail = of_get_property(np, "ibm,ddw-applicable", &len); 732 ddw_avail = of_get_property(np, "ibm,ddw-applicable", &len);
733 win64 = of_find_property(np, DIRECT64_PROPNAME, NULL); 733 win64 = of_find_property(np, DIRECT64_PROPNAME, NULL);
@@ -761,7 +761,8 @@ static void remove_ddw(struct device_node *np)
761 np->full_name, ret, ddw_avail[2], liobn); 761 np->full_name, ret, ddw_avail[2], liobn);
762 762
763delprop: 763delprop:
764 ret = of_remove_property(np, win64); 764 if (remove_prop)
765 ret = of_remove_property(np, win64);
765 if (ret) 766 if (ret)
766 pr_warning("%s: failed to remove direct window property: %d\n", 767 pr_warning("%s: failed to remove direct window property: %d\n",
767 np->full_name, ret); 768 np->full_name, ret);
@@ -805,7 +806,7 @@ static int find_existing_ddw_windows(void)
805 window = kzalloc(sizeof(*window), GFP_KERNEL); 806 window = kzalloc(sizeof(*window), GFP_KERNEL);
806 if (!window || len < sizeof(struct dynamic_dma_window_prop)) { 807 if (!window || len < sizeof(struct dynamic_dma_window_prop)) {
807 kfree(window); 808 kfree(window);
808 remove_ddw(pdn); 809 remove_ddw(pdn, true);
809 continue; 810 continue;
810 } 811 }
811 812
@@ -1045,7 +1046,7 @@ out_free_window:
1045 kfree(window); 1046 kfree(window);
1046 1047
1047out_clear_window: 1048out_clear_window:
1048 remove_ddw(pdn); 1049 remove_ddw(pdn, true);
1049 1050
1050out_free_prop: 1051out_free_prop:
1051 kfree(win64->name); 1052 kfree(win64->name);
@@ -1255,7 +1256,14 @@ static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long acti
1255 1256
1256 switch (action) { 1257 switch (action) {
1257 case OF_RECONFIG_DETACH_NODE: 1258 case OF_RECONFIG_DETACH_NODE:
1258 remove_ddw(np); 1259 /*
1260 * Removing the property will invoke the reconfig
1261 * notifier again, which causes dead-lock on the
1262 * read-write semaphore of the notifier chain. So
1263 * we have to remove the property when releasing
1264 * the device node.
1265 */
1266 remove_ddw(np, false);
1259 if (pci && pci->iommu_table) 1267 if (pci && pci->iommu_table)
1260 iommu_free_table(pci->iommu_table, np->full_name); 1268 iommu_free_table(pci->iommu_table, np->full_name);
1261 1269