As the comment in qapi/error, passing @errp to error_prepend() requires
ERRP_GUARD():
* = Why, when and how to use ERRP_GUARD() =
*
* Without ERRP_GUARD(), use of the @errp parameter is restricted:
...
* - It should not be passed to error_prepend(), error_vprepend() or
*   error_append_hint(), because that doesn't work with &error_fatal.
* ERRP_GUARD() lifts these restrictions.
*
* To use ERRP_GUARD(), add it right at the beginning of the function.
* @errp can then be used without worrying about the argument being
* NULL or &error_fatal.
ERRP_GUARD() could avoid the case when @errp is &error_fatal, the user
can't see this additional information, because exit() happens in
error_setg earlier than information is added [1].
The vhost_vsock_device_realize() passes @errp to error_prepend(), and as
a VirtioDeviceClass.realize method, its @errp is from
DeviceClass.realize so that there is no guarantee that the @errp won't
point to @error_fatal.
To avoid the issue like [1] said, add missing ERRP_GUARD() at the
beginning of this function.
[1]: Issue description in the commit message of commit ae7c80a7bd73
     ("error: New macro ERRP_GUARD()").
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Message-ID: <20240311033822.3142585-26-zhao1.liu@linux.intel.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
		
	
			
		
			
				
	
	
		
			241 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Virtio vsock device
 | 
						|
 *
 | 
						|
 * Copyright 2015 Red Hat, Inc.
 | 
						|
 *
 | 
						|
 * Authors:
 | 
						|
 *  Stefan Hajnoczi <stefanha@redhat.com>
 | 
						|
 *
 | 
						|
 * This work is licensed under the terms of the GNU GPL, version 2 or
 | 
						|
 * (at your option) any later version.  See the COPYING file in the
 | 
						|
 * top-level directory.
 | 
						|
 */
 | 
						|
 | 
						|
#include "qemu/osdep.h"
 | 
						|
#include "standard-headers/linux/virtio_vsock.h"
 | 
						|
#include "qapi/error.h"
 | 
						|
#include "hw/virtio/virtio-access.h"
 | 
						|
#include "qemu/error-report.h"
 | 
						|
#include "qemu/sockets.h"
 | 
						|
#include "hw/qdev-properties.h"
 | 
						|
#include "hw/virtio/vhost-vsock.h"
 | 
						|
#include "monitor/monitor.h"
 | 
						|
 | 
						|
static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config)
 | 
						|
{
 | 
						|
    VHostVSock *vsock = VHOST_VSOCK(vdev);
 | 
						|
    struct virtio_vsock_config vsockcfg = {};
 | 
						|
 | 
						|
    virtio_stq_p(vdev, &vsockcfg.guest_cid, vsock->conf.guest_cid);
 | 
						|
    memcpy(config, &vsockcfg, sizeof(vsockcfg));
 | 
						|
}
 | 
						|
 | 
						|
