diff options
author | Dexuan Cui <decui@microsoft.com> | 2018-07-09 14:16:07 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2018-07-09 14:16:07 -0400 |
commit | 35a88a18d7ea58600e11590405bc93b08e16e7f5 (patch) | |
tree | 50a07b145dde1aba711a577ceed0af2b654b1991 /drivers/pci/controller/pci-hyperv.c | |
parent | 83235822b8b4fe47ecbd6b6bcbcc902860ac00fc (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.c | 8 |
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, |