This patch allows to hot-unplug a quiescent virtio-9p device, and free its
allocated resources. A refcount is added to the v9fs thread pool, so that
its resources are freed as well when the last virtio-9p device is unplugged.

Note that we have an unplug blocker which prevents this code to be reached
if the 9p share is mounted in the guest. No need to bother about in-flight
I/O requests in this case.

This patch fixes a QEMU crash on the source node if the user device_add
a virtio-9p device and then migrate.

Signed-off-by: Greg Kurz <gk...@linux.vnet.ibm.com>
---
V2:
- now introduces v9fs_release_worker_threads() (was in patch 1)
- adds a refcount to the v9fs thread pool
---
 hw/9pfs/virtio-9p-coth.c   |   15 ++++++++++++++-
 hw/9pfs/virtio-9p-coth.h   |    2 ++
 hw/9pfs/virtio-9p-device.c |   12 ++++++++++++
 3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/hw/9pfs/virtio-9p-coth.c b/hw/9pfs/virtio-9p-coth.c
index 1d832ede1ebf..27ccc66c91d8 100644
--- a/hw/9pfs/virtio-9p-coth.c
+++ b/hw/9pfs/virtio-9p-coth.c
@@ -55,7 +55,7 @@ int v9fs_init_worker_threads(void)
     V9fsThPool *p = &v9fs_pool;
     sigset_t set, oldset;
 
-    if (p->pool) {
+    if (p->refcount++) {
         return 0;
     }
 
@@ -81,3 +81,16 @@ err_out:
     pthread_sigmask(SIG_SETMASK, &oldset, NULL);
     return ret;
 }
+
+void v9fs_release_worker_threads(void)
+{
+    V9fsThPool *p = &v9fs_pool;
+
+    if (--p->refcount) {
+        return;
+    }
+
+    g_thread_pool_free(p->pool, TRUE, TRUE);
+    g_async_queue_unref(p->completed);
+    event_notifier_set_handler(&p->e, NULL);
+}
diff --git a/hw/9pfs/virtio-9p-coth.h b/hw/9pfs/virtio-9p-coth.h
index 4f51b250d1d4..2a2617e670a5 100644
--- a/hw/9pfs/virtio-9p-coth.h
+++ b/hw/9pfs/virtio-9p-coth.h
@@ -25,6 +25,7 @@ typedef struct V9fsThPool {
 
     GThreadPool *pool;
     GAsyncQueue *completed;
+    unsigned refcount;
 } V9fsThPool;
 
 /*
@@ -56,6 +57,7 @@ typedef struct V9fsThPool {
 
 extern void co_run_in_worker_bh(void *);
 extern int v9fs_init_worker_threads(void);
+extern void v9fs_release_worker_threads(void);
 extern int v9fs_co_readlink(V9fsPDU *, V9fsPath *, V9fsString *);
 extern int v9fs_co_readdir_r(V9fsPDU *, V9fsFidState *,
                            struct dirent *, struct dirent **result);
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 93a407c45926..ed133c40493a 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -138,6 +138,17 @@ out:
     v9fs_path_free(&path);
 }
 
+static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+    V9fsState *s = VIRTIO_9P(dev);
+
+    v9fs_release_worker_threads();
+    g_free(s->ctx.fs_root);
+    g_free(s->tag);
+    virtio_cleanup(vdev);
+}
+
 /* virtio-9p device */
 
 static Property virtio_9p_properties[] = {
@@ -154,6 +165,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void 
*data)
     dc->props = virtio_9p_properties;
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
     vdc->realize = virtio_9p_device_realize;
+    vdc->unrealize = virtio_9p_device_unrealize;
     vdc->get_features = virtio_9p_get_features;
     vdc->get_config = virtio_9p_get_config;
 }


Reply via email to