xref: /kvmtool/virtio/blk.c (revision 29084a74e900c2760f37827fea7b9c2050f253ed)
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