aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEduardo Bart <edub4rt@gmail.com>2023-11-17 14:04:15 -0300
committerWill Deacon <will@kernel.org>2023-11-21 15:32:10 +0000
commit74af1456dfa0c3fb1c79529450c6130b54fd1c83 (patch)
treeb30dad5681b2d265f34b45cfe7fb8de782b48df2
parent9cb1b46cb765972326a46bdba867d441a842af56 (diff)
downloadkvmtool-74af1456dfa0c3fb1c79529450c6130b54fd1c83.tar.gz
virtio: Cancel and join threads when exiting devices devices
I'm experiencing a segmentation fault in lkvm where it may crash after powering off a guest machine that uses a virtio network device. The crash is hard to reproduce, because looks like it only happens when the guest machine is powering off while extra virtio threads is doing some work, when it happens lkvm crashes in the function virtio_net_rx_thread while attempting to read invalid guest physical memory, because guest physical memory was unmapped. I've isolated the problem and looks like when lkvm exits it unmaps the guest memory while virtio device extra threads may still be executing. I noticed most virtio devices are not executing pthread_cancel + pthread_join to synchronize extra threads when exiting, to make sure this happens I added explicit calls to the virtio device exit function to all virtio devices, which should cancel and join all threads before unmapping guest physical memory, fixing the crash for me. Signed-off-by: Eduardo Bart <edub4rt@gmail.com> Link: https://lore.kernel.org/r/20231117170455.80578-2-edub4rt@gmail.com [will: Added commit message from https://lore.kernel.org/all/CABqCASLWAZ5aq27GuQftWsXSf7yLFCKwrJxWMUF-fiV7Bc4LUA@mail.gmail.com/] Signed-off-by: Will Deacon <will@kernel.org>
-rw-r--r--include/kvm/virtio-9p.h1
-rw-r--r--include/kvm/virtio.h1
-rw-r--r--virtio/9p.c14
-rw-r--r--virtio/balloon.c10
-rw-r--r--virtio/blk.c1
-rw-r--r--virtio/console.c2
-rw-r--r--virtio/core.c6
-rw-r--r--virtio/net.c3
-rw-r--r--virtio/rng.c10
9 files changed, 47 insertions, 1 deletions
diff --git a/include/kvm/virtio-9p.h b/include/kvm/virtio-9p.h
index 1dffc955..09f7e46a 100644
--- a/include/kvm/virtio-9p.h
+++ b/include/kvm/virtio-9p.h
@@ -70,6 +70,7 @@ int virtio_9p_rootdir_parser(const struct option *opt, const char *arg, int unse
int virtio_9p_img_name_parser(const struct option *opt, const char *arg, int unset);
int virtio_9p__register(struct kvm *kvm, const char *root, const char *tag_name);
int virtio_9p__init(struct kvm *kvm);
+int virtio_9p__exit(struct kvm *kvm);
int virtio_p9_pdu_readf(struct p9_pdu *pdu, const char *fmt, ...);
int virtio_p9_pdu_writef(struct p9_pdu *pdu, const char *fmt, ...);
diff --git a/include/kvm/virtio.h b/include/kvm/virtio.h
index 95b5142b..8b7ec1b7 100644
--- a/include/kvm/virtio.h
+++ b/include/kvm/virtio.h
@@ -251,6 +251,7 @@ struct virtio_ops {
int __must_check virtio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
struct virtio_ops *ops, enum virtio_trans trans,
int device_id, int subsys_id, int class);
+void virtio_exit(struct kvm *kvm, struct virtio_device *vdev);
int virtio_compat_add_message(const char *device, const char *config);
const char* virtio_trans_name(enum virtio_trans trans);
void virtio_init_device_vq(struct kvm *kvm, struct virtio_device *vdev,
diff --git a/virtio/9p.c b/virtio/9p.c
index 513164ee..2fa6f284 100644
--- a/virtio/9p.c
+++ b/virtio/9p.c
@@ -1562,6 +1562,20 @@ int virtio_9p__init(struct kvm *kvm)
}
virtio_dev_init(virtio_9p__init);
+int virtio_9p__exit(struct kvm *kvm)
+{
+ struct p9_dev *p9dev, *tmp;
+
+ list_for_each_entry_safe(p9dev, tmp, &devs, list) {
+ list_del(&p9dev->list);
+ virtio_exit(kvm, &p9dev->vdev);
+ free(p9dev);
+ }
+
+ return 0;
+}
+virtio_dev_exit(virtio_9p__exit);
+
int virtio_9p__register(struct kvm *kvm, const char *root, const char *tag_name)
{
struct p9_dev *p9dev;
diff --git a/virtio/balloon.c b/virtio/balloon.c
index 01d19828..5b3e0626 100644
--- a/virtio/balloon.c
+++ b/virtio/balloon.c
@@ -221,6 +221,13 @@ static int init_vq(struct kvm *kvm, void *dev, u32 vq)
return 0;
}
+static void exit_vq(struct kvm *kvm, void *dev, u32 vq)
+{
+ struct bln_dev *bdev = dev;
+
+ thread_pool__cancel_job(&bdev->jobs[vq]);
+}
+
static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
{
struct bln_dev *bdev = dev;
@@ -258,6 +265,7 @@ struct virtio_ops bln_dev_virtio_ops = {
.get_config_size = get_config_size,
.get_host_features = get_host_features,
.init_vq = init_vq,
+ .exit_vq = exit_vq,
.notify_vq = notify_vq,
.get_vq = get_vq,
.get_size_vq = get_size_vq,
@@ -293,6 +301,8 @@ virtio_dev_init(virtio_bln__init);
int virtio_bln__exit(struct kvm *kvm)
{
+ virtio_exit(kvm, &bdev.vdev);
+
return 0;
}
virtio_dev_exit(virtio_bln__exit);
diff --git a/virtio/blk.c b/virtio/blk.c
index a58c7452..b2d6180d 100644
--- a/virtio/blk.c
+++ b/virtio/blk.c
@@ -345,6 +345,7 @@ static int virtio_blk__init_one(struct kvm *kvm, struct disk_image *disk)
static int virtio_blk__exit_one(struct kvm *kvm, struct blk_dev *bdev)
{
list_del(&bdev->list);
+ virtio_exit(kvm, &bdev->vdev);
free(bdev);
return 0;
diff --git a/virtio/console.c b/virtio/console.c
index ebfbaf06..9a775f20 100644
--- a/virtio/console.c
+++ b/virtio/console.c
@@ -243,6 +243,8 @@ virtio_dev_init(virtio_console__init);
int virtio_console__exit(struct kvm *kvm)
{
+ virtio_exit(kvm, &cdev.vdev);
+
return 0;
}
virtio_dev_exit(virtio_console__exit);
diff --git a/virtio/core.c b/virtio/core.c
index a77e23bc..b77e987e 100644
--- a/virtio/core.c
+++ b/virtio/core.c
@@ -400,6 +400,12 @@ int virtio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
return r;
}
+void virtio_exit(struct kvm *kvm, struct virtio_device *vdev)
+{
+ if (vdev->ops && vdev->ops->exit)
+ vdev->ops->exit(kvm, vdev);
+}
+
int virtio_compat_add_message(const char *device, const char *config)
{
int len = 1024;
diff --git a/virtio/net.c b/virtio/net.c
index f09dd0a4..492c5767 100644
--- a/virtio/net.c
+++ b/virtio/net.c
@@ -969,10 +969,13 @@ int virtio_net__exit(struct kvm *kvm)
if (ndev->mode == NET_MODE_TAP &&
strcmp(params->downscript, "none"))
virtio_net_exec_script(params->downscript, ndev->tap_name);
+ virtio_net_stop(ndev);
list_del(&ndev->list);
+ virtio_exit(kvm, &ndev->vdev);
free(ndev);
}
+
return 0;
}
virtio_dev_exit(virtio_net__exit);
diff --git a/virtio/rng.c b/virtio/rng.c
index 6b366552..505c4b26 100644
--- a/virtio/rng.c
+++ b/virtio/rng.c
@@ -122,6 +122,13 @@ static int init_vq(struct kvm *kvm, void *dev, u32 vq)
return 0;
}
+static void exit_vq(struct kvm *kvm, void *dev, u32 vq)
+{
+ struct rng_dev *rdev = dev;
+
+ thread_pool__cancel_job(&rdev->jobs[vq].job_id);
+}
+
static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
{
struct rng_dev *rdev = dev;
@@ -159,6 +166,7 @@ static struct virtio_ops rng_dev_virtio_ops = {
.get_config_size = get_config_size,
.get_host_features = get_host_features,
.init_vq = init_vq,
+ .exit_vq = exit_vq,
.notify_vq = notify_vq,
.get_vq = get_vq,
.get_size_vq = get_size_vq,
@@ -209,7 +217,7 @@ int virtio_rng__exit(struct kvm *kvm)
list_for_each_entry_safe(rdev, tmp, &rdevs, list) {
list_del(&rdev->list);
- rdev->vdev.ops->exit(kvm, &rdev->vdev);
+ virtio_exit(kvm, &rdev->vdev);
free(rdev);
}