1416b2c2dSAsias He #include "kvm/virtio-blk.h" 2b30d05adSPekka Enberg 331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 45a24a9f2SPekka Enberg #include "kvm/disk-image.h" 54ef0f4d6SPekka Enberg #include "kvm/mutex.h" 6fe99fd4eSPekka Enberg #include "kvm/util.h" 78b1ff07eSPekka Enberg #include "kvm/kvm.h" 8b30d05adSPekka Enberg #include "kvm/pci.h" 9fb0957f2SSasha Levin #include "kvm/threadpool.h" 10ec75b82fSSasha Levin #include "kvm/ioeventfd.h" 11404d164bSSasha Levin #include "kvm/guest_compat.h" 12427948d5SSasha Levin #include "kvm/virtio-pci.h" 13f41a132bSSasha Levin #include "kvm/virtio.h" 141c47ce69SSasha Levin #include "kvm/virtio-trans.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) 29f41a132bSSasha Levin #define VIRTIO_BLK_QUEUE_SIZE 128 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; 378b52f877SSasha Levin struct kvm *kvm; 384749e795SSasha Levin }; 394749e795SSasha Levin 40fe2a70d1SSasha Levin struct blk_dev { 410528c2a7SPekka Enberg pthread_mutex_t mutex; 428b52f877SSasha Levin pthread_mutex_t req_mutex; 438b52f877SSasha Levin 44ebe9ac19SSasha Levin struct list_head list; 458b52f877SSasha Levin struct list_head req_list; 460528c2a7SPekka Enberg 471c47ce69SSasha Levin struct virtio_trans vtrans; 4840ce993fSPekka Enberg struct virtio_blk_config blk_config; 4938605e1cSSasha Levin struct disk_image *disk; 50427948d5SSasha Levin u32 features; 5110eca11dSPekka Enberg 5245e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES]; 538b52f877SSasha Levin struct blk_dev_req reqs[VIRTIO_BLK_QUEUE_SIZE]; 54fbc2fbf9SPekka Enberg }; 55fbc2fbf9SPekka Enberg 56ebe9ac19SSasha Levin static LIST_HEAD(bdevs); 57312c62d1SSasha Levin static int compat_id; 5840ce993fSPekka Enberg 598b52f877SSasha Levin void virtio_blk_complete(void *param, long len) 608b52f877SSasha Levin { 618b52f877SSasha Levin struct blk_dev_req *req = param; 628b52f877SSasha Levin struct blk_dev *bdev = req->bdev; 638b52f877SSasha Levin int queueid = req->vq - bdev->vqs; 643fdf659dSSasha Levin u8 *status; 658b52f877SSasha Levin 668b52f877SSasha Levin /* status */ 678b52f877SSasha Levin status = req->iov[req->out + req->in - 1].iov_base; 688b52f877SSasha Levin *status = (len < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 698b52f877SSasha Levin 708b52f877SSasha Levin mutex_lock(&bdev->mutex); 718b52f877SSasha Levin virt_queue__set_used_elem(req->vq, req->head, len); 728b52f877SSasha Levin mutex_unlock(&bdev->mutex); 738b52f877SSasha Levin 747ab3d207SSasha Levin if (virtio_queue__should_signal(&bdev->vqs[queueid])) 751c47ce69SSasha Levin bdev->vtrans.trans_ops->signal_vq(req->kvm, &bdev->vtrans, queueid); 768b52f877SSasha Levin } 778b52f877SSasha Levin 788b52f877SSasha Levin static void virtio_blk_do_io_request(struct kvm *kvm, struct blk_dev_req *req) 798b52f877SSasha Levin { 808b52f877SSasha Levin struct virtio_blk_outhdr *req_hdr; 8169971b13SSasha Levin ssize_t block_cnt; 8269971b13SSasha Levin struct blk_dev *bdev; 8369971b13SSasha Levin struct iovec *iov; 84f41a132bSSasha Levin u16 out, in; 854155ba8cSPekka Enberg 8669971b13SSasha Levin block_cnt = -1; 878b52f877SSasha Levin bdev = req->bdev; 888b52f877SSasha Levin iov = req->iov; 898b52f877SSasha Levin out = req->out; 908b52f877SSasha Levin in = req->in; 918b52f877SSasha Levin req_hdr = iov[0].iov_base; 9203110ff3SAsias He 938b52f877SSasha Levin switch (req_hdr->type) { 9403110ff3SAsias He case VIRTIO_BLK_T_IN: 958b52f877SSasha Levin block_cnt = disk_image__read(bdev->disk, req_hdr->sector, iov + 1, 96fb434ac3SSasha Levin in + out - 2, req); 97258dd093SPekka Enberg break; 9803110ff3SAsias He case VIRTIO_BLK_T_OUT: 998b52f877SSasha Levin block_cnt = disk_image__write(bdev->disk, req_hdr->sector, iov + 1, 100fb434ac3SSasha Levin in + out - 2, req); 101258dd093SPekka Enberg break; 10229084a74SPrasad Joshi case VIRTIO_BLK_T_FLUSH: 10329084a74SPrasad Joshi block_cnt = disk_image__flush(bdev->disk); 104fb434ac3SSasha Levin virtio_blk_complete(req, block_cnt); 10529084a74SPrasad Joshi break; 106ff6462e8SSasha Levin case VIRTIO_BLK_T_GET_ID: 107ff6462e8SSasha Levin block_cnt = VIRTIO_BLK_ID_BYTES; 108ff6462e8SSasha Levin disk_image__get_serial(bdev->disk, (iov + 1)->iov_base, &block_cnt); 109fb434ac3SSasha Levin virtio_blk_complete(req, block_cnt); 110ff6462e8SSasha Levin break; 111258dd093SPekka Enberg default: 1128b52f877SSasha Levin pr_warning("request type %d", req_hdr->type); 11370b53f25SSasha Levin block_cnt = -1; 114407475bfSPekka Enberg break; 11503110ff3SAsias He } 1164155ba8cSPekka Enberg } 1174155ba8cSPekka Enberg 11869971b13SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct virt_queue *vq, struct blk_dev *bdev) 11945e47970SAsias He { 120*2fddfdb5SAsias He struct blk_dev_req *req; 121*2fddfdb5SAsias He u16 head; 122407475bfSPekka Enberg 123*2fddfdb5SAsias He while (virt_queue__available(vq)) { 124*2fddfdb5SAsias He head = virt_queue__pop(vq); 125*2fddfdb5SAsias He req = &bdev->reqs[head]; 126*2fddfdb5SAsias He req->head = virt_queue__get_head_iov(vq, req->iov, &req->out, &req->in, head, kvm); 127*2fddfdb5SAsias He req->vq = vq; 12845e47970SAsias He 1298b52f877SSasha Levin virtio_blk_do_io_request(kvm, req); 13069971b13SSasha Levin } 1314baf6f73SSasha Levin } 1320528c2a7SPekka Enberg 133427948d5SSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset) 134427948d5SSasha Levin { 135427948d5SSasha Levin struct blk_dev *bdev = dev; 136427948d5SSasha Levin 137427948d5SSasha Levin ((u8 *)(&bdev->blk_config))[offset] = data; 138427948d5SSasha Levin } 139427948d5SSasha Levin 140427948d5SSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset) 141427948d5SSasha Levin { 142427948d5SSasha Levin struct blk_dev *bdev = dev; 143427948d5SSasha Levin 144427948d5SSasha Levin return ((u8 *)(&bdev->blk_config))[offset]; 145427948d5SSasha Levin } 146427948d5SSasha Levin 147427948d5SSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev) 148427948d5SSasha Levin { 1497ab3d207SSasha Levin return 1UL << VIRTIO_BLK_F_SEG_MAX 1507ab3d207SSasha Levin | 1UL << VIRTIO_BLK_F_FLUSH 1517ab3d207SSasha Levin | 1UL << VIRTIO_RING_F_EVENT_IDX; 152427948d5SSasha Levin } 153427948d5SSasha Levin 154427948d5SSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features) 155427948d5SSasha Levin { 156427948d5SSasha Levin struct blk_dev *bdev = dev; 157427948d5SSasha Levin 158427948d5SSasha Levin bdev->features = features; 159427948d5SSasha Levin } 160427948d5SSasha Levin 161427948d5SSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn) 162427948d5SSasha Levin { 163427948d5SSasha Levin struct blk_dev *bdev = dev; 164427948d5SSasha Levin struct virt_queue *queue; 165427948d5SSasha Levin void *p; 166427948d5SSasha Levin 167312c62d1SSasha Levin compat__remove_message(compat_id); 168427948d5SSasha Levin 169427948d5SSasha Levin queue = &bdev->vqs[vq]; 170427948d5SSasha Levin queue->pfn = pfn; 171427948d5SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 172427948d5SSasha Levin 173427948d5SSasha Levin vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 174427948d5SSasha Levin 175427948d5SSasha Levin return 0; 176427948d5SSasha Levin } 177427948d5SSasha Levin 178427948d5SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq) 179427948d5SSasha Levin { 180427948d5SSasha Levin struct blk_dev *bdev = dev; 181427948d5SSasha Levin 182427948d5SSasha Levin virtio_blk_do_io(kvm, &bdev->vqs[vq], bdev); 183427948d5SSasha Levin 184427948d5SSasha Levin return 0; 185427948d5SSasha Levin } 186427948d5SSasha Levin 187427948d5SSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq) 188427948d5SSasha Levin { 189427948d5SSasha Levin struct blk_dev *bdev = dev; 190427948d5SSasha Levin 191427948d5SSasha Levin return bdev->vqs[vq].pfn; 192427948d5SSasha Levin } 193427948d5SSasha Levin 194427948d5SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq) 195427948d5SSasha Levin { 196427948d5SSasha Levin return VIRTIO_BLK_QUEUE_SIZE; 197427948d5SSasha Levin } 198427948d5SSasha Levin 1991c47ce69SSasha Levin static struct virtio_ops blk_dev_virtio_ops = (struct virtio_ops) { 2001c47ce69SSasha Levin .set_config = set_config, 2011c47ce69SSasha Levin .get_config = get_config, 2021c47ce69SSasha Levin .get_host_features = get_host_features, 2031c47ce69SSasha Levin .set_guest_features = set_guest_features, 2041c47ce69SSasha Levin .init_vq = init_vq, 2051c47ce69SSasha Levin .notify_vq = notify_vq, 2061c47ce69SSasha Levin .get_pfn_vq = get_pfn_vq, 2071c47ce69SSasha Levin .get_size_vq = get_size_vq, 2081c47ce69SSasha Levin }; 2091c47ce69SSasha Levin 21043835ac9SSasha Levin void virtio_blk__init(struct kvm *kvm, struct disk_image *disk) 2114749e795SSasha Levin { 212fe2a70d1SSasha Levin struct blk_dev *bdev; 213*2fddfdb5SAsias He unsigned int i; 2144749e795SSasha Levin 2154749e795SSasha Levin if (!disk) 2164749e795SSasha Levin return; 2174749e795SSasha Levin 218ebe9ac19SSasha Levin bdev = calloc(1, sizeof(struct blk_dev)); 219ebe9ac19SSasha Levin if (bdev == NULL) 220fe2a70d1SSasha Levin die("Failed allocating bdev"); 2214749e795SSasha Levin 222fe2a70d1SSasha Levin *bdev = (struct blk_dev) { 2234749e795SSasha Levin .mutex = PTHREAD_MUTEX_INITIALIZER, 2248b52f877SSasha Levin .req_mutex = PTHREAD_MUTEX_INITIALIZER, 2254749e795SSasha Levin .disk = disk, 2264749e795SSasha Levin .blk_config = (struct virtio_blk_config) { 2274749e795SSasha Levin .capacity = disk->size / SECTOR_SIZE, 2283d7831a1SAsias He .seg_max = DISK_SEG_MAX, 2294749e795SSasha Levin }, 230427948d5SSasha Levin }; 231427948d5SSasha Levin 2321c47ce69SSasha Levin virtio_trans_init(&bdev->vtrans, VIRTIO_PCI); 2331c47ce69SSasha Levin bdev->vtrans.trans_ops->init(kvm, &bdev->vtrans, bdev, PCI_DEVICE_ID_VIRTIO_BLK, 2341c47ce69SSasha Levin VIRTIO_ID_BLOCK, PCI_CLASS_BLK); 2351c47ce69SSasha Levin bdev->vtrans.virtio_ops = &blk_dev_virtio_ops; 236b30d05adSPekka Enberg 237ebe9ac19SSasha Levin list_add_tail(&bdev->list, &bdevs); 238ebe9ac19SSasha Levin 239*2fddfdb5SAsias He for (i = 0; i < ARRAY_SIZE(bdev->reqs); i++) { 240*2fddfdb5SAsias He bdev->reqs[i].bdev = bdev; 241*2fddfdb5SAsias He bdev->reqs[i].kvm = kvm; 242*2fddfdb5SAsias He } 2438b52f877SSasha Levin 244fb434ac3SSasha Levin disk_image__set_callback(bdev->disk, virtio_blk_complete); 245fb434ac3SSasha Levin 246312c62d1SSasha Levin if (compat_id != -1) 247312c62d1SSasha Levin compat_id = compat__add_message("virtio-blk device was not detected", 248404d164bSSasha Levin "While you have requested a virtio-blk device, " 249fc835ab3SSasha Levin "the guest kernel did not initialize it.\n" 250fc835ab3SSasha Levin "Please make sure that the guest kernel was " 251fc835ab3SSasha Levin "compiled with CONFIG_VIRTIO_BLK=y enabled " 252fc835ab3SSasha Levin "in its .config"); 253b30d05adSPekka Enberg } 254bcb6aacaSPrasad Joshi 255bcb6aacaSPrasad Joshi void virtio_blk__init_all(struct kvm *kvm) 256bcb6aacaSPrasad Joshi { 257bcb6aacaSPrasad Joshi int i; 258bcb6aacaSPrasad Joshi 259bcb6aacaSPrasad Joshi for (i = 0; i < kvm->nr_disks; i++) 260bcb6aacaSPrasad Joshi virtio_blk__init(kvm, kvm->disks[i]); 261bcb6aacaSPrasad Joshi } 262a0a1e3c2SPrasad Joshi 263a0a1e3c2SPrasad Joshi void virtio_blk__delete_all(struct kvm *kvm) 264a0a1e3c2SPrasad Joshi { 265ebe9ac19SSasha Levin while (!list_empty(&bdevs)) { 266ebe9ac19SSasha Levin struct blk_dev *bdev; 267a0a1e3c2SPrasad Joshi 268ebe9ac19SSasha Levin bdev = list_first_entry(&bdevs, struct blk_dev, list); 269ebe9ac19SSasha Levin list_del(&bdev->list); 270ebe9ac19SSasha Levin free(bdev); 271ebe9ac19SSasha Levin } 272a0a1e3c2SPrasad Joshi } 273