vhost-vsock-common.c (8316B)
1/* 2 * Parent class for vhost-vsock devices 3 * 4 * Copyright 2015-2020 Red Hat, Inc. 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or 7 * (at your option) any later version. See the COPYING file in the 8 * top-level directory. 9 */ 10 11#include "qemu/osdep.h" 12#include "standard-headers/linux/virtio_vsock.h" 13#include "qapi/error.h" 14#include "hw/virtio/virtio-access.h" 15#include "qemu/error-report.h" 16#include "hw/qdev-properties.h" 17#include "hw/virtio/vhost-vsock.h" 18#include "qemu/iov.h" 19#include "monitor/monitor.h" 20 21const int feature_bits[] = { 22 VIRTIO_VSOCK_F_SEQPACKET, 23 VHOST_INVALID_FEATURE_BIT 24}; 25 26uint64_t vhost_vsock_common_get_features(VirtIODevice *vdev, uint64_t features, 27 Error **errp) 28{ 29 VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 30 31 if (vvc->seqpacket != ON_OFF_AUTO_OFF) { 32 virtio_add_feature(&features, VIRTIO_VSOCK_F_SEQPACKET); 33 } 34 35 features = vhost_get_features(&vvc->vhost_dev, feature_bits, features); 36 37 if (vvc->seqpacket == ON_OFF_AUTO_ON && 38 !virtio_has_feature(features, VIRTIO_VSOCK_F_SEQPACKET)) { 39 error_setg(errp, "vhost-vsock backend doesn't support seqpacket"); 40 } 41 42 return features; 43} 44 45int vhost_vsock_common_start(VirtIODevice *vdev) 46{ 47 VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 48 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 49 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 50 int ret; 51 int i; 52 53 if (!k->set_guest_notifiers) { 54 error_report("binding does not support guest notifiers"); 55 return -ENOSYS; 56 } 57 58 ret = vhost_dev_enable_notifiers(&vvc->vhost_dev, vdev); 59 if (ret < 0) { 60 error_report("Error enabling host notifiers: %d", -ret); 61 return ret; 62 } 63 64 ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, true); 65 if (ret < 0) { 66 error_report("Error binding guest notifier: %d", -ret); 67 goto err_host_notifiers; 68 } 69 70 vvc->vhost_dev.acked_features = vdev->guest_features; 71 ret = vhost_dev_start(&vvc->vhost_dev, vdev); 72 if (ret < 0) { 73 error_report("Error starting vhost: %d", -ret); 74 goto err_guest_notifiers; 75 } 76 77 /* 78 * guest_notifier_mask/pending not used yet, so just unmask 79 * everything here. virtio-pci will do the right thing by 80 * enabling/disabling irqfd. 81 */ 82 for (i = 0; i < vvc->vhost_dev.nvqs; i++) { 83 vhost_virtqueue_mask(&vvc->vhost_dev, vdev, i, false); 84 } 85 86 return 0; 87 88err_guest_notifiers: 89 k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false); 90err_host_notifiers: 91 vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev); 92 return ret; 93} 94 95void vhost_vsock_common_stop(VirtIODevice *vdev) 96{ 97 VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 98 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 99 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 100 int ret; 101 102 if (!k->set_guest_notifiers) { 103 return; 104 } 105 106 vhost_dev_stop(&vvc->vhost_dev, vdev); 107 108 ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false); 109 if (ret < 0) { 110 error_report("vhost guest notifier cleanup failed: %d", ret); 111 return; 112 } 113 114 vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev); 115} 116 117 118static void vhost_vsock_common_handle_output(VirtIODevice *vdev, VirtQueue *vq) 119{ 120 /* Do nothing */ 121} 122 123static void vhost_vsock_common_guest_notifier_mask(VirtIODevice *vdev, int idx, 124 bool mask) 125{ 126 VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 127 128 vhost_virtqueue_mask(&vvc->vhost_dev, vdev, idx, mask); 129} 130 131static bool vhost_vsock_common_guest_notifier_pending(VirtIODevice *vdev, 132 int idx) 133{ 134 VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 135 136 return vhost_virtqueue_pending(&vvc->vhost_dev, idx); 137} 138 139static void vhost_vsock_common_send_transport_reset(VHostVSockCommon *vvc) 140{ 141 VirtQueueElement *elem; 142 VirtQueue *vq = vvc->event_vq; 143 struct virtio_vsock_event event = { 144 .id = cpu_to_le32(VIRTIO_VSOCK_EVENT_TRANSPORT_RESET), 145 }; 146 147 elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 148 if (!elem) { 149 error_report("vhost-vsock missed transport reset event"); 150 return; 151 } 152 153 if (elem->out_num) { 154 error_report("invalid vhost-vsock event virtqueue element with " 155 "out buffers"); 156 goto out; 157 } 158 159 if (iov_from_buf(elem->in_sg, elem->in_num, 0, 160 &event, sizeof(event)) != sizeof(event)) { 161 error_report("vhost-vsock event virtqueue element is too short"); 162 goto out; 163 } 164 165 virtqueue_push(vq, elem, sizeof(event)); 166 virtio_notify(VIRTIO_DEVICE(vvc), vq); 167 168out: 169 g_free(elem); 170} 171 172static void vhost_vsock_common_post_load_timer_cleanup(VHostVSockCommon *vvc) 173{ 174 if (!vvc->post_load_timer) { 175 return; 176 } 177 178 timer_free(vvc->post_load_timer); 179 vvc->post_load_timer = NULL; 180} 181 182static void vhost_vsock_common_post_load_timer_cb(void *opaque) 183{ 184 VHostVSockCommon *vvc = opaque; 185 186 vhost_vsock_common_post_load_timer_cleanup(vvc); 187 vhost_vsock_common_send_transport_reset(vvc); 188} 189 190int vhost_vsock_common_pre_save(void *opaque) 191{ 192 VHostVSockCommon *vvc = opaque; 193 194 /* 195 * At this point, backend must be stopped, otherwise 196 * it might keep writing to memory. 197 */ 198 assert(!vvc->vhost_dev.started); 199 200 return 0; 201} 202 203int vhost_vsock_common_post_load(void *opaque, int version_id) 204{ 205 VHostVSockCommon *vvc = opaque; 206 VirtIODevice *vdev = VIRTIO_DEVICE(vvc); 207 208 if (virtio_queue_get_addr(vdev, 2)) { 209 /* 210 * Defer transport reset event to a vm clock timer so that virtqueue 211 * changes happen after migration has completed. 212 */ 213 assert(!vvc->post_load_timer); 214 vvc->post_load_timer = 215 timer_new_ns(QEMU_CLOCK_VIRTUAL, 216 vhost_vsock_common_post_load_timer_cb, 217 vvc); 218 timer_mod(vvc->post_load_timer, 1); 219 } 220 return 0; 221} 222 223void vhost_vsock_common_realize(VirtIODevice *vdev, const char *name) 224{ 225 VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 226 227 virtio_init(vdev, name, VIRTIO_ID_VSOCK, 228 sizeof(struct virtio_vsock_config)); 229 230 /* Receive and transmit queues belong to vhost */ 231 vvc->recv_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, 232 vhost_vsock_common_handle_output); 233 vvc->trans_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, 234 vhost_vsock_common_handle_output); 235 236 /* The event queue belongs to QEMU */ 237 vvc->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, 238 vhost_vsock_common_handle_output); 239 240 vvc->vhost_dev.nvqs = ARRAY_SIZE(vvc->vhost_vqs); 241 vvc->vhost_dev.vqs = vvc->vhost_vqs; 242 243 vvc->post_load_timer = NULL; 244} 245 246void vhost_vsock_common_unrealize(VirtIODevice *vdev) 247{ 248 VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); 249 250 vhost_vsock_common_post_load_timer_cleanup(vvc); 251 252 virtio_delete_queue(vvc->recv_vq); 253 virtio_delete_queue(vvc->trans_vq); 254 virtio_delete_queue(vvc->event_vq); 255 virtio_cleanup(vdev); 256} 257 258static Property vhost_vsock_common_properties[] = { 259 DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSockCommon, seqpacket, 260 ON_OFF_AUTO_AUTO), 261 DEFINE_PROP_END_OF_LIST(), 262}; 263 264static void vhost_vsock_common_class_init(ObjectClass *klass, void *data) 265{ 266 DeviceClass *dc = DEVICE_CLASS(klass); 267 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 268 269 device_class_set_props(dc, vhost_vsock_common_properties); 270 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 271 vdc->guest_notifier_mask = vhost_vsock_common_guest_notifier_mask; 272 vdc->guest_notifier_pending = vhost_vsock_common_guest_notifier_pending; 273} 274 275static const TypeInfo vhost_vsock_common_info = { 276 .name = TYPE_VHOST_VSOCK_COMMON, 277 .parent = TYPE_VIRTIO_DEVICE, 278 .instance_size = sizeof(VHostVSockCommon), 279 .class_init = vhost_vsock_common_class_init, 280 .abstract = true, 281}; 282 283static void vhost_vsock_common_register_types(void) 284{ 285 type_register_static(&vhost_vsock_common_info); 286} 287 288type_init(vhost_vsock_common_register_types)