1416b2c2dSAsias He #include "kvm/virtio-blk.h" 2b30d05adSPekka Enberg 331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 42449f6e3SSasha Levin #include "kvm/irq.h" 55a24a9f2SPekka Enberg #include "kvm/disk-image.h" 639d6af07SAsias He #include "kvm/virtio.h" 7b30d05adSPekka Enberg #include "kvm/ioport.h" 84ef0f4d6SPekka Enberg #include "kvm/mutex.h" 9fe99fd4eSPekka Enberg #include "kvm/util.h" 108b1ff07eSPekka Enberg #include "kvm/kvm.h" 11b30d05adSPekka Enberg #include "kvm/pci.h" 12fb0957f2SSasha Levin #include "kvm/threadpool.h" 13ec75b82fSSasha Levin #include "kvm/ioeventfd.h" 14b30d05adSPekka Enberg 1520c64ecaSPekka Enberg #include <linux/virtio_ring.h> 1620c64ecaSPekka Enberg #include <linux/virtio_blk.h> 170528c2a7SPekka Enberg 18ebe9ac19SSasha Levin #include <linux/list.h> 193fdf659dSSasha Levin #include <linux/types.h> 200528c2a7SPekka Enberg #include <pthread.h> 214155ba8cSPekka Enberg 224749e795SSasha Levin #define VIRTIO_BLK_MAX_DEV 4 2310eca11dSPekka Enberg #define NUM_VIRT_QUEUES 1 2410eca11dSPekka Enberg 2503110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE 128 263d7831a1SAsias He /* 273d7831a1SAsias He * the header and status consume too entries 283d7831a1SAsias He */ 293d7831a1SAsias He #define DISK_SEG_MAX (VIRTIO_BLK_QUEUE_SIZE - 2) 3010eca11dSPekka Enberg 31fe2a70d1SSasha Levin struct blk_dev_job { 324749e795SSasha Levin struct virt_queue *vq; 33fe2a70d1SSasha Levin struct blk_dev *bdev; 34*df0c7f57SSasha Levin struct thread_pool__job job_id; 354749e795SSasha Levin }; 364749e795SSasha Levin 37fe2a70d1SSasha Levin struct blk_dev { 380528c2a7SPekka Enberg pthread_mutex_t mutex; 39ebe9ac19SSasha Levin struct list_head list; 400528c2a7SPekka Enberg 4140ce993fSPekka Enberg struct virtio_blk_config blk_config; 4238605e1cSSasha Levin struct disk_image *disk; 43ebe9ac19SSasha Levin u64 base_addr; 443fdf659dSSasha Levin u32 host_features; 453fdf659dSSasha Levin u32 guest_features; 463fdf659dSSasha Levin u16 config_vector; 473fdf659dSSasha Levin u8 status; 48ebfc7327SAsias He u8 isr; 4947bf1d0fSPekka Enberg 5047bf1d0fSPekka Enberg /* virtio queue */ 513fdf659dSSasha Levin u16 queue_selector; 5210eca11dSPekka Enberg 5345e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES]; 54fe2a70d1SSasha Levin struct blk_dev_job jobs[NUM_VIRT_QUEUES]; 55ef1f02f2SSasha Levin struct pci_device_header pci_hdr; 56fbc2fbf9SPekka Enberg }; 57fbc2fbf9SPekka Enberg 58ebe9ac19SSasha Levin static LIST_HEAD(bdevs); 5940ce993fSPekka Enberg 60407475bfSPekka Enberg static bool virtio_blk_dev_in(struct blk_dev *bdev, void *data, unsigned long offset, int size, u32 count) 6140ce993fSPekka Enberg { 62fe2a70d1SSasha Levin u8 *config_space = (u8 *) &bdev->blk_config; 6340ce993fSPekka Enberg 6440ce993fSPekka Enberg if (size != 1 || count != 1) 6540ce993fSPekka Enberg return false; 6640ce993fSPekka Enberg 67b8f43678SSasha Levin ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]); 6840ce993fSPekka Enberg 6940ce993fSPekka Enberg return true; 7040ce993fSPekka Enberg } 7140ce993fSPekka Enberg 723d62dea6SSasha Levin static bool virtio_blk_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count) 73fbc2fbf9SPekka Enberg { 74407475bfSPekka Enberg struct blk_dev *bdev; 75ebe9ac19SSasha Levin u16 offset; 760528c2a7SPekka Enberg bool ret = true; 770528c2a7SPekka Enberg 78ebe9ac19SSasha Levin bdev = ioport->priv; 79ebe9ac19SSasha Levin offset = port - bdev->base_addr; 804749e795SSasha Levin 81fe2a70d1SSasha Levin mutex_lock(&bdev->mutex); 82fbc2fbf9SPekka Enberg 83fbc2fbf9SPekka Enberg switch (offset) { 84fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 85fe2a70d1SSasha Levin ioport__write32(data, bdev->host_features); 86fbc2fbf9SPekka Enberg break; 87fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 880528c2a7SPekka Enberg ret = false; 899ee67e60SAsias He break; 90fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 91fe2a70d1SSasha Levin ioport__write32(data, bdev->vqs[bdev->queue_selector].pfn); 928b1ff07eSPekka Enberg break; 93fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 9410eca11dSPekka Enberg ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); 958b1ff07eSPekka Enberg break; 96fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 97fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 980528c2a7SPekka Enberg ret = false; 999ee67e60SAsias He break; 100fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 101fe2a70d1SSasha Levin ioport__write8(data, bdev->status); 102fbc2fbf9SPekka Enberg break; 103fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 104ebfc7327SAsias He ioport__write8(data, bdev->isr); 10543835ac9SSasha Levin kvm__irq_line(kvm, bdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW); 106ebfc7327SAsias He bdev->isr = VIRTIO_IRQ_LOW; 1077e61688eSPekka Enberg break; 108fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 109fe2a70d1SSasha Levin ioport__write16(data, bdev->config_vector); 11040ce993fSPekka Enberg break; 111fbc2fbf9SPekka Enberg default: 112407475bfSPekka Enberg ret = virtio_blk_dev_in(bdev, data, offset, size, count); 113407475bfSPekka Enberg break; 114fbc2fbf9SPekka Enberg }; 115fbc2fbf9SPekka Enberg 116fe2a70d1SSasha Levin mutex_unlock(&bdev->mutex); 1170528c2a7SPekka Enberg 1180528c2a7SPekka Enberg return ret; 119fbc2fbf9SPekka Enberg } 120fbc2fbf9SPekka Enberg 12143835ac9SSasha Levin static bool virtio_blk_do_io_request(struct kvm *kvm, 122fe2a70d1SSasha Levin struct blk_dev *bdev, 1234749e795SSasha Levin struct virt_queue *queue) 1244155ba8cSPekka Enberg { 12545e47970SAsias He struct iovec iov[VIRTIO_BLK_QUEUE_SIZE]; 1264155ba8cSPekka Enberg struct virtio_blk_outhdr *req; 12770b53f25SSasha Levin ssize_t block_cnt = -1; 1283fdf659dSSasha Levin u16 out, in, head; 1293fdf659dSSasha Levin u8 *status; 1304155ba8cSPekka Enberg 13143835ac9SSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, kvm); 1324155ba8cSPekka Enberg 13345e47970SAsias He /* head */ 13445e47970SAsias He req = iov[0].iov_base; 13503110ff3SAsias He 136258dd093SPekka Enberg switch (req->type) { 13703110ff3SAsias He case VIRTIO_BLK_T_IN: 138b8861977SAsias He block_cnt = disk_image__read(bdev->disk, req->sector, iov + 1, in + out - 2); 139258dd093SPekka Enberg break; 14003110ff3SAsias He case VIRTIO_BLK_T_OUT: 141b8861977SAsias He block_cnt = disk_image__write(bdev->disk, req->sector, iov + 1, in + out - 2); 142258dd093SPekka Enberg break; 14329084a74SPrasad Joshi case VIRTIO_BLK_T_FLUSH: 14429084a74SPrasad Joshi block_cnt = disk_image__flush(bdev->disk); 14529084a74SPrasad Joshi break; 146258dd093SPekka Enberg default: 1474542f276SCyrill Gorcunov pr_warning("request type %d", req->type); 14870b53f25SSasha Levin block_cnt = -1; 149407475bfSPekka Enberg break; 15003110ff3SAsias He } 15103110ff3SAsias He 15245e47970SAsias He /* status */ 15345e47970SAsias He status = iov[out + in - 1].iov_base; 15470b53f25SSasha Levin *status = (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 15503110ff3SAsias He 15645e47970SAsias He virt_queue__set_used_elem(queue, head, block_cnt); 1574155ba8cSPekka Enberg 1584155ba8cSPekka Enberg return true; 1594155ba8cSPekka Enberg } 1604155ba8cSPekka Enberg 161fb0957f2SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, void *param) 16245e47970SAsias He { 163fe2a70d1SSasha Levin struct blk_dev_job *job = param; 164407475bfSPekka Enberg struct virt_queue *vq; 165407475bfSPekka Enberg struct blk_dev *bdev; 166407475bfSPekka Enberg 167407475bfSPekka Enberg vq = job->vq; 168407475bfSPekka Enberg bdev = job->bdev; 16945e47970SAsias He 1700ea58e5bSPekka Enberg while (virt_queue__available(vq)) 171fe2a70d1SSasha Levin virtio_blk_do_io_request(kvm, bdev, vq); 1720ea58e5bSPekka Enberg 173ebfc7327SAsias He virt_queue__trigger_irq(vq, bdev->pci_hdr.irq_line, &bdev->isr, kvm); 1744baf6f73SSasha Levin } 1750528c2a7SPekka Enberg 1763d62dea6SSasha Levin static bool virtio_blk_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count) 177fbc2fbf9SPekka Enberg { 178407475bfSPekka Enberg struct blk_dev *bdev; 179ebe9ac19SSasha Levin u16 offset; 1800528c2a7SPekka Enberg bool ret = true; 1810528c2a7SPekka Enberg 182ebe9ac19SSasha Levin bdev = ioport->priv; 183ebe9ac19SSasha Levin offset = port - bdev->base_addr; 1844749e795SSasha Levin 185fe2a70d1SSasha Levin mutex_lock(&bdev->mutex); 186fbc2fbf9SPekka Enberg 187fbc2fbf9SPekka Enberg switch (offset) { 188fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 189fe2a70d1SSasha Levin bdev->guest_features = ioport__read32(data); 190fbc2fbf9SPekka Enberg break; 19110eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_PFN: { 19210eca11dSPekka Enberg struct virt_queue *queue; 193fe2a70d1SSasha Levin struct blk_dev_job *job; 19410eca11dSPekka Enberg void *p; 19510eca11dSPekka Enberg 196fe2a70d1SSasha Levin job = &bdev->jobs[bdev->queue_selector]; 19710eca11dSPekka Enberg 198fe2a70d1SSasha Levin queue = &bdev->vqs[bdev->queue_selector]; 19910eca11dSPekka Enberg queue->pfn = ioport__read32(data); 20043835ac9SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 20110eca11dSPekka Enberg 202b8f43678SSasha Levin vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 20310eca11dSPekka Enberg 204fe2a70d1SSasha Levin *job = (struct blk_dev_job) { 2054749e795SSasha Levin .vq = queue, 206fe2a70d1SSasha Levin .bdev = bdev, 2074749e795SSasha Levin }; 2084749e795SSasha Levin 209*df0c7f57SSasha Levin thread_pool__init_job(&job->job_id, kvm, virtio_blk_do_io, job); 210fb0957f2SSasha Levin 2117e61688eSPekka Enberg break; 21210eca11dSPekka Enberg } 213fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 214fe2a70d1SSasha Levin bdev->queue_selector = ioport__read16(data); 2157e61688eSPekka Enberg break; 21610eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: { 2173fdf659dSSasha Levin u16 queue_index; 218407475bfSPekka Enberg 21910eca11dSPekka Enberg queue_index = ioport__read16(data); 220*df0c7f57SSasha Levin thread_pool__do_job(&bdev->jobs[queue_index].job_id); 221407475bfSPekka Enberg 2227e61688eSPekka Enberg break; 22310eca11dSPekka Enberg } 224fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 225fe2a70d1SSasha Levin bdev->status = ioport__read8(data); 226fbc2fbf9SPekka Enberg break; 227fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 228fe2a70d1SSasha Levin bdev->config_vector = VIRTIO_MSI_NO_VECTOR; 22940ce993fSPekka Enberg break; 230fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 23140ce993fSPekka Enberg break; 232fbc2fbf9SPekka Enberg default: 2330528c2a7SPekka Enberg ret = false; 234407475bfSPekka Enberg break; 235fbc2fbf9SPekka Enberg }; 236fbc2fbf9SPekka Enberg 237fe2a70d1SSasha Levin mutex_unlock(&bdev->mutex); 2380528c2a7SPekka Enberg 2390528c2a7SPekka Enberg return ret; 240fbc2fbf9SPekka Enberg } 241fbc2fbf9SPekka Enberg 242416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = { 243416b2c2dSAsias He .io_in = virtio_blk_pci_io_in, 244416b2c2dSAsias He .io_out = virtio_blk_pci_io_out, 245fbc2fbf9SPekka Enberg }; 246fbc2fbf9SPekka Enberg 247ec75b82fSSasha Levin static void ioevent_callback(struct kvm *kvm, void *param) 248ec75b82fSSasha Levin { 249ec75b82fSSasha Levin struct blk_dev_job *job = param; 250ec75b82fSSasha Levin 251*df0c7f57SSasha Levin thread_pool__do_job(&job->job_id); 252ec75b82fSSasha Levin } 253ec75b82fSSasha Levin 25443835ac9SSasha Levin void virtio_blk__init(struct kvm *kvm, struct disk_image *disk) 2554749e795SSasha Levin { 2564749e795SSasha Levin u16 blk_dev_base_addr; 257ec75b82fSSasha Levin u8 dev, pin, line, i; 258fe2a70d1SSasha Levin struct blk_dev *bdev; 259ec75b82fSSasha Levin struct ioevent ioevent; 2604749e795SSasha Levin 2614749e795SSasha Levin if (!disk) 2624749e795SSasha Levin return; 2634749e795SSasha Levin 264ebe9ac19SSasha Levin bdev = calloc(1, sizeof(struct blk_dev)); 265ebe9ac19SSasha Levin if (bdev == NULL) 266fe2a70d1SSasha Levin die("Failed allocating bdev"); 2674749e795SSasha Levin 268ebe9ac19SSasha Levin blk_dev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_blk_io_ops, IOPORT_SIZE, bdev); 2694749e795SSasha Levin 270fe2a70d1SSasha Levin *bdev = (struct blk_dev) { 2714749e795SSasha Levin .mutex = PTHREAD_MUTEX_INITIALIZER, 2724749e795SSasha Levin .disk = disk, 273ebe9ac19SSasha Levin .base_addr = blk_dev_base_addr, 2744749e795SSasha Levin .blk_config = (struct virtio_blk_config) { 2754749e795SSasha Levin .capacity = disk->size / SECTOR_SIZE, 2763d7831a1SAsias He .seg_max = DISK_SEG_MAX, 2774749e795SSasha Levin }, 278ef1f02f2SSasha Levin .pci_hdr = (struct pci_device_header) { 279b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 280b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 281b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 282b30d05adSPekka Enberg .revision_id = 0, 283b30d05adSPekka Enberg .class = 0x010000, 284b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 2850a7ab0c6SSasha Levin .subsys_id = VIRTIO_ID_BLOCK, 2864749e795SSasha Levin .bar[0] = blk_dev_base_addr | PCI_BASE_ADDRESS_SPACE_IO, 2874749e795SSasha Levin }, 2883d7831a1SAsias He /* 2893d7831a1SAsias He * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the 2903d7831a1SAsias He * guest kernel will compute disk geometry by own, the 2913d7831a1SAsias He * same applies to VIRTIO_BLK_F_BLK_SIZE 2923d7831a1SAsias He */ 29329084a74SPrasad Joshi .host_features = (1UL << VIRTIO_BLK_F_SEG_MAX | 1UL << VIRTIO_BLK_F_FLUSH), 294b30d05adSPekka Enberg }; 295b30d05adSPekka Enberg 296ebe9ac19SSasha Levin list_add_tail(&bdev->list, &bdevs); 297ebe9ac19SSasha Levin 2980a7ab0c6SSasha Levin if (irq__register_device(VIRTIO_ID_BLOCK, &dev, &pin, &line) < 0) 2992449f6e3SSasha Levin return; 3002449f6e3SSasha Levin 301ef1f02f2SSasha Levin bdev->pci_hdr.irq_pin = pin; 302ef1f02f2SSasha Levin bdev->pci_hdr.irq_line = line; 3032449f6e3SSasha Levin 304ef1f02f2SSasha Levin pci__register(&bdev->pci_hdr, dev); 305ec75b82fSSasha Levin 306ec75b82fSSasha Levin for (i = 0; i < NUM_VIRT_QUEUES; i++) { 307ec75b82fSSasha Levin ioevent = (struct ioevent) { 308ec75b82fSSasha Levin .io_addr = blk_dev_base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 309ec75b82fSSasha Levin .io_len = sizeof(u16), 310ec75b82fSSasha Levin .fn = ioevent_callback, 311ec75b82fSSasha Levin .datamatch = i, 312ec75b82fSSasha Levin .fn_ptr = &bdev->jobs[i], 313ec75b82fSSasha Levin .fn_kvm = kvm, 314ec75b82fSSasha Levin .fd = eventfd(0, 0), 315ec75b82fSSasha Levin }; 316ec75b82fSSasha Levin 317ec75b82fSSasha Levin ioeventfd__add_event(&ioevent); 318ec75b82fSSasha Levin } 319b30d05adSPekka Enberg } 320bcb6aacaSPrasad Joshi 321bcb6aacaSPrasad Joshi void virtio_blk__init_all(struct kvm *kvm) 322bcb6aacaSPrasad Joshi { 323bcb6aacaSPrasad Joshi int i; 324bcb6aacaSPrasad Joshi 325bcb6aacaSPrasad Joshi for (i = 0; i < kvm->nr_disks; i++) 326bcb6aacaSPrasad Joshi virtio_blk__init(kvm, kvm->disks[i]); 327bcb6aacaSPrasad Joshi } 328a0a1e3c2SPrasad Joshi 329a0a1e3c2SPrasad Joshi void virtio_blk__delete_all(struct kvm *kvm) 330a0a1e3c2SPrasad Joshi { 331ebe9ac19SSasha Levin while (!list_empty(&bdevs)) { 332ebe9ac19SSasha Levin struct blk_dev *bdev; 333a0a1e3c2SPrasad Joshi 334ebe9ac19SSasha Levin bdev = list_first_entry(&bdevs, struct blk_dev, list); 335ec75b82fSSasha Levin ioeventfd__del_event(bdev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 0); 336ebe9ac19SSasha Levin list_del(&bdev->list); 337ebe9ac19SSasha Levin free(bdev); 338ebe9ac19SSasha Levin } 339a0a1e3c2SPrasad Joshi } 340