qemu-patch-raspberry4/hw/9pfs/virtio-9p-device.c
Greg Kurz 6cecf09373 virtio-9p-device: add minimal unrealize handler
Since commit 4652f1640e "virtio-9p: add savevm
handlers", if the user hot-unplugs a quiescent 9p device and live
migrates, the source QEMU crashes before migration completetion...
This happens because virtio-9p devices have a realize handler which
calls virtio_init() and register_savevm().  Both calls store pointers
to the device internals, that get dereferenced during migration even
if the device got unplugged.

This patch simply adds an unrealize handler to perform minimal
cleanup and avoid the crash.  Hot unplug of non-quiescent 9p devices
is still not supported in QEMU, and not supported by linux guests
either.

Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Message-id: 20151208155457.27775.69441.stgit@bahia.huguette.org
[PMM: rewrapped long lines in commit message]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2015-12-10 10:46:22 +00:00

193 lines
5.4 KiB
C

/*
* Virtio 9p backend
*
* Copyright IBM, Corp. 2010
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-9p.h"
#include "hw/i386/pc.h"
#include "qemu/sockets.h"
#include "virtio-9p.h"
#include "fsdev/qemu-fsdev.h"
#include "virtio-9p-xattr.h"
#include "virtio-9p-coth.h"
#include "hw/virtio/virtio-access.h"
static uint64_t virtio_9p_get_features(VirtIODevice *vdev, uint64_t features,
Error **errp)
{
virtio_add_feature(&features, VIRTIO_9P_MOUNT_TAG);
return features;
}
static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
{
int len;
struct virtio_9p_config *cfg;
V9fsState *s = VIRTIO_9P(vdev);
len = strlen(s->tag);
cfg = g_malloc0(sizeof(struct virtio_9p_config) + len);
virtio_stw_p(vdev, &cfg->tag_len, len);
/* We don't copy the terminating null to config space */
memcpy(cfg->tag, s->tag, len);
memcpy(config, cfg, s->config_size);
g_free(cfg);
}
static void virtio_9p_save(QEMUFile *f, void *opaque)
{
virtio_save(VIRTIO_DEVICE(opaque), f);
}
static int virtio_9p_load(QEMUFile *f, void *opaque, int version_id)
{
return virtio_load(VIRTIO_DEVICE(opaque), f, version_id);
}
static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
V9fsState *s = VIRTIO_9P(dev);
int i, len;
struct stat stat;
FsDriverEntry *fse;
V9fsPath path;
virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P,
sizeof(struct virtio_9p_config) + MAX_TAG_LEN);
/* initialize pdu allocator */
QLIST_INIT(&s->free_list);
QLIST_INIT(&s->active_list);
for (i = 0; i < (MAX_REQ - 1); i++) {
QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
}
s->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output);
v9fs_path_init(&path);
fse = get_fsdev_fsentry(s->fsconf.fsdev_id);
if (!fse) {
/* We don't have a fsdev identified by fsdev_id */
error_setg(errp, "Virtio-9p device couldn't find fsdev with the "
"id = %s",
s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL");
goto out;
}
if (!s->fsconf.tag) {
/* we haven't specified a mount_tag */
error_setg(errp, "fsdev with id %s needs mount_tag arguments",
s->fsconf.fsdev_id);
goto out;
}
s->ctx.export_flags = fse->export_flags;
s->ctx.fs_root = g_strdup(fse->path);
s->ctx.exops.get_st_gen = NULL;
len = strlen(s->fsconf.tag);
if (len > MAX_TAG_LEN - 1) {
error_setg(errp, "mount tag '%s' (%d bytes) is longer than "
"maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1);
goto out;
}
s->tag = g_strdup(s->fsconf.tag);
s->ctx.uid = -1;
s->ops = fse->ops;
s->config_size = sizeof(struct virtio_9p_config) + len;
s->fid_list = NULL;
qemu_co_rwlock_init(&s->rename_lock);
if (s->ops->init(&s->ctx) < 0) {
error_setg(errp, "Virtio-9p Failed to initialize fs-driver with id:%s"
" and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
goto out;
}
/*
* Check details of export path, We need to use fs driver
* call back to do that. Since we are in the init path, we don't
* use co-routines here.
*/
if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) {
error_setg(errp,
"error in converting name to path %s", strerror(errno));
goto out;
}
if (s->ops->lstat(&s->ctx, &path, &stat)) {
error_setg(errp, "share path %s does not exist", fse->path);
goto out;
} else if (!S_ISDIR(stat.st_mode)) {
error_setg(errp, "share path %s is not a directory", fse->path);
goto out;
}
v9fs_path_free(&path);
register_savevm(dev, "virtio-9p", -1, 1, virtio_9p_save, virtio_9p_load, s);
return;
out:
g_free(s->ctx.fs_root);
g_free(s->tag);
virtio_cleanup(vdev);
v9fs_path_free(&path);
}
static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
V9fsState *s = VIRTIO_9P(dev);
virtio_cleanup(vdev);
unregister_savevm(dev, "virtio-9p", s);
g_free(s->ctx.fs_root);
g_free(s->tag);
}
/* virtio-9p device */
static Property virtio_9p_properties[] = {
DEFINE_PROP_STRING("mount_tag", V9fsState, fsconf.tag),
DEFINE_PROP_STRING("fsdev", V9fsState, fsconf.fsdev_id),
DEFINE_PROP_END_OF_LIST(),
};
static void virtio_9p_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
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;
}
static const TypeInfo virtio_device_info = {
.name = TYPE_VIRTIO_9P,
.parent = TYPE_VIRTIO_DEVICE,
.instance_size = sizeof(V9fsState),
.class_init = virtio_9p_class_init,
};
static void virtio_9p_register_types(void)
{
type_register_static(&virtio_device_info);
}
type_init(virtio_9p_register_types)