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 >

