Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT

Resolves the following lockdep report when booting PREEMPT_RT on Hyper-V
with related guest support enabled:

[ 1.127941] hv_vmbus: registering driver hyperv_drm

[ 1.132518] =============================
[ 1.132519] [ BUG: Invalid wait context ]
[ 1.132521] 6.19.0-rc8+ #9 Not tainted
[ 1.132524] -----------------------------
[ 1.132525] swapper/0/0 is trying to lock:
[ 1.132526] ffff8b9381bb3c90 (&channel->sched_lock){....}-{3:3}, at: vmbus_chan_sched+0xc4/0x2b0
[ 1.132543] other info that might help us debug this:
[ 1.132544] context-{2:2}
[ 1.132545] 1 lock held by swapper/0/0:
[ 1.132547] #0: ffffffffa010c4c0 (rcu_read_lock){....}-{1:3}, at: vmbus_chan_sched+0x31/0x2b0
[ 1.132557] stack backtrace:
[ 1.132560] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.19.0-rc8+ #9 PREEMPT_{RT,(lazy)}
[ 1.132565] Hardware name: Microsoft Corporation Virtual Machine/Virtual Machine, BIOS Hyper-V UEFI Release v4.1 09/25/2025
[ 1.132567] Call Trace:
[ 1.132570] <IRQ>
[ 1.132573] dump_stack_lvl+0x6e/0xa0
[ 1.132581] __lock_acquire+0xee0/0x21b0
[ 1.132592] lock_acquire+0xd5/0x2d0
[ 1.132598] ? vmbus_chan_sched+0xc4/0x2b0
[ 1.132606] ? lock_acquire+0xd5/0x2d0
[ 1.132613] ? vmbus_chan_sched+0x31/0x2b0
[ 1.132619] rt_spin_lock+0x3f/0x1f0
[ 1.132623] ? vmbus_chan_sched+0xc4/0x2b0
[ 1.132629] ? vmbus_chan_sched+0x31/0x2b0
[ 1.132634] vmbus_chan_sched+0xc4/0x2b0
[ 1.132641] vmbus_isr+0x2c/0x150
[ 1.132648] __sysvec_hyperv_callback+0x5f/0xa0
[ 1.132654] sysvec_hyperv_callback+0x88/0xb0
[ 1.132658] </IRQ>
[ 1.132659] <TASK>
[ 1.132660] asm_sysvec_hyperv_callback+0x1a/0x20

As code paths that handle vmbus IRQs use sleepy locks under PREEMPT_RT,
the vmbus_isr execution needs to be moved into thread context. Open-
coding this allows to skip the IPI that irq_work would additionally
bring and which we do not need, being an IRQ, never an NMI.

This affects both x86 and arm64, therefore hook into the common driver
logic.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Reviewed-by: Florian Bezdeka <florian.bezdeka@siemens.com>
Tested-by: Florian Bezdeka <florian.bezdeka@siemens.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Tested-by: Michael Kelley <mhklinux@outlook.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Jan Kiszka and committed by
Wei Liu
f8e6343b 885e78d7

+65 -1
+65 -1
drivers/hv/vmbus_drv.c
··· 25 25 #include <linux/cpu.h> 26 26 #include <linux/sched/isolation.h> 27 27 #include <linux/sched/task_stack.h> 28 + #include <linux/smpboot.h> 28 29 29 30 #include <linux/delay.h> 30 31 #include <linux/panic_notifier.h> ··· 1351 1350 } 1352 1351 } 1353 1352 1354 - void vmbus_isr(void) 1353 + static void __vmbus_isr(void) 1355 1354 { 1356 1355 struct hv_per_cpu_context *hv_cpu 1357 1356 = this_cpu_ptr(hv_context.cpu_context); ··· 1363 1362 vmbus_message_sched(hv_cpu, hv_cpu->para_synic_message_page); 1364 1363 1365 1364 add_interrupt_randomness(vmbus_interrupt); 1365 + } 1366 + 1367 + static DEFINE_PER_CPU(bool, vmbus_irq_pending); 1368 + static DEFINE_PER_CPU(struct task_struct *, vmbus_irqd); 1369 + 1370 + static void vmbus_irqd_wake(void) 1371 + { 1372 + struct task_struct *tsk = __this_cpu_read(vmbus_irqd); 1373 + 1374 + __this_cpu_write(vmbus_irq_pending, true); 1375 + wake_up_process(tsk); 1376 + } 1377 + 1378 + static void vmbus_irqd_setup(unsigned int cpu) 1379 + { 1380 + sched_set_fifo(current); 1381 + } 1382 + 1383 + static int vmbus_irqd_should_run(unsigned int cpu) 1384 + { 1385 + return __this_cpu_read(vmbus_irq_pending); 1386 + } 1387 + 1388 + static void run_vmbus_irqd(unsigned int cpu) 1389 + { 1390 + __this_cpu_write(vmbus_irq_pending, false); 1391 + __vmbus_isr(); 1392 + } 1393 + 1394 + static bool vmbus_irq_initialized; 1395 + 1396 + static struct smp_hotplug_thread vmbus_irq_threads = { 1397 + .store = &vmbus_irqd, 1398 + .setup = vmbus_irqd_setup, 1399 + .thread_should_run = vmbus_irqd_should_run, 1400 + .thread_fn = run_vmbus_irqd, 1401 + .thread_comm = "vmbus_irq/%u", 1402 + }; 1403 + 1404 + void vmbus_isr(void) 1405 + { 1406 + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { 1407 + vmbus_irqd_wake(); 1408 + } else { 1409 + lockdep_hardirq_threaded(); 1410 + __vmbus_isr(); 1411 + } 1366 1412 } 1367 1413 EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl"); 1368 1414 ··· 1510 1462 * the VMbus interrupt handler. 1511 1463 */ 1512 1464 1465 + if (IS_ENABLED(CONFIG_PREEMPT_RT) && !vmbus_irq_initialized) { 1466 + ret = smpboot_register_percpu_thread(&vmbus_irq_threads); 1467 + if (ret) 1468 + goto err_kthread; 1469 + vmbus_irq_initialized = true; 1470 + } 1471 + 1513 1472 if (vmbus_irq == -1) { 1514 1473 hv_setup_vmbus_handler(vmbus_isr); 1515 1474 } else { ··· 1562 1507 free_percpu(vmbus_evt); 1563 1508 } 1564 1509 err_setup: 1510 + if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) { 1511 + smpboot_unregister_percpu_thread(&vmbus_irq_threads); 1512 + vmbus_irq_initialized = false; 1513 + } 1514 + err_kthread: 1565 1515 bus_unregister(&hv_bus); 1566 1516 return ret; 1567 1517 } ··· 3035 2975 } else { 3036 2976 free_percpu_irq(vmbus_irq, vmbus_evt); 3037 2977 free_percpu(vmbus_evt); 2978 + } 2979 + if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) { 2980 + smpboot_unregister_percpu_thread(&vmbus_irq_threads); 2981 + vmbus_irq_initialized = false; 3038 2982 } 3039 2983 for_each_online_cpu(cpu) { 3040 2984 struct hv_per_cpu_context *hv_cpu