1fc0b9b0eSStefan Hajnoczi /* 2fc0b9b0eSStefan Hajnoczi * Virtio vsock device 3fc0b9b0eSStefan Hajnoczi * 4fc0b9b0eSStefan Hajnoczi * Copyright 2015 Red Hat, Inc. 5fc0b9b0eSStefan Hajnoczi * 6fc0b9b0eSStefan Hajnoczi * Authors: 7fc0b9b0eSStefan Hajnoczi * Stefan Hajnoczi <stefanha@redhat.com> 8fc0b9b0eSStefan Hajnoczi * 9fc0b9b0eSStefan Hajnoczi * This work is licensed under the terms of the GNU GPL, version 2 or 10fc0b9b0eSStefan Hajnoczi * (at your option) any later version. See the COPYING file in the 11fc0b9b0eSStefan Hajnoczi * top-level directory. 12fc0b9b0eSStefan Hajnoczi */ 13fc0b9b0eSStefan Hajnoczi 14fc0b9b0eSStefan Hajnoczi #include "qemu/osdep.h" 15fc0b9b0eSStefan Hajnoczi #include "standard-headers/linux/virtio_vsock.h" 16fc0b9b0eSStefan Hajnoczi #include "qapi/error.h" 17fc0b9b0eSStefan Hajnoczi #include "hw/virtio/virtio-access.h" 18fc0b9b0eSStefan Hajnoczi #include "qemu/error-report.h" 19384c2561SStefano Garzarella #include "qemu/sockets.h" 20a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 21fc0b9b0eSStefan Hajnoczi #include "hw/virtio/vhost-vsock.h" 22fc0b9b0eSStefan Hajnoczi #include "monitor/monitor.h" 23fc0b9b0eSStefan Hajnoczi 24fc0b9b0eSStefan Hajnoczi static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config) 25fc0b9b0eSStefan Hajnoczi { 26fc0b9b0eSStefan Hajnoczi VHostVSock *vsock = VHOST_VSOCK(vdev); 27fc0b9b0eSStefan Hajnoczi struct virtio_vsock_config vsockcfg = {}; 28fc0b9b0eSStefan Hajnoczi 29fc0b9b0eSStefan Hajnoczi virtio_stq_p(vdev, &vsockcfg.guest_cid, vsock->conf.guest_cid); 30fc0b9b0eSStefan Hajnoczi memcpy(config, &vsockcfg, sizeof(vsockcfg)); 31fc0b9b0eSStefan Hajnoczi } 32fc0b9b0eSStefan Hajnoczi 33c6136ec0SStefano Garzarella static int vhost_vsock_set_guest_cid(VirtIODevice *vdev) 34fc0b9b0eSStefan Hajnoczi { 35c6136ec0SStefano Garzarella VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 36c6136ec0SStefano Garzarella VHostVSock *vsock = VHOST_VSOCK(vdev); 37c6136ec0SStefano Garzarella const VhostOps *vhost_ops = vvc->vhost_dev.vhost_ops; 38fc0b9b0eSStefan Hajnoczi int ret; 39fc0b9b0eSStefan Hajnoczi 40fc0b9b0eSStefan Hajnoczi if (!vhost_ops->vhost_vsock_set_guest_cid) { 41fc0b9b0eSStefan Hajnoczi return -ENOSYS; 42fc0b9b0eSStefan Hajnoczi } 43fc0b9b0eSStefan Hajnoczi 44c6136ec0SStefano Garzarella ret = vhost_ops->vhost_vsock_set_guest_cid(&vvc->vhost_dev, 45fc0b9b0eSStefan Hajnoczi vsock->conf.guest_cid); 46fc0b9b0eSStefan Hajnoczi if (ret < 0) { 47fc0b9b0eSStefan Hajnoczi return -errno; 48fc0b9b0eSStefan Hajnoczi } 49fc0b9b0eSStefan Hajnoczi return 0; 50fc0b9b0eSStefan Hajnoczi } 51fc0b9b0eSStefan Hajnoczi 52c6136ec0SStefano Garzarella static int vhost_vsock_set_running(VirtIODevice *vdev, int start) 53fc0b9b0eSStefan Hajnoczi { 54c6136ec0SStefano Garzarella VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 55c6136ec0SStefano Garzarella const VhostOps *vhost_ops = vvc->vhost_dev.vhost_ops; 56fc0b9b0eSStefan Hajnoczi int ret; 57fc0b9b0eSStefan Hajnoczi 58fc0b9b0eSStefan Hajnoczi if (!vhost_ops->vhost_vsock_set_running) { 59fc0b9b0eSStefan Hajnoczi return -ENOSYS; 60fc0b9b0eSStefan Hajnoczi } 61fc0b9b0eSStefan Hajnoczi 62c6136ec0SStefano Garzarella ret = vhost_ops->vhost_vsock_set_running(&vvc->vhost_dev, start); 63fc0b9b0eSStefan Hajnoczi if (ret < 0) { 64fc0b9b0eSStefan Hajnoczi return -errno; 65fc0b9b0eSStefan Hajnoczi } 66fc0b9b0eSStefan Hajnoczi return 0; 67fc0b9b0eSStefan Hajnoczi } 68fc0b9b0eSStefan Hajnoczi 69fc0b9b0eSStefan Hajnoczi 70fc0b9b0eSStefan Hajnoczi static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status) 71fc0b9b0eSStefan Hajnoczi { 72c6136ec0SStefano Garzarella VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 73fc0b9b0eSStefan Hajnoczi bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; 74c6136ec0SStefano Garzarella int ret; 75fc0b9b0eSStefan Hajnoczi 76fc0b9b0eSStefan Hajnoczi if (!vdev->vm_running) { 77fc0b9b0eSStefan Hajnoczi should_start = false; 78fc0b9b0eSStefan Hajnoczi } 79fc0b9b0eSStefan Hajnoczi 80c6136ec0SStefano Garzarella if (vvc->vhost_dev.started == should_start) { 81fc0b9b0eSStefan Hajnoczi return; 82fc0b9b0eSStefan Hajnoczi } 83fc0b9b0eSStefan Hajnoczi 84fc0b9b0eSStefan Hajnoczi if (should_start) { 85c6136ec0SStefano Garzarella ret = vhost_vsock_common_start(vdev); 86c6136ec0SStefano Garzarella if (ret < 0) { 87c6136ec0SStefano Garzarella return; 88c6136ec0SStefano Garzarella } 89c6136ec0SStefano Garzarella 90c6136ec0SStefano Garzarella ret = vhost_vsock_set_running(vdev, 1); 91c6136ec0SStefano Garzarella if (ret < 0) { 92c6136ec0SStefano Garzarella vhost_vsock_common_stop(vdev); 93c6136ec0SStefano Garzarella error_report("Error starting vhost vsock: %d", -ret); 94c6136ec0SStefano Garzarella return; 95c6136ec0SStefano Garzarella } 96fc0b9b0eSStefan Hajnoczi } else { 97c6136ec0SStefano Garzarella ret = vhost_vsock_set_running(vdev, 0); 98c6136ec0SStefano Garzarella if (ret < 0) { 99c6136ec0SStefano Garzarella error_report("vhost vsock set running failed: %d", ret); 100c6136ec0SStefano Garzarella return; 101c6136ec0SStefano Garzarella } 102c6136ec0SStefano Garzarella 103c6136ec0SStefano Garzarella vhost_vsock_common_stop(vdev); 104fc0b9b0eSStefan Hajnoczi } 105fc0b9b0eSStefan Hajnoczi } 106fc0b9b0eSStefan Hajnoczi 107fc0b9b0eSStefan Hajnoczi static uint64_t vhost_vsock_get_features(VirtIODevice *vdev, 108fc0b9b0eSStefan Hajnoczi uint64_t requested_features, 109fc0b9b0eSStefan Hajnoczi Error **errp) 110fc0b9b0eSStefan Hajnoczi { 11146ce0171SStefano Garzarella return vhost_vsock_common_get_features(vdev, requested_features, errp); 112fc0b9b0eSStefan Hajnoczi } 113fc0b9b0eSStefan Hajnoczi 11481cc8a65SHalil Pasic static const VMStateDescription vmstate_virtio_vhost_vsock = { 11581cc8a65SHalil Pasic .name = "virtio-vhost_vsock", 11681cc8a65SHalil Pasic .minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION, 11781cc8a65SHalil Pasic .version_id = VHOST_VSOCK_SAVEVM_VERSION, 11881cc8a65SHalil Pasic .fields = (VMStateField[]) { 11981cc8a65SHalil Pasic VMSTATE_VIRTIO_DEVICE, 12081cc8a65SHalil Pasic VMSTATE_END_OF_LIST() 12181cc8a65SHalil Pasic }, 122c6136ec0SStefano Garzarella .pre_save = vhost_vsock_common_pre_save, 123c6136ec0SStefano Garzarella .post_load = vhost_vsock_common_post_load, 12481cc8a65SHalil Pasic }; 125fc0b9b0eSStefan Hajnoczi 126fc0b9b0eSStefan Hajnoczi static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) 127fc0b9b0eSStefan Hajnoczi { 128c6136ec0SStefano Garzarella VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev); 129fc0b9b0eSStefan Hajnoczi VirtIODevice *vdev = VIRTIO_DEVICE(dev); 130fc0b9b0eSStefan Hajnoczi VHostVSock *vsock = VHOST_VSOCK(dev); 131fc0b9b0eSStefan Hajnoczi int vhostfd; 132fc0b9b0eSStefan Hajnoczi int ret; 133fc0b9b0eSStefan Hajnoczi 134fc0b9b0eSStefan Hajnoczi /* Refuse to use reserved CID numbers */ 135fc0b9b0eSStefan Hajnoczi if (vsock->conf.guest_cid <= 2) { 136fc0b9b0eSStefan Hajnoczi error_setg(errp, "guest-cid property must be greater than 2"); 137fc0b9b0eSStefan Hajnoczi return; 138fc0b9b0eSStefan Hajnoczi } 139fc0b9b0eSStefan Hajnoczi 140fc0b9b0eSStefan Hajnoczi if (vsock->conf.guest_cid > UINT32_MAX) { 141fc0b9b0eSStefan Hajnoczi error_setg(errp, "guest-cid property must be a 32-bit number"); 142fc0b9b0eSStefan Hajnoczi return; 143fc0b9b0eSStefan Hajnoczi } 144fc0b9b0eSStefan Hajnoczi 145fc0b9b0eSStefan Hajnoczi if (vsock->conf.vhostfd) { 146947e4744SKevin Wolf vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp); 147fc0b9b0eSStefan Hajnoczi if (vhostfd == -1) { 148fc0b9b0eSStefan Hajnoczi error_prepend(errp, "vhost-vsock: unable to parse vhostfd: "); 149fc0b9b0eSStefan Hajnoczi return; 150fc0b9b0eSStefan Hajnoczi } 151384c2561SStefano Garzarella 152701544cfSMarc-André Lureau if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { 153701544cfSMarc-André Lureau error_setg_errno(errp, errno, 154384c2561SStefano Garzarella "vhost-vsock: unable to set non-blocking mode"); 155384c2561SStefano Garzarella return; 156384c2561SStefano Garzarella } 157fc0b9b0eSStefan Hajnoczi } else { 158fc0b9b0eSStefan Hajnoczi vhostfd = open("/dev/vhost-vsock", O_RDWR); 159fc0b9b0eSStefan Hajnoczi if (vhostfd < 0) { 160f1e92c3dSNick Erdmann error_setg_errno(errp, errno, 161fc0b9b0eSStefan Hajnoczi "vhost-vsock: failed to open vhost device"); 162fc0b9b0eSStefan Hajnoczi return; 163fc0b9b0eSStefan Hajnoczi } 164384c2561SStefano Garzarella 165701544cfSMarc-André Lureau if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { 166701544cfSMarc-André Lureau error_setg_errno(errp, errno, 167701544cfSMarc-André Lureau "Failed to set FD nonblocking"); 168701544cfSMarc-André Lureau return; 169701544cfSMarc-André Lureau } 170fc0b9b0eSStefan Hajnoczi } 171fc0b9b0eSStefan Hajnoczi 172*3857cd5cSJonah Palmer vhost_vsock_common_realize(vdev); 173fc0b9b0eSStefan Hajnoczi 174c6136ec0SStefano Garzarella ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd, 175a6945f22SKevin Wolf VHOST_BACKEND_TYPE_KERNEL, 0, errp); 176fc0b9b0eSStefan Hajnoczi if (ret < 0) { 177d731ab31SDaniil Tatianin /* 178d731ab31SDaniil Tatianin * vhostfd is closed by vhost_dev_cleanup, which is called 179d731ab31SDaniil Tatianin * by vhost_dev_init on initialization error. 180d731ab31SDaniil Tatianin */ 181fc0b9b0eSStefan Hajnoczi goto err_virtio; 182fc0b9b0eSStefan Hajnoczi } 183fc0b9b0eSStefan Hajnoczi 184c6136ec0SStefano Garzarella ret = vhost_vsock_set_guest_cid(vdev); 185fc0b9b0eSStefan Hajnoczi if (ret < 0) { 186fc0b9b0eSStefan Hajnoczi error_setg_errno(errp, -ret, "vhost-vsock: unable to set guest cid"); 187fc0b9b0eSStefan Hajnoczi goto err_vhost_dev; 188fc0b9b0eSStefan Hajnoczi } 189fc0b9b0eSStefan Hajnoczi 190fc0b9b0eSStefan Hajnoczi return; 191fc0b9b0eSStefan Hajnoczi 192fc0b9b0eSStefan Hajnoczi err_vhost_dev: 193e82cdba3SStefano Garzarella /* vhost_dev_cleanup() closes the vhostfd passed to vhost_dev_init() */ 194d731ab31SDaniil Tatianin vhost_dev_cleanup(&vvc->vhost_dev); 195fc0b9b0eSStefan Hajnoczi err_virtio: 196c6136ec0SStefano Garzarella vhost_vsock_common_unrealize(vdev); 197fc0b9b0eSStefan Hajnoczi } 198fc0b9b0eSStefan Hajnoczi 199b69c3c21SMarkus Armbruster static void vhost_vsock_device_unrealize(DeviceState *dev) 200fc0b9b0eSStefan Hajnoczi { 201c6136ec0SStefano Garzarella VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev); 202fc0b9b0eSStefan Hajnoczi VirtIODevice *vdev = VIRTIO_DEVICE(dev); 203fc0b9b0eSStefan Hajnoczi 204fc0b9b0eSStefan Hajnoczi /* This will stop vhost backend if appropriate. */ 205fc0b9b0eSStefan Hajnoczi vhost_vsock_set_status(vdev, 0); 206fc0b9b0eSStefan Hajnoczi 207c6136ec0SStefano Garzarella vhost_dev_cleanup(&vvc->vhost_dev); 208c6136ec0SStefano Garzarella vhost_vsock_common_unrealize(vdev); 209fc0b9b0eSStefan Hajnoczi } 210fc0b9b0eSStefan Hajnoczi 211fc0b9b0eSStefan Hajnoczi static Property vhost_vsock_properties[] = { 212fc0b9b0eSStefan Hajnoczi DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0), 213fc0b9b0eSStefan Hajnoczi DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd), 214fc0b9b0eSStefan Hajnoczi DEFINE_PROP_END_OF_LIST(), 215fc0b9b0eSStefan Hajnoczi }; 216fc0b9b0eSStefan Hajnoczi 217fc0b9b0eSStefan Hajnoczi static void vhost_vsock_class_init(ObjectClass *klass, void *data) 218fc0b9b0eSStefan Hajnoczi { 219fc0b9b0eSStefan Hajnoczi DeviceClass *dc = DEVICE_CLASS(klass); 220fc0b9b0eSStefan Hajnoczi VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 221fc0b9b0eSStefan Hajnoczi 2224f67d30bSMarc-André Lureau device_class_set_props(dc, vhost_vsock_properties); 223fc0b9b0eSStefan Hajnoczi dc->vmsd = &vmstate_virtio_vhost_vsock; 224fc0b9b0eSStefan Hajnoczi vdc->realize = vhost_vsock_device_realize; 225fc0b9b0eSStefan Hajnoczi vdc->unrealize = vhost_vsock_device_unrealize; 226fc0b9b0eSStefan Hajnoczi vdc->get_features = vhost_vsock_get_features; 227fc0b9b0eSStefan Hajnoczi vdc->get_config = vhost_vsock_get_config; 228fc0b9b0eSStefan Hajnoczi vdc->set_status = vhost_vsock_set_status; 229fc0b9b0eSStefan Hajnoczi } 230fc0b9b0eSStefan Hajnoczi 231fc0b9b0eSStefan Hajnoczi static const TypeInfo vhost_vsock_info = { 232fc0b9b0eSStefan Hajnoczi .name = TYPE_VHOST_VSOCK, 233c6136ec0SStefano Garzarella .parent = TYPE_VHOST_VSOCK_COMMON, 234fc0b9b0eSStefan Hajnoczi .instance_size = sizeof(VHostVSock), 235fc0b9b0eSStefan Hajnoczi .class_init = vhost_vsock_class_init, 236fc0b9b0eSStefan Hajnoczi }; 237fc0b9b0eSStefan Hajnoczi 238fc0b9b0eSStefan Hajnoczi static void vhost_vsock_register_types(void) 239fc0b9b0eSStefan Hajnoczi { 240fc0b9b0eSStefan Hajnoczi type_register_static(&vhost_vsock_info); 241fc0b9b0eSStefan Hajnoczi } 242fc0b9b0eSStefan Hajnoczi 243fc0b9b0eSStefan Hajnoczi type_init(vhost_vsock_register_types) 244