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

Drivers: hv: vmbus: Teardown clockevent devices on module unload

Newly introduced clockevent devices made it impossible to unload hv_vmbus
module as clockevents_config_and_register() takes additional reverence to
the module. To make it possible again we do the following:
- avoid setting dev->owner for clockevent devices;
- implement hv_synic_clockevents_cleanup() doing clockevents_unbind_device();
- call it from vmbus_exit().

In theory hv_synic_clockevents_cleanup() can be merged with hv_synic_cleanup(),
however, we call hv_synic_cleanup() from smp_call_function_single() and this
doesn't work for clockevents_unbind_device() as it does such call on its own. I
opted for a separate function.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Vitaly Kuznetsov and committed by
Greg Kroah-Hartman
e086748c 32a15832

+27 -1
+24 -1
drivers/hv/hv.c
··· 312 312 dev->features = CLOCK_EVT_FEAT_ONESHOT; 313 313 dev->cpumask = cpumask_of(cpu); 314 314 dev->rating = 1000; 315 - dev->owner = THIS_MODULE; 315 + /* 316 + * Avoid settint dev->owner = THIS_MODULE deliberately as doing so will 317 + * result in clockevents_config_and_register() taking additional 318 + * references to the hv_vmbus module making it impossible to unload. 319 + */ 316 320 317 321 dev->set_mode = hv_ce_setmode; 318 322 dev->set_next_event = hv_ce_set_next_event; ··· 474 470 } 475 471 476 472 /* 473 + * hv_synic_clockevents_cleanup - Cleanup clockevent devices 474 + */ 475 + void hv_synic_clockevents_cleanup(void) 476 + { 477 + int cpu; 478 + 479 + if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)) 480 + return; 481 + 482 + for_each_online_cpu(cpu) 483 + clockevents_unbind_device(hv_context.clk_evt[cpu], cpu); 484 + } 485 + 486 + /* 477 487 * hv_synic_cleanup - Cleanup routine for hv_synic_init(). 478 488 */ 479 489 void hv_synic_cleanup(void *arg) ··· 500 482 501 483 if (!hv_context.synic_initialized) 502 484 return; 485 + 486 + /* Turn off clockevent device */ 487 + if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) 488 + hv_ce_setmode(CLOCK_EVT_MODE_SHUTDOWN, 489 + hv_context.clk_evt[cpu]); 503 490 504 491 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); 505 492
+2
drivers/hv/hyperv_vmbus.h
··· 572 572 573 573 extern void hv_synic_cleanup(void *arg); 574 574 575 + extern void hv_synic_clockevents_cleanup(void); 576 + 575 577 /* 576 578 * Host version information. 577 579 */
+1
drivers/hv/vmbus_drv.c
··· 1032 1032 int cpu; 1033 1033 1034 1034 vmbus_connection.conn_state = DISCONNECTED; 1035 + hv_synic_clockevents_cleanup(); 1035 1036 hv_remove_vmbus_irq(); 1036 1037 vmbus_free_channels(); 1037 1038 bus_unregister(&hv_bus);