xref: /kvmtool/virtio/blk.c (revision 4baf6f73fca645821c4fb07a8babf38988f292a1)
1416b2c2dSAsias He #include "kvm/virtio-blk.h"
2b30d05adSPekka Enberg 
3416b2c2dSAsias He #include "kvm/virtio-pci.h"
476b6349fSPekka Enberg 
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"
12b30d05adSPekka Enberg 
1320c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1420c64ecaSPekka Enberg #include <linux/virtio_blk.h>
150528c2a7SPekka Enberg 
164155ba8cSPekka Enberg #include <inttypes.h>
170528c2a7SPekka Enberg #include <pthread.h>
184155ba8cSPekka Enberg 
198b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ		14
20984b7ae0SPekka Enberg 
2110eca11dSPekka Enberg #define NUM_VIRT_QUEUES		1
2210eca11dSPekka Enberg 
2303110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE	128
2410eca11dSPekka Enberg 
25802e07a0SAsias He struct blk_device {
260528c2a7SPekka Enberg 	pthread_mutex_t			mutex;
270528c2a7SPekka Enberg 
2840ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
29c435b91dSPekka Enberg 	uint32_t			host_features;
30fbc2fbf9SPekka Enberg 	uint32_t			guest_features;
3140ce993fSPekka Enberg 	uint16_t			config_vector;
32fbc2fbf9SPekka Enberg 	uint8_t				status;
33*4baf6f73SSasha Levin 	pthread_t			io_thread;
34*4baf6f73SSasha Levin 	pthread_mutex_t		io_mutex;
35*4baf6f73SSasha Levin 	pthread_cond_t		io_cond;
3647bf1d0fSPekka Enberg 
3747bf1d0fSPekka Enberg 	/* virtio queue */
3847bf1d0fSPekka Enberg 	uint16_t			queue_selector;
39*4baf6f73SSasha Levin 	uint64_t			virtio_blk_queue_set_flags;
4010eca11dSPekka Enberg 
4145e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
42fbc2fbf9SPekka Enberg };
43fbc2fbf9SPekka Enberg 
4403110ff3SAsias He #define DISK_SEG_MAX	126
4540ce993fSPekka Enberg 
46802e07a0SAsias He static struct blk_device blk_device = {
470528c2a7SPekka Enberg 	.mutex			= PTHREAD_MUTEX_INITIALIZER,
480528c2a7SPekka Enberg 
4940ce993fSPekka Enberg 	.blk_config		= (struct virtio_blk_config) {
50e199f440SPekka Enberg 		/* VIRTIO_BLK_F_SEG_MAX */
5103110ff3SAsias He 		.seg_max		= DISK_SEG_MAX,
5240ce993fSPekka Enberg 	},
531ef2738dSCyrill Gorcunov 	/*
541ef2738dSCyrill Gorcunov 	 * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the
551ef2738dSCyrill Gorcunov 	 * node kernel will compute disk geometry by own, the
561ef2738dSCyrill Gorcunov 	 * same applies to VIRTIO_BLK_F_BLK_SIZE
571ef2738dSCyrill Gorcunov 	 */
5803110ff3SAsias He 	.host_features		= (1UL << VIRTIO_BLK_F_SEG_MAX),
59*4baf6f73SSasha Levin 
60*4baf6f73SSasha Levin 	.io_mutex			= PTHREAD_MUTEX_INITIALIZER,
61*4baf6f73SSasha Levin 	.io_cond			= PTHREAD_COND_INITIALIZER
62c435b91dSPekka Enberg };
63fbc2fbf9SPekka Enberg 
64416b2c2dSAsias He static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count)
6540ce993fSPekka Enberg {
66802e07a0SAsias He 	uint8_t *config_space = (uint8_t *) &blk_device.blk_config;
6740ce993fSPekka Enberg 
6840ce993fSPekka Enberg 	if (size != 1 || count != 1)
6940ce993fSPekka Enberg 		return false;
7040ce993fSPekka Enberg 
7140ce993fSPekka Enberg 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
7240ce993fSPekka Enberg 
7340ce993fSPekka Enberg 	return true;
7440ce993fSPekka Enberg }
7540ce993fSPekka Enberg 
76416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
77fbc2fbf9SPekka Enberg {
78fbc2fbf9SPekka Enberg 	unsigned long offset;
790528c2a7SPekka Enberg 	bool ret = true;
800528c2a7SPekka Enberg 
814ef0f4d6SPekka Enberg 	mutex_lock(&blk_device.mutex);
82fbc2fbf9SPekka Enberg 
83d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
84fbc2fbf9SPekka Enberg 
85fbc2fbf9SPekka Enberg 	switch (offset) {
86fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_HOST_FEATURES:
87802e07a0SAsias He 		ioport__write32(data, blk_device.host_features);
88fbc2fbf9SPekka Enberg 		break;
89fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
900528c2a7SPekka Enberg 		ret		= false;
919ee67e60SAsias He 		break;
92fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN:
9345e47970SAsias He 		ioport__write32(data, blk_device.vqs[blk_device.queue_selector].pfn);
948b1ff07eSPekka Enberg 		break;
95fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NUM:
9610eca11dSPekka Enberg 		ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE);
978b1ff07eSPekka Enberg 		break;
98fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
99fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY:
1000528c2a7SPekka Enberg 		ret		= false;
1019ee67e60SAsias He 		break;
102fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
103802e07a0SAsias He 		ioport__write8(data, blk_device.status);
104fbc2fbf9SPekka Enberg 		break;
105fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_ISR:
1068b1ff07eSPekka Enberg 		ioport__write8(data, 0x1);
1078b1ff07eSPekka Enberg 		kvm__irq_line(self, VIRTIO_BLK_IRQ, 0);
1087e61688eSPekka Enberg 		break;
109fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
110802e07a0SAsias He 		ioport__write16(data, blk_device.config_vector);
11140ce993fSPekka Enberg 		break;
112fbc2fbf9SPekka Enberg 	default:
1130528c2a7SPekka Enberg 		ret		= virtio_blk_pci_io_device_specific_in(data, offset, size, count);
114fbc2fbf9SPekka Enberg 	};
115fbc2fbf9SPekka Enberg 
1164ef0f4d6SPekka Enberg 	mutex_unlock(&blk_device.mutex);
1170528c2a7SPekka Enberg 
1180528c2a7SPekka Enberg 	return ret;
119fbc2fbf9SPekka Enberg }
120fbc2fbf9SPekka Enberg 
12145e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue)
1224155ba8cSPekka Enberg {
12345e47970SAsias He 	struct iovec iov[VIRTIO_BLK_QUEUE_SIZE];
1244155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
12570b53f25SSasha Levin 	ssize_t block_cnt = -1;
12645e47970SAsias He 	uint16_t out, in, head;
1274155ba8cSPekka Enberg 	uint8_t *status;
1284155ba8cSPekka Enberg 
12945e47970SAsias He 	head		= virt_queue__get_iov(queue, iov, &out, &in, self);
1304155ba8cSPekka Enberg 
13145e47970SAsias He 	/* head */
13245e47970SAsias He 	req		= iov[0].iov_base;
13303110ff3SAsias He 
134258dd093SPekka Enberg 	switch (req->type) {
13503110ff3SAsias He 	case VIRTIO_BLK_T_IN:
1362d103098SSasha Levin 		block_cnt = disk_image__read_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2);
13770b53f25SSasha Levin 
138258dd093SPekka Enberg 		break;
13903110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
1402d103098SSasha Levin 		block_cnt = disk_image__write_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2);
14170b53f25SSasha Levin 
142258dd093SPekka Enberg 		break;
14370b53f25SSasha Levin 
144258dd093SPekka Enberg 	default:
1454155ba8cSPekka Enberg 		warning("request type %d", req->type);
14670b53f25SSasha Levin 		block_cnt = -1;
14703110ff3SAsias He 	}
14803110ff3SAsias He 
14945e47970SAsias He 	/* status */
15045e47970SAsias He 	status			= iov[out + in - 1].iov_base;
15170b53f25SSasha Levin 	*status			= (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
15203110ff3SAsias He 
15345e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
1544155ba8cSPekka Enberg 
1554155ba8cSPekka Enberg 	return true;
1564155ba8cSPekka Enberg }
1574155ba8cSPekka Enberg 
158*4baf6f73SSasha Levin 
159*4baf6f73SSasha Levin 
160*4baf6f73SSasha Levin static int virtio_blk_get_selected_queue(struct blk_device *dev)
16145e47970SAsias He {
162*4baf6f73SSasha Levin 	int i;
163*4baf6f73SSasha Levin 
164*4baf6f73SSasha Levin 	for (i = 0 ; i < NUM_VIRT_QUEUES ; i++) {
165*4baf6f73SSasha Levin 		if (dev->virtio_blk_queue_set_flags & (1 << i)) {
166*4baf6f73SSasha Levin 			dev->virtio_blk_queue_set_flags &= ~(1 << i);
167*4baf6f73SSasha Levin 			return i;
168*4baf6f73SSasha Levin 		}
169*4baf6f73SSasha Levin 	}
170*4baf6f73SSasha Levin 
171*4baf6f73SSasha Levin 	return -1;
172*4baf6f73SSasha Levin }
173*4baf6f73SSasha Levin 
174*4baf6f73SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct blk_device *dev)
175*4baf6f73SSasha Levin {
176*4baf6f73SSasha Levin 	for (;;) {
177*4baf6f73SSasha Levin 		struct virt_queue *vq;
178*4baf6f73SSasha Levin 		int queue_index;
179*4baf6f73SSasha Levin 
180*4baf6f73SSasha Levin 		mutex_lock(&dev->io_mutex);
181*4baf6f73SSasha Levin 		queue_index = virtio_blk_get_selected_queue(dev);
182*4baf6f73SSasha Levin 		mutex_unlock(&dev->io_mutex);
183*4baf6f73SSasha Levin 
184*4baf6f73SSasha Levin 		if (queue_index < 0)
185*4baf6f73SSasha Levin 			break;
186*4baf6f73SSasha Levin 
187*4baf6f73SSasha Levin 		vq = &dev->vqs[queue_index];
18845e47970SAsias He 
1890ea58e5bSPekka Enberg 		while (virt_queue__available(vq))
190*4baf6f73SSasha Levin 			virtio_blk_do_io_request(kvm, vq);
1910ea58e5bSPekka Enberg 
192*4baf6f73SSasha Levin 		kvm__irq_line(kvm, VIRTIO_BLK_IRQ, 1);
193*4baf6f73SSasha Levin 	}
194*4baf6f73SSasha Levin }
19545e47970SAsias He 
196*4baf6f73SSasha Levin static void *virtio_blk_io_thread(void *ptr)
197*4baf6f73SSasha Levin {
198*4baf6f73SSasha Levin 	struct kvm *self = ptr;
199*4baf6f73SSasha Levin 
200*4baf6f73SSasha Levin 	for (;;) {
201*4baf6f73SSasha Levin 		int ret;
202*4baf6f73SSasha Levin 
203*4baf6f73SSasha Levin 		mutex_lock(&blk_device.io_mutex);
204*4baf6f73SSasha Levin 		ret = pthread_cond_wait(&blk_device.io_cond, &blk_device.io_mutex);
205*4baf6f73SSasha Levin 		mutex_unlock(&blk_device.io_mutex);
206*4baf6f73SSasha Levin 
207*4baf6f73SSasha Levin 		if (ret != 0)
208*4baf6f73SSasha Levin 			break;
209*4baf6f73SSasha Levin 
210*4baf6f73SSasha Levin 		virtio_blk_do_io(self, &blk_device);
211*4baf6f73SSasha Levin 	}
212*4baf6f73SSasha Levin 
213*4baf6f73SSasha Levin 	return NULL;
214*4baf6f73SSasha Levin }
215*4baf6f73SSasha Levin 
216*4baf6f73SSasha Levin static void virtio_blk_handle_callback(struct blk_device *dev, uint16_t queue_index)
217*4baf6f73SSasha Levin {
218*4baf6f73SSasha Levin 	mutex_lock(&dev->io_mutex);
219*4baf6f73SSasha Levin 
220*4baf6f73SSasha Levin 	dev->virtio_blk_queue_set_flags |= (1 << queue_index);
221*4baf6f73SSasha Levin 
222*4baf6f73SSasha Levin 	mutex_unlock(&dev->io_mutex);
223*4baf6f73SSasha Levin 
224*4baf6f73SSasha Levin 	pthread_cond_signal(&dev->io_cond);
22545e47970SAsias He }
2260528c2a7SPekka Enberg 
227416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
228fbc2fbf9SPekka Enberg {
229fbc2fbf9SPekka Enberg 	unsigned long offset;
2300528c2a7SPekka Enberg 	bool ret = true;
2310528c2a7SPekka Enberg 
2324ef0f4d6SPekka Enberg 	mutex_lock(&blk_device.mutex);
233fbc2fbf9SPekka Enberg 
234d1888318SCyrill Gorcunov 	offset		= port - IOPORT_VIRTIO_BLK;
235fbc2fbf9SPekka Enberg 
236fbc2fbf9SPekka Enberg 	switch (offset) {
237fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_GUEST_FEATURES:
238802e07a0SAsias He 		blk_device.guest_features	= ioport__read32(data);
239fbc2fbf9SPekka Enberg 		break;
24010eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_PFN: {
24110eca11dSPekka Enberg 		struct virt_queue *queue;
24210eca11dSPekka Enberg 		void *p;
24310eca11dSPekka Enberg 
24445e47970SAsias He 		queue			= &blk_device.vqs[blk_device.queue_selector];
24510eca11dSPekka Enberg 
24610eca11dSPekka Enberg 		queue->pfn		= ioport__read32(data);
24710eca11dSPekka Enberg 
24810eca11dSPekka Enberg 		p			= guest_flat_to_host(self, queue->pfn << 12);
24910eca11dSPekka Enberg 
25010eca11dSPekka Enberg 		vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096);
25110eca11dSPekka Enberg 
2527e61688eSPekka Enberg 		break;
25310eca11dSPekka Enberg 	}
254fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_QUEUE_SEL:
255802e07a0SAsias He 		blk_device.queue_selector	= ioport__read16(data);
2567e61688eSPekka Enberg 		break;
25710eca11dSPekka Enberg 	case VIRTIO_PCI_QUEUE_NOTIFY: {
25810eca11dSPekka Enberg 		uint16_t queue_index;
25910eca11dSPekka Enberg 		queue_index		= ioport__read16(data);
260*4baf6f73SSasha Levin 		virtio_blk_handle_callback(&blk_device, queue_index);
2617e61688eSPekka Enberg 		break;
26210eca11dSPekka Enberg 	}
263fbc2fbf9SPekka Enberg 	case VIRTIO_PCI_STATUS:
264802e07a0SAsias He 		blk_device.status		= ioport__read8(data);
265fbc2fbf9SPekka Enberg 		break;
266fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_CONFIG_VECTOR:
267802e07a0SAsias He 		blk_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
26840ce993fSPekka Enberg 		break;
269fbc2fbf9SPekka Enberg 	case VIRTIO_MSI_QUEUE_VECTOR:
27040ce993fSPekka Enberg 		break;
271fbc2fbf9SPekka Enberg 	default:
2720528c2a7SPekka Enberg 		ret		= false;
273fbc2fbf9SPekka Enberg 	};
274fbc2fbf9SPekka Enberg 
2754ef0f4d6SPekka Enberg 	mutex_unlock(&blk_device.mutex);
2760528c2a7SPekka Enberg 
2770528c2a7SPekka Enberg 	return ret;
278fbc2fbf9SPekka Enberg }
279fbc2fbf9SPekka Enberg 
280416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = {
281416b2c2dSAsias He 	.io_in		= virtio_blk_pci_io_in,
282416b2c2dSAsias He 	.io_out		= virtio_blk_pci_io_out,
283fbc2fbf9SPekka Enberg };
284fbc2fbf9SPekka Enberg 
285b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
286b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
287b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
288b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
289b30d05adSPekka Enberg 
290416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = {
291b30d05adSPekka Enberg 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
292b30d05adSPekka Enberg 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
293b30d05adSPekka Enberg 	.header_type		= PCI_HEADER_TYPE_NORMAL,
294b30d05adSPekka Enberg 	.revision_id		= 0,
295b30d05adSPekka Enberg 	.class			= 0x010000,
296b30d05adSPekka Enberg 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
297b30d05adSPekka Enberg 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
298d1888318SCyrill Gorcunov 	.bar[0]			= IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO,
299dc53a427SPekka Enberg 	.irq_pin		= 1,
3008b1ff07eSPekka Enberg 	.irq_line		= VIRTIO_BLK_IRQ,
301b30d05adSPekka Enberg };
302b30d05adSPekka Enberg 
303f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1
304f05bbe8dSAsias He 
305416b2c2dSAsias He void virtio_blk__init(struct kvm *self)
306b30d05adSPekka Enberg {
3071f848897SPekka Enberg 	if (!self->disk_image)
3081f848897SPekka Enberg 		return;
3091f848897SPekka Enberg 
310*4baf6f73SSasha Levin 	pthread_create(&blk_device.io_thread, NULL, virtio_blk_io_thread, self);
311*4baf6f73SSasha Levin 
312802e07a0SAsias He 	blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE;
313ca7c891bSCyrill Gorcunov 
314416b2c2dSAsias He 	pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM);
315b30d05adSPekka Enberg 
316416b2c2dSAsias He 	ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE);
317b30d05adSPekka Enberg }
318