xref: /kvmtool/virtio/blk.c (revision db927775acdbc647b5beeb8e29522d1e3fe39987)
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"
14b30d05adSPekka Enberg 
1520c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1620c64ecaSPekka Enberg #include <linux/virtio_blk.h>
17427948d5SSasha Levin #include <linux/kernel.h>
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 
243d7831a1SAsias He /*
253d7831a1SAsias He  * the header and status consume too entries
263d7831a1SAsias He  */
273d7831a1SAsias He #define DISK_SEG_MAX			(VIRTIO_BLK_QUEUE_SIZE - 2)
284059ad8bSAsias He #define VIRTIO_BLK_QUEUE_SIZE		256
29f41a132bSSasha Levin #define NUM_VIRT_QUEUES			1
3010eca11dSPekka Enberg 
318b52f877SSasha Levin struct blk_dev_req {
324749e795SSasha Levin 	struct virt_queue		*vq;
33fe2a70d1SSasha Levin 	struct blk_dev			*bdev;
3469971b13SSasha Levin 	struct iovec			iov[VIRTIO_BLK_QUEUE_SIZE];
3569971b13SSasha Levin 	u16				out, in, head;
368b52f877SSasha Levin 	struct kvm			*kvm;
374749e795SSasha Levin };
384749e795SSasha Levin 
39fe2a70d1SSasha Levin struct blk_dev {
40d3476f7dSSasha Levin 	struct mutex			mutex;
418b52f877SSasha Levin 
42ebe9ac19SSasha Levin 	struct list_head		list;
430528c2a7SPekka Enberg 
4402eca50cSAsias He 	struct virtio_device		vdev;
4540ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
4638605e1cSSasha Levin 	struct disk_image		*disk;
47427948d5SSasha Levin 	u32				features;
4810eca11dSPekka Enberg 
4945e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
508b52f877SSasha Levin 	struct blk_dev_req		reqs[VIRTIO_BLK_QUEUE_SIZE];
515ac1178bSAsias He 
525ac1178bSAsias He 	pthread_t			io_thread;
535ac1178bSAsias He 	int				io_efd;
545ac1178bSAsias He 
555ac1178bSAsias He 	struct kvm			*kvm;
56fbc2fbf9SPekka Enberg };
57fbc2fbf9SPekka Enberg 
58ebe9ac19SSasha Levin static LIST_HEAD(bdevs);
59bdbbcb63SAsias He static int compat_id = -1;
6040ce993fSPekka Enberg 
618b52f877SSasha Levin void virtio_blk_complete(void *param, long len)
628b52f877SSasha Levin {
638b52f877SSasha Levin 	struct blk_dev_req *req = param;
648b52f877SSasha Levin 	struct blk_dev *bdev = req->bdev;
658b52f877SSasha Levin 	int queueid = req->vq - bdev->vqs;
663fdf659dSSasha Levin 	u8 *status;
678b52f877SSasha Levin 
688b52f877SSasha Levin 	/* status */
698b52f877SSasha Levin 	status	= req->iov[req->out + req->in - 1].iov_base;
708b52f877SSasha Levin 	*status	= (len < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
718b52f877SSasha Levin 
728b52f877SSasha Levin 	mutex_lock(&bdev->mutex);
738b52f877SSasha Levin 	virt_queue__set_used_elem(req->vq, req->head, len);
748b52f877SSasha Levin 	mutex_unlock(&bdev->mutex);
758b52f877SSasha Levin 
767ab3d207SSasha Levin 	if (virtio_queue__should_signal(&bdev->vqs[queueid]))
7702eca50cSAsias He 		bdev->vdev.ops->signal_vq(req->kvm, &bdev->vdev, queueid);
788b52f877SSasha Levin }
798b52f877SSasha Levin 
8001dafc9eSMarc Zyngier static void virtio_blk_do_io_request(struct kvm *kvm, struct virt_queue *vq, struct blk_dev_req *req)
818b52f877SSasha Levin {
828b52f877SSasha Levin 	struct virtio_blk_outhdr *req_hdr;
8369971b13SSasha Levin 	ssize_t block_cnt;
8469971b13SSasha Levin 	struct blk_dev *bdev;
8569971b13SSasha Levin 	struct iovec *iov;
86f41a132bSSasha Levin 	u16 out, in;
8701dafc9eSMarc Zyngier 	u32 type;
8801dafc9eSMarc Zyngier 	u64 sector;
894155ba8cSPekka Enberg 
9069971b13SSasha Levin 	block_cnt	= -1;
918b52f877SSasha Levin 	bdev		= req->bdev;
928b52f877SSasha Levin 	iov		= req->iov;
938b52f877SSasha Levin 	out		= req->out;
948b52f877SSasha Levin 	in		= req->in;
958b52f877SSasha Levin 	req_hdr		= iov[0].iov_base;
9603110ff3SAsias He 
9701dafc9eSMarc Zyngier 	type = virtio_guest_to_host_u32(vq, req_hdr->type);
9801dafc9eSMarc Zyngier 	sector = virtio_guest_to_host_u64(vq, req_hdr->sector);
9901dafc9eSMarc Zyngier 
10001dafc9eSMarc Zyngier 	switch (type) {
10103110ff3SAsias He 	case VIRTIO_BLK_T_IN:
10201dafc9eSMarc Zyngier 		block_cnt = disk_image__read(bdev->disk, sector,
10334239c78SAsias He 				iov + 1, in + out - 2, req);
104258dd093SPekka Enberg 		break;
10503110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
10601dafc9eSMarc Zyngier 		block_cnt = disk_image__write(bdev->disk, sector,
10734239c78SAsias He 				iov + 1, in + out - 2, req);
108258dd093SPekka Enberg 		break;
10929084a74SPrasad Joshi 	case VIRTIO_BLK_T_FLUSH:
11029084a74SPrasad Joshi 		block_cnt = disk_image__flush(bdev->disk);
111fb434ac3SSasha Levin 		virtio_blk_complete(req, block_cnt);
11229084a74SPrasad Joshi 		break;
113ff6462e8SSasha Levin 	case VIRTIO_BLK_T_GET_ID:
114ff6462e8SSasha Levin 		block_cnt = VIRTIO_BLK_ID_BYTES;
11534239c78SAsias He 		disk_image__get_serial(bdev->disk,
11634239c78SAsias He 				(iov + 1)->iov_base, &block_cnt);
117fb434ac3SSasha Levin 		virtio_blk_complete(req, block_cnt);
118ff6462e8SSasha Levin 		break;
119258dd093SPekka Enberg 	default:
12001dafc9eSMarc Zyngier 		pr_warning("request type %d", type);
12170b53f25SSasha Levin 		block_cnt	= -1;
122407475bfSPekka Enberg 		break;
12303110ff3SAsias He 	}
1244155ba8cSPekka Enberg }
1254155ba8cSPekka Enberg 
12669971b13SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct virt_queue *vq, struct blk_dev *bdev)
12745e47970SAsias He {
1282fddfdb5SAsias He 	struct blk_dev_req *req;
1292fddfdb5SAsias He 	u16 head;
130407475bfSPekka Enberg 
1312fddfdb5SAsias He 	while (virt_queue__available(vq)) {
1322fddfdb5SAsias He 		head		= virt_queue__pop(vq);
1332fddfdb5SAsias He 		req		= &bdev->reqs[head];
13434239c78SAsias He 		req->head	= virt_queue__get_head_iov(vq, req->iov, &req->out,
13534239c78SAsias He 					&req->in, head, kvm);
1362fddfdb5SAsias He 		req->vq		= vq;
13745e47970SAsias He 
13801dafc9eSMarc Zyngier 		virtio_blk_do_io_request(kvm, vq, req);
13969971b13SSasha Levin 	}
1404baf6f73SSasha Levin }
1410528c2a7SPekka Enberg 
142c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
143427948d5SSasha Levin {
144427948d5SSasha Levin 	struct blk_dev *bdev = dev;
145427948d5SSasha Levin 
146c5ae742bSSasha Levin 	return ((u8 *)(&bdev->blk_config));
147427948d5SSasha Levin }
148427948d5SSasha Levin 
149427948d5SSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
150427948d5SSasha Levin {
1515c5cae75SJean-Philippe Brucker 	struct blk_dev *bdev = dev;
1525c5cae75SJean-Philippe Brucker 
1537ab3d207SSasha Levin 	return	1UL << VIRTIO_BLK_F_SEG_MAX
1547ab3d207SSasha Levin 		| 1UL << VIRTIO_BLK_F_FLUSH
155754c8ce3SSasha Levin 		| 1UL << VIRTIO_RING_F_EVENT_IDX
1565c5cae75SJean-Philippe Brucker 		| 1UL << VIRTIO_RING_F_INDIRECT_DESC
1575c5cae75SJean-Philippe Brucker 		| (bdev->disk->readonly ? 1UL << VIRTIO_BLK_F_RO : 0);
158427948d5SSasha Levin }
159427948d5SSasha Levin 
160427948d5SSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
161427948d5SSasha Levin {
162427948d5SSasha Levin 	struct blk_dev *bdev = dev;
16301dafc9eSMarc Zyngier 	struct virtio_blk_config *conf = &bdev->blk_config;
164427948d5SSasha Levin 
165427948d5SSasha Levin 	bdev->features = features;
16601dafc9eSMarc Zyngier 
16701dafc9eSMarc Zyngier 	conf->capacity = virtio_host_to_guest_u64(&bdev->vdev, conf->capacity);
16801dafc9eSMarc Zyngier 	conf->size_max = virtio_host_to_guest_u32(&bdev->vdev, conf->size_max);
16901dafc9eSMarc Zyngier 	conf->seg_max = virtio_host_to_guest_u32(&bdev->vdev, conf->seg_max);
17001dafc9eSMarc Zyngier 
17101dafc9eSMarc Zyngier 	/* Geometry */
172eaeaf608SAndre Przywara 	conf->geometry.cylinders = virtio_host_to_guest_u16(&bdev->vdev,
173eaeaf608SAndre Przywara 						conf->geometry.cylinders);
17401dafc9eSMarc Zyngier 
17501dafc9eSMarc Zyngier 	conf->blk_size = virtio_host_to_guest_u32(&bdev->vdev, conf->blk_size);
17601dafc9eSMarc Zyngier 	conf->min_io_size = virtio_host_to_guest_u16(&bdev->vdev, conf->min_io_size);
17701dafc9eSMarc Zyngier 	conf->opt_io_size = virtio_host_to_guest_u32(&bdev->vdev, conf->opt_io_size);
178427948d5SSasha Levin }
179427948d5SSasha Levin 
18095242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status)
18195242e44SJean-Philippe Brucker {
18295242e44SJean-Philippe Brucker }
18395242e44SJean-Philippe Brucker 
1845ac1178bSAsias He static void *virtio_blk_thread(void *dev)
1855ac1178bSAsias He {
1865ac1178bSAsias He 	struct blk_dev *bdev = dev;
1875ac1178bSAsias He 	u64 data;
188a7aa454eSSasha Levin 	int r;
1895ac1178bSAsias He 
190a4d8c55eSSasha Levin 	kvm__set_thread_name("virtio-blk-io");
191a4d8c55eSSasha Levin 
1925ac1178bSAsias He 	while (1) {
193a7aa454eSSasha Levin 		r = read(bdev->io_efd, &data, sizeof(u64));
194a7aa454eSSasha Levin 		if (r < 0)
195a7aa454eSSasha Levin 			continue;
1965ac1178bSAsias He 		virtio_blk_do_io(bdev->kvm, &bdev->vqs[0], bdev);
1975ac1178bSAsias He 	}
1985ac1178bSAsias He 
1995ac1178bSAsias He 	pthread_exit(NULL);
2005ac1178bSAsias He 	return NULL;
2015ac1178bSAsias He }
2025ac1178bSAsias He 
2036730b51fSJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
2046730b51fSJean-Philippe Brucker 		   u32 pfn)
2056730b51fSJean-Philippe Brucker {
2066730b51fSJean-Philippe Brucker 	unsigned int i;
2076730b51fSJean-Philippe Brucker 	struct blk_dev *bdev = dev;
2086730b51fSJean-Philippe Brucker 	struct virt_queue *queue;
2096730b51fSJean-Philippe Brucker 	void *p;
2106730b51fSJean-Philippe Brucker 
2116730b51fSJean-Philippe Brucker 	compat__remove_message(compat_id);
2126730b51fSJean-Philippe Brucker 
2136730b51fSJean-Philippe Brucker 	queue		= &bdev->vqs[vq];
2146730b51fSJean-Philippe Brucker 	queue->pfn	= pfn;
2156730b51fSJean-Philippe Brucker 	p		= virtio_get_vq(kvm, queue->pfn, page_size);
2166730b51fSJean-Philippe Brucker 
2176730b51fSJean-Philippe Brucker 	vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, align);
2186730b51fSJean-Philippe Brucker 	virtio_init_device_vq(&bdev->vdev, queue);
2196730b51fSJean-Philippe Brucker 
2206730b51fSJean-Philippe Brucker 	if (vq != 0)
2216730b51fSJean-Philippe Brucker 		return 0;
2226730b51fSJean-Philippe Brucker 
2236730b51fSJean-Philippe Brucker 	for (i = 0; i < ARRAY_SIZE(bdev->reqs); i++) {
2246730b51fSJean-Philippe Brucker 		bdev->reqs[i] = (struct blk_dev_req) {
2256730b51fSJean-Philippe Brucker 			.bdev = bdev,
2266730b51fSJean-Philippe Brucker 			.kvm = kvm,
2276730b51fSJean-Philippe Brucker 		};
2286730b51fSJean-Philippe Brucker 	}
2296730b51fSJean-Philippe Brucker 
2306730b51fSJean-Philippe Brucker 	mutex_init(&bdev->mutex);
2316730b51fSJean-Philippe Brucker 	bdev->io_efd = eventfd(0, 0);
2326730b51fSJean-Philippe Brucker 	if (bdev->io_efd < 0)
2336730b51fSJean-Philippe Brucker 		return -errno;
2346730b51fSJean-Philippe Brucker 
2356730b51fSJean-Philippe Brucker 	if (pthread_create(&bdev->io_thread, NULL, virtio_blk_thread, bdev))
2366730b51fSJean-Philippe Brucker 		return -errno;
2376730b51fSJean-Philippe Brucker 
2386730b51fSJean-Philippe Brucker 	return 0;
2396730b51fSJean-Philippe Brucker }
2406730b51fSJean-Philippe Brucker 
2416730b51fSJean-Philippe Brucker static void exit_vq(struct kvm *kvm, void *dev, u32 vq)
2426730b51fSJean-Philippe Brucker {
2436730b51fSJean-Philippe Brucker 	struct blk_dev *bdev = dev;
2446730b51fSJean-Philippe Brucker 
2456730b51fSJean-Philippe Brucker 	if (vq != 0)
2466730b51fSJean-Philippe Brucker 		return;
2476730b51fSJean-Philippe Brucker 
2486730b51fSJean-Philippe Brucker 	close(bdev->io_efd);
2496730b51fSJean-Philippe Brucker 	pthread_cancel(bdev->io_thread);
2506730b51fSJean-Philippe Brucker 	pthread_join(bdev->io_thread, NULL);
2513f218e89SJean-Philippe Brucker 
2523f218e89SJean-Philippe Brucker 	disk_image__wait(bdev->disk);
2536730b51fSJean-Philippe Brucker }
2546730b51fSJean-Philippe Brucker 
255427948d5SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
256427948d5SSasha Levin {
257427948d5SSasha Levin 	struct blk_dev *bdev = dev;
2585ac1178bSAsias He 	u64 data = 1;
259a7aa454eSSasha Levin 	int r;
260427948d5SSasha Levin 
261a7aa454eSSasha Levin 	r = write(bdev->io_efd, &data, sizeof(data));
262a7aa454eSSasha Levin 	if (r < 0)
263a7aa454eSSasha Levin 		return r;
264427948d5SSasha Levin 
265427948d5SSasha Levin 	return 0;
266427948d5SSasha Levin }
267427948d5SSasha Levin 
26853fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
269427948d5SSasha Levin {
270427948d5SSasha Levin 	struct blk_dev *bdev = dev;
271427948d5SSasha Levin 
27253fbb17bSJean-Philippe Brucker 	return &bdev->vqs[vq];
273427948d5SSasha Levin }
274427948d5SSasha Levin 
275427948d5SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
276427948d5SSasha Levin {
277ffcc904aSAsias He 	/* FIXME: dynamic */
278427948d5SSasha Levin 	return VIRTIO_BLK_QUEUE_SIZE;
279427948d5SSasha Levin }
280427948d5SSasha Levin 
281ffcc904aSAsias He static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
282ffcc904aSAsias He {
283ffcc904aSAsias He 	/* FIXME: dynamic */
284ffcc904aSAsias He 	return size;
285ffcc904aSAsias He }
286ffcc904aSAsias He 
287b98ac591SJean-Philippe Brucker static int get_vq_count(struct kvm *kvm, void *dev)
288b98ac591SJean-Philippe Brucker {
289b98ac591SJean-Philippe Brucker 	return NUM_VIRT_QUEUES;
290b98ac591SJean-Philippe Brucker }
291b98ac591SJean-Philippe Brucker 
29215542babSAndre Przywara static struct virtio_ops blk_dev_virtio_ops = {
2931c47ce69SSasha Levin 	.get_config		= get_config,
2941c47ce69SSasha Levin 	.get_host_features	= get_host_features,
2951c47ce69SSasha Levin 	.set_guest_features	= set_guest_features,
296b98ac591SJean-Philippe Brucker 	.get_vq_count		= get_vq_count,
2971c47ce69SSasha Levin 	.init_vq		= init_vq,
2986730b51fSJean-Philippe Brucker 	.exit_vq		= exit_vq,
29995242e44SJean-Philippe Brucker 	.notify_status		= notify_status,
3001c47ce69SSasha Levin 	.notify_vq		= notify_vq,
30153fbb17bSJean-Philippe Brucker 	.get_vq			= get_vq,
3021c47ce69SSasha Levin 	.get_size_vq		= get_size_vq,
303ffcc904aSAsias He 	.set_size_vq		= set_size_vq,
3041c47ce69SSasha Levin };
3051c47ce69SSasha Levin 
3069f9207c5SSasha Levin static int virtio_blk__init_one(struct kvm *kvm, struct disk_image *disk)
3074749e795SSasha Levin {
308fe2a70d1SSasha Levin 	struct blk_dev *bdev;
309*db927775SAlexandru Elisei 	int r;
3104749e795SSasha Levin 
3114749e795SSasha Levin 	if (!disk)
3129f9207c5SSasha Levin 		return -EINVAL;
3134749e795SSasha Levin 
314ebe9ac19SSasha Levin 	bdev = calloc(1, sizeof(struct blk_dev));
315ebe9ac19SSasha Levin 	if (bdev == NULL)
3169f9207c5SSasha Levin 		return -ENOMEM;
3174749e795SSasha Levin 
318fe2a70d1SSasha Levin 	*bdev = (struct blk_dev) {
3194749e795SSasha Levin 		.disk			= disk,
3204749e795SSasha Levin 		.blk_config		= (struct virtio_blk_config) {
3214749e795SSasha Levin 			.capacity	= disk->size / SECTOR_SIZE,
3223d7831a1SAsias He 			.seg_max	= DISK_SEG_MAX,
3234749e795SSasha Levin 		},
3245ac1178bSAsias He 		.kvm			= kvm,
325427948d5SSasha Levin 	};
326427948d5SSasha Levin 
327*db927775SAlexandru Elisei 	list_add_tail(&bdev->list, &bdevs);
328*db927775SAlexandru Elisei 
329*db927775SAlexandru Elisei 	r = virtio_init(kvm, bdev, &bdev->vdev, &blk_dev_virtio_ops,
330d97dadecSWill Deacon 			VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_BLK,
331ae06ce71SWill Deacon 			VIRTIO_ID_BLOCK, PCI_CLASS_BLK);
332*db927775SAlexandru Elisei 	if (r < 0)
333*db927775SAlexandru Elisei 		return r;
334ebe9ac19SSasha Levin 
335fb434ac3SSasha Levin 	disk_image__set_callback(bdev->disk, virtio_blk_complete);
336fb434ac3SSasha Levin 
337d278197dSAsias He 	if (compat_id == -1)
33852f34d2cSAsias He 		compat_id = virtio_compat_add_message("virtio-blk", "CONFIG_VIRTIO_BLK");
3395ac1178bSAsias He 
3409f9207c5SSasha Levin 	return 0;
341b30d05adSPekka Enberg }
342bcb6aacaSPrasad Joshi 
3439f9207c5SSasha Levin static int virtio_blk__exit_one(struct kvm *kvm, struct blk_dev *bdev)
344bcb6aacaSPrasad Joshi {
3459f9207c5SSasha Levin 	list_del(&bdev->list);
3469f9207c5SSasha Levin 	free(bdev);
347bcb6aacaSPrasad Joshi 
3489f9207c5SSasha Levin 	return 0;
349bcb6aacaSPrasad Joshi }
350a0a1e3c2SPrasad Joshi 
3519f9207c5SSasha Levin int virtio_blk__init(struct kvm *kvm)
3529f9207c5SSasha Levin {
3539f9207c5SSasha Levin 	int i, r = 0;
3549f9207c5SSasha Levin 
3559f9207c5SSasha Levin 	for (i = 0; i < kvm->nr_disks; i++) {
356a67da3beSAsias He 		if (kvm->disks[i]->wwpn)
357a67da3beSAsias He 			continue;
3589f9207c5SSasha Levin 		r = virtio_blk__init_one(kvm, kvm->disks[i]);
3599f9207c5SSasha Levin 		if (r < 0)
3609f9207c5SSasha Levin 			goto cleanup;
3619f9207c5SSasha Levin 	}
3629f9207c5SSasha Levin 
3639f9207c5SSasha Levin 	return 0;
3649f9207c5SSasha Levin cleanup:
365*db927775SAlexandru Elisei 	virtio_blk__exit(kvm);
366*db927775SAlexandru Elisei 	return r;
3679f9207c5SSasha Levin }
36849a8afd1SSasha Levin virtio_dev_init(virtio_blk__init);
3699f9207c5SSasha Levin 
3709f9207c5SSasha Levin int virtio_blk__exit(struct kvm *kvm)
371a0a1e3c2SPrasad Joshi {
372ebe9ac19SSasha Levin 	while (!list_empty(&bdevs)) {
373ebe9ac19SSasha Levin 		struct blk_dev *bdev;
374a0a1e3c2SPrasad Joshi 
375ebe9ac19SSasha Levin 		bdev = list_first_entry(&bdevs, struct blk_dev, list);
3769f9207c5SSasha Levin 		virtio_blk__exit_one(kvm, bdev);
377ebe9ac19SSasha Levin 	}
3789f9207c5SSasha Levin 
3799f9207c5SSasha Levin 	return 0;
380a0a1e3c2SPrasad Joshi }
38149a8afd1SSasha Levin virtio_dev_exit(virtio_blk__exit);
382