xref: /kvmtool/virtio/blk.c (revision 427948d58f960af26d7e24d37ef474ca6080d5a7)
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"
13*427948d5SSasha Levin #include "kvm/virtio-pci.h"
14b30d05adSPekka Enberg 
1520c64ecaSPekka Enberg #include <linux/virtio_ring.h>
1620c64ecaSPekka Enberg #include <linux/virtio_blk.h>
170528c2a7SPekka Enberg 
18*427948d5SSasha 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 
32fe2a70d1SSasha Levin struct blk_dev_job {
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;
37df0c7f57SSasha Levin 	struct thread_pool__job		job_id;
384749e795SSasha Levin };
394749e795SSasha Levin 
40fe2a70d1SSasha Levin struct blk_dev {
410528c2a7SPekka Enberg 	pthread_mutex_t			mutex;
42ebe9ac19SSasha Levin 	struct list_head		list;
430528c2a7SPekka Enberg 
44*427948d5SSasha Levin 	struct virtio_pci		vpci;
4540ce993fSPekka Enberg 	struct virtio_blk_config	blk_config;
4638605e1cSSasha Levin 	struct disk_image		*disk;
47404d164bSSasha Levin 	int				compat_id;
48*427948d5SSasha Levin 	u32				features;
4910eca11dSPekka Enberg 
5045e47970SAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
5169971b13SSasha Levin 	struct blk_dev_job		jobs[VIRTIO_BLK_QUEUE_SIZE];
5269971b13SSasha Levin 	u16				job_idx;
53fbc2fbf9SPekka Enberg };
54fbc2fbf9SPekka Enberg 
55ebe9ac19SSasha Levin static LIST_HEAD(bdevs);
5640ce993fSPekka Enberg 
5769971b13SSasha Levin static void virtio_blk_do_io_request(struct kvm *kvm, void *param)
584155ba8cSPekka Enberg {
594155ba8cSPekka Enberg 	struct virtio_blk_outhdr *req;
603fdf659dSSasha Levin 	u8 *status;
6169971b13SSasha Levin 	ssize_t block_cnt;
6269971b13SSasha Levin 	struct blk_dev_job *job;
6369971b13SSasha Levin 	struct blk_dev *bdev;
6469971b13SSasha Levin 	struct virt_queue *queue;
6569971b13SSasha Levin 	struct iovec *iov;
6669971b13SSasha Levin 	u16 out, in, head;
674155ba8cSPekka Enberg 
6869971b13SSasha Levin 	block_cnt	= -1;
6969971b13SSasha Levin 	job		= param;
7069971b13SSasha Levin 	bdev		= job->bdev;
7169971b13SSasha Levin 	queue		= job->vq;
7269971b13SSasha Levin 	iov		= job->iov;
7369971b13SSasha Levin 	out		= job->out;
7469971b13SSasha Levin 	in		= job->in;
7569971b13SSasha Levin 	head		= job->head;
7645e47970SAsias He 	req		= iov[0].iov_base;
7703110ff3SAsias He 
78258dd093SPekka Enberg 	switch (req->type) {
7903110ff3SAsias He 	case VIRTIO_BLK_T_IN:
80b8861977SAsias He 		block_cnt	= disk_image__read(bdev->disk, req->sector, iov + 1, in + out - 2);
81258dd093SPekka Enberg 		break;
8203110ff3SAsias He 	case VIRTIO_BLK_T_OUT:
83b8861977SAsias He 		block_cnt	= disk_image__write(bdev->disk, req->sector, iov + 1, in + out - 2);
84258dd093SPekka Enberg 		break;
8529084a74SPrasad Joshi 	case VIRTIO_BLK_T_FLUSH:
8629084a74SPrasad Joshi 		block_cnt       = disk_image__flush(bdev->disk);
8729084a74SPrasad Joshi 		break;
88ff6462e8SSasha Levin 	case VIRTIO_BLK_T_GET_ID:
89ff6462e8SSasha Levin 		block_cnt	= VIRTIO_BLK_ID_BYTES;
90ff6462e8SSasha Levin 		disk_image__get_serial(bdev->disk, (iov + 1)->iov_base, &block_cnt);
91ff6462e8SSasha Levin 		break;
92258dd093SPekka Enberg 	default:
934542f276SCyrill Gorcunov 		pr_warning("request type %d", req->type);
9470b53f25SSasha Levin 		block_cnt	= -1;
95407475bfSPekka Enberg 		break;
9603110ff3SAsias He 	}
9703110ff3SAsias He 
9845e47970SAsias He 	/* status */
9945e47970SAsias He 	status			= iov[out + in - 1].iov_base;
10070b53f25SSasha Levin 	*status			= (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
10103110ff3SAsias He 
10269971b13SSasha Levin 	mutex_lock(&bdev->mutex);
10345e47970SAsias He 	virt_queue__set_used_elem(queue, head, block_cnt);
10469971b13SSasha Levin 	mutex_unlock(&bdev->mutex);
1054155ba8cSPekka Enberg 
106*427948d5SSasha Levin 	virtio_pci__signal_vq(kvm, &bdev->vpci, queue - bdev->vqs);
1074155ba8cSPekka Enberg }
1084155ba8cSPekka Enberg 
10969971b13SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct virt_queue *vq, struct blk_dev *bdev)
11045e47970SAsias He {
11169971b13SSasha Levin 	while (virt_queue__available(vq)) {
11269971b13SSasha Levin 		struct blk_dev_job *job = &bdev->jobs[bdev->job_idx++ % VIRTIO_BLK_QUEUE_SIZE];
113407475bfSPekka Enberg 
11469971b13SSasha Levin 		*job			= (struct blk_dev_job) {
11569971b13SSasha Levin 			.vq			= vq,
11669971b13SSasha Levin 			.bdev			= bdev,
11769971b13SSasha Levin 		};
11869971b13SSasha Levin 		job->head = virt_queue__get_iov(vq, job->iov, &job->out, &job->in, kvm);
11945e47970SAsias He 
12069971b13SSasha Levin 		thread_pool__init_job(&job->job_id, kvm, virtio_blk_do_io_request, job);
12169971b13SSasha Levin 		thread_pool__do_job(&job->job_id);
12269971b13SSasha Levin 	}
1234baf6f73SSasha Levin }
1240528c2a7SPekka Enberg 
125ec75b82fSSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
126ec75b82fSSasha Levin {
12769971b13SSasha Levin 	struct blk_dev *bdev = param;
128ec75b82fSSasha Levin 
12969971b13SSasha Levin 	virtio_blk_do_io(kvm, &bdev->vqs[0], bdev);
130ec75b82fSSasha Levin }
131ec75b82fSSasha Levin 
132*427948d5SSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
133*427948d5SSasha Levin {
134*427948d5SSasha Levin 	struct blk_dev *bdev = dev;
135*427948d5SSasha Levin 
136*427948d5SSasha Levin 	((u8 *)(&bdev->blk_config))[offset] = data;
137*427948d5SSasha Levin }
138*427948d5SSasha Levin 
139*427948d5SSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
140*427948d5SSasha Levin {
141*427948d5SSasha Levin 	struct blk_dev *bdev = dev;
142*427948d5SSasha Levin 
143*427948d5SSasha Levin 	return ((u8 *)(&bdev->blk_config))[offset];
144*427948d5SSasha Levin }
145*427948d5SSasha Levin 
146*427948d5SSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
147*427948d5SSasha Levin {
148*427948d5SSasha Levin 	return 1UL << VIRTIO_BLK_F_SEG_MAX | 1UL << VIRTIO_BLK_F_FLUSH;
149*427948d5SSasha Levin }
150*427948d5SSasha Levin 
151*427948d5SSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
152*427948d5SSasha Levin {
153*427948d5SSasha Levin 	struct blk_dev *bdev = dev;
154*427948d5SSasha Levin 
155*427948d5SSasha Levin 	bdev->features = features;
156*427948d5SSasha Levin }
157*427948d5SSasha Levin 
158*427948d5SSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
159*427948d5SSasha Levin {
160*427948d5SSasha Levin 	struct blk_dev *bdev = dev;
161*427948d5SSasha Levin 	struct virt_queue *queue;
162*427948d5SSasha Levin 	void *p;
163*427948d5SSasha Levin 	struct ioevent ioevent;
164*427948d5SSasha Levin 
165*427948d5SSasha Levin 	compat__remove_message(bdev->compat_id);
166*427948d5SSasha Levin 
167*427948d5SSasha Levin 	queue			= &bdev->vqs[vq];
168*427948d5SSasha Levin 	queue->pfn		= pfn;
169*427948d5SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
170*427948d5SSasha Levin 
171*427948d5SSasha Levin 	vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
172*427948d5SSasha Levin 
173*427948d5SSasha Levin 	ioevent = (struct ioevent) {
174*427948d5SSasha Levin 		.io_addr	= bdev->vpci.base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
175*427948d5SSasha Levin 		.io_len		= sizeof(u16),
176*427948d5SSasha Levin 		.fn		= ioevent_callback,
177*427948d5SSasha Levin 		.fn_ptr		= bdev,
178*427948d5SSasha Levin 		.datamatch	= vq,
179*427948d5SSasha Levin 		.fn_kvm		= kvm,
180*427948d5SSasha Levin 		.fd		= eventfd(0, 0),
181*427948d5SSasha Levin 	};
182*427948d5SSasha Levin 
183*427948d5SSasha Levin 	ioeventfd__add_event(&ioevent);
184*427948d5SSasha Levin 
185*427948d5SSasha Levin 	return 0;
186*427948d5SSasha Levin }
187*427948d5SSasha Levin 
188*427948d5SSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
189*427948d5SSasha Levin {
190*427948d5SSasha Levin 	struct blk_dev *bdev = dev;
191*427948d5SSasha Levin 
192*427948d5SSasha Levin 	virtio_blk_do_io(kvm, &bdev->vqs[vq], bdev);
193*427948d5SSasha Levin 
194*427948d5SSasha Levin 	return 0;
195*427948d5SSasha Levin }
196*427948d5SSasha Levin 
197*427948d5SSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
198*427948d5SSasha Levin {
199*427948d5SSasha Levin 	struct blk_dev *bdev = dev;
200*427948d5SSasha Levin 
201*427948d5SSasha Levin 	return bdev->vqs[vq].pfn;
202*427948d5SSasha Levin }
203*427948d5SSasha Levin 
204*427948d5SSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
205*427948d5SSasha Levin {
206*427948d5SSasha Levin 	return VIRTIO_BLK_QUEUE_SIZE;
207*427948d5SSasha Levin }
208*427948d5SSasha Levin 
20943835ac9SSasha Levin void virtio_blk__init(struct kvm *kvm, struct disk_image *disk)
2104749e795SSasha Levin {
211fe2a70d1SSasha Levin 	struct blk_dev *bdev;
2124749e795SSasha Levin 
2134749e795SSasha Levin 	if (!disk)
2144749e795SSasha Levin 		return;
2154749e795SSasha Levin 
216ebe9ac19SSasha Levin 	bdev = calloc(1, sizeof(struct blk_dev));
217ebe9ac19SSasha Levin 	if (bdev == NULL)
218fe2a70d1SSasha Levin 		die("Failed allocating bdev");
2194749e795SSasha Levin 
220fe2a70d1SSasha Levin 	*bdev = (struct blk_dev) {
2214749e795SSasha Levin 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
2224749e795SSasha Levin 		.disk			= disk,
2234749e795SSasha Levin 		.blk_config		= (struct virtio_blk_config) {
2244749e795SSasha Levin 			.capacity	= disk->size / SECTOR_SIZE,
2253d7831a1SAsias He 			.seg_max	= DISK_SEG_MAX,
2264749e795SSasha Levin 		},
227*427948d5SSasha Levin 	};
228*427948d5SSasha Levin 
229*427948d5SSasha Levin 	virtio_pci__init(kvm, &bdev->vpci, bdev, PCI_DEVICE_ID_VIRTIO_BLK, VIRTIO_ID_BLOCK);
230*427948d5SSasha Levin 	bdev->vpci.ops = (struct virtio_pci_ops) {
231*427948d5SSasha Levin 		.set_config		= set_config,
232*427948d5SSasha Levin 		.get_config		= get_config,
233*427948d5SSasha Levin 		.get_host_features	= get_host_features,
234*427948d5SSasha Levin 		.set_guest_features	= set_guest_features,
235*427948d5SSasha Levin 		.init_vq		= init_vq,
236*427948d5SSasha Levin 		.notify_vq		= notify_vq,
237*427948d5SSasha Levin 		.get_pfn_vq		= get_pfn_vq,
238*427948d5SSasha Levin 		.get_size_vq		= get_size_vq,
239b30d05adSPekka Enberg 	};
240b30d05adSPekka Enberg 
241ebe9ac19SSasha Levin 	list_add_tail(&bdev->list, &bdevs);
242ebe9ac19SSasha Levin 
243404d164bSSasha Levin 	bdev->compat_id = compat__add_message("virtio-blk device was not detected",
244404d164bSSasha Levin 						"While you have requested a virtio-blk device, "
245404d164bSSasha Levin 						"the guest kernel didn't seem to detect it.\n"
246404d164bSSasha Levin 						"Please make sure that the kernel was compiled"
247404d164bSSasha Levin 						"with CONFIG_VIRTIO_BLK.");
248b30d05adSPekka Enberg }
249bcb6aacaSPrasad Joshi 
250bcb6aacaSPrasad Joshi void virtio_blk__init_all(struct kvm *kvm)
251bcb6aacaSPrasad Joshi {
252bcb6aacaSPrasad Joshi 	int i;
253bcb6aacaSPrasad Joshi 
254bcb6aacaSPrasad Joshi 	for (i = 0; i < kvm->nr_disks; i++)
255bcb6aacaSPrasad Joshi 		virtio_blk__init(kvm, kvm->disks[i]);
256bcb6aacaSPrasad Joshi }
257a0a1e3c2SPrasad Joshi 
258a0a1e3c2SPrasad Joshi void virtio_blk__delete_all(struct kvm *kvm)
259a0a1e3c2SPrasad Joshi {
260ebe9ac19SSasha Levin 	while (!list_empty(&bdevs)) {
261ebe9ac19SSasha Levin 		struct blk_dev *bdev;
262a0a1e3c2SPrasad Joshi 
263ebe9ac19SSasha Levin 		bdev = list_first_entry(&bdevs, struct blk_dev, list);
264*427948d5SSasha Levin 		ioeventfd__del_event(bdev->vpci.base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 0);
265ebe9ac19SSasha Levin 		list_del(&bdev->list);
266ebe9ac19SSasha Levin 		free(bdev);
267ebe9ac19SSasha Levin 	}
268a0a1e3c2SPrasad Joshi }
269