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" 13b30d05adSPekka Enberg 1420c64ecaSPekka Enberg #include <linux/virtio_ring.h> 1520c64ecaSPekka Enberg #include <linux/virtio_blk.h> 160528c2a7SPekka Enberg 173fdf659dSSasha Levin #include <linux/types.h> 180528c2a7SPekka Enberg #include <pthread.h> 194155ba8cSPekka Enberg 204749e795SSasha Levin #define VIRTIO_BLK_MAX_DEV 4 2110eca11dSPekka Enberg #define NUM_VIRT_QUEUES 1 2210eca11dSPekka Enberg 2303110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE 128 243d7831a1SAsias He /* 253d7831a1SAsias He * the header and status consume too entries 263d7831a1SAsias He */ 273d7831a1SAsias He #define DISK_SEG_MAX (VIRTIO_BLK_QUEUE_SIZE - 2) 2810eca11dSPekka Enberg 29fe2a70d1SSasha Levin struct blk_dev_job { 304749e795SSasha Levin struct virt_queue *vq; 31fe2a70d1SSasha Levin struct blk_dev *bdev; 324749e795SSasha Levin void *job_id; 334749e795SSasha Levin }; 344749e795SSasha Levin 35fe2a70d1SSasha Levin struct blk_dev { 360528c2a7SPekka Enberg pthread_mutex_t mutex; 370528c2a7SPekka Enberg 3840ce993fSPekka Enberg struct virtio_blk_config blk_config; 3938605e1cSSasha Levin struct disk_image *disk; 403fdf659dSSasha Levin u32 host_features; 413fdf659dSSasha Levin u32 guest_features; 423fdf659dSSasha Levin u16 config_vector; 433fdf659dSSasha Levin u8 status; 44ebfc7327SAsias He u8 isr; 454749e795SSasha Levin u8 idx; 4647bf1d0fSPekka Enberg 4747bf1d0fSPekka Enberg /* virtio queue */ 483fdf659dSSasha Levin u16 queue_selector; 4910eca11dSPekka Enberg 5045e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES]; 51fe2a70d1SSasha Levin struct blk_dev_job jobs[NUM_VIRT_QUEUES]; 52ef1f02f2SSasha Levin struct pci_device_header pci_hdr; 53fbc2fbf9SPekka Enberg }; 54fbc2fbf9SPekka Enberg 55fe2a70d1SSasha Levin static struct blk_dev *bdevs[VIRTIO_BLK_MAX_DEV]; 5640ce993fSPekka Enberg 57407475bfSPekka Enberg static bool virtio_blk_dev_in(struct blk_dev *bdev, void *data, unsigned long offset, int size, u32 count) 5840ce993fSPekka Enberg { 59fe2a70d1SSasha Levin u8 *config_space = (u8 *) &bdev->blk_config; 6040ce993fSPekka Enberg 6140ce993fSPekka Enberg if (size != 1 || count != 1) 6240ce993fSPekka Enberg return false; 6340ce993fSPekka Enberg 64b8f43678SSasha Levin ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]); 6540ce993fSPekka Enberg 6640ce993fSPekka Enberg return true; 6740ce993fSPekka Enberg } 6840ce993fSPekka Enberg 694749e795SSasha Levin /* Translate port into device id + offset in that device addr space */ 70407475bfSPekka Enberg static void virtio_blk_port2dev(u16 port, u16 base, u16 size, u16 *dev_idx, u16 *offset) 714749e795SSasha Levin { 724749e795SSasha Levin *dev_idx = (port - base) / size; 734749e795SSasha Levin *offset = port - (base + *dev_idx * size); 744749e795SSasha Levin } 75407475bfSPekka Enberg 7643835ac9SSasha Levin static bool virtio_blk_pci_io_in(struct kvm *kvm, u16 port, void *data, int size, u32 count) 77fbc2fbf9SPekka Enberg { 78407475bfSPekka Enberg struct blk_dev *bdev; 794749e795SSasha Levin u16 offset, dev_idx; 800528c2a7SPekka Enberg bool ret = true; 810528c2a7SPekka Enberg 82407475bfSPekka Enberg virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE, &dev_idx, &offset); 83fbc2fbf9SPekka Enberg 84fe2a70d1SSasha Levin bdev = bdevs[dev_idx]; 854749e795SSasha Levin 86fe2a70d1SSasha Levin mutex_lock(&bdev->mutex); 87fbc2fbf9SPekka Enberg 88fbc2fbf9SPekka Enberg switch (offset) { 89fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 90fe2a70d1SSasha Levin ioport__write32(data, bdev->host_features); 91fbc2fbf9SPekka Enberg break; 92fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 930528c2a7SPekka Enberg ret = false; 949ee67e60SAsias He break; 95fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 96fe2a70d1SSasha Levin ioport__write32(data, bdev->vqs[bdev->queue_selector].pfn); 978b1ff07eSPekka Enberg break; 98fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 9910eca11dSPekka Enberg ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); 1008b1ff07eSPekka Enberg break; 101fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 102fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 1030528c2a7SPekka Enberg ret = false; 1049ee67e60SAsias He break; 105fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 106fe2a70d1SSasha Levin ioport__write8(data, bdev->status); 107fbc2fbf9SPekka Enberg break; 108fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 109ebfc7327SAsias He ioport__write8(data, bdev->isr); 11043835ac9SSasha Levin kvm__irq_line(kvm, bdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW); 111ebfc7327SAsias He bdev->isr = VIRTIO_IRQ_LOW; 1127e61688eSPekka Enberg break; 113fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 114fe2a70d1SSasha Levin ioport__write16(data, bdev->config_vector); 11540ce993fSPekka Enberg break; 116fbc2fbf9SPekka Enberg default: 117407475bfSPekka Enberg ret = virtio_blk_dev_in(bdev, data, offset, size, count); 118407475bfSPekka Enberg break; 119fbc2fbf9SPekka Enberg }; 120fbc2fbf9SPekka Enberg 121fe2a70d1SSasha Levin mutex_unlock(&bdev->mutex); 1220528c2a7SPekka Enberg 1230528c2a7SPekka Enberg return ret; 124fbc2fbf9SPekka Enberg } 125fbc2fbf9SPekka Enberg 12643835ac9SSasha Levin static bool virtio_blk_do_io_request(struct kvm *kvm, 127fe2a70d1SSasha Levin struct blk_dev *bdev, 1284749e795SSasha Levin struct virt_queue *queue) 1294155ba8cSPekka Enberg { 13045e47970SAsias He struct iovec iov[VIRTIO_BLK_QUEUE_SIZE]; 1314155ba8cSPekka Enberg struct virtio_blk_outhdr *req; 13270b53f25SSasha Levin ssize_t block_cnt = -1; 1333fdf659dSSasha Levin u16 out, in, head; 1343fdf659dSSasha Levin u8 *status; 1354155ba8cSPekka Enberg 13643835ac9SSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, kvm); 1374155ba8cSPekka Enberg 13845e47970SAsias He /* head */ 13945e47970SAsias He req = iov[0].iov_base; 14003110ff3SAsias He 141258dd093SPekka Enberg switch (req->type) { 14203110ff3SAsias He case VIRTIO_BLK_T_IN: 143407475bfSPekka Enberg block_cnt = disk_image__read_sector_iov(bdev->disk, req->sector, iov + 1, in + out - 2); 144258dd093SPekka Enberg break; 14503110ff3SAsias He case VIRTIO_BLK_T_OUT: 146407475bfSPekka Enberg block_cnt = disk_image__write_sector_iov(bdev->disk, req->sector, iov + 1, in + out - 2); 147258dd093SPekka Enberg break; 148*29084a74SPrasad Joshi case VIRTIO_BLK_T_FLUSH: 149*29084a74SPrasad Joshi block_cnt = disk_image__flush(bdev->disk); 150*29084a74SPrasad Joshi break; 151258dd093SPekka Enberg default: 1524155ba8cSPekka Enberg warning("request type %d", req->type); 15370b53f25SSasha Levin block_cnt = -1; 154407475bfSPekka Enberg break; 15503110ff3SAsias He } 15603110ff3SAsias He 15745e47970SAsias He /* status */ 15845e47970SAsias He status = iov[out + in - 1].iov_base; 15970b53f25SSasha Levin *status = (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 16003110ff3SAsias He 16145e47970SAsias He virt_queue__set_used_elem(queue, head, block_cnt); 1624155ba8cSPekka Enberg 1634155ba8cSPekka Enberg return true; 1644155ba8cSPekka Enberg } 1654155ba8cSPekka Enberg 166fb0957f2SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, void *param) 16745e47970SAsias He { 168fe2a70d1SSasha Levin struct blk_dev_job *job = param; 169407475bfSPekka Enberg struct virt_queue *vq; 170407475bfSPekka Enberg struct blk_dev *bdev; 171407475bfSPekka Enberg 172407475bfSPekka Enberg vq = job->vq; 173407475bfSPekka Enberg bdev = job->bdev; 17445e47970SAsias He 1750ea58e5bSPekka Enberg while (virt_queue__available(vq)) 176fe2a70d1SSasha Levin virtio_blk_do_io_request(kvm, bdev, vq); 1770ea58e5bSPekka Enberg 178ebfc7327SAsias He virt_queue__trigger_irq(vq, bdev->pci_hdr.irq_line, &bdev->isr, kvm); 1794baf6f73SSasha Levin } 1800528c2a7SPekka Enberg 18143835ac9SSasha Levin static bool virtio_blk_pci_io_out(struct kvm *kvm, u16 port, void *data, int size, u32 count) 182fbc2fbf9SPekka Enberg { 183407475bfSPekka Enberg struct blk_dev *bdev; 1844749e795SSasha Levin u16 offset, dev_idx; 1850528c2a7SPekka Enberg bool ret = true; 1860528c2a7SPekka Enberg 187407475bfSPekka Enberg virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE, &dev_idx, &offset); 188fbc2fbf9SPekka Enberg 189fe2a70d1SSasha Levin bdev = bdevs[dev_idx]; 1904749e795SSasha Levin 191fe2a70d1SSasha Levin mutex_lock(&bdev->mutex); 192fbc2fbf9SPekka Enberg 193fbc2fbf9SPekka Enberg switch (offset) { 194fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 195fe2a70d1SSasha Levin bdev->guest_features = ioport__read32(data); 196fbc2fbf9SPekka Enberg break; 19710eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_PFN: { 19810eca11dSPekka Enberg struct virt_queue *queue; 199fe2a70d1SSasha Levin struct blk_dev_job *job; 20010eca11dSPekka Enberg void *p; 20110eca11dSPekka Enberg 202fe2a70d1SSasha Levin job = &bdev->jobs[bdev->queue_selector]; 20310eca11dSPekka Enberg 204fe2a70d1SSasha Levin queue = &bdev->vqs[bdev->queue_selector]; 20510eca11dSPekka Enberg queue->pfn = ioport__read32(data); 20643835ac9SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 20710eca11dSPekka Enberg 208b8f43678SSasha Levin vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 20910eca11dSPekka Enberg 210fe2a70d1SSasha Levin *job = (struct blk_dev_job) { 2114749e795SSasha Levin .vq = queue, 212fe2a70d1SSasha Levin .bdev = bdev, 2134749e795SSasha Levin }; 2144749e795SSasha Levin 21543835ac9SSasha Levin job->job_id = thread_pool__add_job(kvm, virtio_blk_do_io, job); 216fb0957f2SSasha Levin 2177e61688eSPekka Enberg break; 21810eca11dSPekka Enberg } 219fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 220fe2a70d1SSasha Levin bdev->queue_selector = ioport__read16(data); 2217e61688eSPekka Enberg break; 22210eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: { 2233fdf659dSSasha Levin u16 queue_index; 224407475bfSPekka Enberg 22510eca11dSPekka Enberg queue_index = ioport__read16(data); 226fe2a70d1SSasha Levin thread_pool__do_job(bdev->jobs[queue_index].job_id); 227407475bfSPekka Enberg 2287e61688eSPekka Enberg break; 22910eca11dSPekka Enberg } 230fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 231fe2a70d1SSasha Levin bdev->status = ioport__read8(data); 232fbc2fbf9SPekka Enberg break; 233fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 234fe2a70d1SSasha Levin bdev->config_vector = VIRTIO_MSI_NO_VECTOR; 23540ce993fSPekka Enberg break; 236fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 23740ce993fSPekka Enberg break; 238fbc2fbf9SPekka Enberg default: 2390528c2a7SPekka Enberg ret = false; 240407475bfSPekka Enberg break; 241fbc2fbf9SPekka Enberg }; 242fbc2fbf9SPekka Enberg 243fe2a70d1SSasha Levin mutex_unlock(&bdev->mutex); 2440528c2a7SPekka Enberg 2450528c2a7SPekka Enberg return ret; 246fbc2fbf9SPekka Enberg } 247fbc2fbf9SPekka Enberg 248416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = { 249416b2c2dSAsias He .io_in = virtio_blk_pci_io_in, 250416b2c2dSAsias He .io_out = virtio_blk_pci_io_out, 251fbc2fbf9SPekka Enberg }; 252fbc2fbf9SPekka Enberg 2534749e795SSasha Levin static int virtio_blk_find_empty_dev(void) 2544749e795SSasha Levin { 2554749e795SSasha Levin int i; 2564749e795SSasha Levin 2574749e795SSasha Levin for (i = 0; i < VIRTIO_BLK_MAX_DEV; i++) { 258fe2a70d1SSasha Levin if (bdevs[i] == NULL) 2594749e795SSasha Levin return i; 2604749e795SSasha Levin } 2614749e795SSasha Levin 2624749e795SSasha Levin return -1; 2634749e795SSasha Levin } 2644749e795SSasha Levin 26543835ac9SSasha Levin void virtio_blk__init(struct kvm *kvm, struct disk_image *disk) 2664749e795SSasha Levin { 2674749e795SSasha Levin u16 blk_dev_base_addr; 2682449f6e3SSasha Levin u8 dev, pin, line; 269fe2a70d1SSasha Levin struct blk_dev *bdev; 270407475bfSPekka Enberg int new_dev_idx; 2714749e795SSasha Levin 2724749e795SSasha Levin if (!disk) 2734749e795SSasha Levin return; 2744749e795SSasha Levin 2754749e795SSasha Levin new_dev_idx = virtio_blk_find_empty_dev(); 2764749e795SSasha Levin if (new_dev_idx < 0) 2774749e795SSasha Levin die("Could not find an empty block device slot"); 2784749e795SSasha Levin 279fe2a70d1SSasha Levin bdevs[new_dev_idx] = calloc(1, sizeof(struct blk_dev)); 280fe2a70d1SSasha Levin if (bdevs[new_dev_idx] == NULL) 281fe2a70d1SSasha Levin die("Failed allocating bdev"); 2824749e795SSasha Levin 283fe2a70d1SSasha Levin bdev = bdevs[new_dev_idx]; 284fe2a70d1SSasha Levin 2854749e795SSasha Levin blk_dev_base_addr = IOPORT_VIRTIO_BLK + new_dev_idx * IOPORT_VIRTIO_BLK_SIZE; 2864749e795SSasha Levin 287fe2a70d1SSasha Levin *bdev = (struct blk_dev) { 2884749e795SSasha Levin .mutex = PTHREAD_MUTEX_INITIALIZER, 2894749e795SSasha Levin .disk = disk, 2904749e795SSasha Levin .idx = new_dev_idx, 2914749e795SSasha Levin .blk_config = (struct virtio_blk_config) { 2924749e795SSasha Levin .capacity = disk->size / SECTOR_SIZE, 2933d7831a1SAsias He .seg_max = DISK_SEG_MAX, 2944749e795SSasha Levin }, 295ef1f02f2SSasha Levin .pci_hdr = (struct pci_device_header) { 296b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 297b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 298b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 299b30d05adSPekka Enberg .revision_id = 0, 300b30d05adSPekka Enberg .class = 0x010000, 301b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 302b30d05adSPekka Enberg .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, 3034749e795SSasha Levin .bar[0] = blk_dev_base_addr | PCI_BASE_ADDRESS_SPACE_IO, 3044749e795SSasha Levin }, 3053d7831a1SAsias He /* 3063d7831a1SAsias He * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the 3073d7831a1SAsias He * guest kernel will compute disk geometry by own, the 3083d7831a1SAsias He * same applies to VIRTIO_BLK_F_BLK_SIZE 3093d7831a1SAsias He */ 310*29084a74SPrasad Joshi .host_features = (1UL << VIRTIO_BLK_F_SEG_MAX | 1UL << VIRTIO_BLK_F_FLUSH), 311b30d05adSPekka Enberg }; 312b30d05adSPekka Enberg 3132449f6e3SSasha Levin if (irq__register_device(PCI_DEVICE_ID_VIRTIO_BLK, &dev, &pin, &line) < 0) 3142449f6e3SSasha Levin return; 3152449f6e3SSasha Levin 316ef1f02f2SSasha Levin bdev->pci_hdr.irq_pin = pin; 317ef1f02f2SSasha Levin bdev->pci_hdr.irq_line = line; 3182449f6e3SSasha Levin 319ef1f02f2SSasha Levin pci__register(&bdev->pci_hdr, dev); 320f05bbe8dSAsias He 3214749e795SSasha Levin ioport__register(blk_dev_base_addr, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE); 322b30d05adSPekka Enberg } 323