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