diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index f001ae880e1d..cb01784e5c3b 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -21,21 +21,29 @@
 #include <linux/export.h>
 #include <linux/io.h>
 #include <linux/set_memory.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
 #include <asm/mshyperv.h>
 
 #include "hyperv_vmbus.h"
 
 
 struct vmbus_connection vmbus_connection = {
-	.conn_state		= DISCONNECTED,
-	.unload_event		= COMPLETION_INITIALIZER(
-				  vmbus_connection.unload_event),
-	.next_gpadl_handle	= ATOMIC_INIT(0xE1E10),
-
-	.ready_for_suspend_event = COMPLETION_INITIALIZER(
-				  vmbus_connection.ready_for_suspend_event),
+	.conn_state			= DISCONNECTED,
+	.unload_event			= COMPLETION_INITIALIZER(
+						vmbus_connection.unload_event),
+	.next_gpadl_handle		= ATOMIC_INIT(0xE1E10),
+
+	.vmbus_irq_chip.name		= "VMBus",
+	.vmbus_irq_chip.irq_set_affinity = vmbus_irq_set_affinity,
+	.vmbus_irq_chip.irq_mask	= vmbus_irq_mask,
+	.vmbus_irq_chip.irq_unmask	= vmbus_irq_unmask,
+
+	.ready_for_suspend_event	= COMPLETION_INITIALIZER(
+					vmbus_connection.ready_for_suspend_event),
 	.ready_for_resume_event	= COMPLETION_INITIALIZER(
-				  vmbus_connection.ready_for_resume_event),
+					vmbus_connection.ready_for_resume_event),
 };
 EXPORT_SYMBOL_GPL(vmbus_connection);
 
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 76ac5185a01a..95d4d47d34f7 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -18,7 +18,11 @@
 #include <asm/hyperv-tlfs.h>
 #include <linux/atomic.h>
 #include <linux/hyperv.h>
+#include <linux/fwnode.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
 
 #include "hv_trace.h"
 
@@ -258,6 +262,11 @@ struct vmbus_connection {
 	/* Array of channels */
 	struct vmbus_channel **channels;
 
+	/* IRQ domain data */
+	struct fwnode_handle *vmbus_fwnode;
+	struct irq_domain *vmbus_irq_domain;
+	struct irq_chip	vmbus_irq_chip;
+
 	/*
 	 * An offer message is handled first on the work_queue, and then
 	 * is further handled on handle_primary_chan_wq or
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 291a8358370b..cbccdfad49a2 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -36,6 +36,9 @@
 #include <linux/syscore_ops.h>
 #include <linux/dma-map-ops.h>
 #include <linux/pci.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/hardirq.h>
 #include <clocksource/hyperv_timer.h>
 #include <asm/mshyperv.h>
 #include "hyperv_vmbus.h"
@@ -1306,6 +1309,40 @@ static irqreturn_t vmbus_percpu_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+int vmbus_irq_set_affinity(struct irq_data *data,
+				  const struct cpumask *dest, bool force)
+{
+	return 0;
+}
+
+/*
+ * VMBus channel interrupts do not need to be masked or unmasked, and the
+ * Hyper-V synic doesn't provide any masking functionality anyway. But in the
+ * absence of these irqchip functions, the IRQ subsystem keeps the IRQ marked
+ * as "masked". To prevent any problems associated with staying the "masked"
+ * state, and so that IRQ status shown in debugfs doesn't indicate "masked",
+ * provide null implementations.
+ */
+void vmbus_irq_unmask(struct irq_data *data)
+{
+}
+
+void vmbus_irq_mask(struct irq_data *data)
+{
+}
+
+static int vmbus_irq_map(struct irq_domain *d, unsigned int irq,
+			 irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(irq,
+			&vmbus_connection.vmbus_irq_chip, handle_simple_irq);
+	return 0;
+}
+
+static const struct irq_domain_ops vmbus_domain_ops = {
+	.map = vmbus_irq_map,
+};
+
 /*
  * vmbus_bus_init -Main vmbus driver initialization routine.
  *
@@ -1340,6 +1377,7 @@ static int vmbus_bus_init(void)
 	if (vmbus_irq == -1) {
 		hv_setup_vmbus_handler(vmbus_isr);
 	} else {
+		irq_set_handler(vmbus_irq, handle_percpu_demux_irq);
 		vmbus_evt = alloc_percpu(long);
 		ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr,
 				"Hyper-V VMbus", vmbus_evt);
@@ -1355,6 +1393,20 @@ static int vmbus_bus_init(void)
 	if (ret)
 		goto err_alloc;
 
+	/* Create IRQ domain for VMBus devices */
+	vmbus_connection.vmbus_fwnode = irq_domain_alloc_named_fwnode("hv-vmbus");
+	if (!vmbus_connection.vmbus_fwnode) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+	vmbus_connection.vmbus_irq_domain = irq_domain_create_linear(
+			vmbus_connection.vmbus_fwnode, MAX_CHANNEL_RELIDS,
+			&vmbus_domain_ops, NULL);
+	if (!vmbus_connection.vmbus_irq_domain) {
+		ret = -ENOMEM;
+		goto err_fwnode;
+	}
+
 	/*
 	 * Initialize the per-cpu interrupt state and stimer state.
 	 * Then connect to the host.
@@ -1362,7 +1414,7 @@ static int vmbus_bus_init(void)
 	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hyperv/vmbus:online",
 				hv_synic_init, hv_synic_cleanup);
 	if (ret < 0)
-		goto err_alloc;
+		goto err_domain;
 	hyperv_cpuhp_online = ret;
 
 	ret = vmbus_connect();
@@ -1382,6 +1434,10 @@ static int vmbus_bus_init(void)
 
 err_connect:
 	cpuhp_remove_state(hyperv_cpuhp_online);
+err_domain:
+	irq_domain_remove(vmbus_connection.vmbus_irq_domain);
+err_fwnode:
+	irq_domain_free_fwnode(vmbus_connection.vmbus_fwnode);
 err_alloc:
 	hv_synic_free();
 	if (vmbus_irq == -1) {
@@ -2690,6 +2746,8 @@ static void __exit vmbus_exit(void)
 	hv_debug_rm_all_dir();
 
 	vmbus_free_channels();
+	irq_domain_remove(vmbus_connection.vmbus_irq_domain);
+	irq_domain_free_fwnode(vmbus_connection.vmbus_fwnode);
 	kfree(vmbus_connection.channels);
 
 	/*
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index 8fe7aaab2599..0488ff8b511f 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -24,6 +24,7 @@
 #include <acpi/acpi_numa.h>
 #include <linux/cpumask.h>
 #include <linux/nmi.h>
+#include <linux/irq.h>
 #include <asm/ptrace.h>
 #include <asm/hyperv-tlfs.h>
 
@@ -187,6 +188,11 @@ void hv_remove_kexec_handler(void);
 void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
 void hv_remove_crash_handler(void);
 
+extern void vmbus_irq_mask(struct irq_data *data);
+extern void vmbus_irq_unmask(struct irq_data *data);
+extern int vmbus_irq_set_affinity(struct irq_data *data,
+				const struct cpumask *dest, bool force);
+
 extern int vmbus_interrupt;
 extern int vmbus_irq;
 
