diff options
author | Vitaly Kuznetsov <vkuznets@redhat.com> | 2015-02-27 14:25:54 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-03-01 22:29:05 -0500 |
commit | 09a196288ec4617a920e051af6651ce03968c8b9 (patch) | |
tree | 40fb660a649ccd7e12f850138dd82d8b0c75a087 | |
parent | adcde069a85cb6674bdcf5f49f434d18b2db6e58 (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.c | 17 | ||||
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 1 | ||||
-rw-r--r-- | drivers/hv/vmbus_drv.c | 6 |
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 | ||
217 | cleanup: | 217 | cleanup: |
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) | 228 | void 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 | ||
694 | int vmbus_connect(void); | 694 | int vmbus_connect(void); |
695 | void vmbus_disconnect(void); | ||
695 | 696 | ||
696 | int vmbus_post_msg(void *buffer, size_t buflen); | 697 | int 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 | ||
1026 | static void __exit vmbus_exit(void) | 1030 | static 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 | ||