| /* |
| * Vhost-user vsock virtio device |
| * |
| * Copyright 2020 Red Hat, Inc. |
| * |
| * 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 "qapi/error.h" |
| #include "qemu/error-report.h" |
| #include "hw/qdev-properties.h" |
| #include "hw/qdev-properties-system.h" |
| #include "hw/virtio/vhost-user-vsock.h" |
| |
| static const int user_feature_bits[] = { |
| VIRTIO_F_VERSION_1, |
| VIRTIO_RING_F_INDIRECT_DESC, |
| VIRTIO_RING_F_EVENT_IDX, |
| VIRTIO_F_NOTIFY_ON_EMPTY, |
| VHOST_INVALID_FEATURE_BIT |
| }; |
| |
| static void vuv_get_config(VirtIODevice *vdev, uint8_t *config) |
| { |
| VHostUserVSock *vsock = VHOST_USER_VSOCK(vdev); |
| |
| memcpy(config, &vsock->vsockcfg, sizeof(struct virtio_vsock_config)); |
| } |
| |
| static int vuv_handle_config_change(struct vhost_dev *dev) |
| { |
| VHostUserVSock *vsock = VHOST_USER_VSOCK(dev->vdev); |
| Error *local_err = NULL; |
| int ret = vhost_dev_get_config(dev, (uint8_t *)&vsock->vsockcfg, |
| sizeof(struct virtio_vsock_config), |
| &local_err); |
| if (ret < 0) { |
| error_report_err(local_err); |
| return -1; |
| } |
| |
| virtio_notify_config(dev->vdev); |
| |
| return 0; |
| } |
| |
| const VhostDevConfigOps vsock_ops = { |
| .vhost_dev_config_notifier = vuv_handle_config_change, |
| }; |
| |
| static void vuv_set_status(VirtIODevice *vdev, uint8_t status) |
| { |
| VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); |
| bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; |
| |
| if (!vdev->vm_running) { |
| should_start = false; |
| } |
| |
| if (vvc->vhost_dev.started == should_start) { |
| return; |
| } |
| |
| if (should_start) { |
| int ret = vhost_vsock_common_start(vdev); |
| if (ret < 0) { |
| return; |
| } |
| } else { |
| vhost_vsock_common_stop(vdev); |
| } |
| } |
| |
| static uint64_t vuv_get_features(VirtIODevice *vdev, |
| uint64_t features, |
| Error **errp) |
| { |
| VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); |
| |
| features = vhost_get_features(&vvc->vhost_dev, user_feature_bits, features); |
| |
| return vhost_vsock_common_get_features(vdev, features, errp); |
| } |
| |
| static const VMStateDescription vuv_vmstate = { |
| .name = "vhost-user-vsock", |
| .unmigratable = 1, |
| }; |
| |
| static void vuv_device_realize(DeviceState *dev, Error **errp) |
| { |
| VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev); |
| VirtIODevice *vdev = VIRTIO_DEVICE(dev); |
| VHostUserVSock *vsock = VHOST_USER_VSOCK(dev); |
| int ret; |
| |
| if (!vsock->conf.chardev.chr) { |
| error_setg(errp, "missing chardev"); |
| return; |
| } |
| |
| if (!vhost_user_init(&vsock->vhost_user, &vsock->conf.chardev, errp)) { |
| return; |
| } |
| |
| vhost_vsock_common_realize(vdev); |
| |
| vhost_dev_set_config_notifier(&vvc->vhost_dev, &vsock_ops); |
| |
| ret = vhost_dev_init(&vvc->vhost_dev, &vsock->vhost_user, |
| VHOST_BACKEND_TYPE_USER, 0, errp); |
| if (ret < 0) { |
| goto err_virtio; |
| } |
| |
| ret = vhost_dev_get_config(&vvc->vhost_dev, (uint8_t *)&vsock->vsockcfg, |
| sizeof(struct virtio_vsock_config), errp); |
| if (ret < 0) { |
| goto err_vhost_dev; |
| } |
| |
| return; |
| |
| err_vhost_dev: |
| vhost_dev_cleanup(&vvc->vhost_dev); |
| err_virtio: |
| vhost_vsock_common_unrealize(vdev); |
| vhost_user_cleanup(&vsock->vhost_user); |
| return; |
| } |
| |
| static void vuv_device_unrealize(DeviceState *dev) |
| { |
| VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev); |
| VirtIODevice *vdev = VIRTIO_DEVICE(dev); |
| VHostUserVSock *vsock = VHOST_USER_VSOCK(dev); |
| |
| /* This will stop vhost backend if appropriate. */ |
| vuv_set_status(vdev, 0); |
| |
| vhost_dev_cleanup(&vvc->vhost_dev); |
| |
| vhost_vsock_common_unrealize(vdev); |
| |
| vhost_user_cleanup(&vsock->vhost_user); |
| |
| } |
| |
| static Property vuv_properties[] = { |
| DEFINE_PROP_CHR("chardev", VHostUserVSock, conf.chardev), |
| DEFINE_PROP_END_OF_LIST(), |
| }; |
| |
| static void vuv_class_init(ObjectClass *klass, void *data) |
| { |
| DeviceClass *dc = DEVICE_CLASS(klass); |
| VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); |
| |
| device_class_set_props(dc, vuv_properties); |
| dc->vmsd = &vuv_vmstate; |
| vdc->realize = vuv_device_realize; |
| vdc->unrealize = vuv_device_unrealize; |
| vdc->get_features = vuv_get_features; |
| vdc->get_config = vuv_get_config; |
| vdc->set_status = vuv_set_status; |
| } |
| |
| static const TypeInfo vuv_info = { |
| .name = TYPE_VHOST_USER_VSOCK, |
| .parent = TYPE_VHOST_VSOCK_COMMON, |
| .instance_size = sizeof(VHostUserVSock), |
| .class_init = vuv_class_init, |
| }; |
| |
| static void vuv_register_types(void) |
| { |
| type_register_static(&vuv_info); |
| } |
| |
| type_init(vuv_register_types) |