From: [email protected] <[email protected]> Sent: Friday, August 21, 2026 
5:36 PM
> 
> hv_compose_msi_msg() sends PCI_CREATE_INTERRUPT carrying
> int_desc.vector, and the vPCI backend programs the device with the
> address/data it returns for it.
> 
> An affinity change only ran irq_chip_set_affinity_parent(), which
> re-allocates the x86 vector and nothing else, and hv_arch_irq_unmask()
> then issued MAP_DEVICE_INTERRUPT for the new (VP, vector).  Nothing
> re-composed the interrupt, so the device kept signalling the vector it
> was created with and the new mapping was never used. This led to loss of
> interrupts.

Shift the wording in the above paragraph to present tense.

> 
> Do the re-target where the vector is known and the interrupt is
> quiescent.  If the vector changed, re-compose the VMBus interrupt for

I don't think of this as "VMbus interrupt". VMBus interrupts are
channel interrupts or control message interrupts tied to the
HYPERVISOR_CALLBACK_VECTOR. I think this is just an MSI/MSI-X
Interrupt from the guest's standpoint.

> it, write the resulting message to the device, and only then map the new
> (vp, vector); the old mapping is destroyed as the new message is
> composed.
> 
> Signed-off-by: Wei Liu <[email protected]>
> ---
>  drivers/pci/controller/pci-hyperv.c | 41 +++++++++++++++++++++++++++--
>  1 file changed, 39 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/pci/controller/pci-hyperv.c 
> b/drivers/pci/controller/pci-hyperv.c
> index 5a36382742bf..5acb8e41567e 100644
> --- a/drivers/pci/controller/pci-hyperv.c
> +++ b/drivers/pci/controller/pci-hyperv.c
> @@ -294,6 +294,7 @@ struct tran_int_desc {
>  struct hv_msi_int_entry {
>       struct tran_int_desc            int_desc;
>       struct hv_interrupt_entry       hv_entry;
> +     unsigned int                    mapped_vector;
>  };
> 
>  /* chip_data is passed around as a struct tran_int_desc *, so it must be 
> first. */
> @@ -742,6 +743,8 @@ static void hv_irq_retarget_interrupt(struct irq_data 
> *data)
>                       "%s() failed: %#llx", __func__, res);
>  }
> 
> +static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
> +
>  static void hv_arch_irq_unmask(struct irq_data *data)
>  {
>       if (hv_root_partition()) {
> @@ -752,9 +755,17 @@ static void hv_arch_irq_unmask(struct irq_data *data)
>                * RETARGET_INTERRUPT.
>                *
>                * Keep the returned entry so the mapping can be removed again
> -              * when the interrupt is torn down.
> +              * when the interrupt is re-targeted or torn down.
> +              *
> +              * This is also the re-target point.  The core calls us from
> +              * __irq_move_irq() with the interrupt masked once the new
> +              * vector has been assigned, so if the vector changed the vmbus
> +              * interrupt is re-composed for it first -- PCI_CREATE_INTERRUPT
> +              * carries the vector, so the device would otherwise keep
> +              * signalling the one it was created with.
>                */
>               struct hv_msi_int_entry *ie = data->chip_data;

Use irq_data_to_irq_chip_data()??

> +             unsigned int vec = hv_msi_get_int_vector(data);
> 
>               /*
>                * A NULL chip_data means hv_compose_msi_msg() failed and the
> @@ -763,8 +774,29 @@ static void hv_arch_irq_unmask(struct irq_data *data)
>               if (!ie)
>                       return;
> 
> -             if (hv_map_msi_interrupt(data, &ie->hv_entry))
> +             /* Already mapped for this vector, nothing changed. */
> +             if (ie->mapped_vector == vec && ie->hv_entry.source)
> +                     return;
> +
> +             if (ie->mapped_vector && ie->mapped_vector != vec) {
> +                     struct msi_msg msg;
> +
> +                     hv_compose_msi_msg(data, &msg);

hv_compose_msi_msg() sends a VMBus message, and busy
waits for the ack, which is really ugly, but necessary. I have no idea
whether it's OK to do all that from the irq_unmask function.

> +
> +                     ie = data->chip_data;
> +                     if (!ie)
> +                             return;

Setting ie and checking for NULL appears to be duplicative of code
above.

> +
> +                     if (data->chip->irq_write_msi_msg)
> +                             data->chip->irq_write_msi_msg(data, &msg);

Is the irq_write_msi_msg function defaulting to pci_msi_domain_write_msg()?
I didn't go follow all the paths to see how this function gets set .....  Is it
even necessary to check for NULL before calling? I don't see such checks
in the few other places irq_write_msi_msg is invoked.

> +             }
> +
> +             if (hv_map_msi_interrupt(data, &ie->hv_entry)) {
>                       memset(&ie->hv_entry, 0, sizeof(ie->hv_entry));
> +                     ie->mapped_vector = 0;
> +                     return;
> +             }
> +             ie->mapped_vector = vec;
>       } else {
>               hv_irq_retarget_interrupt(data);
>       }
> @@ -1974,6 +2006,11 @@ static void hv_compose_msi_msg(struct irq_data *data, 
> struct msi_msg *msg)
>       if (data->chip_data && !multi_msi) {
>               int_desc = data->chip_data;
>               data->chip_data = NULL;
> +             /*
> +              * The descriptor is about to be destroyed, so release the
> +              * hypervisor mapping that belongs to it first.
> +              */
> +             hv_vmbus_unmap_msi_interrupt(pdev, int_desc);
>               hv_int_desc_free(hpdev, int_desc);
>       }
> 
> --
> 2.53.0
> 


Reply via email to