aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVitaly Kuznetsov <vkuznets@redhat.com>2015-02-27 14:25:54 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-01 22:29:05 -0500
commit09a196288ec4617a920e051af6651ce03968c8b9 (patch)
tree40fb660a649ccd7e12f850138dd82d8b0c75a087
parentadcde069a85cb6674bdcf5f49f434d18b2db6e58 (diff)
Drivers: hv: vmbus: teardown hv_vmbus_con workqueue and vmbus_connection pages on shutdown
We need to destroy hv_vmbus_con on module shutdown, otherwise the following crash is sometimes observed: [ 76.569845] hv_vmbus: Hyper-V Host Build:9600-6.3-17-0.17039; Vmbus version:3.0 [ 82.598859] BUG: unable to handle kernel paging request at ffffffffa0003480 [ 82.599287] IP: [<ffffffffa0003480>] 0xffffffffa0003480 [ 82.599287] PGD 1f34067 PUD 1f35063 PMD 3f72d067 PTE 0 [ 82.599287] Oops: 0010 [#1] SMP [ 82.599287] Modules linked in: [last unloaded: hv_vmbus] [ 82.599287] CPU: 0 PID: 26 Comm: kworker/0:1 Not tainted 3.19.0-rc5_bug923184+ #488 [ 82.599287] Hardware name: Microsoft Corporation Virtual Machine/Virtual Machine, BIOS Hyper-V UEFI Release v1.0 11/26/2012 [ 82.599287] Workqueue: hv_vmbus_con 0xffffffffa0003480 [ 82.599287] task: ffff88007b6ddfa0 ti: ffff88007f8f8000 task.ti: ffff88007f8f8000 [ 82.599287] RIP: 0010:[<ffffffffa0003480>] [<ffffffffa0003480>] 0xffffffffa0003480 [ 82.599287] RSP: 0018:ffff88007f8fbe00 EFLAGS: 00010202 ... To avoid memory leaks we need to free monitor_pages and int_page for vmbus_connection. Implement vmbus_disconnect() function by separating cleanup path from vmbus_connect(). As we use hv_vmbus_con to release channels (see free_channel() in channel_mgmt.c) we need to make sure the work was done before we remove the queue, do that with drain_workqueue(). We also need to avoid handling messages which can (potentially) create new channels, so set vmbus_connection.conn_state = DISCONNECTED at the very beginning of vmbus_exit() and check for that in vmbus_onmessage_work(). 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>
-rw-r--r--drivers/hv/connection.c17
-rw-r--r--drivers/hv/hyperv_vmbus.h1
-rw-r--r--drivers/hv/vmbus_drv.c6
3 files changed, 19 insertions, 5 deletions
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index a63a795300b9..c4acd1ce7c0c 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -216,10 +216,21 @@ int vmbus_connect(void)
216 216
217cleanup: 217cleanup:
218 pr_err("Unable to connect to host\n"); 218 pr_err("Unable to connect to host\n");
219
219 vmbus_connection.conn_state = DISCONNECTED; 220 vmbus_connection.conn_state = DISCONNECTED;
221 vmbus_disconnect();
222
223 kfree(msginfo);
224
225 return ret;
226}
220 227
221 if (vmbus_connection.work_queue) 228void vmbus_disconnect(void)
229{
230 if (vmbus_connection.work_queue) {
231 drain_workqueue(vmbus_connection.work_queue);
222 destroy_workqueue(vmbus_connection.work_queue); 232 destroy_workqueue(vmbus_connection.work_queue);
233 }
223 234
224 if (vmbus_connection.int_page) { 235 if (vmbus_connection.int_page) {
225 free_pages((unsigned long)vmbus_connection.int_page, 0); 236 free_pages((unsigned long)vmbus_connection.int_page, 0);
@@ -230,10 +241,6 @@ cleanup:
230 free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0); 241 free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0);
231 vmbus_connection.monitor_pages[0] = NULL; 242 vmbus_connection.monitor_pages[0] = NULL;
232 vmbus_connection.monitor_pages[1] = NULL; 243 vmbus_connection.monitor_pages[1] = NULL;
233
234 kfree(msginfo);
235
236 return ret;
237} 244}
238 245
239/* 246/*
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 44b1c9424712..6cf2de9f487a 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -692,6 +692,7 @@ void vmbus_free_channels(void);
692/* Connection interface */ 692/* Connection interface */
693 693
694int vmbus_connect(void); 694int vmbus_connect(void);
695void vmbus_disconnect(void);
695 696
696int vmbus_post_msg(void *buffer, size_t buflen); 697int vmbus_post_msg(void *buffer, size_t buflen);
697 698
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index e334ccc70582..76db97de09fd 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -574,6 +574,10 @@ static void vmbus_onmessage_work(struct work_struct *work)
574{ 574{
575 struct onmessage_work_context *ctx; 575 struct onmessage_work_context *ctx;
576 576
577 /* Do not process messages if we're in DISCONNECTED state */
578 if (vmbus_connection.conn_state == DISCONNECTED)
579 return;
580
577 ctx = container_of(work, struct onmessage_work_context, 581 ctx = container_of(work, struct onmessage_work_context,
578 work); 582 work);
579 vmbus_onmessage(&ctx->msg); 583 vmbus_onmessage(&ctx->msg);
@@ -1025,12 +1029,14 @@ cleanup:
1025 1029
1026static void __exit vmbus_exit(void) 1030static void __exit vmbus_exit(void)
1027{ 1031{
1032 vmbus_connection.conn_state = DISCONNECTED;
1028 hv_remove_vmbus_irq(); 1033 hv_remove_vmbus_irq();
1029 vmbus_free_channels(); 1034 vmbus_free_channels();
1030 bus_unregister(&hv_bus); 1035 bus_unregister(&hv_bus);
1031 hv_cleanup(); 1036 hv_cleanup();
1032 acpi_bus_unregister_driver(&vmbus_acpi_driver); 1037 acpi_bus_unregister_driver(&vmbus_acpi_driver);
1033 hv_cpu_hotplug_quirk(false); 1038 hv_cpu_hotplug_quirk(false);
1039 vmbus_disconnect();
1034} 1040}
1035 1041
1036 1042