1 /* 2 * vhost-user-blk host device 3 * 4 * Copyright(C) 2017 Intel Corporation. 5 * 6 * Authors: 7 * Changpeng Liu <changpeng.liu@intel.com> 8 * 9 * Largely based on the "vhost-user-scsi.c" and "vhost-scsi.c" implemented by: 10 * Felipe Franciosi <felipe@nutanix.com> 11 * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> 12 * Nicholas Bellinger <nab@risingtidesystems.com> 13 * 14 * This work is licensed under the terms of the GNU LGPL, version 2 or later. 15 * See the COPYING.LIB file in the top-level directory. 16 * 17 */ 18 19 #include "qemu/osdep.h" 20 #include "qapi/error.h" 21 #include "qemu/error-report.h" 22 #include "qemu/cutils.h" 23 #include "qom/object.h" 24 #include "hw/qdev-core.h" 25 #include "hw/virtio/vhost.h" 26 #include "hw/virtio/vhost-user-blk.h" 27 #include "hw/virtio/virtio.h" 28 #include "hw/virtio/virtio-bus.h" 29 #include "hw/virtio/virtio-access.h" 30 31 static const int user_feature_bits[] = { 32 VIRTIO_BLK_F_SIZE_MAX, 33 VIRTIO_BLK_F_SEG_MAX, 34 VIRTIO_BLK_F_GEOMETRY, 35 VIRTIO_BLK_F_BLK_SIZE, 36 VIRTIO_BLK_F_TOPOLOGY, 37 VIRTIO_BLK_F_MQ, 38 VIRTIO_BLK_F_RO, 39 VIRTIO_BLK_F_FLUSH, 40 VIRTIO_BLK_F_CONFIG_WCE, 41 VIRTIO_BLK_F_DISCARD, 42 VIRTIO_BLK_F_WRITE_ZEROES, 43 VIRTIO_F_VERSION_1, 44 VIRTIO_RING_F_INDIRECT_DESC, 45 VIRTIO_RING_F_EVENT_IDX, 46 VIRTIO_F_NOTIFY_ON_EMPTY, 47 VHOST_INVALID_FEATURE_BIT 48 }; 49 50 static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config) 51 { 52 VHostUserBlk *s = VHOST_USER_BLK(vdev); 53 54 memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config)); 55 } 56 57 static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config) 58 { 59 VHostUserBlk *s = VHOST_USER_BLK(vdev); 60 struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config; 61 int ret; 62 63 if (blkcfg->wce == s->blkcfg.wce) { 64 return; 65 } 66 67 ret = vhost_dev_set_config(&s->dev, &blkcfg->wce, 68 offsetof(struct virtio_blk_config, wce), 69 sizeof(blkcfg->wce), 70 VHOST_SET_CONFIG_TYPE_MASTER); 71 if (ret) { 72 error_report("set device config space failed"); 73 return; 74 } 75 76 s->blkcfg.wce = blkcfg->wce; 77 } 78 79 static int vhost_user_blk_handle_config_change(struct vhost_dev *dev) 80 { 81 int ret; 82 struct virtio_blk_config blkcfg; 83 VHostUserBlk *s = VHOST_USER_BLK(dev->vdev); 84 85 ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg, 86 sizeof(struct virtio_blk_config)); 87 if (ret < 0) { 88 error_report("get config space failed"); 89 return -1; 90 } 91 92 /* valid for resize only */ 93 if (blkcfg.capacity != s->blkcfg.capacity) { 94 s->blkcfg.capacity = blkcfg.capacity; 95 memcpy(dev->vdev->config, &s->blkcfg, sizeof(struct virtio_blk_config)); 96 virtio_notify_config(dev->vdev); 97 } 98 99 return 0; 100 } 101 102 const VhostDevConfigOps blk_ops = { 103 .vhost_dev_config_notifier = vhost_user_blk_handle_config_change, 104 }; 105 106 static void vhost_user_blk_start(VirtIODevice *vdev) 107 { 108 VHostUserBlk *s = VHOST_USER_BLK(vdev); 109 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 110 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 111 int i, ret; 112 113 if (!k->set_guest_notifiers) { 114 error_report("binding does not support guest notifiers"); 115 return; 116 } 117 118 ret = vhost_dev_enable_notifiers(&s->dev, vdev); 119 if (ret < 0) { 120 error_report("Error enabling host notifiers: %d", -ret); 121 return; 122 } 123 124 ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true); 125 if (ret < 0) { 126 error_report("Error binding guest notifier: %d", -ret); 127 goto err_host_notifiers; 128 } 129 130 s->dev.acked_features = vdev->guest_features; 131 ret = vhost_dev_start(&s->dev, vdev); 132 if (ret < 0) { 133 error_report("Error starting vhost: %d", -ret); 134 goto err_guest_notifiers; 135 } 136 137 /* guest_notifier_mask/pending not used yet, so just unmask 138 * everything here. virtio-pci will do the right thing by 139 * enabling/disabling irqfd. 140 */ 141 for (i = 0; i < s->dev.nvqs; i++) { 142 vhost_virtqueue_mask(&s->dev, vdev, i, false); 143 } 144 145 return; 146 147 err_guest_notifiers: 148 k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 149 err_host_notifiers: 150 vhost_dev_disable_notifiers(&s->dev, vdev); 151 } 152 153 static void vhost_user_blk_stop(VirtIODevice *vdev) 154 { 155 VHostUserBlk *s = VHOST_USER_BLK(vdev); 156 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 157 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 158 int ret; 159 160 if (!k->set_guest_notifiers) { 161 return; 162 } 163 164 vhost_dev_stop(&s->dev, vdev); 165 166 ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 167 if (ret < 0) { 168 error_report("vhost guest notifier cleanup failed: %d", ret); 169 return; 170 } 171 172 vhost_dev_disable_notifiers(&s->dev, vdev); 173 } 174 175 static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) 176 { 177 VHostUserBlk *s = VHOST_USER_BLK(vdev); 178 bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; 179 180 if (!vdev->vm_running) { 181 should_start = false; 182 } 183 184 if (s->dev.started == should_start) { 185 return; 186 } 187 188 if (should_start) { 189 vhost_user_blk_start(vdev); 190 } else { 191 vhost_user_blk_stop(vdev); 192 } 193 194 } 195 196 static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, 197 uint64_t features, 198 Error **errp) 199 { 200 VHostUserBlk *s = VHOST_USER_BLK(vdev); 201 202 /* Turn on pre-defined features */ 203 virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); 204 virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); 205 virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); 206 virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); 207 virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH); 208 virtio_add_feature(&features, VIRTIO_BLK_F_RO); 209 virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD); 210 virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES); 211 212 if (s->config_wce) { 213 virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); 214 } 215 if (s->num_queues > 1) { 216 virtio_add_feature(&features, VIRTIO_BLK_F_MQ); 217 } 218 219 return vhost_get_features(&s->dev, user_feature_bits, features); 220 } 221 222 static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) 223 { 224 VHostUserBlk *s = VHOST_USER_BLK(vdev); 225 int i; 226 227 if (!(virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1) && 228 !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1))) { 229 return; 230 } 231 232 if (s->dev.started) { 233 return; 234 } 235 236 /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start 237 * vhost here instead of waiting for .set_status(). 238 */ 239 vhost_user_blk_start(vdev); 240 241 /* Kick right away to begin processing requests already in vring */ 242 for (i = 0; i < s->dev.nvqs; i++) { 243 VirtQueue *kick_vq = virtio_get_queue(vdev, i); 244 245 if (!virtio_queue_get_desc_addr(vdev, i)) { 246 continue; 247 } 248 event_notifier_set(virtio_queue_get_host_notifier(kick_vq)); 249 } 250 } 251 252 static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) 253 { 254 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 255 VHostUserBlk *s = VHOST_USER_BLK(vdev); 256 VhostUserState *user; 257 struct vhost_virtqueue *vqs = NULL; 258 int i, ret; 259 260 if (!s->chardev.chr) { 261 error_setg(errp, "vhost-user-blk: chardev is mandatory"); 262 return; 263 } 264 265 if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) { 266 error_setg(errp, "vhost-user-blk: invalid number of IO queues"); 267 return; 268 } 269 270 if (!s->queue_size) { 271 error_setg(errp, "vhost-user-blk: queue size must be non-zero"); 272 return; 273 } 274 275 user = vhost_user_init(); 276 if (!user) { 277 error_setg(errp, "vhost-user-blk: failed to init vhost_user"); 278 return; 279 } 280 281 user->chr = &s->chardev; 282 s->vhost_user = user; 283 284 virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, 285 sizeof(struct virtio_blk_config)); 286 287 for (i = 0; i < s->num_queues; i++) { 288 virtio_add_queue(vdev, s->queue_size, 289 vhost_user_blk_handle_output); 290 } 291 292 s->dev.nvqs = s->num_queues; 293 s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs); 294 s->dev.vq_index = 0; 295 s->dev.backend_features = 0; 296 vqs = s->dev.vqs; 297 298 vhost_dev_set_config_notifier(&s->dev, &blk_ops); 299 300 ret = vhost_dev_init(&s->dev, s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); 301 if (ret < 0) { 302 error_setg(errp, "vhost-user-blk: vhost initialization failed: %s", 303 strerror(-ret)); 304 goto virtio_err; 305 } 306 307 ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg, 308 sizeof(struct virtio_blk_config)); 309 if (ret < 0) { 310 error_setg(errp, "vhost-user-blk: get block config failed"); 311 goto vhost_err; 312 } 313 314 if (s->blkcfg.num_queues != s->num_queues) { 315 s->blkcfg.num_queues = s->num_queues; 316 } 317 318 return; 319 320 vhost_err: 321 vhost_dev_cleanup(&s->dev); 322 virtio_err: 323 g_free(vqs); 324 virtio_cleanup(vdev); 325 326 vhost_user_cleanup(user); 327 g_free(user); 328 s->vhost_user = NULL; 329 } 330 331 static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) 332 { 333 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 334 VHostUserBlk *s = VHOST_USER_BLK(dev); 335 struct vhost_virtqueue *vqs = s->dev.vqs; 336 337 vhost_user_blk_set_status(vdev, 0); 338 vhost_dev_cleanup(&s->dev); 339 g_free(vqs); 340 virtio_cleanup(vdev); 341 342 if (s->vhost_user) { 343 vhost_user_cleanup(s->vhost_user); 344 g_free(s->vhost_user); 345 s->vhost_user = NULL; 346 } 347 } 348 349 static void vhost_user_blk_instance_init(Object *obj) 350 { 351 VHostUserBlk *s = VHOST_USER_BLK(obj); 352 353 device_add_bootindex_property(obj, &s->bootindex, "bootindex", 354 "/disk@0,0", DEVICE(obj), NULL); 355 } 356 357 static const VMStateDescription vmstate_vhost_user_blk = { 358 .name = "vhost-user-blk", 359 .minimum_version_id = 1, 360 .version_id = 1, 361 .fields = (VMStateField[]) { 362 VMSTATE_VIRTIO_DEVICE, 363 VMSTATE_END_OF_LIST() 364 }, 365 }; 366 367 static Property vhost_user_blk_properties[] = { 368 DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev), 369 DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, 1), 370 DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128), 371 DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true), 372 DEFINE_PROP_END_OF_LIST(), 373 }; 374 375 static void vhost_user_blk_class_init(ObjectClass *klass, void *data) 376 { 377 DeviceClass *dc = DEVICE_CLASS(klass); 378 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 379 380 dc->props = vhost_user_blk_properties; 381 dc->vmsd = &vmstate_vhost_user_blk; 382 set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); 383 vdc->realize = vhost_user_blk_device_realize; 384 vdc->unrealize = vhost_user_blk_device_unrealize; 385 vdc->get_config = vhost_user_blk_update_config; 386 vdc->set_config = vhost_user_blk_set_config; 387 vdc->get_features = vhost_user_blk_get_features; 388 vdc->set_status = vhost_user_blk_set_status; 389 } 390 391 static const TypeInfo vhost_user_blk_info = { 392 .name = TYPE_VHOST_USER_BLK, 393 .parent = TYPE_VIRTIO_DEVICE, 394 .instance_size = sizeof(VHostUserBlk), 395 .instance_init = vhost_user_blk_instance_init, 396 .class_init = vhost_user_blk_class_init, 397 }; 398 399 static void virtio_register_types(void) 400 { 401 type_register_static(&vhost_user_blk_info); 402 } 403 404 type_init(virtio_register_types) 405