1416b2c2dSAsias He #include "kvm/virtio-blk.h"
2b30d05adSPekka Enberg
331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h"
45a24a9f2SPekka Enberg #include "kvm/disk-image.h"
548427891SJean-Philippe Brucker #include "kvm/iovec.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"
14f41a132bSSasha Levin #include "kvm/virtio.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)
294059ad8bSAsias He #define VIRTIO_BLK_QUEUE_SIZE 256
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;
3748427891SJean-Philippe Brucker u8 *status;
388b52f877SSasha Levin struct kvm *kvm;
394749e795SSasha Levin };
404749e795SSasha Levin
41fe2a70d1SSasha Levin struct blk_dev {
42d3476f7dSSasha Levin struct mutex mutex;
438b52f877SSasha Levin
44ebe9ac19SSasha Levin struct list_head list;
450528c2a7SPekka Enberg
4602eca50cSAsias He struct virtio_device vdev;
4740ce993fSPekka Enberg struct virtio_blk_config blk_config;
48867b15ccSJean-Philippe Brucker u64 capacity;
4938605e1cSSasha Levin struct disk_image *disk;
5010eca11dSPekka Enberg
5145e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES];
528b52f877SSasha Levin struct blk_dev_req reqs[VIRTIO_BLK_QUEUE_SIZE];
535ac1178bSAsias He
545ac1178bSAsias He pthread_t io_thread;
555ac1178bSAsias He int io_efd;
565ac1178bSAsias He
575ac1178bSAsias He struct kvm *kvm;
58fbc2fbf9SPekka Enberg };
59fbc2fbf9SPekka Enberg
60ebe9ac19SSasha Levin static LIST_HEAD(bdevs);
61bdbbcb63SAsias He static int compat_id = -1;
6240ce993fSPekka Enberg
virtio_blk_complete(void * param,long len)638b52f877SSasha Levin void virtio_blk_complete(void *param, long len)
648b52f877SSasha Levin {
658b52f877SSasha Levin struct blk_dev_req *req = param;
668b52f877SSasha Levin struct blk_dev *bdev = req->bdev;
678b52f877SSasha Levin int queueid = req->vq - bdev->vqs;
683fdf659dSSasha Levin u8 *status;
698b52f877SSasha Levin
708b52f877SSasha Levin /* status */
7148427891SJean-Philippe Brucker status = req->status;
728b52f877SSasha Levin *status = (len < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
738b52f877SSasha Levin
748b52f877SSasha Levin mutex_lock(&bdev->mutex);
758b52f877SSasha Levin virt_queue__set_used_elem(req->vq, req->head, len);
768b52f877SSasha Levin mutex_unlock(&bdev->mutex);
778b52f877SSasha Levin
787ab3d207SSasha Levin if (virtio_queue__should_signal(&bdev->vqs[queueid]))
7902eca50cSAsias He bdev->vdev.ops->signal_vq(req->kvm, &bdev->vdev, queueid);
808b52f877SSasha Levin }
818b52f877SSasha Levin
virtio_blk_do_io_request(struct kvm * kvm,struct virt_queue * vq,struct blk_dev_req * req)8201dafc9eSMarc Zyngier static void virtio_blk_do_io_request(struct kvm *kvm, struct virt_queue *vq, struct blk_dev_req *req)
838b52f877SSasha Levin {
8448427891SJean-Philippe Brucker struct virtio_blk_outhdr req_hdr;
8548427891SJean-Philippe Brucker size_t iovcount, last_iov;
8669971b13SSasha Levin struct blk_dev *bdev;
8769971b13SSasha Levin struct iovec *iov;
8848427891SJean-Philippe Brucker ssize_t len;
8901dafc9eSMarc Zyngier u32 type;
9001dafc9eSMarc Zyngier u64 sector;
914155ba8cSPekka Enberg
928b52f877SSasha Levin bdev = req->bdev;
938b52f877SSasha Levin iov = req->iov;
9403110ff3SAsias He
9548427891SJean-Philippe Brucker iovcount = req->out;
9648427891SJean-Philippe Brucker len = memcpy_fromiovec_safe(&req_hdr, &iov, sizeof(req_hdr), &iovcount);
9748427891SJean-Philippe Brucker if (len) {
9848427891SJean-Philippe Brucker pr_warning("Failed to get header");
9948427891SJean-Philippe Brucker return;
10048427891SJean-Philippe Brucker }
10148427891SJean-Philippe Brucker
102b17552eeSAndre Przywara type = virtio_guest_to_host_u32(vq->endian, req_hdr.type);
103b17552eeSAndre Przywara sector = virtio_guest_to_host_u64(vq->endian, req_hdr.sector);
10448427891SJean-Philippe Brucker
10548427891SJean-Philippe Brucker iovcount += req->in;
10648427891SJean-Philippe Brucker if (!iov_size(iov, iovcount)) {
10748427891SJean-Philippe Brucker pr_warning("Invalid IOV");
10848427891SJean-Philippe Brucker return;
10948427891SJean-Philippe Brucker }
11048427891SJean-Philippe Brucker
11148427891SJean-Philippe Brucker /* Extract status byte from iovec */
11248427891SJean-Philippe Brucker last_iov = iovcount - 1;
11348427891SJean-Philippe Brucker while (!iov[last_iov].iov_len)
11448427891SJean-Philippe Brucker last_iov--;
11548427891SJean-Philippe Brucker iov[last_iov].iov_len--;
11648427891SJean-Philippe Brucker req->status = iov[last_iov].iov_base + iov[last_iov].iov_len;
11748427891SJean-Philippe Brucker if (!iov[last_iov].iov_len)
11848427891SJean-Philippe Brucker iovcount--;
11901dafc9eSMarc Zyngier
12001dafc9eSMarc Zyngier switch (type) {
12103110ff3SAsias He case VIRTIO_BLK_T_IN:
12248427891SJean-Philippe Brucker disk_image__read(bdev->disk, sector, iov, iovcount, req);
123258dd093SPekka Enberg break;
12403110ff3SAsias He case VIRTIO_BLK_T_OUT:
12548427891SJean-Philippe Brucker disk_image__write(bdev->disk, sector, iov, iovcount, req);
126258dd093SPekka Enberg break;
12729084a74SPrasad Joshi case VIRTIO_BLK_T_FLUSH:
12848427891SJean-Philippe Brucker len = disk_image__flush(bdev->disk);
12948427891SJean-Philippe Brucker virtio_blk_complete(req, len);
13029084a74SPrasad Joshi break;
131ff6462e8SSasha Levin case VIRTIO_BLK_T_GET_ID:
13248427891SJean-Philippe Brucker len = disk_image__get_serial(bdev->disk, iov, iovcount,
13348427891SJean-Philippe Brucker VIRTIO_BLK_ID_BYTES);
13448427891SJean-Philippe Brucker virtio_blk_complete(req, len);
135ff6462e8SSasha Levin break;
136258dd093SPekka Enberg default:
13701dafc9eSMarc Zyngier pr_warning("request type %d", type);
138407475bfSPekka Enberg break;
13903110ff3SAsias He }
1404155ba8cSPekka Enberg }
1414155ba8cSPekka Enberg
virtio_blk_do_io(struct kvm * kvm,struct virt_queue * vq,struct blk_dev * bdev)14269971b13SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct virt_queue *vq, struct blk_dev *bdev)
14345e47970SAsias He {
1442fddfdb5SAsias He struct blk_dev_req *req;
1452fddfdb5SAsias He u16 head;
146407475bfSPekka Enberg
1472fddfdb5SAsias He while (virt_queue__available(vq)) {
1482fddfdb5SAsias He head = virt_queue__pop(vq);
1492fddfdb5SAsias He req = &bdev->reqs[head];
15034239c78SAsias He req->head = virt_queue__get_head_iov(vq, req->iov, &req->out,
15134239c78SAsias He &req->in, head, kvm);
1522fddfdb5SAsias He req->vq = vq;
15345e47970SAsias He
15401dafc9eSMarc Zyngier virtio_blk_do_io_request(kvm, vq, req);
15569971b13SSasha Levin }
1564baf6f73SSasha Levin }
1570528c2a7SPekka Enberg
get_config(struct kvm * kvm,void * dev)158c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
159427948d5SSasha Levin {
160427948d5SSasha Levin struct blk_dev *bdev = dev;
161427948d5SSasha Levin
162c5ae742bSSasha Levin return ((u8 *)(&bdev->blk_config));
163427948d5SSasha Levin }
164427948d5SSasha Levin
get_config_size(struct kvm * kvm,void * dev)165e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev)
166e4730284SMartin Radev {
167e4730284SMartin Radev struct blk_dev *bdev = dev;
168e4730284SMartin Radev
169e4730284SMartin Radev return sizeof(bdev->blk_config);
170e4730284SMartin Radev }
171e4730284SMartin Radev
get_host_features(struct kvm * kvm,void * dev)1723c8f82b8SJean-Philippe Brucker static u64 get_host_features(struct kvm *kvm, void *dev)
173427948d5SSasha Levin {
1745c5cae75SJean-Philippe Brucker struct blk_dev *bdev = dev;
1755c5cae75SJean-Philippe Brucker
1767ab3d207SSasha Levin return 1UL << VIRTIO_BLK_F_SEG_MAX
1777ab3d207SSasha Levin | 1UL << VIRTIO_BLK_F_FLUSH
178754c8ce3SSasha Levin | 1UL << VIRTIO_RING_F_EVENT_IDX
1795c5cae75SJean-Philippe Brucker | 1UL << VIRTIO_RING_F_INDIRECT_DESC
18048427891SJean-Philippe Brucker | 1UL << VIRTIO_F_ANY_LAYOUT
1815c5cae75SJean-Philippe Brucker | (bdev->disk->readonly ? 1UL << VIRTIO_BLK_F_RO : 0);
182427948d5SSasha Levin }
183427948d5SSasha Levin
notify_status(struct kvm * kvm,void * dev,u32 status)18495242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status)
18595242e44SJean-Philippe Brucker {
186867b15ccSJean-Philippe Brucker struct blk_dev *bdev = dev;
187867b15ccSJean-Philippe Brucker struct virtio_blk_config *conf = &bdev->blk_config;
188867b15ccSJean-Philippe Brucker
189867b15ccSJean-Philippe Brucker if (!(status & VIRTIO__STATUS_CONFIG))
190867b15ccSJean-Philippe Brucker return;
191867b15ccSJean-Philippe Brucker
192b17552eeSAndre Przywara conf->capacity = virtio_host_to_guest_u64(bdev->vdev.endian, bdev->capacity);
193b17552eeSAndre Przywara conf->seg_max = virtio_host_to_guest_u32(bdev->vdev.endian, DISK_SEG_MAX);
19495242e44SJean-Philippe Brucker }
19595242e44SJean-Philippe Brucker
virtio_blk_thread(void * dev)1965ac1178bSAsias He static void *virtio_blk_thread(void *dev)
1975ac1178bSAsias He {
1985ac1178bSAsias He struct blk_dev *bdev = dev;
1995ac1178bSAsias He u64 data;
200a7aa454eSSasha Levin int r;
2015ac1178bSAsias He
202a4d8c55eSSasha Levin kvm__set_thread_name("virtio-blk-io");
203a4d8c55eSSasha Levin
2045ac1178bSAsias He while (1) {
205a7aa454eSSasha Levin r = read(bdev->io_efd, &data, sizeof(u64));
206a7aa454eSSasha Levin if (r < 0)
207a7aa454eSSasha Levin continue;
2085ac1178bSAsias He virtio_blk_do_io(bdev->kvm, &bdev->vqs[0], bdev);
2095ac1178bSAsias He }
2105ac1178bSAsias He
2115ac1178bSAsias He pthread_exit(NULL);
2125ac1178bSAsias He return NULL;
2135ac1178bSAsias He }
2145ac1178bSAsias He
init_vq(struct kvm * kvm,void * dev,u32 vq)215609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq)
2166730b51fSJean-Philippe Brucker {
2176730b51fSJean-Philippe Brucker unsigned int i;
2186730b51fSJean-Philippe Brucker struct blk_dev *bdev = dev;
2196730b51fSJean-Philippe Brucker
2206730b51fSJean-Philippe Brucker compat__remove_message(compat_id);
2216730b51fSJean-Philippe Brucker
222fd41cde0SJean-Philippe Brucker virtio_init_device_vq(kvm, &bdev->vdev, &bdev->vqs[vq],
223609ee906SJean-Philippe Brucker VIRTIO_BLK_QUEUE_SIZE);
2246730b51fSJean-Philippe Brucker
2256730b51fSJean-Philippe Brucker if (vq != 0)
2266730b51fSJean-Philippe Brucker return 0;
2276730b51fSJean-Philippe Brucker
2286730b51fSJean-Philippe Brucker for (i = 0; i < ARRAY_SIZE(bdev->reqs); i++) {
2296730b51fSJean-Philippe Brucker bdev->reqs[i] = (struct blk_dev_req) {
2306730b51fSJean-Philippe Brucker .bdev = bdev,
2316730b51fSJean-Philippe Brucker .kvm = kvm,
2326730b51fSJean-Philippe Brucker };
2336730b51fSJean-Philippe Brucker }
2346730b51fSJean-Philippe Brucker
2356730b51fSJean-Philippe Brucker mutex_init(&bdev->mutex);
2366730b51fSJean-Philippe Brucker bdev->io_efd = eventfd(0, 0);
2376730b51fSJean-Philippe Brucker if (bdev->io_efd < 0)
2386730b51fSJean-Philippe Brucker return -errno;
2396730b51fSJean-Philippe Brucker
2406730b51fSJean-Philippe Brucker if (pthread_create(&bdev->io_thread, NULL, virtio_blk_thread, bdev))
2416730b51fSJean-Philippe Brucker return -errno;
2426730b51fSJean-Philippe Brucker
2436730b51fSJean-Philippe Brucker return 0;
2446730b51fSJean-Philippe Brucker }
2456730b51fSJean-Philippe Brucker
exit_vq(struct kvm * kvm,void * dev,u32 vq)2466730b51fSJean-Philippe Brucker static void exit_vq(struct kvm *kvm, void *dev, u32 vq)
2476730b51fSJean-Philippe Brucker {
2486730b51fSJean-Philippe Brucker struct blk_dev *bdev = dev;
2496730b51fSJean-Philippe Brucker
2506730b51fSJean-Philippe Brucker if (vq != 0)
2516730b51fSJean-Philippe Brucker return;
2526730b51fSJean-Philippe Brucker
2536730b51fSJean-Philippe Brucker close(bdev->io_efd);
2546730b51fSJean-Philippe Brucker pthread_cancel(bdev->io_thread);
2556730b51fSJean-Philippe Brucker pthread_join(bdev->io_thread, NULL);
2563f218e89SJean-Philippe Brucker
2573f218e89SJean-Philippe Brucker disk_image__wait(bdev->disk);
2586730b51fSJean-Philippe Brucker }
2596730b51fSJean-Philippe Brucker
notify_vq(struct kvm * kvm,void * dev,u32 vq)260427948d5SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
261427948d5SSasha Levin {
262427948d5SSasha Levin struct blk_dev *bdev = dev;
2635ac1178bSAsias He u64 data = 1;
264a7aa454eSSasha Levin int r;
265427948d5SSasha Levin
266a7aa454eSSasha Levin r = write(bdev->io_efd, &data, sizeof(data));
267a7aa454eSSasha Levin if (r < 0)
268a7aa454eSSasha Levin return r;
269427948d5SSasha Levin
270427948d5SSasha Levin return 0;
271427948d5SSasha Levin }
272427948d5SSasha Levin
get_vq(struct kvm * kvm,void * dev,u32 vq)27353fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
274427948d5SSasha Levin {
275427948d5SSasha Levin struct blk_dev *bdev = dev;
276427948d5SSasha Levin
27753fbb17bSJean-Philippe Brucker return &bdev->vqs[vq];
278427948d5SSasha Levin }
279427948d5SSasha Levin
get_size_vq(struct kvm * kvm,void * dev,u32 vq)280427948d5SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
281427948d5SSasha Levin {
282ffcc904aSAsias He /* FIXME: dynamic */
283427948d5SSasha Levin return VIRTIO_BLK_QUEUE_SIZE;
284427948d5SSasha Levin }
285427948d5SSasha Levin
set_size_vq(struct kvm * kvm,void * dev,u32 vq,int size)286ffcc904aSAsias He static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
287ffcc904aSAsias He {
288ffcc904aSAsias He /* FIXME: dynamic */
289ffcc904aSAsias He return size;
290ffcc904aSAsias He }
291ffcc904aSAsias He
get_vq_count(struct kvm * kvm,void * dev)29231e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
293b98ac591SJean-Philippe Brucker {
294b98ac591SJean-Philippe Brucker return NUM_VIRT_QUEUES;
295b98ac591SJean-Philippe Brucker }
296b98ac591SJean-Philippe Brucker
29715542babSAndre Przywara static struct virtio_ops blk_dev_virtio_ops = {
2981c47ce69SSasha Levin .get_config = get_config,
299e4730284SMartin Radev .get_config_size = get_config_size,
3001c47ce69SSasha Levin .get_host_features = get_host_features,
301b98ac591SJean-Philippe Brucker .get_vq_count = get_vq_count,
3021c47ce69SSasha Levin .init_vq = init_vq,
3036730b51fSJean-Philippe Brucker .exit_vq = exit_vq,
30495242e44SJean-Philippe Brucker .notify_status = notify_status,
3051c47ce69SSasha Levin .notify_vq = notify_vq,
30653fbb17bSJean-Philippe Brucker .get_vq = get_vq,
3071c47ce69SSasha Levin .get_size_vq = get_size_vq,
308ffcc904aSAsias He .set_size_vq = set_size_vq,
3091c47ce69SSasha Levin };
3101c47ce69SSasha Levin
virtio_blk__init_one(struct kvm * kvm,struct disk_image * disk)3119f9207c5SSasha Levin static int virtio_blk__init_one(struct kvm *kvm, struct disk_image *disk)
3124749e795SSasha Levin {
313fe2a70d1SSasha Levin struct blk_dev *bdev;
314db927775SAlexandru Elisei int r;
3154749e795SSasha Levin
3164749e795SSasha Levin if (!disk)
3179f9207c5SSasha Levin return -EINVAL;
3184749e795SSasha Levin
319ebe9ac19SSasha Levin bdev = calloc(1, sizeof(struct blk_dev));
320ebe9ac19SSasha Levin if (bdev == NULL)
3219f9207c5SSasha Levin return -ENOMEM;
3224749e795SSasha Levin
323fe2a70d1SSasha Levin *bdev = (struct blk_dev) {
3244749e795SSasha Levin .disk = disk,
3254749e795SSasha Levin .capacity = disk->size / SECTOR_SIZE,
3265ac1178bSAsias He .kvm = kvm,
327427948d5SSasha Levin };
328427948d5SSasha Levin
329db927775SAlexandru Elisei list_add_tail(&bdev->list, &bdevs);
330db927775SAlexandru Elisei
331db927775SAlexandru Elisei r = virtio_init(kvm, bdev, &bdev->vdev, &blk_dev_virtio_ops,
3329b46ebc5SRajnesh Kanwal kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_BLK,
333ae06ce71SWill Deacon VIRTIO_ID_BLOCK, PCI_CLASS_BLK);
334db927775SAlexandru Elisei if (r < 0)
335db927775SAlexandru Elisei return r;
336ebe9ac19SSasha Levin
337fb434ac3SSasha Levin disk_image__set_callback(bdev->disk, virtio_blk_complete);
338fb434ac3SSasha Levin
339d278197dSAsias He if (compat_id == -1)
34052f34d2cSAsias He compat_id = virtio_compat_add_message("virtio-blk", "CONFIG_VIRTIO_BLK");
3415ac1178bSAsias He
3429f9207c5SSasha Levin return 0;
343b30d05adSPekka Enberg }
344bcb6aacaSPrasad Joshi
virtio_blk__exit_one(struct kvm * kvm,struct blk_dev * bdev)3459f9207c5SSasha Levin static int virtio_blk__exit_one(struct kvm *kvm, struct blk_dev *bdev)
346bcb6aacaSPrasad Joshi {
3479f9207c5SSasha Levin list_del(&bdev->list);
348*74af1456SEduardo Bart virtio_exit(kvm, &bdev->vdev);
3499f9207c5SSasha Levin free(bdev);
350bcb6aacaSPrasad Joshi
3519f9207c5SSasha Levin return 0;
352bcb6aacaSPrasad Joshi }
353a0a1e3c2SPrasad Joshi
virtio_blk__init(struct kvm * kvm)3549f9207c5SSasha Levin int virtio_blk__init(struct kvm *kvm)
3559f9207c5SSasha Levin {
3569f9207c5SSasha Levin int i, r = 0;
3579f9207c5SSasha Levin
3589f9207c5SSasha Levin for (i = 0; i < kvm->nr_disks; i++) {
359a67da3beSAsias He if (kvm->disks[i]->wwpn)
360a67da3beSAsias He continue;
3619f9207c5SSasha Levin r = virtio_blk__init_one(kvm, kvm->disks[i]);
3629f9207c5SSasha Levin if (r < 0)
3639f9207c5SSasha Levin goto cleanup;
3649f9207c5SSasha Levin }
3659f9207c5SSasha Levin
3669f9207c5SSasha Levin return 0;
3679f9207c5SSasha Levin cleanup:
368db927775SAlexandru Elisei virtio_blk__exit(kvm);
369db927775SAlexandru Elisei return r;
3709f9207c5SSasha Levin }
37149a8afd1SSasha Levin virtio_dev_init(virtio_blk__init);
3729f9207c5SSasha Levin
virtio_blk__exit(struct kvm * kvm)3739f9207c5SSasha Levin int virtio_blk__exit(struct kvm *kvm)
374a0a1e3c2SPrasad Joshi {
375ebe9ac19SSasha Levin while (!list_empty(&bdevs)) {
376ebe9ac19SSasha Levin struct blk_dev *bdev;
377a0a1e3c2SPrasad Joshi
378ebe9ac19SSasha Levin bdev = list_first_entry(&bdevs, struct blk_dev, list);
3799f9207c5SSasha Levin virtio_blk__exit_one(kvm, bdev);
380ebe9ac19SSasha Levin }
3819f9207c5SSasha Levin
3829f9207c5SSasha Levin return 0;
383a0a1e3c2SPrasad Joshi }
38449a8afd1SSasha Levin virtio_dev_exit(virtio_blk__exit);
385