aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/controller/pci-hyperv.c
diff options
context:
space:
mode:
authorDexuan Cui <decui@microsoft.com>2018-07-09 14:16:07 -0400
committerBjorn Helgaas <bhelgaas@google.com>2018-07-09 14:16:07 -0400
commit35a88a18d7ea58600e11590405bc93b08e16e7f5 (patch)
tree50a07b145dde1aba711a577ceed0af2b654b1991 /drivers/pci/controller/pci-hyperv.c
parent83235822b8b4fe47ecbd6b6bcbcc902860ac00fc (diff)
PCI: hv: Disable/enable IRQs rather than BH in hv_compose_msi_msg()
Commit de0aa7b2f97d ("PCI: hv: Fix 2 hang issues in hv_compose_msi_msg()") uses local_bh_disable()/enable(), because hv_pci_onchannelcallback() can also run in tasklet context as the channel event callback, so bottom halves should be disabled to prevent a race condition. With CONFIG_PROVE_LOCKING=y in the recent mainline, or old kernels that don't have commit f71b74bca637 ("irq/softirqs: Use lockdep to assert IRQs are disabled/enabled"), when the upper layer IRQ code calls hv_compose_msi_msg() with local IRQs disabled, we'll see a warning at the beginning of __local_bh_enable_ip(): IRQs not enabled as expected WARNING: CPU: 0 PID: 408 at kernel/softirq.c:162 __local_bh_enable_ip The warning exposes an issue in de0aa7b2f97d: local_bh_enable() can potentially call do_softirq(), which is not supposed to run when local IRQs are disabled. Let's fix this by using local_irq_save()/restore() instead. Note: hv_pci_onchannelcallback() is not a hot path because it's only called when the PCI device is hot added and removed, which is infrequent. Fixes: de0aa7b2f97d ("PCI: hv: Fix 2 hang issues in hv_compose_msi_msg()") Signed-off-by: Dexuan Cui <decui@microsoft.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> Cc: stable@vger.kernel.org Cc: Stephen Hemminger <sthemmin@microsoft.com> Cc: K. Y. Srinivasan <kys@microsoft.com>
Diffstat (limited to 'drivers/pci/controller/pci-hyperv.c')
-rw-r--r--drivers/pci/controller/pci-hyperv.c8
1 files changed, 5 insertions, 3 deletions
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 6cc5036ac83c..f6325f1a89e8 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -1073,6 +1073,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
1073 struct pci_bus *pbus; 1073 struct pci_bus *pbus;
1074 struct pci_dev *pdev; 1074 struct pci_dev *pdev;
1075 struct cpumask *dest; 1075 struct cpumask *dest;
1076 unsigned long flags;
1076 struct compose_comp_ctxt comp; 1077 struct compose_comp_ctxt comp;
1077 struct tran_int_desc *int_desc; 1078 struct tran_int_desc *int_desc;
1078 struct { 1079 struct {
@@ -1164,14 +1165,15 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
1164 * the channel callback directly when channel->target_cpu is 1165 * the channel callback directly when channel->target_cpu is
1165 * the current CPU. When the higher level interrupt code 1166 * the current CPU. When the higher level interrupt code
1166 * calls us with interrupt enabled, let's add the 1167 * calls us with interrupt enabled, let's add the
1167 * local_bh_disable()/enable() to avoid race. 1168 * local_irq_save()/restore() to avoid race:
1169 * hv_pci_onchannelcallback() can also run in tasklet.
1168 */ 1170 */
1169 local_bh_disable(); 1171 local_irq_save(flags);
1170 1172
1171 if (hbus->hdev->channel->target_cpu == smp_processor_id()) 1173 if (hbus->hdev->channel->target_cpu == smp_processor_id())
1172 hv_pci_onchannelcallback(hbus); 1174 hv_pci_onchannelcallback(hbus);
1173 1175
1174 local_bh_enable(); 1176 local_irq_restore(flags);
1175 1177
1176 if (hpdev->state == hv_pcichild_ejecting) { 1178 if (hpdev->state == hv_pcichild_ejecting) {
1177 dev_err_once(&hbus->hdev->device, 1179 dev_err_once(&hbus->hdev->device,