From: Mukesh R <mrathor@linux.microsoft.com>
To: hpa@zytor.com, robin.murphy@arm.com, robh@kernel.org,
linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org,
iommu@lists.linux.dev, linux-pci@vger.kernel.org,
linux-arch@vger.kernel.org
Subject: [PATCH v0 2/3] hyperv: Implement irq remap for passthru devices
Date: Wed, 29 Apr 2026 16:15:18 -0700 [thread overview]
Message-ID: <20260429231519.2569088-3-mrathor@linux.microsoft.com> (raw)
In-Reply-To: <20260429231519.2569088-1-mrathor@linux.microsoft.com>
Implement interrupt remapping for direct attached and domain attached
devices on Hyper-V.
Please note there are few constraints when it comes to mapping device
interrupts on Hyper-V. For example, the hypervisor will not allow mapping
device interrupts to root if the device is a direct attached device. Since
the target guest cpu and vector info is not available during the initial
VFIO irq setup, we work around by skipping this initial map. Then later
during irqbypass trigger, when both guest target cpu vector are available,
we do the map in the hypervisor, update the device, and enable the
interrupt vector on the device. Rather than special case direct attached,
we do same for domain attached also. This implies irqbypass is required
for MSHV pci device passthru. Also noteworthy is that the hypervisor
will automatically setup any direct hw injection like posted interrupts.
Signed-off-by: Mukesh R <mrathor@linux.microsoft.com>
---
arch/x86/hyperv/irqdomain.c | 18 +-
drivers/hv/mshv_eventfd.c | 422 +++++++++++++++++++++++++++-
drivers/iommu/hyperv-iommu-root.c | 14 +
drivers/pci/controller/pci-hyperv.c | 10 +
include/asm-generic/mshyperv.h | 4 +
5 files changed, 464 insertions(+), 4 deletions(-)
diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c
index 527835b99a70..d32e912ad4a9 100644
--- a/arch/x86/hyperv/irqdomain.c
+++ b/arch/x86/hyperv/irqdomain.c
@@ -222,7 +222,7 @@ int hv_map_msi_interrupt(struct irq_data *data,
msidesc = irq_data_get_msi_desc(data);
pdev = msi_desc_to_pci_dev(msidesc);
- hv_devid.as_uint64 = hv_build_devid_type_pci(pdev);
+ hv_devid.as_uint64 = hv_devid_from_pdev(pdev);
cpu = cpumask_first(irq_data_get_effective_affinity_mask(data));
return hv_map_interrupt(hv_current_partition_id, hv_devid, false, cpu,
@@ -258,6 +258,20 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
return;
}
+ /*
+ * For direct attached devices, we cannot map interrupts in the
+ * hypervisor because it will not allow it until we have guest target
+ * vcpu and vector. So defer it until irqbypass. Also, do the same
+ * for domain attached devices for simplicity.
+ */
+ if (hv_pcidev_is_pthru_dev(pdev)) {
+ if (data->chip_data)
+ entry_to_msi_msg(data->chip_data, msg);
+ else
+ memset(msg, 0, sizeof(struct msi_msg));
+ return;
+ }
+
if (data->chip_data) {
/*
* This interrupt is already mapped. Let's unmap first.
@@ -297,7 +311,7 @@ static int hv_unmap_msi_interrupt(struct pci_dev *pdev,
{
union hv_device_id hv_devid;
- hv_devid.as_uint64 = hv_build_devid_type_pci(pdev);
+ hv_devid.as_uint64 = hv_devid_from_pdev(pdev);
return hv_unmap_interrupt(hv_devid.as_uint64, irq_entry);
}
diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
index 90959f639dc3..666e28f4a4b5 100644
--- a/drivers/hv/mshv_eventfd.c
+++ b/drivers/hv/mshv_eventfd.c
@@ -7,7 +7,6 @@
*
* All credits to kvm developers.
*/
-
#include <linux/syscalls.h>
#include <linux/wait.h>
#include <linux/poll.h>
@@ -15,7 +14,8 @@
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/eventfd.h>
-
+#include <linux/pci.h>
+#include <linux/vfio_pci_core.h>
#if IS_ENABLED(CONFIG_X86_64)
#include <asm/apic.h>
#endif
@@ -27,6 +27,376 @@
static struct workqueue_struct *irqfd_cleanup_wq;
+#if IS_ENABLED(CONFIG_X86_64)
+
+static int mshv_parse_mshv_irqfd(struct mshv_irqfd *irqfd,
+ struct pci_dev **out_pdev,
+ struct irq_data **out_irqdata)
+{
+ struct irq_bypass_producer *prod;
+ struct msi_desc *msidesc;
+ struct irq_data *irqdata;
+
+ if (irqfd == NULL || irqfd->irqfd_bypass_prod == NULL)
+ return -ENODEV;
+
+ prod = irqfd->irqfd_bypass_prod;
+
+ irqdata = irq_get_irq_data(prod->irq);
+ if (irqdata == NULL) {
+ pr_err("Hyper-V: irqbypass fail, no irqdata. irq:0x%x\n",
+ prod->irq);
+ return -EINVAL;
+ }
+ *out_irqdata = irqdata;
+
+ msidesc = irq_data_get_msi_desc(irqdata);
+ if (msidesc == NULL) {
+ pr_err("Hyper-V: irqbypass msi fail. irq:0x%x\n", prod->irq);
+ return -EINVAL;
+ }
+
+ *out_pdev = msi_desc_to_pci_dev(msidesc);
+ if (*out_pdev == NULL) {
+ pr_err("Hyper-V: mshv_irqfd parse fail. irq:0x%x\n", prod->irq);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Must be called with interrupts disabled */
+static int hv_vpset_from_hyp_disabled(
+ struct hv_input_get_vp_set_from_mda *input,
+ union hv_output_get_vp_set_from_mda *output,
+ struct mshv_lapic_irq *lapic_irq, u64 partid)
+{
+ u64 status;
+
+ memset(input, 0, sizeof(*input));
+ input->target_partid = partid;
+ input->dest_address = lapic_irq->lapic_apic_id;
+ input->input_vtl = 0;
+ input->destmode_logical = lapic_irq->lapic_control.logical_dest_mode;
+
+ status = hv_do_hypercall(HVCALL_GET_VPSET_FROM_MDA, input, output);
+ if (!hv_result_success(status)) {
+ hv_status_err(status, "apicid:0x%llx dest:0x%x\n",
+ lapic_irq->lapic_apic_id,
+ lapic_irq->lapic_control.logical_dest_mode);
+ }
+
+ return hv_result_to_errno(status);
+}
+
+/* Returns number of banks copied, -errno in case of error */
+static int hv_copy_vpset(struct hv_vpset *dest, struct hv_vpset *src)
+{
+ u64 bank_mask;
+ int banks, tot_banks = hv_max_vp_index / HV_VCPUS_PER_SPARSE_BANK;
+
+ if (tot_banks >= HV_MAX_SPARSE_VCPU_BANKS)
+ return -EINVAL;
+
+ dest->format = src->format;
+ dest->valid_bank_mask = src->valid_bank_mask;
+ bank_mask = src->valid_bank_mask;
+ for (banks = 0; banks <= tot_banks; banks++) {
+ if (bank_mask == 0)
+ break;
+
+ if (bank_mask & 1)
+ dest->bank_contents[banks] = src->bank_contents[banks];
+ bank_mask = bank_mask >> 1;
+ }
+
+ return banks;
+}
+
+static int mshv_map_device_interrupt(u64 ptid, union hv_device_id hv_devid,
+ struct mshv_lapic_irq *ginfo,
+ struct hv_interrupt_entry *ret_entry,
+ u64 *ret_status)
+{
+ struct hv_input_map_device_interrupt *irq_input;
+ struct hv_output_map_device_interrupt *irq_output;
+ struct hv_device_interrupt_descriptor *intdesc;
+ struct hv_input_get_vp_set_from_mda *mda_input;
+ union hv_output_get_vp_set_from_mda *mda_output;
+ ulong flags;
+ u64 status;
+ int rc, var_size;
+
+ *ret_status = U64_MAX;
+ local_irq_save(flags);
+
+ mda_input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ mda_output = *this_cpu_ptr(hyperv_pcpu_output_arg);
+
+ /*
+ * Map Device Interrupt hcall needs vp set based on vp indexes used
+ * during vp creation. Here we have lapic-id of the vp only. Easiest
+ * is to just ask the hypervisor for the vp set matching the lapic-id.
+ */
+ rc = hv_vpset_from_hyp_disabled(mda_input, mda_output, ginfo, ptid);
+ if (rc)
+ goto out; /* error already printed */
+
+ irq_input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ irq_output = *this_cpu_ptr(hyperv_pcpu_output_arg);
+ memset(irq_input, 0, sizeof(*irq_input));
+
+ irq_input->partition_id = ptid;
+ irq_input->device_id = hv_devid.as_uint64;
+
+ intdesc = &irq_input->interrupt_descriptor;
+ intdesc->interrupt_type = HV_X64_INTERRUPT_TYPE_FIXED;
+ intdesc->vector_count = 1;
+ intdesc->target.vector = ginfo->lapic_vector;
+ intdesc->trigger_mode = HV_INTERRUPT_TRIGGER_MODE_EDGE;
+
+ intdesc->target.vp_set.valid_bank_mask = 0;
+ intdesc->target.vp_set.format = HV_GENERIC_SET_SPARSE_4K;
+ intdesc->target.flags = HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET;
+ rc = hv_copy_vpset(&intdesc->target.vp_set, &mda_output->target_vpset);
+ if (rc <= 0) {
+ pr_err("Hyper-V: ptid %lld - (irq)vpset copy failed (%d)\n",
+ ptid, rc);
+ goto out;
+ }
+
+ /*
+ * var-sized hcall: var-size starts after vp_mask (thus vp_set.format
+ * does not count, but vp_set.valid_bank_mask does).
+ */
+ var_size = rc + 1;
+ status = hv_do_rep_hypercall(HVCALL_MAP_DEVICE_INTERRUPT, 0, var_size,
+ irq_input, irq_output);
+ *ret_entry = irq_output->interrupt_entry;
+ local_irq_restore(flags);
+
+ rc = 0;
+ if (!hv_result_success(status)) {
+ if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY)
+ hv_status_err(status, "pt:%lld vec:%d lapic-id:%lld\n",
+ ptid, ginfo->lapic_vector, ginfo->lapic_apic_id);
+ *ret_status = status;
+ rc = hv_result_to_errno(status);
+ }
+
+ return rc;
+
+out:
+ local_irq_restore(flags);
+ return rc;
+
+}
+
+static int mshv_unmap_device_interrupt(union hv_device_id hv_devid,
+ struct hv_interrupt_entry *irq_entry)
+{
+ unsigned long flags;
+ struct hv_input_unmap_device_interrupt *input;
+ u64 status;
+
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ memset(input, 0, sizeof(*input));
+
+ if (hv_devid.device_type == HV_DEVICE_TYPE_LOGICAL)
+ input->partition_id = hv_get_current_partid();
+ else
+ input->partition_id = hv_current_partition_id;
+
+ input->device_id = hv_devid.as_uint64;
+ input->interrupt_entry = *irq_entry;
+
+ status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL);
+ local_irq_restore(flags);
+
+ if (!hv_result_success(status))
+ hv_status_err(status, "\n");
+
+ return hv_result_to_errno(status);
+}
+
+static int mshv_chk_unmap_irq(union hv_device_id hv_devid,
+ struct irq_data *irqdata)
+{
+ int rc;
+
+ if (irqdata->chip_data == NULL)
+ return 0;
+
+ rc = mshv_unmap_device_interrupt(hv_devid, irqdata->chip_data);
+ if (rc)
+ return rc;
+
+ kfree(irqdata->chip_data);
+ irqdata->chip_data = NULL;
+
+ return 0;
+}
+
+/*
+ * Synchronize device update with VFIO.
+ * See: vfio_pci_memory_lock_and_enable()
+ */
+static u16 mshv_pci_memory_lock_and_enable(struct vfio_pci_core_device *cdev)
+{
+ u16 cmd;
+
+ down_write(&cdev->memory_lock);
+ pci_read_config_word(cdev->pdev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MEMORY))
+ pci_write_config_word(cdev->pdev, PCI_COMMAND,
+ cmd | PCI_COMMAND_MEMORY);
+ return cmd;
+}
+
+static void mshv_pci_memory_unlock_and_restore(
+ struct vfio_pci_core_device *cdev,
+ u16 cmd)
+{
+ pci_write_config_word(cdev->pdev, PCI_COMMAND, cmd);
+ up_write(&cdev->memory_lock);
+}
+
+static void mshv_make_device_usable(struct pci_dev *pdev, int vector,
+ struct hv_interrupt_entry *hv_entry)
+{
+ int lirq;
+ struct msi_msg msimsg;
+ struct irq_data *irqdata;
+ u16 pcicmd;
+ struct vfio_pci_core_device *coredev = dev_get_drvdata(&pdev->dev);
+
+ if (pdev->dev.driver == NULL ||
+ strcmp(pdev->dev.driver->name, "vfio-pci") != 0) {
+ pr_err("Hyper-V: irqbypass: non vfio device %s\n",
+ pci_name(pdev));
+ return;
+ }
+ if (coredev == NULL) {
+ pr_err("Hyper-V: irqbypass: null vfio device for %s\n",
+ pci_name(pdev));
+ return;
+ }
+
+ if (hv_entry->source != HV_INTERRUPT_SOURCE_MSI) {
+ pr_err("Hyper-V: %s irq source not msi\n", pci_name(pdev));
+ return;
+ }
+
+ lirq = pci_irq_vector(pdev, vector);
+ irqdata = irq_get_irq_data(lirq);
+ if (irqdata == NULL) {
+ pr_err("Hyper-V: null irq_data for write msimsg. lirq:0x%x\n",
+ lirq);
+ return;
+ }
+
+ msimsg.address_hi = 0;
+ msimsg.address_lo = hv_entry->msi_entry.address.as_uint32;
+ msimsg.data = hv_entry->msi_entry.data.as_uint32;
+
+ pcicmd = mshv_pci_memory_lock_and_enable(coredev);
+ pci_write_msi_msg(lirq, &msimsg);
+ mshv_pci_memory_unlock_and_restore(coredev, pcicmd);
+
+ pci_msi_unmask_irq(irqdata);
+
+ if (irqdata->parent_data)
+ irq_chip_unmask_parent(irqdata);
+}
+
+/*
+ * This guest has a device passthru'd to it. VFIO did the initial setup of
+ * the device interrupts, but we left them unmapped in the hypervisor
+ * because we didn't have the guest target cpu and vector (required by
+ * hypervisor). We have them now, so do the map hypercall.
+ * Also, when here, it is expected that the device global mask is unset
+ * but individual MSI/x masks are set. Goal here is to map the interrupt in
+ * the hypervisor, update the corresponding device MSI/x entry, and enable it.
+ */
+static void mshv_pthru_dev_irq_remap(struct mshv_irqfd *irqfd)
+{
+ u64 ptid, status;
+ struct pci_dev *pdev;
+ int rc, deposit_pgs = 16;
+ struct mshv_lapic_irq *ginfo = &irqfd->irqfd_lapic_irq;
+ union hv_device_id hv_devid;
+ struct hv_interrupt_entry *new_entry;
+ struct irq_data *irqdata;
+
+ if (!irqfd->irqfd_girq_ent.girq_entry_valid ||
+ irqfd->irqfd_bypass_prod == NULL)
+ return;
+
+ rc = mshv_parse_mshv_irqfd(irqfd, &pdev, &irqdata);
+ if (rc)
+ return;
+
+ hv_devid.as_uint64 = hv_devid_from_pdev(pdev);
+
+ rc = mshv_chk_unmap_irq(hv_devid, irqdata);
+ if (rc)
+ return;
+
+ new_entry = kmalloc(sizeof(*new_entry), GFP_ATOMIC);
+ if (new_entry == NULL)
+ return;
+
+ ptid = irqfd->irqfd_partn->pt_id;
+
+ while (deposit_pgs--) {
+ rc = mshv_map_device_interrupt(ptid, hv_devid, ginfo, new_entry,
+ &status);
+ if (rc == 0)
+ break;
+ if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY)
+ break;
+
+ rc = hv_call_deposit_pages(NUMA_NO_NODE, ptid, 1);
+ if (rc)
+ break;
+ }
+ if (rc) {
+ kfree(new_entry);
+ return;
+ }
+
+ irqdata->chip_data = new_entry;
+
+ mshv_make_device_usable(pdev, irqdata->hwirq, new_entry);
+}
+
+static void mshv_pthru_dev_irq_undo(struct mshv_irqfd *irqfd)
+{
+ struct pci_dev *pdev;
+ union hv_device_id hv_devid;
+ struct irq_data *irqdata;
+ int rc;
+
+ if (!irqfd->irqfd_girq_ent.girq_entry_valid ||
+ irqfd->irqfd_bypass_prod == NULL)
+ return;
+
+ rc = mshv_parse_mshv_irqfd(irqfd, &pdev, &irqdata);
+ if (rc)
+ return;
+
+ hv_devid.as_uint64 = hv_devid_from_pdev(pdev);
+ mshv_chk_unmap_irq(hv_devid, irqdata);
+}
+
+#else /* IS_ENABLED(CONFIG_X86_64) */
+
+static void mshv_pthru_dev_irq_remap(struct mshv_irqfd *irqfd) { }
+static void mshv_pthru_dev_irq_undo(struct mshv_irqfd *irqfd) { }
+
+#endif /* IS_ENABLED(CONFIG_X86_64) */
+
void mshv_register_irq_ack_notifier(struct mshv_partition *partition,
struct mshv_irq_ack_notifier *mian)
{
@@ -264,6 +634,7 @@ static void mshv_irqfd_shutdown(struct work_struct *work)
/*
* It is now safe to release the object's resources
*/
+ irq_bypass_unregister_consumer(&irqfd->irqfd_bypass_cons);
eventfd_ctx_put(irqfd->irqfd_eventfd_ctx);
kfree(irqfd);
}
@@ -286,6 +657,12 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd)
hlist_del(&irqfd->irqfd_hnode);
+ /*
+ * Cleanup interrupt map (kfree chip_data) while in a VMM thread as
+ * unmap needs partition id. mshv_irqfd_shutdown() runs in a kthread.
+ */
+ mshv_pthru_dev_irq_undo(irqfd);
+
queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown);
}
@@ -383,6 +760,45 @@ static void mshv_irqfd_queue_proc(struct file *file, wait_queue_head_t *wqh,
add_wait_queue_priority(wqh, &irqfd->irqfd_wait);
}
+static int mshv_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
+ struct irq_bypass_producer *prod)
+{
+ struct mshv_irqfd *irqfd;
+
+ irqfd = container_of(cons, struct mshv_irqfd, irqfd_bypass_cons);
+ irqfd->irqfd_bypass_prod = prod;
+
+ mshv_pthru_dev_irq_remap(irqfd);
+
+ return 0;
+}
+
+static void mshv_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
+ struct irq_bypass_producer *prod)
+{
+ struct mshv_irqfd *irqfd;
+
+ irqfd = container_of(cons, struct mshv_irqfd, irqfd_bypass_cons);
+
+ WARN_ON(irqfd->irqfd_bypass_prod != prod);
+ irqfd->irqfd_bypass_prod = NULL;
+
+}
+
+static void mshv_setup_irq_bypass(struct mshv_irqfd *irqfd,
+ struct eventfd_ctx *eventfd)
+{
+ struct irq_bypass_consumer *consumer = &irqfd->irqfd_bypass_cons;
+ int rc;
+
+ consumer->add_producer = mshv_irq_bypass_add_producer;
+ consumer->del_producer = mshv_irq_bypass_del_producer;
+ rc = irq_bypass_register_consumer(&irqfd->irqfd_bypass_cons, eventfd);
+ if (rc)
+ pr_err("Hyper-V: irq bypass consumer registration failed: %d\n",
+ rc);
+}
+
static int mshv_irqfd_assign(struct mshv_partition *pt,
struct mshv_user_irqfd *args)
{
@@ -509,6 +925,8 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
if (events & EPOLLIN)
mshv_assert_irq_slow(irqfd);
+ mshv_setup_irq_bypass(irqfd, eventfd);
+
srcu_read_unlock(&pt->pt_irq_srcu, idx);
return 0;
diff --git a/drivers/iommu/hyperv-iommu-root.c b/drivers/iommu/hyperv-iommu-root.c
index 739bbf39dea2..3e078e9213f9 100644
--- a/drivers/iommu/hyperv-iommu-root.c
+++ b/drivers/iommu/hyperv-iommu-root.c
@@ -219,6 +219,20 @@ u64 hv_build_devid_oftype(struct pci_dev *pdev, enum hv_device_type type)
}
EXPORT_SYMBOL_GPL(hv_build_devid_oftype);
+/* Build device id for the interrupt path */
+u64 hv_devid_from_pdev(struct pci_dev *pdev)
+{
+ enum hv_device_type dev_type;
+
+ if (hv_pcidev_is_attached_dev(pdev))
+ dev_type = HV_DEVICE_TYPE_LOGICAL;
+ else
+ dev_type = HV_DEVICE_TYPE_PCI;
+
+ return hv_build_devid_oftype(pdev, dev_type);
+}
+EXPORT_SYMBOL_GPL(hv_devid_from_pdev);
+
/* Create a new device domain in the hypervisor */
static int hv_iommu_create_hyp_devdom(struct hv_domain *hvdom)
{
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 8f6b818ee09b..8ecc909c3415 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -1745,6 +1745,16 @@ static void hv_irq_mask(struct irq_data *data)
static void hv_irq_unmask(struct irq_data *data)
{
+ struct pci_dev *pdev;
+ struct msi_desc *msi_desc;
+
+ msi_desc = irq_data_get_msi_desc(data);
+ pdev = msi_desc_to_pci_dev(msi_desc);
+
+ /* Done during bypass setup in mshv_eventfd.c: mshv_irqfd_assign() */
+ if (hv_pcidev_is_pthru_dev(pdev))
+ return;
+
hv_arch_irq_unmask(data);
if (data->parent_data->chip->irq_unmask)
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index edbcfc2a9b60..887605aa9c95 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -341,6 +341,7 @@ u64 hv_get_current_partid(void);
bool hv_pcidev_is_attached_dev(struct pci_dev *pdev);
bool hv_pcidev_is_pthru_dev(struct pci_dev *pdev);
u64 hv_build_devid_oftype(struct pci_dev *pdev, enum hv_device_type type);
+u64 hv_devid_from_pdev(struct pci_dev *pdev);
#else /* Remove following after arm64 implementation is done */
@@ -354,6 +355,9 @@ static inline u64 hv_build_devid_oftype(struct pci_dev *pdev,
enum hv_device_type type)
{ return 0; }
+static inline u64 hv_devid_from_pdev(struct pci_dev *pdev)
+{ return 0; }
+
static inline u64 hv_get_current_partid(void)
{ return HV_PARTITION_ID_INVALID; }
#endif /* IS_ENABLED(CONFIG_HYPERV_IOMMU) */
--
2.51.2.vfs.0.1
next prev parent reply other threads:[~2026-04-29 23:15 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-29 23:15 [PATCH v0 0/3] PCI passthru on Hyper-V (Part II) Mukesh R
2026-04-29 23:15 ` [PATCH v0 1/3] mshv: Import declarations for irq remap and add irqbypass support Mukesh R
2026-04-29 23:15 ` Mukesh R [this message]
2026-04-29 23:15 ` [PATCH v0 3/3] mshv: Implement guest irq migration for passthru'd devices Mukesh R
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260429231519.2569088-3-mrathor@linux.microsoft.com \
--to=mrathor@linux.microsoft.com \
--cc=hpa@zytor.com \
--cc=iommu@lists.linux.dev \
--cc=linux-arch@vger.kernel.org \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=robh@kernel.org \
--cc=robin.murphy@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox