RE: [PATCH] PCI: pci-hyperv: Retry PCI bus D0 entry when the first attempt failed with invalid device state 0xC0000184.

From: Michael Kelley
Date: Sun Apr 26 2020 - 20:16:07 EST


From: Wei Hu <weh@xxxxxxxxxxxxx> Sent: Sunday, April 26, 2020 6:25 AM
>
> In the case of kdump, the PCI device was not cleanly shut down
> before the kdump kernel starts. This causes the initial
> attempt of entering D0 state in the kdump kernel to fail with
> invalid device state 0xC0000184 returned from Hyper-V host.
> When this happens, explicitly call PCI bus exit and retry to
> enter the D0 state.
>
> Also fix the PCI probe failure path to release the PCI device
> resource properly.
>
> Signed-off-by: Wei Hu <weh@xxxxxxxxxxxxx>
> ---
> drivers/pci/controller/pci-hyperv.c | 34 ++++++++++++++++++++++++++++-
> 1 file changed, 33 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
> index e15022ff63e3..eb4781fa058d 100644
> --- a/drivers/pci/controller/pci-hyperv.c
> +++ b/drivers/pci/controller/pci-hyperv.c
> @@ -2736,6 +2736,10 @@ static void hv_free_config_window(struct hv_pcibus_device
> *hbus)
> vmbus_free_mmio(hbus->mem_config->start, PCI_CONFIG_MMIO_LENGTH);
> }
>
> +#define STATUS_INVALID_DEVICE_STATE 0xC0000184
> +
> +static int hv_pci_bus_exit(struct hv_device *hdev, bool hibernating);
> +
> /**
> * hv_pci_enter_d0() - Bring the "bus" into the D0 power state
> * @hdev: VMBus's tracking struct for this root PCI bus
> @@ -2748,8 +2752,10 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
> struct pci_bus_d0_entry *d0_entry;
> struct hv_pci_compl comp_pkt;
> struct pci_packet *pkt;
> + bool retry = true;
> int ret;
>
> +enter_d0_retry:
> /*
> * Tell the host that the bus is ready to use, and moved into the
> * powered-on state. This includes telling the host which region
> @@ -2780,6 +2786,30 @@ static int hv_pci_enter_d0(struct hv_device *hdev)
> dev_err(&hdev->device,
> "PCI Pass-through VSP failed D0 Entry with status %x\n",
> comp_pkt.completion_status);

The above error message will be output even if a retry is attempted.
And if the retry succeeds, there's no further message, which could leave an
incorrect impression for someone looking at the boot logs. If the error message
is output, there should be a follow-up message indicating the retry succeeded.
Or don't output the above message at all -- output only a message that says
"doing a retry". This could be accomplished by doing a separate test for
STATUS_INVALID_DEVICE_STATE that is not nested under checking the
completion_status for 0. Here's a structure that also has the benefit of
reducing the indentation levels:

if ((comp_pkt.completion_status == STATUS_INVALID_DEVICE_STATE) && retry) {
retry = false;
dev_err(&hdev->device, "Retrying D0 Entry\n");
ret = hv_pci_bus_exit(hdev, true);
if (ret == 0) {
kfree(pkt);
goto enter_do_retry;
}
dev_err(&hdev->device, "Retrying D0 Entry failed with %d\n", ret);
}

if (comp_pkt.completion_status < 0) {
dev_err(&hdev->device,
"PCI Pass-through VSP failed D0 Entry with status %x\n",
comp_pkt.completion_status);
ret = -EPROTO;
goto exit;
}

> +
> + /*
> + * In certain case (Kdump) the pci device of interest was
> + * not cleanly shut down and resource is still held on host
> + * side, the host could return STATUS_INVALID_DEVICE_STATE.
> + * We need to explicitly request host to release the resource
> + * and try to enter D0 again.
> + */
> + if (comp_pkt.completion_status == STATUS_INVALID_DEVICE_STATE &&
> + retry) {
> + ret = hv_pci_bus_exit(hdev, true);
> +
> + retry = false;
> +
> + if (ret == 0) {
> + kfree(pkt);
> + goto enter_d0_retry;
> + } else {
> + dev_err(&hdev->device,
> + "PCI bus D0 exit failed with ret %d\n",
> + ret);
> + }
> + }
> +
> ret = -EPROTO;
> goto exit;
> }
> @@ -3136,7 +3166,7 @@ static int hv_pci_probe(struct hv_device *hdev,
>
> ret = hv_pci_allocate_bridge_windows(hbus);
> if (ret)
> - goto free_irq_domain;
> + goto exit_d0;
>
> ret = hv_send_resources_allocated(hdev);
> if (ret)

The above is good. But there's another error case that isn't handled
correctly. If create_root_hv_pci_bus() fails, hv_send_resources_released()
should be called.

Fixing these two error cases should probably go in a separate patch: One
patch for the retry problem in kdump, and a separate patch for these error
cases.

Michael


> @@ -3154,6 +3184,8 @@ static int hv_pci_probe(struct hv_device *hdev,
>
> free_windows:
> hv_pci_free_bridge_windows(hbus);
> +exit_d0:
> + (void) hv_pci_bus_exit(hdev, true);
> free_irq_domain:
> irq_domain_remove(hbus->irq_domain);
> free_fwnode:
> --
> 2.20.1