PCI: hv: Fix a race condition bug in hv_pci_query_relations()
commit 440b5e3663271b0ffbd4908115044a6a51fb938b upstream. Since day 1 of the driver, there has been a race between hv_pci_query_relations() and survey_child_resources(): during fast device hotplug, hv_pci_query_relations() may error out due to device-remove and the stack variable 'comp' is no longer valid; however, pci_devices_present_work() -> survey_child_resources() -> complete() may be running on another CPU and accessing the no-longer-valid 'comp'. Fix the race by flushing the workqueue before we exit from hv_pci_query_relations(). Fixes: 4daace0d8ce8 ("PCI: hv: Add paravirtual PCI front-end for Microsoft Hyper-V VMs") Signed-off-by: Dexuan Cui <decui@microsoft.com> Reviewed-by: Michael Kelley <mikelley@microsoft.com> Acked-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20230615044451.5580-2-decui@microsoft.com Signed-off-by: Wei Liu <wei.liu@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f02a676907
commit
c06c568e43
@ -2739,6 +2739,24 @@ static int hv_pci_query_relations(struct hv_device *hdev)
|
||||
if (!ret)
|
||||
ret = wait_for_response(hdev, &comp);
|
||||
|
||||
/*
|
||||
* In the case of fast device addition/removal, it's possible that
|
||||
* vmbus_sendpacket() or wait_for_response() returns -ENODEV but we
|
||||
* already got a PCI_BUS_RELATIONS* message from the host and the
|
||||
* channel callback already scheduled a work to hbus->wq, which can be
|
||||
* running pci_devices_present_work() -> survey_child_resources() ->
|
||||
* complete(&hbus->survey_event), even after hv_pci_query_relations()
|
||||
* exits and the stack variable 'comp' is no longer valid; as a result,
|
||||
* a hang or a page fault may happen when the complete() calls
|
||||
* raw_spin_lock_irqsave(). Flush hbus->wq before we exit from
|
||||
* hv_pci_query_relations() to avoid the issues. Note: if 'ret' is
|
||||
* -ENODEV, there can't be any more work item scheduled to hbus->wq
|
||||
* after the flush_workqueue(): see vmbus_onoffer_rescind() ->
|
||||
* vmbus_reset_channel_cb(), vmbus_rescind_cleanup() ->
|
||||
* channel->rescind = true.
|
||||
*/
|
||||
flush_workqueue(hbus->wq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user