> 
> From: Wei Liu <[email protected]>
> 
> On a nested root partition the vPCI MSI/MSI-X interrupts of vmbus devices
> (e.g. the MANA NIC) are mapped in the hypervisor with a
> MAP_DEVICE_INTERRUPT hypercall.  This is done from hv_arch_irq_unmask()
> -> hv_map_msi_interrupt() because the nested hypervisor performs the
> interrupt remapping and a RETARGET_INTERRUPT is not usable there.
> 
> The mapping was never removed: hv_arch_irq_unmask() called
> hv_map_msi_interrupt(data, NULL), so the returned hv_interrupt_entry was
> discarded, and hv_msi_free() tears the interrupt down with a vmbus
> PCI_DELETE_INTERRUPT message (hv_int_desc_free()) without issuing
> UNMAP_DEVICE_INTERRUPT.
> 
> This has led to MSHV rejecting already-mapped (vp, vector) pair from being
> used. When this happens during early boot, the system hangs.
> 
> Keep the hypervisor mapping in sync with the kernel's interrupt lifecycle.
> 
> The mapping is only created on x86 (hv_arch_irq_unmask() is a stub on
> arm64), so the unmap hypercall is guarded accordingly.
> 
> Signed-off-by: Wei Liu <[email protected]>
> ---
>  drivers/pci/controller/pci-hyperv.c | 85 ++++++++++++++++++++++++++---
>  1 file changed, 78 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-
> hyperv.c
> index cfc8fa403dad..5a36382742bf 100644
> --- a/drivers/pci/controller/pci-hyperv.c
> +++ b/drivers/pci/controller/pci-hyperv.c
> @@ -283,6 +283,35 @@ struct tran_int_desc {
>       u64     address;
>  } __packed;
> 
> +/*
> + * On a nested root partition a vPCI MSI is mapped in the hypervisor
> +with a
> + * MAP_DEVICE_INTERRUPT hypercall in hv_arch_irq_unmask().  Keep the
> +entry the
> + * hypervisor returns next to the per-interrupt transaction descriptor
> +so the
> + * mapping can be removed again with UNMAP_DEVICE_INTERRUPT when
> the
> +interrupt
> + * is torn down.  tran_int_desc stays first: chip_data is used as a
> +struct
> + * tran_int_desc throughout this driver.
> + */
> +struct hv_msi_int_entry {
> +     struct tran_int_desc            int_desc;
> +     struct hv_interrupt_entry       hv_entry;
> +};
> +
> +/* chip_data is passed around as a struct tran_int_desc *, so it must
> +be first. */ static_assert(offsetof(struct hv_msi_int_entry, int_desc)
> +== 0);
> +
> +static void hv_vmbus_unmap_msi_interrupt(struct pci_dev *pdev
> __maybe_unused,
> +                                      void *chip_data)
> +{
> +     struct hv_msi_int_entry *ie = chip_data;
> +
> +     if (!ie || !ie->hv_entry.source)
> +             return;
> +#ifdef CONFIG_X86
> +     hv_unmap_msi_interrupt(pdev, &ie->hv_entry); #endif

Since you don't want to check the return value from hv_unmap_msi_interrupt(), 
add a (void) to the front to prevent some tools from giving warnings.

Reply via email to