1416b2c2dSAsias He #include "kvm/virtio-blk.h" 2b30d05adSPekka Enberg 331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 45a24a9f2SPekka Enberg #include "kvm/disk-image.h" 548427891SJean-Philippe Brucker #include "kvm/iovec.h" 64ef0f4d6SPekka Enberg #include "kvm/mutex.h" 7fe99fd4eSPekka Enberg #include "kvm/util.h" 88b1ff07eSPekka Enberg #include "kvm/kvm.h" 9b30d05adSPekka Enberg #include "kvm/pci.h" 10fb0957f2SSasha Levin #include "kvm/threadpool.h" 11ec75b82fSSasha Levin #include "kvm/ioeventfd.h" 12404d164bSSasha Levin #include "kvm/guest_compat.h" 13427948d5SSasha Levin #include "kvm/virtio-pci.h" 14f41a132bSSasha Levin #include "kvm/virtio.h" 15b30d05adSPekka Enberg 1620c64ecaSPekka Enberg #include <linux/virtio_ring.h> 1720c64ecaSPekka Enberg #include <linux/virtio_blk.h> 18427948d5SSasha Levin #include <linux/kernel.h> 19ebe9ac19SSasha Levin #include <linux/list.h> 203fdf659dSSasha Levin #include <linux/types.h> 210528c2a7SPekka Enberg #include <pthread.h> 224155ba8cSPekka Enberg 234749e795SSasha Levin #define VIRTIO_BLK_MAX_DEV 4 2410eca11dSPekka Enberg 253d7831a1SAsias He /* 263d7831a1SAsias He * the header and status consume too entries 273d7831a1SAsias He */ 283d7831a1SAsias He #define DISK_SEG_MAX (VIRTIO_BLK_QUEUE_SIZE - 2) 294059ad8bSAsias He #define VIRTIO_BLK_QUEUE_SIZE 256 30f41a132bSSasha Levin #define NUM_VIRT_QUEUES 1 3110eca11dSPekka Enberg 328b52f877SSasha Levin struct blk_dev_req { 334749e795SSasha Levin struct virt_queue *vq; 34fe2a70d1SSasha Levin struct blk_dev *bdev; 3569971b13SSasha Levin struct iovec iov[VIRTIO_BLK_QUEUE_SIZE]; 3669971b13SSasha Levin u16 out, in, head; 3748427891SJean-Philippe Brucker u8 *status; 388b52f877SSasha Levin struct kvm *kvm; 394749e795SSasha Levin }; 404749e795SSasha Levin 41fe2a70d1SSasha Levin struct blk_dev { 42d3476f7dSSasha Levin struct mutex mutex; 438b52f877SSasha Levin 44ebe9ac19SSasha Levin struct list_head list; 450528c2a7SPekka Enberg 4602eca50cSAsias He struct virtio_device vdev; 4740ce993fSPekka Enberg struct virtio_blk_config blk_config; 48867b15ccSJean-Philippe Brucker u64 capacity; 4938605e1cSSasha Levin struct disk_image *disk; 5010eca11dSPekka Enberg 5145e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES]; 528b52f877SSasha Levin struct blk_dev_req reqs[VIRTIO_BLK_QUEUE_SIZE]; 535ac1178bSAsias He 545ac1178bSAsias He pthread_t io_thread; 555ac1178bSAsias He int io_efd; 565ac1178bSAsias He 575ac1178bSAsias He struct kvm *kvm; 58fbc2fbf9SPekka Enberg }; 59fbc2fbf9SPekka Enberg 60ebe9ac19SSasha Levin static LIST_HEAD(bdevs); 61bdbbcb63SAsias He static int compat_id = -1; 6240ce993fSPekka Enberg 638b52f877SSasha Levin void virtio_blk_complete(void *param, long len) 648b52f877SSasha Levin { 658b52f877SSasha Levin struct blk_dev_req *req = param; 668b52f877SSasha Levin struct blk_dev *bdev = req->bdev; 678b52f877SSasha Levin int queueid = req->vq - bdev->vqs; 683fdf659dSSasha Levin u8 *status; 698b52f877SSasha Levin 708b52f877SSasha Levin /* status */ 7148427891SJean-Philippe Brucker status = req->status; 728b52f877SSasha Levin *status = (len < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 738b52f877SSasha Levin 748b52f877SSasha Levin mutex_lock(&bdev->mutex); 758b52f877SSasha Levin virt_queue__set_used_elem(req->vq, req->head, len); 768b52f877SSasha Levin mutex_unlock(&bdev->mutex); 778b52f877SSasha Levin 787ab3d207SSasha Levin if (virtio_queue__should_signal(&bdev->vqs[queueid])) 7902eca50cSAsias He bdev->vdev.ops->signal_vq(req->kvm, &bdev->vdev, queueid); 808b52f877SSasha Levin } 818b52f877SSasha Levin 8201dafc9eSMarc Zyngier static void virtio_blk_do_io_request(struct kvm *kvm, struct virt_queue *vq, struct blk_dev_req *req) 838b52f877SSasha Levin { 8448427891SJean-Philippe Brucker struct virtio_blk_outhdr req_hdr; 8548427891SJean-Philippe Brucker size_t iovcount, last_iov; 8669971b13SSasha Levin struct blk_dev *bdev; 8769971b13SSasha Levin struct iovec *iov; 8848427891SJean-Philippe Brucker ssize_t len; 8901dafc9eSMarc Zyngier u32 type; 9001dafc9eSMarc Zyngier u64 sector; 914155ba8cSPekka Enberg 928b52f877SSasha Levin bdev = req->bdev; 938b52f877SSasha Levin iov = req->iov; 9403110ff3SAsias He 9548427891SJean-Philippe Brucker iovcount = req->out; 9648427891SJean-Philippe Brucker len = memcpy_fromiovec_safe(&req_hdr, &iov, sizeof(req_hdr), &iovcount); 9748427891SJean-Philippe Brucker if (len) { 9848427891SJean-Philippe Brucker pr_warning("Failed to get header"); 9948427891SJean-Philippe Brucker return; 10048427891SJean-Philippe Brucker } 10148427891SJean-Philippe Brucker 102*b17552eeSAndre Przywara type = virtio_guest_to_host_u32(vq->endian, req_hdr.type); 103*b17552eeSAndre Przywara sector = virtio_guest_to_host_u64(vq->endian, req_hdr.sector); 10448427891SJean-Philippe Brucker 10548427891SJean-Philippe Brucker iovcount += req->in; 10648427891SJean-Philippe Brucker if (!iov_size(iov, iovcount)) { 10748427891SJean-Philippe Brucker pr_warning("Invalid IOV"); 10848427891SJean-Philippe Brucker return; 10948427891SJean-Philippe Brucker } 11048427891SJean-Philippe Brucker 11148427891SJean-Philippe Brucker /* Extract status byte from iovec */ 11248427891SJean-Philippe Brucker last_iov = iovcount - 1; 11348427891SJean-Philippe Brucker while (!iov[last_iov].iov_len) 11448427891SJean-Philippe Brucker last_iov--; 11548427891SJean-Philippe Brucker iov[last_iov].iov_len--; 11648427891SJean-Philippe Brucker req->status = iov[last_iov].iov_base + iov[last_iov].iov_len; 11748427891SJean-Philippe Brucker if (!iov[last_iov].iov_len) 11848427891SJean-Philippe Brucker iovcount--; 11901dafc9eSMarc Zyngier 12001dafc9eSMarc Zyngier switch (type) { 12103110ff3SAsias He case VIRTIO_BLK_T_IN: 12248427891SJean-Philippe Brucker disk_image__read(bdev->disk, sector, iov, iovcount, req); 123258dd093SPekka Enberg break; 12403110ff3SAsias He case VIRTIO_BLK_T_OUT: 12548427891SJean-Philippe Brucker disk_image__write(bdev->disk, sector, iov, iovcount, req); 126258dd093SPekka Enberg break; 12729084a74SPrasad Joshi case VIRTIO_BLK_T_FLUSH: 12848427891SJean-Philippe Brucker len = disk_image__flush(bdev->disk); 12948427891SJean-Philippe Brucker virtio_blk_complete(req, len); 13029084a74SPrasad Joshi break; 131ff6462e8SSasha Levin case VIRTIO_BLK_T_GET_ID: 13248427891SJean-Philippe Brucker len = disk_image__get_serial(bdev->disk, iov, iovcount, 13348427891SJean-Philippe Brucker VIRTIO_BLK_ID_BYTES); 13448427891SJean-Philippe Brucker virtio_blk_complete(req, len); 135ff6462e8SSasha Levin break; 136258dd093SPekka Enberg default: 13701dafc9eSMarc Zyngier pr_warning("request type %d", type); 138407475bfSPekka Enberg break; 13903110ff3SAsias He } 1404155ba8cSPekka Enberg } 1414155ba8cSPekka Enberg 14269971b13SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct virt_queue *vq, struct blk_dev *bdev) 14345e47970SAsias He { 1442fddfdb5SAsias He struct blk_dev_req *req; 1452fddfdb5SAsias He u16 head; 146407475bfSPekka Enberg 1472fddfdb5SAsias He while (virt_queue__available(vq)) { 1482fddfdb5SAsias He head = virt_queue__pop(vq); 1492fddfdb5SAsias He req = &bdev->reqs[head]; 15034239c78SAsias He req->head = virt_queue__get_head_iov(vq, req->iov, &req->out, 15134239c78SAsias He &req->in, head, kvm); 1522fddfdb5SAsias He req->vq = vq; 15345e47970SAsias He 15401dafc9eSMarc Zyngier virtio_blk_do_io_request(kvm, vq, req); 15569971b13SSasha Levin } 1564baf6f73SSasha Levin } 1570528c2a7SPekka Enberg 158c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev) 159427948d5SSasha Levin { 160427948d5SSasha Levin struct blk_dev *bdev = dev; 161427948d5SSasha Levin 162c5ae742bSSasha Levin return ((u8 *)(&bdev->blk_config)); 163427948d5SSasha Levin } 164427948d5SSasha Levin 165e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev) 166e4730284SMartin Radev { 167e4730284SMartin Radev struct blk_dev *bdev = dev; 168e4730284SMartin Radev 169e4730284SMartin Radev return sizeof(bdev->blk_config); 170e4730284SMartin Radev } 171e4730284SMartin Radev 1723c8f82b8SJean-Philippe Brucker static u64 get_host_features(struct kvm *kvm, void *dev) 173427948d5SSasha Levin { 1745c5cae75SJean-Philippe Brucker struct blk_dev *bdev = dev; 1755c5cae75SJean-Philippe Brucker 1767ab3d207SSasha Levin return 1UL << VIRTIO_BLK_F_SEG_MAX 1777ab3d207SSasha Levin | 1UL << VIRTIO_BLK_F_FLUSH 178754c8ce3SSasha Levin | 1UL << VIRTIO_RING_F_EVENT_IDX 1795c5cae75SJean-Philippe Brucker | 1UL << VIRTIO_RING_F_INDIRECT_DESC 18048427891SJean-Philippe Brucker | 1UL << VIRTIO_F_ANY_LAYOUT 1815c5cae75SJean-Philippe Brucker | (bdev->disk->readonly ? 1UL << VIRTIO_BLK_F_RO : 0); 182427948d5SSasha Levin } 183427948d5SSasha Levin 18495242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status) 18595242e44SJean-Philippe Brucker { 186867b15ccSJean-Philippe Brucker struct blk_dev *bdev = dev; 187867b15ccSJean-Philippe Brucker struct virtio_blk_config *conf = &bdev->blk_config; 188867b15ccSJean-Philippe Brucker 189867b15ccSJean-Philippe Brucker if (!(status & VIRTIO__STATUS_CONFIG)) 190867b15ccSJean-Philippe Brucker return; 191867b15ccSJean-Philippe Brucker 192*b17552eeSAndre Przywara conf->capacity = virtio_host_to_guest_u64(bdev->vdev.endian, bdev->capacity); 193*b17552eeSAndre Przywara conf->seg_max = virtio_host_to_guest_u32(bdev->vdev.endian, DISK_SEG_MAX); 19495242e44SJean-Philippe Brucker } 19595242e44SJean-Philippe Brucker 1965ac1178bSAsias He static void *virtio_blk_thread(void *dev) 1975ac1178bSAsias He { 1985ac1178bSAsias He struct blk_dev *bdev = dev; 1995ac1178bSAsias He u64 data; 200a7aa454eSSasha Levin int r; 2015ac1178bSAsias He 202a4d8c55eSSasha Levin kvm__set_thread_name("virtio-blk-io"); 203a4d8c55eSSasha Levin 2045ac1178bSAsias He while (1) { 205a7aa454eSSasha Levin r = read(bdev->io_efd, &data, sizeof(u64)); 206a7aa454eSSasha Levin if (r < 0) 207a7aa454eSSasha Levin continue; 2085ac1178bSAsias He virtio_blk_do_io(bdev->kvm, &bdev->vqs[0], bdev); 2095ac1178bSAsias He } 2105ac1178bSAsias He 2115ac1178bSAsias He pthread_exit(NULL); 2125ac1178bSAsias He return NULL; 2135ac1178bSAsias He } 2145ac1178bSAsias He 215609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq) 2166730b51fSJean-Philippe Brucker { 2176730b51fSJean-Philippe Brucker unsigned int i; 2186730b51fSJean-Philippe Brucker struct blk_dev *bdev = dev; 2196730b51fSJean-Philippe Brucker 2206730b51fSJean-Philippe Brucker compat__remove_message(compat_id); 2216730b51fSJean-Philippe Brucker 222fd41cde0SJean-Philippe Brucker virtio_init_device_vq(kvm, &bdev->vdev, &bdev->vqs[vq], 223609ee906SJean-Philippe Brucker VIRTIO_BLK_QUEUE_SIZE); 2246730b51fSJean-Philippe Brucker 2256730b51fSJean-Philippe Brucker if (vq != 0) 2266730b51fSJean-Philippe Brucker return 0; 2276730b51fSJean-Philippe Brucker 2286730b51fSJean-Philippe Brucker for (i = 0; i < ARRAY_SIZE(bdev->reqs); i++) { 2296730b51fSJean-Philippe Brucker bdev->reqs[i] = (struct blk_dev_req) { 2306730b51fSJean-Philippe Brucker .bdev = bdev, 2316730b51fSJean-Philippe Brucker .kvm = kvm, 2326730b51fSJean-Philippe Brucker }; 2336730b51fSJean-Philippe Brucker } 2346730b51fSJean-Philippe Brucker 2356730b51fSJean-Philippe Brucker mutex_init(&bdev->mutex); 2366730b51fSJean-Philippe Brucker bdev->io_efd = eventfd(0, 0); 2376730b51fSJean-Philippe Brucker if (bdev->io_efd < 0) 2386730b51fSJean-Philippe Brucker return -errno; 2396730b51fSJean-Philippe Brucker 2406730b51fSJean-Philippe Brucker if (pthread_create(&bdev->io_thread, NULL, virtio_blk_thread, bdev)) 2416730b51fSJean-Philippe Brucker return -errno; 2426730b51fSJean-Philippe Brucker 2436730b51fSJean-Philippe Brucker return 0; 2446730b51fSJean-Philippe Brucker } 2456730b51fSJean-Philippe Brucker 2466730b51fSJean-Philippe Brucker static void exit_vq(struct kvm *kvm, void *dev, u32 vq) 2476730b51fSJean-Philippe Brucker { 2486730b51fSJean-Philippe Brucker struct blk_dev *bdev = dev; 2496730b51fSJean-Philippe Brucker 2506730b51fSJean-Philippe Brucker if (vq != 0) 2516730b51fSJean-Philippe Brucker return; 2526730b51fSJean-Philippe Brucker 2536730b51fSJean-Philippe Brucker close(bdev->io_efd); 2546730b51fSJean-Philippe Brucker pthread_cancel(bdev->io_thread); 2556730b51fSJean-Philippe Brucker pthread_join(bdev->io_thread, NULL); 2563f218e89SJean-Philippe Brucker 2573f218e89SJean-Philippe Brucker disk_image__wait(bdev->disk); 2586730b51fSJean-Philippe Brucker } 2596730b51fSJean-Philippe Brucker 260427948d5SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq) 261427948d5SSasha Levin { 262427948d5SSasha Levin struct blk_dev *bdev = dev; 2635ac1178bSAsias He u64 data = 1; 264a7aa454eSSasha Levin int r; 265427948d5SSasha Levin 266a7aa454eSSasha Levin r = write(bdev->io_efd, &data, sizeof(data)); 267a7aa454eSSasha Levin if (r < 0) 268a7aa454eSSasha Levin return r; 269427948d5SSasha Levin 270427948d5SSasha Levin return 0; 271427948d5SSasha Levin } 272427948d5SSasha Levin 27353fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq) 274427948d5SSasha Levin { 275427948d5SSasha Levin struct blk_dev *bdev = dev; 276427948d5SSasha Levin 27753fbb17bSJean-Philippe Brucker return &bdev->vqs[vq]; 278427948d5SSasha Levin } 279427948d5SSasha Levin 280427948d5SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq) 281427948d5SSasha Levin { 282ffcc904aSAsias He /* FIXME: dynamic */ 283427948d5SSasha Levin return VIRTIO_BLK_QUEUE_SIZE; 284427948d5SSasha Levin } 285427948d5SSasha Levin 286ffcc904aSAsias He static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) 287ffcc904aSAsias He { 288ffcc904aSAsias He /* FIXME: dynamic */ 289ffcc904aSAsias He return size; 290ffcc904aSAsias He } 291ffcc904aSAsias He 29231e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev) 293b98ac591SJean-Philippe Brucker { 294b98ac591SJean-Philippe Brucker return NUM_VIRT_QUEUES; 295b98ac591SJean-Philippe Brucker } 296b98ac591SJean-Philippe Brucker 29715542babSAndre Przywara static struct virtio_ops blk_dev_virtio_ops = { 2981c47ce69SSasha Levin .get_config = get_config, 299e4730284SMartin Radev .get_config_size = get_config_size, 3001c47ce69SSasha Levin .get_host_features = get_host_features, 301b98ac591SJean-Philippe Brucker .get_vq_count = get_vq_count, 3021c47ce69SSasha Levin .init_vq = init_vq, 3036730b51fSJean-Philippe Brucker .exit_vq = exit_vq, 30495242e44SJean-Philippe Brucker .notify_status = notify_status, 3051c47ce69SSasha Levin .notify_vq = notify_vq, 30653fbb17bSJean-Philippe Brucker .get_vq = get_vq, 3071c47ce69SSasha Levin .get_size_vq = get_size_vq, 308ffcc904aSAsias He .set_size_vq = set_size_vq, 3091c47ce69SSasha Levin }; 3101c47ce69SSasha Levin 3119f9207c5SSasha Levin static int virtio_blk__init_one(struct kvm *kvm, struct disk_image *disk) 3124749e795SSasha Levin { 313fe2a70d1SSasha Levin struct blk_dev *bdev; 314db927775SAlexandru Elisei int r; 3154749e795SSasha Levin 3164749e795SSasha Levin if (!disk) 3179f9207c5SSasha Levin return -EINVAL; 3184749e795SSasha Levin 319ebe9ac19SSasha Levin bdev = calloc(1, sizeof(struct blk_dev)); 320ebe9ac19SSasha Levin if (bdev == NULL) 3219f9207c5SSasha Levin return -ENOMEM; 3224749e795SSasha Levin 323fe2a70d1SSasha Levin *bdev = (struct blk_dev) { 3244749e795SSasha Levin .disk = disk, 3254749e795SSasha Levin .capacity = disk->size / SECTOR_SIZE, 3265ac1178bSAsias He .kvm = kvm, 327427948d5SSasha Levin }; 328427948d5SSasha Levin 329db927775SAlexandru Elisei list_add_tail(&bdev->list, &bdevs); 330db927775SAlexandru Elisei 331db927775SAlexandru Elisei r = virtio_init(kvm, bdev, &bdev->vdev, &blk_dev_virtio_ops, 3329b46ebc5SRajnesh Kanwal kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_BLK, 333ae06ce71SWill Deacon VIRTIO_ID_BLOCK, PCI_CLASS_BLK); 334db927775SAlexandru Elisei if (r < 0) 335db927775SAlexandru Elisei return r; 336ebe9ac19SSasha Levin 337fb434ac3SSasha Levin disk_image__set_callback(bdev->disk, virtio_blk_complete); 338fb434ac3SSasha Levin 339d278197dSAsias He if (compat_id == -1) 34052f34d2cSAsias He compat_id = virtio_compat_add_message("virtio-blk", "CONFIG_VIRTIO_BLK"); 3415ac1178bSAsias He 3429f9207c5SSasha Levin return 0; 343b30d05adSPekka Enberg } 344bcb6aacaSPrasad Joshi 3459f9207c5SSasha Levin static int virtio_blk__exit_one(struct kvm *kvm, struct blk_dev *bdev) 346bcb6aacaSPrasad Joshi { 3479f9207c5SSasha Levin list_del(&bdev->list); 3489f9207c5SSasha Levin free(bdev); 349bcb6aacaSPrasad Joshi 3509f9207c5SSasha Levin return 0; 351bcb6aacaSPrasad Joshi } 352a0a1e3c2SPrasad Joshi 3539f9207c5SSasha Levin int virtio_blk__init(struct kvm *kvm) 3549f9207c5SSasha Levin { 3559f9207c5SSasha Levin int i, r = 0; 3569f9207c5SSasha Levin 3579f9207c5SSasha Levin for (i = 0; i < kvm->nr_disks; i++) { 358a67da3beSAsias He if (kvm->disks[i]->wwpn) 359a67da3beSAsias He continue; 3609f9207c5SSasha Levin r = virtio_blk__init_one(kvm, kvm->disks[i]); 3619f9207c5SSasha Levin if (r < 0) 3629f9207c5SSasha Levin goto cleanup; 3639f9207c5SSasha Levin } 3649f9207c5SSasha Levin 3659f9207c5SSasha Levin return 0; 3669f9207c5SSasha Levin cleanup: 367db927775SAlexandru Elisei virtio_blk__exit(kvm); 368db927775SAlexandru Elisei return r; 3699f9207c5SSasha Levin } 37049a8afd1SSasha Levin virtio_dev_init(virtio_blk__init); 3719f9207c5SSasha Levin 3729f9207c5SSasha Levin int virtio_blk__exit(struct kvm *kvm) 373a0a1e3c2SPrasad Joshi { 374ebe9ac19SSasha Levin while (!list_empty(&bdevs)) { 375ebe9ac19SSasha Levin struct blk_dev *bdev; 376a0a1e3c2SPrasad Joshi 377ebe9ac19SSasha Levin bdev = list_first_entry(&bdevs, struct blk_dev, list); 3789f9207c5SSasha Levin virtio_blk__exit_one(kvm, bdev); 379ebe9ac19SSasha Levin } 3809f9207c5SSasha Levin 3819f9207c5SSasha Levin return 0; 382a0a1e3c2SPrasad Joshi } 38349a8afd1SSasha Levin virtio_dev_exit(virtio_blk__exit); 384