Skip to content

Commit 7c54a55

Browse files
kelleymhtyhicks
authored andcommitted
Drivers: hv: vmbus: Move handling of VMbus interrupts
VMbus interrupts are most naturally modelled as per-cpu IRQs. But because x86/x64 doesn't have per-cpu IRQs, the core VMbus interrupt handling machinery is done in code under arch/x86 and Linux IRQs are not used. Adding support for ARM64 means adding equivalent code using per-cpu IRQs under arch/arm64. A better model is to treat per-cpu IRQs as the normal path (which it is for modern architectures), and the x86/x64 path as the exception. Do this by incorporating standard Linux per-cpu IRQ allocation into the main VMbus driver, and bypassing it in the x86/x64 exception case. For x86/x64, special case code is retained under arch/x86, but no VMbus interrupt handling code is needed under arch/arm64. No functional change. Signed-off-by: Michael Kelley <mikelley@microsoft.com> Reviewed-by: Boqun Feng <boqun.feng@gmail.com> Link: https://lore.kernel.org/r/1614721102-2241-7-git-send-email-mikelley@microsoft.com Signed-off-by: Wei Liu <wei.liu@kernel.org> (cherry picked from commit d608715) Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
1 parent 7353c9b commit 7c54a55

5 files changed

Lines changed: 70 additions & 22 deletions

File tree

arch/x86/include/asm/mshyperv.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ static inline u64 hv_get_register(unsigned int reg)
3232
#define hv_enable_vdso_clocksource() \
3333
vclocks_set_used(VDSO_CLOCKMODE_HVCLOCK);
3434
#define hv_get_raw_timer() rdtsc_ordered()
35-
#define hv_get_vector() HYPERVISOR_CALLBACK_VECTOR
3635

3736
/*
3837
* Reference to pv_ops must be inline so objtool

arch/x86/kernel/cpu/mshyperv.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,18 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_callback)
5555
set_irq_regs(old_regs);
5656
}
5757

58-
int hv_setup_vmbus_irq(int irq, void (*handler)(void))
58+
void hv_setup_vmbus_handler(void (*handler)(void))
5959
{
60-
/*
61-
* The 'irq' argument is ignored on x86/x64 because a hard-coded
62-
* interrupt vector is used for Hyper-V interrupts.
63-
*/
6460
vmbus_handler = handler;
65-
return 0;
6661
}
62+
EXPORT_SYMBOL_GPL(hv_setup_vmbus_handler);
6763

68-
void hv_remove_vmbus_irq(void)
64+
void hv_remove_vmbus_handler(void)
6965
{
7066
/* We have no way to deallocate the interrupt gate */
7167
vmbus_handler = NULL;
7268
}
73-
EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
74-
EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
69+
EXPORT_SYMBOL_GPL(hv_remove_vmbus_handler);
7570

7671
/*
7772
* Routines to do per-architecture handling of stimer0

drivers/hv/hv.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/version.h>
1717
#include <linux/random.h>
1818
#include <linux/clockchips.h>
19+
#include <linux/interrupt.h>
1920
#include <clocksource/hyperv_timer.h>
2021
#include <asm/mshyperv.h>
2122
#include "hyperv_vmbus.h"
@@ -214,10 +215,12 @@ void hv_synic_enable_regs(unsigned int cpu)
214215
hv_set_register(HV_REGISTER_SIEFP, siefp.as_uint64);
215216

216217
/* Setup the shared SINT. */
218+
if (vmbus_irq != -1)
219+
enable_percpu_irq(vmbus_irq, 0);
217220
shared_sint.as_uint64 = hv_get_register(HV_REGISTER_SINT0 +
218221
VMBUS_MESSAGE_SINT);
219222

220-
shared_sint.vector = hv_get_vector();
223+
shared_sint.vector = vmbus_interrupt;
221224
shared_sint.masked = false;
222225

223226
/*
@@ -285,6 +288,9 @@ void hv_synic_disable_regs(unsigned int cpu)
285288
sctrl.as_uint64 = hv_get_register(HV_REGISTER_SCONTROL);
286289
sctrl.enable = 0;
287290
hv_set_register(HV_REGISTER_SCONTROL, sctrl.as_uint64);
291+
292+
if (vmbus_irq != -1)
293+
disable_percpu_irq(vmbus_irq);
288294
}
289295

290296

drivers/hv/vmbus_drv.c

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ static int hyperv_cpuhp_online;
4848

4949
static void *hv_panic_page;
5050

51+
static long __percpu *vmbus_evt;
52+
5153
/* Values parsed from ACPI DSDT */
52-
static int vmbus_irq;
54+
int vmbus_irq;
5355
int vmbus_interrupt;
5456

5557
/*
@@ -1351,7 +1353,13 @@ static void vmbus_isr(void)
13511353
tasklet_schedule(&hv_cpu->msg_dpc);
13521354
}
13531355

1354-
add_interrupt_randomness(hv_get_vector(), 0);
1356+
add_interrupt_randomness(vmbus_interrupt, 0);
1357+
}
1358+
1359+
static irqreturn_t vmbus_percpu_isr(int irq, void *dev_id)
1360+
{
1361+
vmbus_isr();
1362+
return IRQ_HANDLED;
13551363
}
13561364

13571365
/*
@@ -1448,9 +1456,28 @@ static int vmbus_bus_init(void)
14481456
if (ret)
14491457
return ret;
14501458

1451-
ret = hv_setup_vmbus_irq(vmbus_irq, vmbus_isr);
1452-
if (ret)
1453-
goto err_setup;
1459+
/*
1460+
* VMbus interrupts are best modeled as per-cpu interrupts. If
1461+
* on an architecture with support for per-cpu IRQs (e.g. ARM64),
1462+
* allocate a per-cpu IRQ using standard Linux kernel functionality.
1463+
* If not on such an architecture (e.g., x86/x64), then rely on
1464+
* code in the arch-specific portion of the code tree to connect
1465+
* the VMbus interrupt handler.
1466+
*/
1467+
1468+
if (vmbus_irq == -1) {
1469+
hv_setup_vmbus_handler(vmbus_isr);
1470+
} else {
1471+
vmbus_evt = alloc_percpu(long);
1472+
ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr,
1473+
"Hyper-V VMbus", vmbus_evt);
1474+
if (ret) {
1475+
pr_err("Can't request Hyper-V VMbus IRQ %d, Err %d",
1476+
vmbus_irq, ret);
1477+
free_percpu(vmbus_evt);
1478+
goto err_setup;
1479+
}
1480+
}
14541481

14551482
ret = hv_synic_alloc();
14561483
if (ret)
@@ -1524,7 +1551,12 @@ static int vmbus_bus_init(void)
15241551
err_cpuhp:
15251552
hv_synic_free();
15261553
err_alloc:
1527-
hv_remove_vmbus_irq();
1554+
if (vmbus_irq == -1) {
1555+
hv_remove_vmbus_handler();
1556+
} else {
1557+
free_percpu_irq(vmbus_irq, vmbus_evt);
1558+
free_percpu(vmbus_evt);
1559+
}
15281560
err_setup:
15291561
bus_unregister(&hv_bus);
15301562
unregister_sysctl_table(hv_ctl_table_hdr);
@@ -2639,6 +2671,18 @@ static int __init hv_acpi_init(void)
26392671
ret = -ETIMEDOUT;
26402672
goto cleanup;
26412673
}
2674+
2675+
/*
2676+
* If we're on an architecture with a hardcoded hypervisor
2677+
* vector (i.e. x86/x64), override the VMbus interrupt found
2678+
* in the ACPI tables. Ensure vmbus_irq is not set since the
2679+
* normal Linux IRQ mechanism is not used in this case.
2680+
*/
2681+
#ifdef HYPERVISOR_CALLBACK_VECTOR
2682+
vmbus_interrupt = HYPERVISOR_CALLBACK_VECTOR;
2683+
vmbus_irq = -1;
2684+
#endif
2685+
26422686
hv_debug_init();
26432687

26442688
ret = vmbus_bus_init();
@@ -2669,7 +2713,12 @@ static void __exit vmbus_exit(void)
26692713
vmbus_connection.conn_state = DISCONNECTED;
26702714
hv_stimer_global_cleanup();
26712715
vmbus_disconnect();
2672-
hv_remove_vmbus_irq();
2716+
if (vmbus_irq == -1) {
2717+
hv_remove_vmbus_handler();
2718+
} else {
2719+
free_percpu_irq(vmbus_irq, vmbus_evt);
2720+
free_percpu(vmbus_evt);
2721+
}
26732722
for_each_online_cpu(cpu) {
26742723
struct hv_per_cpu_context *hv_cpu
26752724
= per_cpu_ptr(hv_context.cpu_context, cpu);

include/asm-generic/mshyperv.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,16 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
8989
}
9090
}
9191

92-
int hv_setup_vmbus_irq(int irq, void (*handler)(void));
93-
void hv_remove_vmbus_irq(void);
94-
void hv_enable_vmbus_irq(void);
95-
void hv_disable_vmbus_irq(void);
92+
void hv_setup_vmbus_handler(void (*handler)(void));
93+
void hv_remove_vmbus_handler(void);
9694

9795
void hv_setup_kexec_handler(void (*handler)(void));
9896
void hv_remove_kexec_handler(void);
9997
void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
10098
void hv_remove_crash_handler(void);
10199

102100
extern int vmbus_interrupt;
101+
extern int vmbus_irq;
103102

104103
#if IS_ENABLED(CONFIG_HYPERV)
105104
/*

0 commit comments

Comments
 (0)