From: Wei Liu <[email protected]>
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.
Do the re-target where the vector is known and the interrupt is
quiescent. If the vector changed, re-compose the VMBus interrupt for
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;
+ 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);
+
+ ie = data->chip_data;
+ if (!ie)
+ return;
+
+ if (data->chip->irq_write_msi_msg)
+ data->chip->irq_write_msi_msg(data, &msg);
+ }
+
+ 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.