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 int 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 -ENOSYS; 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 ret; 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 132 if (!s->inflight->addr) { 133 ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight); 134 if (ret < 0) { 135 error_report("Error get inflight: %d", -ret); 136 goto err_guest_notifiers; 137 } 138 } 139 140 ret = vhost_dev_set_inflight(&s->dev, s->inflight); 141 if (ret < 0) { 142 error_report("Error set inflight: %d", -ret); 143 goto err_guest_notifiers; 144 } 145 146 ret = vhost_dev_start(&s->dev, vdev); 147 if (ret < 0) { 148 error_report("Error starting vhost: %d", -ret); 149 goto err_guest_notifiers; 150 } 151 152 /* guest_notifier_mask/pending not used yet, so just unmask 153 * everything here. virtio-pci will do the right thing by 154 * enabling/disabling irqfd. 155 */ 156 for (i = 0; i < s->dev.nvqs; i++) { 157 vhost_virtqueue_mask(&s->dev, vdev, i, false); 158 } 159 160 return ret; 161 162 err_guest_notifiers: 163 k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 164 err_host_notifiers: 165 vhost_dev_disable_notifiers(&s->dev, vdev); 166 return ret; 167 } 168 169 static void vhost_user_blk_stop(VirtIODevice *vdev) 170 { 171 VHostUserBlk *s = VHOST_USER_BLK(vdev); 172 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 173 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 174 int ret; 175 176 if (!k->set_guest_notifiers) { 177 return; 178 } 179 180 vhost_dev_stop(&s->dev, vdev); 181 182 ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 183 if (ret < 0) { 184 error_report("vhost guest notifier cleanup failed: %d", ret); 185 return; 186 } 187 188 vhost_dev_disable_notifiers(&s->dev, vdev); 189 } 190 191 static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) 192 { 193 VHostUserBlk *s = VHOST_USER_BLK(vdev); 194 bool should_start = vdev->started; 195 int ret; 196 197 if (!vdev->vm_running) { 198 should_start = false; 199 } 200 201 if (!s->connected) { 202 return; 203 } 204 205 if (s->dev.started == should_start) { 206 return; 207 } 208 209 if (should_start) { 210 ret = vhost_user_blk_start(vdev); 211 if (ret < 0) { 212 error_report("vhost-user-blk: vhost start failed: %s", 213 strerror(-ret)); 214 qemu_chr_fe_disconnect(&s->chardev); 215 } 216 } else { 217 vhost_user_blk_stop(vdev); 218 } 219 220 } 221 222 static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, 223 uint64_t features, 224 Error **errp) 225 { 226 VHostUserBlk *s = VHOST_USER_BLK(vdev); 227 228 /* Turn on pre-defined features */ 229 virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); 230 virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); 231 virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); 232 virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); 233 virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH); 234 virtio_add_feature(&features, VIRTIO_BLK_F_RO); 235 virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD); 236 virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES); 237 238 if (s->config_wce) { 239 virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); 240 } 241 if (s->num_queues > 1) { 242 virtio_add_feature(&features, VIRTIO_BLK_F_MQ); 243 } 244 245 return vhost_get_features(&s->dev, user_feature_bits, features); 246 } 247 248 static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) 249 { 250 VHostUserBlk *s = VHOST_USER_BLK(vdev); 251 int i, ret; 252 253 if (!vdev->start_on_kick) { 254 return; 255 } 256 257 if (!s->connected) { 258 return; 259 } 260 261 if (s->dev.started) { 262 return; 263 } 264 265 /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start 266 * vhost here instead of waiting for .set_status(). 267 */ 268 ret = vhost_user_blk_start(vdev); 269 if (ret < 0) { 270 error_report("vhost-user-blk: vhost start failed: %s", 271 strerror(-ret)); 272 qemu_chr_fe_disconnect(&s->chardev); 273 return; 274 } 275 276 /* Kick right away to begin processing requests already in vring */ 277 for (i = 0; i < s->dev.nvqs; i++) { 278 VirtQueue *kick_vq = virtio_get_queue(vdev, i); 279 280 if (!virtio_queue_get_desc_addr(vdev, i)) { 281 continue; 282 } 283 event_notifier_set(virtio_queue_get_host_notifier(kick_vq)); 284 } 285 } 286 287 static void vhost_user_blk_reset(VirtIODevice *vdev) 288 { 289 VHostUserBlk *s = VHOST_USER_BLK(vdev); 290 291 vhost_dev_free_inflight(s->inflight); 292 } 293 294 static int vhost_user_blk_connect(DeviceState *dev) 295 { 296 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 297 VHostUserBlk *s = VHOST_USER_BLK(vdev); 298 int ret = 0; 299 300 if (s->connected) { 301 return 0; 302 } 303 s->connected = true; 304 305 s->dev.nvqs = s->num_queues; 306 s->dev.vqs = s->vqs; 307 s->dev.vq_index = 0; 308 s->dev.backend_features = 0; 309 310 vhost_dev_set_config_notifier(&s->dev, &blk_ops); 311 312 ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); 313 if (ret < 0) { 314 error_report("vhost-user-blk: vhost initialization failed: %s", 315 strerror(-ret)); 316 return ret; 317 } 318 319 /* restore vhost state */ 320 if (vdev->started) { 321 ret = vhost_user_blk_start(vdev); 322 if (ret < 0) { 323 error_report("vhost-user-blk: vhost start failed: %s", 324 strerror(-ret)); 325 return ret; 326 } 327 } 328 329 return 0; 330 } 331 332 static void vhost_user_blk_disconnect(DeviceState *dev) 333 { 334 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 335 VHostUserBlk *s = VHOST_USER_BLK(vdev); 336 337 if (!s->connected) { 338 return; 339 } 340 s->connected = false; 341 342 if (s->dev.started) { 343 vhost_user_blk_stop(vdev); 344 } 345 346 vhost_dev_cleanup(&s->dev); 347 } 348 349 static gboolean vhost_user_blk_watch(GIOChannel *chan, GIOCondition cond, 350 void *opaque) 351 { 352 DeviceState *dev = opaque; 353 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 354 VHostUserBlk *s = VHOST_USER_BLK(vdev); 355 356 qemu_chr_fe_disconnect(&s->chardev); 357 358 return true; 359 } 360 361 static void vhost_user_blk_event(void *opaque, int event) 362 { 363 DeviceState *dev = opaque; 364 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 365 VHostUserBlk *s = VHOST_USER_BLK(vdev); 366 367 switch (event) { 368 case CHR_EVENT_OPENED: 369 if (vhost_user_blk_connect(dev) < 0) { 370 qemu_chr_fe_disconnect(&s->chardev); 371 return; 372 } 373 s->watch = qemu_chr_fe_add_watch(&s->chardev, G_IO_HUP, 374 vhost_user_blk_watch, dev); 375 break; 376 case CHR_EVENT_CLOSED: 377 vhost_user_blk_disconnect(dev); 378 if (s->watch) { 379 g_source_remove(s->watch); 380 s->watch = 0; 381 } 382 break; 383 } 384 } 385 386 static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) 387 { 388 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 389 VHostUserBlk *s = VHOST_USER_BLK(vdev); 390 Error *err = NULL; 391 int i, ret; 392 393 if (!s->chardev.chr) { 394 error_setg(errp, "vhost-user-blk: chardev is mandatory"); 395 return; 396 } 397 398 if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) { 399 error_setg(errp, "vhost-user-blk: invalid number of IO queues"); 400 return; 401 } 402 403 if (!s->queue_size) { 404 error_setg(errp, "vhost-user-blk: queue size must be non-zero"); 405 return; 406 } 407 408 if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) { 409 return; 410 } 411 412 virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, 413 sizeof(struct virtio_blk_config)); 414 415 for (i = 0; i < s->num_queues; i++) { 416 virtio_add_queue(vdev, s->queue_size, 417 vhost_user_blk_handle_output); 418 } 419 420 s->inflight = g_new0(struct vhost_inflight, 1); 421 s->vqs = g_new(struct vhost_virtqueue, s->num_queues); 422 s->watch = 0; 423 s->connected = false; 424 425 qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event, 426 NULL, (void *)dev, NULL, true); 427 428 reconnect: 429 if (qemu_chr_fe_wait_connected(&s->chardev, &err) < 0) { 430 error_report_err(err); 431 goto virtio_err; 432 } 433 434 /* check whether vhost_user_blk_connect() failed or not */ 435 if (!s->connected) { 436 goto reconnect; 437 } 438 439 ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg, 440 sizeof(struct virtio_blk_config)); 441 if (ret < 0) { 442 error_report("vhost-user-blk: get block config failed"); 443 goto reconnect; 444 } 445 446 if (s->blkcfg.num_queues != s->num_queues) { 447 s->blkcfg.num_queues = s->num_queues; 448 } 449 450 return; 451 452 virtio_err: 453 g_free(s->vqs); 454 g_free(s->inflight); 455 virtio_cleanup(vdev); 456 vhost_user_cleanup(&s->vhost_user); 457 } 458 459 static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) 460 { 461 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 462 VHostUserBlk *s = VHOST_USER_BLK(dev); 463 464 virtio_set_status(vdev, 0); 465 qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, NULL, 466 NULL, NULL, NULL, false); 467 vhost_dev_cleanup(&s->dev); 468 vhost_dev_free_inflight(s->inflight); 469 g_free(s->vqs); 470 g_free(s->inflight); 471 virtio_cleanup(vdev); 472 vhost_user_cleanup(&s->vhost_user); 473 } 474 475 static void vhost_user_blk_instance_init(Object *obj) 476 { 477 VHostUserBlk *s = VHOST_USER_BLK(obj); 478 479 device_add_bootindex_property(obj, &s->bootindex, "bootindex", 480 "/disk@0,0", DEVICE(obj), NULL); 481 } 482 483 static const VMStateDescription vmstate_vhost_user_blk = { 484 .name = "vhost-user-blk", 485 .minimum_version_id = 1, 486 .version_id = 1, 487 .fields = (VMStateField[]) { 488 VMSTATE_VIRTIO_DEVICE, 489 VMSTATE_END_OF_LIST() 490 }, 491 }; 492 493 static Property vhost_user_blk_properties[] = { 494 DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev), 495 DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, 1), 496 DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128), 497 DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true), 498 DEFINE_PROP_END_OF_LIST(), 499 }; 500 501 static void vhost_user_blk_class_init(ObjectClass *klass, void *data) 502 { 503 DeviceClass *dc = DEVICE_CLASS(klass); 504 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 505 506 dc->props = vhost_user_blk_properties; 507 dc->vmsd = &vmstate_vhost_user_blk; 508 set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); 509 vdc->realize = vhost_user_blk_device_realize; 510 vdc->unrealize = vhost_user_blk_device_unrealize; 511 vdc->get_config = vhost_user_blk_update_config; 512 vdc->set_config = vhost_user_blk_set_config; 513 vdc->get_features = vhost_user_blk_get_features; 514 vdc->set_status = vhost_user_blk_set_status; 515 vdc->reset = vhost_user_blk_reset; 516 } 517 518 static const TypeInfo vhost_user_blk_info = { 519 .name = TYPE_VHOST_USER_BLK, 520 .parent = TYPE_VIRTIO_DEVICE, 521 .instance_size = sizeof(VHostUserBlk), 522 .instance_init = vhost_user_blk_instance_init, 523 .class_init = vhost_user_blk_class_init, 524 }; 525 526 static void virtio_register_types(void) 527 { 528 type_register_static(&vhost_user_blk_info); 529 } 530 531 type_init(virtio_register_types) 532