On Mon, Feb 02, 2026 at 06:27:06PM +0000, Anirudh Rayabharam wrote:
> From: Anirudh Rayabharam (Microsoft) <[email protected]>
>
> On x86, the HYPERVISOR_CALLBACK_VECTOR is used to receive synthetic
> interrupts (SINTs) from the hypervisor for doorbells and intercepts.
> There is no such vector reserved for arm64.
>
> On arm64, the INTID for SINTs should be in the SGI or PPI range. The
> hypervisor exposes a virtual device in the ACPI that reserves a
> PPI for this use. Introduce a platform_driver that binds to this ACPI
> device and obtains the interrupt vector that can be used for SINTs.
>
> To better unify x86 and arm64 paths, introduce mshv_sint_vector_init() that
> either registers the platform_driver and obtains the INTID (arm64) or
> just uses HYPERVISOR_CALLBACK_VECTOR as the interrupt vector (x86).
>
> Signed-off-by: Anirudh Rayabharam (Microsoft) <[email protected]>
> ---
> drivers/hv/mshv_synic.c | 163 ++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 156 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c
> index 98c58755846d..de5fee6e9f29 100644
> --- a/drivers/hv/mshv_synic.c
> +++ b/drivers/hv/mshv_synic.c
> @@ -10,17 +10,24 @@
> #include <linux/kernel.h>
> #include <linux/slab.h>
> #include <linux/mm.h>
> +#include <linux/interrupt.h>
> #include <linux/io.h>
> #include <linux/random.h>
> #include <linux/cpuhotplug.h>
> #include <linux/reboot.h>
> #include <asm/mshyperv.h>
> +#include <linux/platform_device.h>
> +#include <linux/acpi.h>
>
> #include "mshv_eventfd.h"
> #include "mshv.h"
>
> static int synic_cpuhp_online;
> static struct hv_synic_pages __percpu *synic_pages;
> +static int mshv_sint_vector = -1; /* hwirq for the SynIC SINTs */
> +#ifndef HYPERVISOR_CALLBACK_VECTOR
> +static int mshv_sint_irq = -1; /* Linux IRQ for mshv_sint_vector */
> +#endif
>
> static u32 synic_event_ring_get_queued_port(u32 sint_index)
> {
> @@ -456,9 +463,7 @@ static int mshv_synic_cpu_init(unsigned int cpu)
> union hv_synic_simp simp;
> union hv_synic_siefp siefp;
> union hv_synic_sirbp sirbp;
> -#ifdef HYPERVISOR_CALLBACK_VECTOR
> union hv_synic_sint sint;
> -#endif
> union hv_synic_scontrol sctrl;
> struct hv_synic_pages *spages = this_cpu_ptr(synic_pages);
> struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
> @@ -501,10 +506,13 @@ static int mshv_synic_cpu_init(unsigned int cpu)
>
> hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
>
> -#ifdef HYPERVISOR_CALLBACK_VECTOR
> +#ifndef HYPERVISOR_CALLBACK_VECTOR
> + enable_percpu_irq(mshv_sint_irq, 0);
> +#endif
> +
> /* Enable intercepts */
> sint.as_uint64 = 0;
> - sint.vector = HYPERVISOR_CALLBACK_VECTOR;
> + sint.vector = mshv_sint_vector;
> sint.masked = false;
> sint.auto_eoi = hv_recommend_using_aeoi();
> hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX,
> @@ -512,13 +520,12 @@ static int mshv_synic_cpu_init(unsigned int cpu)
>
> /* Doorbell SINT */
> sint.as_uint64 = 0;
> - sint.vector = HYPERVISOR_CALLBACK_VECTOR;
> + sint.vector = mshv_sint_vector;
> sint.masked = false;
> sint.as_intercept = 1;
> sint.auto_eoi = hv_recommend_using_aeoi();
> hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
> sint.as_uint64);
> -#endif
>
> /* Enable global synic bit */
> sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL);
> @@ -573,6 +580,10 @@ static int mshv_synic_cpu_exit(unsigned int cpu)
> hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
> sint.as_uint64);
>
> +#ifndef HYPERVISOR_CALLBACK_VECTOR
> + disable_percpu_irq(mshv_sint_irq);
> +#endif
> +
> /* Disable Synic's event ring page */
> sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP);
> sirbp.sirbp_enabled = false;
> @@ -680,14 +691,149 @@ static struct notifier_block mshv_synic_reboot_nb = {
> .notifier_call = mshv_synic_reboot_notify,
> };
>
> +#ifndef HYPERVISOR_CALLBACK_VECTOR
You have introduced 4 ifdef branches (one aroung the variable and three
in mshv_synic_cpu_init) and then you still have a big ifdef branch here.
Why is it better than simply introducing two different
mshv_synic_cpu_init functions and have a single big ifdef instead
(especially giving that this code is arch-specific anyway and thus won't
bloat the binary)?
This will also allows to get rid of redundant mshv_sint_vector variable
on x86.
Thanks,
Stanislav
> +#ifdef CONFIG_ACPI
> +static long __percpu *mshv_evt;
> +
> +static acpi_status mshv_walk_resources(struct acpi_resource *res, void *ctx)
> +{
> + struct resource r;
> +
> + if (res->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
> + if (!acpi_dev_resource_interrupt(res, 0, &r)) {
> + pr_err("Unable to parse MSHV ACPI interrupt\n");
> + return AE_ERROR;
> + }
> + /* ARM64 INTID */
> + mshv_sint_vector = res->data.extended_irq.interrupts[0];
> + /* Linux IRQ number */
> + mshv_sint_irq = r.start;
> + }
> +
> + return AE_OK;
> +}
> +
> +static irqreturn_t mshv_percpu_isr(int irq, void *dev_id)
> +{
> + mshv_isr();
> + return IRQ_HANDLED;
> +}
> +
> +static int mshv_sint_probe(struct platform_device *pdev)
> +{
> + acpi_status result;
> + int ret;
> + struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
> +
> + result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
> + mshv_walk_resources, NULL);
> + if (ACPI_FAILURE(result)) {
> + ret = -ENODEV;
> + goto out_fail;
> + }
> +
> + mshv_evt = alloc_percpu(long);
> + if (!mshv_evt) {
> + ret = -ENOMEM;
> + goto out_fail;
> + }
> +
> + ret = request_percpu_irq(mshv_sint_irq, mshv_percpu_isr, "MSHV",
> + mshv_evt);
> + if (ret)
> + goto free_evt;
> +
> + return 0;
> +
> +free_evt:
> + free_percpu(mshv_evt);
> +out_fail:
> + mshv_sint_vector = -1;
> + mshv_sint_irq = -1;
> + return ret;
> +}
> +
> +static void mshv_sint_remove(struct platform_device *pdev)
> +{
> + free_percpu_irq(mshv_sint_irq, mshv_evt);
> + free_percpu(mshv_evt);
> +}
> +#else
> +static int mshv_sint_probe(struct platform_device *pdev)
> +{
> + return -ENODEV;
> +}
> +
> +static void mshv_sint_remove(struct platform_device *pdev)
> +{
> +}
> +#endif
> +
> +static const __maybe_unused struct acpi_device_id mshv_sint_device_ids[] = {
> + {"MSFT1003", 0},
> + {"", 0},
> +};
> +
> +static struct platform_driver mshv_sint_drv = {
> + .probe = mshv_sint_probe,
> + .remove = mshv_sint_remove,
> + .driver = {
> + .name = "mshv_sint",
> + .acpi_match_table = ACPI_PTR(mshv_sint_device_ids),
> + .probe_type = PROBE_FORCE_SYNCHRONOUS,
> + },
> +};
> +
> +static int __init mshv_sint_vector_init(void)
> +{
> + int ret;
> +
> + if (acpi_disabled)
> + return -ENODEV;
> +
> + ret = platform_driver_register(&mshv_sint_drv);
> + if (ret)
> + return ret;
> +
> + if (mshv_sint_vector == -1 || mshv_sint_irq == -1) {
> + platform_driver_unregister(&mshv_sint_drv);
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static void mshv_sint_vector_cleanup(void)
> +{
> + platform_driver_unregister(&mshv_sint_drv);
> +}
> +#else /* HYPERVISOR_CALLBACK_VECTOR */
> +static int __init mshv_sint_vector_init(void)
> +{
> + mshv_sint_vector = HYPERVISOR_CALLBACK_VECTOR;
> + return 0;
> +}
> +
> +static void mshv_sint_vector_cleanup(void)
> +{
> +}
> +#endif /* HYPERVISOR_CALLBACK_VECTOR */
> +
> int __init mshv_synic_init(struct device *dev)
> {
> int ret = 0;
>
> + ret = mshv_sint_vector_init();
> + if (ret) {
> + dev_err(dev, "Failed to get MSHV SINT vector: %i\n", ret);
> + return ret;
> + }
> +
> synic_pages = alloc_percpu(struct hv_synic_pages);
> if (!synic_pages) {
> dev_err(dev, "Failed to allocate percpu synic page\n");
> - return -ENOMEM;
> + ret = -ENOMEM;
> + goto sint_vector_cleanup;
> }
>
> ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mshv_synic",
> @@ -712,6 +858,8 @@ int __init mshv_synic_init(struct device *dev)
> cpuhp_remove_state(synic_cpuhp_online);
> free_synic_pages:
> free_percpu(synic_pages);
> +sint_vector_cleanup:
> + mshv_sint_vector_cleanup();
> return ret;
> }
>
> @@ -721,4 +869,5 @@ void mshv_synic_cleanup(void)
> unregister_reboot_notifier(&mshv_synic_reboot_nb);
> cpuhp_remove_state(synic_cpuhp_online);
> free_percpu(synic_pages);
> + mshv_sint_vector_cleanup();
> }
> --
> 2.34.1
>