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