xref: /kvmtool/virtio/blk.c (revision bdbbcb639c6652c6d1d31e6c531f2af9e760046c)
1416b2c2dSAsias He #include "kvm/virtio-blk.h"
2b30d05adSPekka Enberg 
331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h"
45a24a9f2SPekka Enberg #include "kvm/disk-image.h"
54ef0f4d6SPekka Enberg #include "kvm/mutex.h"
6fe99fd4eSPekka Enberg #include "kvm/util.h"
78b1ff07eSPekka Enberg #include "kvm/kvm.h"
8b30d05adSPekka Enberg #include "kvm/pci.h"
9fb0957f2SSasha Levin #include "kvm/threadpool.h"
10ec75b82fSSasha Levin #include "kvm/ioeventfd.h"
11404d164bSSasha Levin #include "kvm/guest_compat.h"
12427948d5SSasha Levin #include "kvm/virtio-pci.h"
13f41a132bSSasha Levin #include "kvm/virtio.h"
141c47ce69SSasha Levin #include "kvm/virtio-trans.h"
15b30d05adSPekka Enberg 
1620c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1720c64ecaSPekka Enberg #include <linux/virtio_blk.h>
18427948d5SSasha Levin #include <linux/kernel.h>
19ebe9ac19SSasha Levin #include <linux/list.h>
203fdf659dSSasha Levin #include <linux/types.h>
210528c2a7SPekka Enberg #include <pthread.h>
224155ba8cSPekka Enberg 
234749e795SSasha Levin #define VIRTIO_BLK_MAX_DEV		4
2410eca11dSPekka Enberg 
253d7831a1SAsias He /*
263d7831a1SAsias He  * the header and status consume too entries
273d7831a1SAsias He  */
283d7831a1SAsias He #define DISK_SEG_MAX			(VIRTIO_BLK_QUEUE_SIZE - 2)
29f41a132bSSasha Levin #define VIRTIO_BLK_QUEUE_SIZE		128
30f41a132bSSasha Levin #define NUM_VIRT_QUEUES			1
3110eca11dSPekka Enberg 
328b52f877SSasha Levin struct blk_dev_req {
334749e795SSasha Levin 	struct virt_queue		*vq;
34fe2a70d1SSasha Levin 	struct blk_dev			*bdev;
3569971b13SSasha Levin 	struct iovec			iov[VIRTIO_BLK_QUEUE_SIZE];
3669971b13SSasha Levin 	u16				out, in, head;
378b52f877SSasha Levin 	struct kvm			*kvm;
384749e795SSasha Levin };
394749e795SSasha Levin 
40fe2a70d1SSasha Levin struct blk_dev {
410528c2a7SPekka Enberg 	pthread_mutex_t			mutex;
428b52f877SSasha Levin 	pthread_mutex_t			req_mutex;
438b52f877SSasha Levin 
44ebe9ac19SSasha Levin 	struct list_head		list;
458b52f877SSasha Levin 	struct list_head		req_list;
460528c2a7SPekka Enberg 
471c47ce69SSasha Levin 	struct virtio_trans		vtrans;
4840ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
4938605e1cSSasha Levin 	struct disk_image		*disk;
50427948d5SSasha Levin 	u32				features;
5110eca11dSPekka Enberg 
5245e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
538b52f877SSasha Levin 	struct blk_dev_req		reqs[VIRTIO_BLK_QUEUE_SIZE];
54fbc2fbf9SPekka Enberg };
55fbc2fbf9SPekka Enberg 
56ebe9ac19SSasha Levin static LIST_HEAD(bdevs);
57*bdbbcb63SAsias He static int compat_id = -1;
5840ce993fSPekka Enberg 
598b52f877SSasha Levin void virtio_blk_complete(void *param, long len)
608b52f877SSasha Levin {
618b52f877SSasha Levin 	struct blk_dev_req *req = param;
628b52f877SSasha Levin 	struct blk_dev *bdev = req->bdev;
638b52f877SSasha Levin 	int queueid = req->vq - bdev->vqs;
643fdf659dSSasha Levin 	u8 *status;
658b52f877SSasha Levin 
668b52f877SSasha Levin 	/* status */
678b52f877SSasha Levin 	status	= req->iov[req->out + req->in - 1].iov_base;
688b52f877SSasha Levin 	*status	= (len < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
698b52f877SSasha Levin 
708b52f877SSasha Levin 	mutex_lock(&bdev->mutex);
718b52f877SSasha Levin 	virt_queue__set_used_elem(req->vq, req->head, len);
728b52f877SSasha Levin 	mutex_unlock(&bdev->mutex);
738b52f877SSasha Levin 
747ab3d207SSasha Levin 	if (virtio_queue__should_signal(&bdev->vqs[queueid]))
751c47ce69SSasha Levin 		bdev->vtrans.trans_ops->signal_vq(req->kvm, &bdev->vtrans, queueid);
768b52f877SSasha Levin }
778b52f877SSasha Levin 
788b52f877SSasha Levin static void virtio_blk_do_io_request(struct kvm *kvm, struct blk_dev_req *req)
798b52f877SSasha Levin {
808b52f877SSasha Levin 	struct virtio_blk_outhdr *req_hdr;
8169971b13SSasha Levin 	ssize_t block_cnt;
8269971b13SSasha Levin 	struct blk_dev *bdev;
8369971b13SSasha Levin 	struct iovec *iov;
84f41a132bSSasha Levin 	u16 out, in;
854155ba8cSPekka Enberg 
8669971b13SSasha Levin 	block_cnt	= -1;
878b52f877SSasha Levin 	bdev		= req->bdev;
888b52f877SSasha Levin 	iov		= req->iov;
898b52f877SSasha Levin 	out		= req->out;
908b52f877SSasha Levin 	in		= req->in;
918b52f877SSasha Levin 	req_hdr		= iov[0].iov_base;
9203110ff3SAsias He 
938b52f877SSasha Levin 	switch (req_hdr->type) {
9403110ff3SAsias He 	case VIRTIO_BLK_T_IN:
958b52f877SSasha Levin 		block_cnt	= disk_image__read(bdev->disk, req_hdr->sector, iov + 1,
96fb434ac3SSasha Levin 					in + out - 2, req);
97258dd093SPekka Enberg 		break;
9803110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
998b52f877SSasha Levin 		block_cnt	= disk_image__write(bdev->disk, req_hdr->sector, iov + 1,
100fb434ac3SSasha Levin 					in + out - 2, req);
101258dd093SPekka Enberg 		break;
10229084a74SPrasad Joshi 	case VIRTIO_BLK_T_FLUSH:
10329084a74SPrasad Joshi 		block_cnt       = disk_image__flush(bdev->disk);
104fb434ac3SSasha Levin 		virtio_blk_complete(req, block_cnt);
10529084a74SPrasad Joshi 		break;
106ff6462e8SSasha Levin 	case VIRTIO_BLK_T_GET_ID:
107ff6462e8SSasha Levin 		block_cnt	= VIRTIO_BLK_ID_BYTES;
108ff6462e8SSasha Levin 		disk_image__get_serial(bdev->disk, (iov + 1)->iov_base, &block_cnt);
109fb434ac3SSasha Levin 		virtio_blk_complete(req, block_cnt);
110ff6462e8SSasha Levin 		break;
111258dd093SPekka Enberg 	default:
1128b52f877SSasha Levin 		pr_warning("request type %d", req_hdr->type);
11370b53f25SSasha Levin 		block_cnt	= -1;
114407475bfSPekka Enberg 		break;
11503110ff3SAsias He 	}
1164155ba8cSPekka Enberg }
1174155ba8cSPekka Enberg 
11869971b13SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct virt_queue *vq, struct blk_dev *bdev)
11945e47970SAsias He {
1202fddfdb5SAsias He 	struct blk_dev_req *req;
1212fddfdb5SAsias He 	u16 head;
122407475bfSPekka Enberg 
1232fddfdb5SAsias He 	while (virt_queue__available(vq)) {
1242fddfdb5SAsias He 		head		= virt_queue__pop(vq);
1252fddfdb5SAsias He 		req		= &bdev->reqs[head];
1262fddfdb5SAsias He 		req->head	= virt_queue__get_head_iov(vq, req->iov, &req->out, &req->in, head, kvm);
1272fddfdb5SAsias He 		req->vq		= vq;
12845e47970SAsias He 
1298b52f877SSasha Levin 		virtio_blk_do_io_request(kvm, req);
13069971b13SSasha Levin 	}
1314baf6f73SSasha Levin }
1320528c2a7SPekka Enberg 
133427948d5SSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
134427948d5SSasha Levin {
135427948d5SSasha Levin 	struct blk_dev *bdev = dev;
136427948d5SSasha Levin 
137427948d5SSasha Levin 	((u8 *)(&bdev->blk_config))[offset] = data;
138427948d5SSasha Levin }
139427948d5SSasha Levin 
140427948d5SSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
141427948d5SSasha Levin {
142427948d5SSasha Levin 	struct blk_dev *bdev = dev;
143427948d5SSasha Levin 
144427948d5SSasha Levin 	return ((u8 *)(&bdev->blk_config))[offset];
145427948d5SSasha Levin }
146427948d5SSasha Levin 
147427948d5SSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
148427948d5SSasha Levin {
1497ab3d207SSasha Levin 	return	1UL << VIRTIO_BLK_F_SEG_MAX
1507ab3d207SSasha Levin 		| 1UL << VIRTIO_BLK_F_FLUSH
151754c8ce3SSasha Levin 		| 1UL << VIRTIO_RING_F_EVENT_IDX
152754c8ce3SSasha Levin 		| 1UL << VIRTIO_RING_F_INDIRECT_DESC;
153427948d5SSasha Levin }
154427948d5SSasha Levin 
155427948d5SSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
156427948d5SSasha Levin {
157427948d5SSasha Levin 	struct blk_dev *bdev = dev;
158427948d5SSasha Levin 
159427948d5SSasha Levin 	bdev->features = features;
160427948d5SSasha Levin }
161427948d5SSasha Levin 
162427948d5SSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
163427948d5SSasha Levin {
164427948d5SSasha Levin 	struct blk_dev *bdev = dev;
165427948d5SSasha Levin 	struct virt_queue *queue;
166427948d5SSasha Levin 	void *p;
167427948d5SSasha Levin 
168312c62d1SSasha Levin 	compat__remove_message(compat_id);
169427948d5SSasha Levin 
170427948d5SSasha Levin 	queue			= &bdev->vqs[vq];
171427948d5SSasha Levin 	queue->pfn		= pfn;
172427948d5SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
173427948d5SSasha Levin 
174427948d5SSasha Levin 	vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
175427948d5SSasha Levin 
176427948d5SSasha Levin 	return 0;
177427948d5SSasha Levin }
178427948d5SSasha Levin 
179427948d5SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
180427948d5SSasha Levin {
181427948d5SSasha Levin 	struct blk_dev *bdev = dev;
182427948d5SSasha Levin 
183427948d5SSasha Levin 	virtio_blk_do_io(kvm, &bdev->vqs[vq], bdev);
184427948d5SSasha Levin 
185427948d5SSasha Levin 	return 0;
186427948d5SSasha Levin }
187427948d5SSasha Levin 
188427948d5SSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
189427948d5SSasha Levin {
190427948d5SSasha Levin 	struct blk_dev *bdev = dev;
191427948d5SSasha Levin 
192427948d5SSasha Levin 	return bdev->vqs[vq].pfn;
193427948d5SSasha Levin }
194427948d5SSasha Levin 
195427948d5SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
196427948d5SSasha Levin {
197427948d5SSasha Levin 	return VIRTIO_BLK_QUEUE_SIZE;
198427948d5SSasha Levin }
199427948d5SSasha Levin 
2001c47ce69SSasha Levin static struct virtio_ops blk_dev_virtio_ops = (struct virtio_ops) {
2011c47ce69SSasha Levin 	.set_config		= set_config,
2021c47ce69SSasha Levin 	.get_config		= get_config,
2031c47ce69SSasha Levin 	.get_host_features	= get_host_features,
2041c47ce69SSasha Levin 	.set_guest_features	= set_guest_features,
2051c47ce69SSasha Levin 	.init_vq		= init_vq,
2061c47ce69SSasha Levin 	.notify_vq		= notify_vq,
2071c47ce69SSasha Levin 	.get_pfn_vq		= get_pfn_vq,
2081c47ce69SSasha Levin 	.get_size_vq		= get_size_vq,
2091c47ce69SSasha Levin };
2101c47ce69SSasha Levin 
21143835ac9SSasha Levin void virtio_blk__init(struct kvm *kvm, struct disk_image *disk)
2124749e795SSasha Levin {
213fe2a70d1SSasha Levin 	struct blk_dev *bdev;
2142fddfdb5SAsias He 	unsigned int i;
2154749e795SSasha Levin 
2164749e795SSasha Levin 	if (!disk)
2174749e795SSasha Levin 		return;
2184749e795SSasha Levin 
219ebe9ac19SSasha Levin 	bdev = calloc(1, sizeof(struct blk_dev));
220ebe9ac19SSasha Levin 	if (bdev == NULL)
221fe2a70d1SSasha Levin 		die("Failed allocating bdev");
2224749e795SSasha Levin 
223fe2a70d1SSasha Levin 	*bdev = (struct blk_dev) {
2244749e795SSasha Levin 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
2258b52f877SSasha Levin 		.req_mutex		= PTHREAD_MUTEX_INITIALIZER,
2264749e795SSasha Levin 		.disk			= disk,
2274749e795SSasha Levin 		.blk_config		= (struct virtio_blk_config) {
2284749e795SSasha Levin 			.capacity	= disk->size / SECTOR_SIZE,
2293d7831a1SAsias He 			.seg_max	= DISK_SEG_MAX,
2304749e795SSasha Levin 		},
231427948d5SSasha Levin 	};
232427948d5SSasha Levin 
2331c47ce69SSasha Levin 	virtio_trans_init(&bdev->vtrans, VIRTIO_PCI);
2341c47ce69SSasha Levin 	bdev->vtrans.trans_ops->init(kvm, &bdev->vtrans, bdev, PCI_DEVICE_ID_VIRTIO_BLK,
2351c47ce69SSasha Levin 					VIRTIO_ID_BLOCK, PCI_CLASS_BLK);
2361c47ce69SSasha Levin 	bdev->vtrans.virtio_ops = &blk_dev_virtio_ops;
237b30d05adSPekka Enberg 
238ebe9ac19SSasha Levin 	list_add_tail(&bdev->list, &bdevs);
239ebe9ac19SSasha Levin 
2402fddfdb5SAsias He 	for (i = 0; i < ARRAY_SIZE(bdev->reqs); i++) {
2412fddfdb5SAsias He 		bdev->reqs[i].bdev	= bdev;
2422fddfdb5SAsias He 		bdev->reqs[i].kvm	= kvm;
2432fddfdb5SAsias He 	}
2448b52f877SSasha Levin 
245fb434ac3SSasha Levin 	disk_image__set_callback(bdev->disk, virtio_blk_complete);
246fb434ac3SSasha Levin 
247312c62d1SSasha Levin 	if (compat_id != -1)
248312c62d1SSasha Levin 		compat_id = compat__add_message("virtio-blk device was not detected",
249404d164bSSasha Levin 						"While you have requested a virtio-blk device, "
250fc835ab3SSasha Levin 						"the guest kernel did not initialize it.\n"
251fc835ab3SSasha Levin 						"Please make sure that the guest kernel was "
252fc835ab3SSasha Levin 						"compiled with CONFIG_VIRTIO_BLK=y enabled "
253fc835ab3SSasha Levin 						"in its .config");
254b30d05adSPekka Enberg }
255bcb6aacaSPrasad Joshi 
256bcb6aacaSPrasad Joshi void virtio_blk__init_all(struct kvm *kvm)
257bcb6aacaSPrasad Joshi {
258bcb6aacaSPrasad Joshi 	int i;
259bcb6aacaSPrasad Joshi 
260bcb6aacaSPrasad Joshi 	for (i = 0; i < kvm->nr_disks; i++)
261bcb6aacaSPrasad Joshi 		virtio_blk__init(kvm, kvm->disks[i]);
262bcb6aacaSPrasad Joshi }
263a0a1e3c2SPrasad Joshi 
264a0a1e3c2SPrasad Joshi void virtio_blk__delete_all(struct kvm *kvm)
265a0a1e3c2SPrasad Joshi {
266ebe9ac19SSasha Levin 	while (!list_empty(&bdevs)) {
267ebe9ac19SSasha Levin 		struct blk_dev *bdev;
268a0a1e3c2SPrasad Joshi 
269ebe9ac19SSasha Levin 		bdev = list_first_entry(&bdevs, struct blk_dev, list);
270ebe9ac19SSasha Levin 		list_del(&bdev->list);
271ebe9ac19SSasha Levin 		free(bdev);
272ebe9ac19SSasha Levin 	}
273a0a1e3c2SPrasad Joshi }
274