xref: /kvmtool/virtio/blk.c (revision 8b52f877bfc9ed991c942d6a10d28b7998a121e3)
1416b2c2dSAsias He #include "kvm/virtio-blk.h"
2b30d05adSPekka Enberg 
331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h"
45a24a9f2SPekka Enberg #include "kvm/disk-image.h"
539d6af07SAsias He #include "kvm/virtio.h"
64ef0f4d6SPekka Enberg #include "kvm/mutex.h"
7fe99fd4eSPekka Enberg #include "kvm/util.h"
88b1ff07eSPekka Enberg #include "kvm/kvm.h"
9b30d05adSPekka Enberg #include "kvm/pci.h"
10fb0957f2SSasha Levin #include "kvm/threadpool.h"
11ec75b82fSSasha Levin #include "kvm/ioeventfd.h"
12404d164bSSasha Levin #include "kvm/guest_compat.h"
13427948d5SSasha Levin #include "kvm/virtio-pci.h"
14b30d05adSPekka Enberg 
1520c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1620c64ecaSPekka Enberg #include <linux/virtio_blk.h>
170528c2a7SPekka Enberg 
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 #define NUM_VIRT_QUEUES			1
2510eca11dSPekka Enberg 
2603110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE		128
273d7831a1SAsias He /*
283d7831a1SAsias He  * the header and status consume too entries
293d7831a1SAsias He  */
303d7831a1SAsias He #define DISK_SEG_MAX			(VIRTIO_BLK_QUEUE_SIZE - 2)
3110eca11dSPekka Enberg 
32*8b52f877SSasha Levin struct blk_dev_req {
33*8b52f877SSasha Levin 	struct list_head		list;
344749e795SSasha Levin 	struct virt_queue		*vq;
35fe2a70d1SSasha Levin 	struct blk_dev			*bdev;
3669971b13SSasha Levin 	struct iovec			iov[VIRTIO_BLK_QUEUE_SIZE];
3769971b13SSasha Levin 	u16				out, in, head;
38*8b52f877SSasha Levin 	struct kvm			*kvm;
394749e795SSasha Levin };
404749e795SSasha Levin 
41fe2a70d1SSasha Levin struct blk_dev {
420528c2a7SPekka Enberg 	pthread_mutex_t			mutex;
43*8b52f877SSasha Levin 	pthread_mutex_t			req_mutex;
44*8b52f877SSasha Levin 
45ebe9ac19SSasha Levin 	struct list_head		list;
46*8b52f877SSasha Levin 	struct list_head		req_list;
470528c2a7SPekka Enberg 
48427948d5SSasha Levin 	struct virtio_pci		vpci;
4940ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
5038605e1cSSasha Levin 	struct disk_image		*disk;
51427948d5SSasha Levin 	u32				features;
5210eca11dSPekka Enberg 
5345e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
54*8b52f877SSasha Levin 	struct blk_dev_req		reqs[VIRTIO_BLK_QUEUE_SIZE];
55fbc2fbf9SPekka Enberg };
56fbc2fbf9SPekka Enberg 
57ebe9ac19SSasha Levin static LIST_HEAD(bdevs);
58312c62d1SSasha Levin static int compat_id;
5940ce993fSPekka Enberg 
60*8b52f877SSasha Levin static struct blk_dev_req *virtio_blk_req_pop(struct blk_dev *bdev)
614155ba8cSPekka Enberg {
62*8b52f877SSasha Levin 	struct blk_dev_req *req = NULL;
63*8b52f877SSasha Levin 
64*8b52f877SSasha Levin 	mutex_lock(&bdev->req_mutex);
65*8b52f877SSasha Levin 	if (!list_empty(&bdev->req_list)) {
66*8b52f877SSasha Levin 		req = list_first_entry(&bdev->req_list, struct blk_dev_req, list);
67*8b52f877SSasha Levin 		list_del_init(&req->list);
68*8b52f877SSasha Levin 	}
69*8b52f877SSasha Levin 	mutex_unlock(&bdev->req_mutex);
70*8b52f877SSasha Levin 
71*8b52f877SSasha Levin 	return req;
72*8b52f877SSasha Levin }
73*8b52f877SSasha Levin 
74*8b52f877SSasha Levin static void virtio_blk_req_push(struct blk_dev *bdev, struct blk_dev_req *req)
75*8b52f877SSasha Levin {
76*8b52f877SSasha Levin 	mutex_lock(&bdev->req_mutex);
77*8b52f877SSasha Levin 	list_add(&req->list, &bdev->req_list);
78*8b52f877SSasha Levin 	mutex_unlock(&bdev->req_mutex);
79*8b52f877SSasha Levin }
80*8b52f877SSasha Levin 
81*8b52f877SSasha Levin void virtio_blk_complete(void *param, long len)
82*8b52f877SSasha Levin {
83*8b52f877SSasha Levin 	struct blk_dev_req *req = param;
84*8b52f877SSasha Levin 	struct blk_dev *bdev = req->bdev;
85*8b52f877SSasha Levin 	int queueid = req->vq - bdev->vqs;
863fdf659dSSasha Levin 	u8 *status;
87*8b52f877SSasha Levin 
88*8b52f877SSasha Levin 	/* status */
89*8b52f877SSasha Levin 	status			= req->iov[req->out + req->in - 1].iov_base;
90*8b52f877SSasha Levin 	*status			= (len < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
91*8b52f877SSasha Levin 
92*8b52f877SSasha Levin 	mutex_lock(&bdev->mutex);
93*8b52f877SSasha Levin 	virt_queue__set_used_elem(req->vq, req->head, len);
94*8b52f877SSasha Levin 	mutex_unlock(&bdev->mutex);
95*8b52f877SSasha Levin 
96*8b52f877SSasha Levin 	virtio_pci__signal_vq(req->kvm, &bdev->vpci, queueid);
97*8b52f877SSasha Levin 
98*8b52f877SSasha Levin 	virtio_blk_req_push(req->bdev, req);
99*8b52f877SSasha Levin }
100*8b52f877SSasha Levin 
101*8b52f877SSasha Levin static void virtio_blk_do_io_request(struct kvm *kvm, struct blk_dev_req *req)
102*8b52f877SSasha Levin {
103*8b52f877SSasha Levin 	struct virtio_blk_outhdr *req_hdr;
10469971b13SSasha Levin 	ssize_t block_cnt;
10569971b13SSasha Levin 	struct blk_dev *bdev;
10669971b13SSasha Levin 	struct virt_queue *queue;
10769971b13SSasha Levin 	struct iovec *iov;
10869971b13SSasha Levin 	u16 out, in, head;
1094155ba8cSPekka Enberg 
11069971b13SSasha Levin 	block_cnt	= -1;
111*8b52f877SSasha Levin 	bdev		= req->bdev;
112*8b52f877SSasha Levin 	queue		= req->vq;
113*8b52f877SSasha Levin 	iov		= req->iov;
114*8b52f877SSasha Levin 	out		= req->out;
115*8b52f877SSasha Levin 	in		= req->in;
116*8b52f877SSasha Levin 	head		= req->head;
117*8b52f877SSasha Levin 	req_hdr		= iov[0].iov_base;
11803110ff3SAsias He 
119*8b52f877SSasha Levin 	switch (req_hdr->type) {
12003110ff3SAsias He 	case VIRTIO_BLK_T_IN:
121*8b52f877SSasha Levin 		block_cnt	= disk_image__read(bdev->disk, req_hdr->sector, iov + 1,
1225af21162SSasha Levin 					in + out - 2, NULL);
123258dd093SPekka Enberg 		break;
12403110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
125*8b52f877SSasha Levin 		block_cnt	= disk_image__write(bdev->disk, req_hdr->sector, iov + 1,
1265af21162SSasha Levin 					in + out - 2, NULL);
127258dd093SPekka Enberg 		break;
12829084a74SPrasad Joshi 	case VIRTIO_BLK_T_FLUSH:
12929084a74SPrasad Joshi 		block_cnt       = disk_image__flush(bdev->disk);
13029084a74SPrasad Joshi 		break;
131ff6462e8SSasha Levin 	case VIRTIO_BLK_T_GET_ID:
132ff6462e8SSasha Levin 		block_cnt	= VIRTIO_BLK_ID_BYTES;
133ff6462e8SSasha Levin 		disk_image__get_serial(bdev->disk, (iov + 1)->iov_base, &block_cnt);
134ff6462e8SSasha Levin 		break;
135258dd093SPekka Enberg 	default:
136*8b52f877SSasha Levin 		pr_warning("request type %d", req_hdr->type);
13770b53f25SSasha Levin 		block_cnt	= -1;
138407475bfSPekka Enberg 		break;
13903110ff3SAsias He 	}
14003110ff3SAsias He 
141*8b52f877SSasha Levin 	virtio_blk_complete(req, block_cnt);
1424155ba8cSPekka Enberg }
1434155ba8cSPekka Enberg 
14469971b13SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct virt_queue *vq, struct blk_dev *bdev)
14545e47970SAsias He {
14669971b13SSasha Levin 	while (virt_queue__available(vq)) {
147*8b52f877SSasha Levin 		struct blk_dev_req *req = virtio_blk_req_pop(bdev);
148407475bfSPekka Enberg 
149*8b52f877SSasha Levin 		*req		= (struct blk_dev_req) {
15069971b13SSasha Levin 			.vq	= vq,
15169971b13SSasha Levin 			.bdev	= bdev,
152*8b52f877SSasha Levin 			.kvm	= kvm,
15369971b13SSasha Levin 		};
154*8b52f877SSasha Levin 		req->head = virt_queue__get_iov(vq, req->iov, &req->out, &req->in, kvm);
15545e47970SAsias He 
156*8b52f877SSasha Levin 		virtio_blk_do_io_request(kvm, req);
15769971b13SSasha Levin 	}
1584baf6f73SSasha Levin }
1590528c2a7SPekka Enberg 
160427948d5SSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
161427948d5SSasha Levin {
162427948d5SSasha Levin 	struct blk_dev *bdev = dev;
163427948d5SSasha Levin 
164427948d5SSasha Levin 	((u8 *)(&bdev->blk_config))[offset] = data;
165427948d5SSasha Levin }
166427948d5SSasha Levin 
167427948d5SSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
168427948d5SSasha Levin {
169427948d5SSasha Levin 	struct blk_dev *bdev = dev;
170427948d5SSasha Levin 
171427948d5SSasha Levin 	return ((u8 *)(&bdev->blk_config))[offset];
172427948d5SSasha Levin }
173427948d5SSasha Levin 
174427948d5SSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
175427948d5SSasha Levin {
176427948d5SSasha Levin 	return 1UL << VIRTIO_BLK_F_SEG_MAX | 1UL << VIRTIO_BLK_F_FLUSH;
177427948d5SSasha Levin }
178427948d5SSasha Levin 
179427948d5SSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
180427948d5SSasha Levin {
181427948d5SSasha Levin 	struct blk_dev *bdev = dev;
182427948d5SSasha Levin 
183427948d5SSasha Levin 	bdev->features = features;
184427948d5SSasha Levin }
185427948d5SSasha Levin 
186427948d5SSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
187427948d5SSasha Levin {
188427948d5SSasha Levin 	struct blk_dev *bdev = dev;
189427948d5SSasha Levin 	struct virt_queue *queue;
190427948d5SSasha Levin 	void *p;
191427948d5SSasha Levin 
192312c62d1SSasha Levin 	compat__remove_message(compat_id);
193427948d5SSasha Levin 
194427948d5SSasha Levin 	queue			= &bdev->vqs[vq];
195427948d5SSasha Levin 	queue->pfn		= pfn;
196427948d5SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
197427948d5SSasha Levin 
198427948d5SSasha Levin 	vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
199427948d5SSasha Levin 
200427948d5SSasha Levin 	return 0;
201427948d5SSasha Levin }
202427948d5SSasha Levin 
203427948d5SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
204427948d5SSasha Levin {
205427948d5SSasha Levin 	struct blk_dev *bdev = dev;
206427948d5SSasha Levin 
207427948d5SSasha Levin 	virtio_blk_do_io(kvm, &bdev->vqs[vq], bdev);
208427948d5SSasha Levin 
209427948d5SSasha Levin 	return 0;
210427948d5SSasha Levin }
211427948d5SSasha Levin 
212427948d5SSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
213427948d5SSasha Levin {
214427948d5SSasha Levin 	struct blk_dev *bdev = dev;
215427948d5SSasha Levin 
216427948d5SSasha Levin 	return bdev->vqs[vq].pfn;
217427948d5SSasha Levin }
218427948d5SSasha Levin 
219427948d5SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
220427948d5SSasha Levin {
221427948d5SSasha Levin 	return VIRTIO_BLK_QUEUE_SIZE;
222427948d5SSasha Levin }
223427948d5SSasha Levin 
22443835ac9SSasha Levin void virtio_blk__init(struct kvm *kvm, struct disk_image *disk)
2254749e795SSasha Levin {
226fe2a70d1SSasha Levin 	struct blk_dev *bdev;
227*8b52f877SSasha Levin 	size_t i;
2284749e795SSasha Levin 
2294749e795SSasha Levin 	if (!disk)
2304749e795SSasha Levin 		return;
2314749e795SSasha Levin 
232ebe9ac19SSasha Levin 	bdev = calloc(1, sizeof(struct blk_dev));
233ebe9ac19SSasha Levin 	if (bdev == NULL)
234fe2a70d1SSasha Levin 		die("Failed allocating bdev");
2354749e795SSasha Levin 
236fe2a70d1SSasha Levin 	*bdev = (struct blk_dev) {
2374749e795SSasha Levin 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
238*8b52f877SSasha Levin 		.req_mutex		= PTHREAD_MUTEX_INITIALIZER,
2394749e795SSasha Levin 		.disk			= disk,
2404749e795SSasha Levin 		.blk_config		= (struct virtio_blk_config) {
2414749e795SSasha Levin 			.capacity	= disk->size / SECTOR_SIZE,
2423d7831a1SAsias He 			.seg_max	= DISK_SEG_MAX,
2434749e795SSasha Levin 		},
244427948d5SSasha Levin 	};
245427948d5SSasha Levin 
246507e02d8SAsias He 	virtio_pci__init(kvm, &bdev->vpci, bdev, PCI_DEVICE_ID_VIRTIO_BLK, VIRTIO_ID_BLOCK, PCI_CLASS_BLK);
247427948d5SSasha Levin 	bdev->vpci.ops = (struct virtio_pci_ops) {
248427948d5SSasha Levin 		.set_config		= set_config,
249427948d5SSasha Levin 		.get_config		= get_config,
250427948d5SSasha Levin 		.get_host_features	= get_host_features,
251427948d5SSasha Levin 		.set_guest_features	= set_guest_features,
252427948d5SSasha Levin 		.init_vq		= init_vq,
253427948d5SSasha Levin 		.notify_vq		= notify_vq,
254427948d5SSasha Levin 		.get_pfn_vq		= get_pfn_vq,
255427948d5SSasha Levin 		.get_size_vq		= get_size_vq,
256b30d05adSPekka Enberg 	};
257b30d05adSPekka Enberg 
258ebe9ac19SSasha Levin 	list_add_tail(&bdev->list, &bdevs);
259ebe9ac19SSasha Levin 
260*8b52f877SSasha Levin 	INIT_LIST_HEAD(&bdev->req_list);
261*8b52f877SSasha Levin 	for (i = 0; i < ARRAY_SIZE(bdev->reqs); i++)
262*8b52f877SSasha Levin 		list_add(&bdev->reqs[i].list, &bdev->req_list);
263*8b52f877SSasha Levin 
264312c62d1SSasha Levin 	if (compat_id != -1)
265312c62d1SSasha Levin 		compat_id = compat__add_message("virtio-blk device was not detected",
266404d164bSSasha Levin 						"While you have requested a virtio-blk device, "
267fc835ab3SSasha Levin 						"the guest kernel did not initialize it.\n"
268fc835ab3SSasha Levin 						"Please make sure that the guest kernel was "
269fc835ab3SSasha Levin 						"compiled with CONFIG_VIRTIO_BLK=y enabled "
270fc835ab3SSasha Levin 						"in its .config");
271b30d05adSPekka Enberg }
272bcb6aacaSPrasad Joshi 
273bcb6aacaSPrasad Joshi void virtio_blk__init_all(struct kvm *kvm)
274bcb6aacaSPrasad Joshi {
275bcb6aacaSPrasad Joshi 	int i;
276bcb6aacaSPrasad Joshi 
277bcb6aacaSPrasad Joshi 	for (i = 0; i < kvm->nr_disks; i++)
278bcb6aacaSPrasad Joshi 		virtio_blk__init(kvm, kvm->disks[i]);
279bcb6aacaSPrasad Joshi }
280a0a1e3c2SPrasad Joshi 
281a0a1e3c2SPrasad Joshi void virtio_blk__delete_all(struct kvm *kvm)
282a0a1e3c2SPrasad Joshi {
283ebe9ac19SSasha Levin 	while (!list_empty(&bdevs)) {
284ebe9ac19SSasha Levin 		struct blk_dev *bdev;
285a0a1e3c2SPrasad Joshi 
286ebe9ac19SSasha Levin 		bdev = list_first_entry(&bdevs, struct blk_dev, list);
287ebe9ac19SSasha Levin 		list_del(&bdev->list);
288ebe9ac19SSasha Levin 		free(bdev);
289ebe9ac19SSasha Levin 	}
290a0a1e3c2SPrasad Joshi }
291