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 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; 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 } 167 168 static void vhost_user_blk_stop(VirtIODevice *vdev) 169 { 170 VHostUserBlk *s = VHOST_USER_BLK(vdev); 171 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 172 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 173 int ret; 174 175 if (!k->set_guest_notifiers) { 176 return; 177 } 178 179 vhost_dev_stop(&s->dev, vdev); 180 181 ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 182 if (ret < 0) { 183 error_report("vhost guest notifier cleanup failed: %d", ret); 184 return; 185 } 186 187 vhost_dev_disable_notifiers(&s->dev, vdev); 188 } 189 190 static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) 191 { 192 VHostUserBlk *s = VHOST_USER_BLK(vdev); 193 bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; 194 195 if (!vdev->vm_running) { 196 should_start = false; 197 } 198 199 if (s->dev.started == should_start) { 200 return; 201 } 202 203 if (should_start) { 204 vhost_user_blk_start(vdev); 205 } else { 206 vhost_user_blk_stop(vdev); 207 } 208 209 } 210 211 static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, 212 uint64_t features, 213 Error **errp) 214 { 215 VHostUserBlk *s = VHOST_USER_BLK(vdev); 216 217 /* Turn on pre-defined features */ 218 virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); 219 virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); 220 virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); 221 virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); 222 virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH); 223 virtio_add_feature(&features, VIRTIO_BLK_F_RO); 224 virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD); 225 virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES); 226 227 if (s->config_wce) { 228 virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); 229 } 230 if (s->num_queues > 1) { 231 virtio_add_feature(&features, VIRTIO_BLK_F_MQ); 232 } 233 234 return vhost_get_features(&s->dev, user_feature_bits, features); 235 } 236 237 static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) 238 { 239 VHostUserBlk *s = VHOST_USER_BLK(vdev); 240 int i; 241 242 if (!(virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1) && 243 !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1))) { 244 return; 245 } 246 247 if (s->dev.started) { 248 return; 249 } 250 251 /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start 252 * vhost here instead of waiting for .set_status(). 253 */ 254 vhost_user_blk_start(vdev); 255 256 /* Kick right away to begin processing requests already in vring */ 257 for (i = 0; i < s->dev.nvqs; i++) { 258 VirtQueue *kick_vq = virtio_get_queue(vdev, i); 259 260 if (!virtio_queue_get_desc_addr(vdev, i)) { 261 continue; 262 } 263 event_notifier_set(virtio_queue_get_host_notifier(kick_vq)); 264 } 265 } 266 267 static void vhost_user_blk_reset(VirtIODevice *vdev) 268 { 269 VHostUserBlk *s = VHOST_USER_BLK(vdev); 270 271 vhost_dev_free_inflight(s->inflight); 272 } 273 274 static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) 275 { 276 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 277 VHostUserBlk *s = VHOST_USER_BLK(vdev); 278 struct vhost_virtqueue *vqs = NULL; 279 int i, ret; 280 281 if (!s->chardev.chr) { 282 error_setg(errp, "vhost-user-blk: chardev is mandatory"); 283 return; 284 } 285 286 if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) { 287 error_setg(errp, "vhost-user-blk: invalid number of IO queues"); 288 return; 289 } 290 291 if (!s->queue_size) { 292 error_setg(errp, "vhost-user-blk: queue size must be non-zero"); 293 return; 294 } 295 296 if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) { 297 return; 298 } 299 300 virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, 301 sizeof(struct virtio_blk_config)); 302 303 for (i = 0; i < s->num_queues; i++) { 304 virtio_add_queue(vdev, s->queue_size, 305 vhost_user_blk_handle_output); 306 } 307 308 s->inflight = g_new0(struct vhost_inflight, 1); 309 310 s->dev.nvqs = s->num_queues; 311 s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs); 312 s->dev.vq_index = 0; 313 s->dev.backend_features = 0; 314 vqs = s->dev.vqs; 315 316 vhost_dev_set_config_notifier(&s->dev, &blk_ops); 317 318 ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); 319 if (ret < 0) { 320 error_setg(errp, "vhost-user-blk: vhost initialization failed: %s", 321 strerror(-ret)); 322 goto virtio_err; 323 } 324 325 ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg, 326 sizeof(struct virtio_blk_config)); 327 if (ret < 0) { 328 error_setg(errp, "vhost-user-blk: get block config failed"); 329 goto vhost_err; 330 } 331 332 if (s->blkcfg.num_queues != s->num_queues) { 333 s->blkcfg.num_queues = s->num_queues; 334 } 335 336 return; 337 338 vhost_err: 339 vhost_dev_cleanup(&s->dev); 340 virtio_err: 341 g_free(vqs); 342 g_free(s->inflight); 343 virtio_cleanup(vdev); 344 vhost_user_cleanup(&s->vhost_user); 345 } 346 347 static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) 348 { 349 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 350 VHostUserBlk *s = VHOST_USER_BLK(dev); 351 struct vhost_virtqueue *vqs = s->dev.vqs; 352 353 vhost_user_blk_set_status(vdev, 0); 354 vhost_dev_cleanup(&s->dev); 355 vhost_dev_free_inflight(s->inflight); 356 g_free(vqs); 357 g_free(s->inflight); 358 virtio_cleanup(vdev); 359 vhost_user_cleanup(&s->vhost_user); 360 } 361 362 static void vhost_user_blk_instance_init(Object *obj) 363 { 364 VHostUserBlk *s = VHOST_USER_BLK(obj); 365 366 device_add_bootindex_property(obj, &s->bootindex, "bootindex", 367 "/disk@0,0", DEVICE(obj), NULL); 368 } 369 370 static const VMStateDescription vmstate_vhost_user_blk = { 371 .name = "vhost-user-blk", 372 .minimum_version_id = 1, 373 .version_id = 1, 374 .fields = (VMStateField[]) { 375 VMSTATE_VIRTIO_DEVICE, 376 VMSTATE_END_OF_LIST() 377 }, 378 }; 379 380 static Property vhost_user_blk_properties[] = { 381 DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev), 382 DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, 1), 383 DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128), 384 DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true), 385 DEFINE_PROP_END_OF_LIST(), 386 }; 387 388 static void vhost_user_blk_class_init(ObjectClass *klass, void *data) 389 { 390 DeviceClass *dc = DEVICE_CLASS(klass); 391 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 392 393 dc->props = vhost_user_blk_properties; 394 dc->vmsd = &vmstate_vhost_user_blk; 395 set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); 396 vdc->realize = vhost_user_blk_device_realize; 397 vdc->unrealize = vhost_user_blk_device_unrealize; 398 vdc->get_config = vhost_user_blk_update_config; 399 vdc->set_config = vhost_user_blk_set_config; 400 vdc->get_features = vhost_user_blk_get_features; 401 vdc->set_status = vhost_user_blk_set_status; 402 vdc->reset = vhost_user_blk_reset; 403 } 404 405 static const TypeInfo vhost_user_blk_info = { 406 .name = TYPE_VHOST_USER_BLK, 407 .parent = TYPE_VIRTIO_DEVICE, 408 .instance_size = sizeof(VHostUserBlk), 409 .instance_init = vhost_user_blk_instance_init, 410 .class_init = vhost_user_blk_class_init, 411 }; 412 413 static void virtio_register_types(void) 414 { 415 type_register_static(&vhost_user_blk_info); 416 } 417 418 type_init(virtio_register_types) 419