static int vhost_vsock_set_guest_cid(VirtIODevice *vdev)
 | 
						|
{
 | 
						|
    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 | 
						|
    VHostVSock *vsock = VHOST_VSOCK(vdev);
 | 
						|
    const VhostOps *vhost_ops = vvc->vhost_dev.vhost_ops;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    if (!vhost_ops->vhost_vsock_set_guest_cid) {
 | 
						|
        return -ENOSYS;
 | 
						|
    }
 | 
						|
 | 
						|
    ret = vhost_ops->vhost_vsock_set_guest_cid(&vvc->vhost_dev,
 | 
						|
                                               vsock->conf.guest_cid);
 | 
						|
    if (ret < 0) {
 | 
						|
        return -errno;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int vhost_vsock_set_running(VirtIODevice *vdev, int start)
 | 
						|
{
 | 
						|
    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 | 
						|
    const VhostOps *vhost_ops = vvc->vhost_dev.vhost_ops;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    if (!vhost_ops->vhost_vsock_set_running) {
 | 
						|
        return -ENOSYS;
 | 
						|
    }
 | 
						|
 | 
						|
    ret = vhost_ops->vhost_vsock_set_running(&vvc->vhost_dev, start);
 | 
						|
    if (ret < 0) {
 | 
						|
        return -errno;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status)
 | 
						|
{
 | 
						|
    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
 | 
						|
    bool should_start = virtio_device_should_start(vdev, status);
 | 
						|
    int ret;
 | 
						|
 | 
						|
    if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (should_start) {
 | 
						|
        ret = vhost_vsock_common_start(vdev);
 | 
						|
        if (ret < 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        ret = vhost_vsock_set_running(vdev, 1);
 | 
						|
        if (ret < 0) {
 | 
						|
            vhost_vsock_common_stop(vdev);
 | 
						|
            error_report("Error starting vhost vsock: %d", -ret);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        ret = vhost_vsock_set_running(vdev, 0);
 | 
						|
        if (ret < 0) {
 | 
						|
            error_report("vhost vsock set running failed: %d", ret);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        vhost_vsock_common_stop(vdev);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static uint64_t vhost_vsock_get_features(VirtIODevice *vdev,
 | 
						|
                                         uint64_t requested_features,
 | 
						|
                                         Error **errp)
 | 
						|
{
 | 
						|
    return vhost_vsock_common_get_features(vdev, requested_features, errp);
 | 
						|
}
 | 
						|
 | 
						|
static const VMStateDescription vmstate_virtio_vhost_vsock = {
 | 
						|
    .name = "virtio-vhost_vsock",
 | 
						|
    .minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION,
 | 
						|
    .version_id = VHOST_VSOCK_SAVEVM_VERSION,
 | 
						|
    .fields = (const VMStateField[]) {
 | 
						|
        VMSTATE_VIRTIO_DEVICE,
 | 
						|
        VMSTATE_END_OF_LIST()
 | 
						|
    },
 | 
						|
    .pre_save = vhost_vsock_common_pre_save,
 | 
						|
    .post_load = vhost_vsock_common_post_load,
 | 
						|
};
 | 
						|
 | 
						|
static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
 | 
						|
{
 | 
						|
    ERRP_GUARD();
 | 
						|
    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
 | 
						|
    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 | 
						|
    VHostVSock *vsock = VHOST_VSOCK(dev);
 | 
						|
    int vhostfd;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    /* Refuse to use reserved CID numbers */
 | 
						|
    if (vsock->conf.guest_cid <= 2) {
 | 
						|
        error_setg(errp, "guest-cid property must be greater than 2");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (vsock->conf.guest_cid > UINT32_MAX) {
 | 
						|
        error_setg(errp, "guest-cid property must be a 32-bit number");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (vsock->conf.vhostfd) {
 | 
						|
        vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp);
 | 
						|
        if (vhostfd == -1) {
 | 
						|
            error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) {
 | 
						|
            error_setg_errno(errp, errno,
 | 
						|
                             "vhost-vsock: unable to set non-blocking mode");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        vhostfd = open("/dev/vhost-vsock", O_RDWR);
 | 
						|
        if (vhostfd < 0) {
 | 
						|
            error_setg_errno(errp, errno,
 | 
						|
                             "vhost-vsock: failed to open vhost device");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) {
 | 
						|
            error_setg_errno(errp, errno,
 | 
						|
                             "Failed to set FD nonblocking");
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    vhost_vsock_common_realize(vdev);
 | 
						|
 | 
						|
    ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd,
 | 
						|
                         VHOST_BACKEND_TYPE_KERNEL, 0, errp);
 | 
						|
    if (ret < 0) {
 | 
						|
        /*
 | 
						|
         * vhostfd is closed by vhost_dev_cleanup, which is called
 | 
						|
         * by vhost_dev_init on initialization error.
 | 
						|
         */
 | 
						|
        goto err_virtio;
 | 
						|
    }
 | 
						|
 | 
						|
    ret = vhost_vsock_set_guest_cid(vdev);
 | 
						|
    if (ret < 0) {
 | 
						|
        error_setg_errno(errp, -ret, "vhost-vsock: unable to set guest cid");
 | 
						|
        goto err_vhost_dev;
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
 | 
						|
err_vhost_dev:
 | 
						|
    /* vhost_dev_cleanup() closes the vhostfd passed to vhost_dev_init() */
 | 
						|
    vhost_dev_cleanup(&vvc->vhost_dev);
 | 
						|
err_virtio:
 | 
						|
    vhost_vsock_common_unrealize(vdev);
 | 
						|
}
 | 
						|
 | 
						|
static void vhost_vsock_device_unrealize(DeviceState *dev)
 | 
						|
{
 | 
						|
    VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
 | 
						|
    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
 | 
						|
 | 
						|
    /* This will stop vhost backend if appropriate. */
 | 
						|
    vhost_vsock_set_status(vdev, 0);
 | 
						|
 | 
						|
    vhost_dev_cleanup(&vvc->vhost_dev);
 | 
						|
    vhost_vsock_common_unrealize(vdev);
 | 
						|
}
 | 
						|
 | 
						|
static Property vhost_vsock_properties[] = {
 | 
						|
    DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0),
 | 
						|
    DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd),
 | 
						|
    DEFINE_PROP_END_OF_LIST(),
 | 
						|
};
 | 
						|
 | 
						|
static void vhost_vsock_class_init(ObjectClass *klass, void *data)
 | 
						|
{
 | 
						|
    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
						|
    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
 | 
						|
 | 
						|
    device_class_set_props(dc, vhost_vsock_properties);
 | 
						|
    dc->vmsd = &vmstate_virtio_vhost_vsock;
 | 
						|
    vdc->realize = vhost_vsock_device_realize;
 | 
						|
    vdc->unrealize = vhost_vsock_device_unrealize;
 | 
						|
    vdc->get_features = vhost_vsock_get_features;
 | 
						|
    vdc->get_config = vhost_vsock_get_config;
 | 
						|
    vdc->set_status = vhost_vsock_set_status;
 | 
						|
}
 | 
						|
 | 
						|
static const TypeInfo vhost_vsock_info = {
 | 
						|
    .name = TYPE_VHOST_VSOCK,
 | 
						|
    .parent = TYPE_VHOST_VSOCK_COMMON,
 | 
						|
    .instance_size = sizeof(VHostVSock),
 | 
						|
    .class_init = vhost_vsock_class_init,
 | 
						|
};
 | 
						|
 | 
						|
static void vhost_vsock_register_types(void)
 | 
						|
{
 | 
						|
    type_register_static(&vhost_vsock_info);
 | 
						|
}
 | 
						|
 | 
						|
type_init(vhost_vsock_register_types)
 |