aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Grosbein <eugen@FreeBSD.org>2022-03-16 04:41:51 +0000
committerEugene Grosbein <eugen@FreeBSD.org>2022-03-19 12:36:26 +0000
commitfa67c45842bb5d34780a536b1bff1ac64f381562 (patch)
treecb24dff77d2fd1d2e044af4702832b118d2d069d
parent2ac7903dcbbafd0ad8c466344477fe62f16dccea (diff)
downloadsrc-fa67c45842bb.tar.gz
src-fa67c45842bb.zip
virtio_random(8): MFC: avoid deadlock at shutdown time (regression fix)
FreeBSD 13+ running as virtual guest may load virtio_random(8) driver by means of devd(8) unless the driver is blacklisted or disabled via device.hints(5). Currently, the driver may prevent the system from rebooting or shutting down correctly. This change deactivates virtio_random at very late stage during system shutdown sequence to avoid deadlock that results in kernel hang. PR: 253175 Tested by: tom Relnotes: yes Approved by: re (gjb) (cherry picked from commit adbf7727b3a2aad3c2faa6e543ee7fa7a6c9a3d5) (cherry picked from commit 4a11315a2c3fc55333772f48aaef32ae1eb11ceb)
-rw-r--r--sys/dev/virtio/random/virtio_random.c35
1 files changed, 35 insertions, 0 deletions
diff --git a/sys/dev/virtio/random/virtio_random.c b/sys/dev/virtio/random/virtio_random.c
index a8553ecab287..a95dcadcddcd 100644
--- a/sys/dev/virtio/random/virtio_random.c
+++ b/sys/dev/virtio/random/virtio_random.c
@@ -32,6 +32,8 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/eventhandler.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
@@ -52,6 +54,8 @@ struct vtrnd_softc {
device_t vtrnd_dev;
uint64_t vtrnd_features;
struct virtqueue *vtrnd_vq;
+ eventhandler_tag eh;
+ bool inactive;
};
static int vtrnd_modevent(module_t, int, void *);
@@ -59,6 +63,7 @@ static int vtrnd_modevent(module_t, int, void *);
static int vtrnd_probe(device_t);
static int vtrnd_attach(device_t);
static int vtrnd_detach(device_t);
+static int vtrnd_shutdown(device_t);
static int vtrnd_negotiate_features(struct vtrnd_softc *);
static int vtrnd_setup_features(struct vtrnd_softc *);
@@ -86,6 +91,7 @@ static device_method_t vtrnd_methods[] = {
DEVMETHOD(device_probe, vtrnd_probe),
DEVMETHOD(device_attach, vtrnd_attach),
DEVMETHOD(device_detach, vtrnd_detach),
+ DEVMETHOD(device_shutdown, vtrnd_shutdown),
DEVMETHOD_END
};
@@ -160,6 +166,16 @@ vtrnd_attach(device_t dev)
error = EEXIST;
goto fail;
}
+
+ sc->eh = EVENTHANDLER_REGISTER(shutdown_post_sync,
+ vtrnd_shutdown, dev, SHUTDOWN_PRI_LAST + 1); /* ??? */
+ if (sc->eh == NULL) {
+ device_printf(dev, "Shutdown event registration failed\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->inactive = false;
random_source_register(&random_vtrnd);
fail:
@@ -179,12 +195,28 @@ vtrnd_detach(device_t dev)
atomic_load_explicit(&g_vtrnd_softc, memory_order_acquire) == sc,
("only one global instance at a time"));
+ sc->inactive = true;
+ if (sc->eh != NULL) {
+ EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->eh);
+ sc->eh = NULL;
+ }
random_source_deregister(&random_vtrnd);
atomic_store_explicit(&g_vtrnd_softc, NULL, memory_order_release);
return (0);
}
static int
+vtrnd_shutdown(device_t dev)
+{
+ struct vtrnd_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->inactive = true;
+
+ return(0);
+}
+
+static int
vtrnd_negotiate_features(struct vtrnd_softc *sc)
{
device_t dev;
@@ -235,6 +267,9 @@ vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz)
_Static_assert(sizeof(value) < PAGE_SIZE, "sglist assumption");
+ if (sc->inactive)
+ return (EDEADLK);
+
sglist_init(&sg, 1, segs);
error = sglist_append(&sg, value, *sz);
if (error != 0)