blk.c 7.65 KB
Newer Older
1
#include "kvm/virtio-blk.h"
2

3
#include "kvm/virtio-pci-dev.h"
4
#include "kvm/disk-image.h"
5
#include "kvm/mutex.h"
6
#include "kvm/util.h"
7
#include "kvm/kvm.h"
8
#include "kvm/pci.h"
9
#include "kvm/threadpool.h"
10
#include "kvm/ioeventfd.h"
11
#include "kvm/guest_compat.h"
12
#include "kvm/virtio-pci.h"
13
#include "kvm/virtio.h"
14

Pekka Enberg's avatar
Pekka Enberg committed
15
16
#include <linux/virtio_ring.h>
#include <linux/virtio_blk.h>
17
#include <linux/kernel.h>
18
#include <linux/list.h>
19
#include <linux/types.h>
20
#include <pthread.h>
21

22
#define VIRTIO_BLK_MAX_DEV		4
23

24
25
26
27
/*
 * the header and status consume too entries
 */
#define DISK_SEG_MAX			(VIRTIO_BLK_QUEUE_SIZE - 2)
28
#define VIRTIO_BLK_QUEUE_SIZE		256
29
#define NUM_VIRT_QUEUES			1
30

31
struct blk_dev_req {
32
	struct virt_queue		*vq;
33
	struct blk_dev			*bdev;
34
35
	struct iovec			iov[VIRTIO_BLK_QUEUE_SIZE];
	u16				out, in, head;
36
	struct kvm			*kvm;
37
38
};

39
struct blk_dev {
40
	struct mutex			mutex;
41

42
	struct list_head		list;
43

44
	struct virtio_device		vdev;
45
	struct virtio_blk_config	blk_config;
46
	struct disk_image		*disk;
47
	u32				features;
48

49
	struct virt_queue		vqs[NUM_VIRT_QUEUES];
50
	struct blk_dev_req		reqs[VIRTIO_BLK_QUEUE_SIZE];
51
52
53
54
55

	pthread_t			io_thread;
	int				io_efd;

	struct kvm			*kvm;
56
57
};

58
static LIST_HEAD(bdevs);
59
static int compat_id = -1;
60

61
void virtio_blk_complete(void *param, long len)
62
{
63
64
65
	struct blk_dev_req *req = param;
	struct blk_dev *bdev = req->bdev;
	int queueid = req->vq - bdev->vqs;
66
	u8 *status;
67
68

	/* status */
69
70
	status	= req->iov[req->out + req->in - 1].iov_base;
	*status	= (len < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK;
71
72
73
74
75

	mutex_lock(&bdev->mutex);
	virt_queue__set_used_elem(req->vq, req->head, len);
	mutex_unlock(&bdev->mutex);

76
	if (virtio_queue__should_signal(&bdev->vqs[queueid]))
77
		bdev->vdev.ops->signal_vq(req->kvm, &bdev->vdev, queueid);
78
79
}

80
static void virtio_blk_do_io_request(struct kvm *kvm, struct virt_queue *vq, struct blk_dev_req *req)
81
82
{
	struct virtio_blk_outhdr *req_hdr;
83
84
85
	ssize_t block_cnt;
	struct blk_dev *bdev;
	struct iovec *iov;
86
	u16 out, in;
87
88
	u32 type;
	u64 sector;
89

90
	block_cnt	= -1;
91
92
93
94
95
96
	bdev		= req->bdev;
	iov		= req->iov;
	out		= req->out;
	in		= req->in;
	req_hdr		= iov[0].iov_base;

97
98
99
100
	type = virtio_guest_to_host_u32(vq, req_hdr->type);
	sector = virtio_guest_to_host_u64(vq, req_hdr->sector);

	switch (type) {
101
	case VIRTIO_BLK_T_IN:
102
		block_cnt = disk_image__read(bdev->disk, sector,
103
				iov + 1, in + out - 2, req);
104
105
		break;
	case VIRTIO_BLK_T_OUT:
106
		block_cnt = disk_image__write(bdev->disk, sector,
107
				iov + 1, in + out - 2, req);
108
		break;
109
	case VIRTIO_BLK_T_FLUSH:
110
		block_cnt = disk_image__flush(bdev->disk);
111
		virtio_blk_complete(req, block_cnt);
112
		break;
113
	case VIRTIO_BLK_T_GET_ID:
114
115
116
		block_cnt = VIRTIO_BLK_ID_BYTES;
		disk_image__get_serial(bdev->disk,
				(iov + 1)->iov_base, &block_cnt);
117
		virtio_blk_complete(req, block_cnt);
118
		break;
119
	default:
120
		pr_warning("request type %d", type);
121
		block_cnt	= -1;
122
		break;
123
	}
124
125
}

126
static void virtio_blk_do_io(struct kvm *kvm, struct virt_queue *vq, struct blk_dev *bdev)
127
{
128
129
	struct blk_dev_req *req;
	u16 head;
130

131
132
133
	while (virt_queue__available(vq)) {
		head		= virt_queue__pop(vq);
		req		= &bdev->reqs[head];
134
135
		req->head	= virt_queue__get_head_iov(vq, req->iov, &req->out,
					&req->in, head, kvm);
136
		req->vq		= vq;
137

138
		virtio_blk_do_io_request(kvm, vq, req);
139
	}
140
}
141

142
static u8 *get_config(struct kvm *kvm, void *dev)
143
144
{
	struct blk_dev *bdev = dev;
145

146
	return ((u8 *)(&bdev->blk_config));
147
}
148

149
150
static u32 get_host_features(struct kvm *kvm, void *dev)
{
151
152
	return	1UL << VIRTIO_BLK_F_SEG_MAX
		| 1UL << VIRTIO_BLK_F_FLUSH
153
154
		| 1UL << VIRTIO_RING_F_EVENT_IDX
		| 1UL << VIRTIO_RING_F_INDIRECT_DESC;
155
}
156

157
158
159
static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
{
	struct blk_dev *bdev = dev;
160
161
	struct virtio_blk_config *conf = &bdev->blk_config;
	struct virtio_blk_geometry *geo = &conf->geometry;
162

163
	bdev->features = features;
164
165
166
167
168
169
170
171
172
173
174

	conf->capacity = virtio_host_to_guest_u64(&bdev->vdev, conf->capacity);
	conf->size_max = virtio_host_to_guest_u32(&bdev->vdev, conf->size_max);
	conf->seg_max = virtio_host_to_guest_u32(&bdev->vdev, conf->seg_max);

	/* Geometry */
	geo->cylinders = virtio_host_to_guest_u16(&bdev->vdev, geo->cylinders);

	conf->blk_size = virtio_host_to_guest_u32(&bdev->vdev, conf->blk_size);
	conf->min_io_size = virtio_host_to_guest_u16(&bdev->vdev, conf->min_io_size);
	conf->opt_io_size = virtio_host_to_guest_u32(&bdev->vdev, conf->opt_io_size);
175
}
176

177
178
179
180
static void notify_status(struct kvm *kvm, void *dev, u32 status)
{
}

181
182
static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
		   u32 pfn)
183
184
185
186
187
{
	struct blk_dev *bdev = dev;
	struct virt_queue *queue;
	void *p;

188
	compat__remove_message(compat_id);
189

190
191
	queue		= &bdev->vqs[vq];
	queue->pfn	= pfn;
192
	p		= virtio_get_vq(kvm, queue->pfn, page_size);
193

194
	vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, align);
195
	virtio_init_device_vq(&bdev->vdev, queue);
196
197

	return 0;
198
199
}

200
201
202
203
static void *virtio_blk_thread(void *dev)
{
	struct blk_dev *bdev = dev;
	u64 data;
204
	int r;
205

206
207
	kvm__set_thread_name("virtio-blk-io");

208
	while (1) {
209
210
211
		r = read(bdev->io_efd, &data, sizeof(u64));
		if (r < 0)
			continue;
212
213
214
215
216
217
218
		virtio_blk_do_io(bdev->kvm, &bdev->vqs[0], bdev);
	}

	pthread_exit(NULL);
	return NULL;
}

219
220
221
static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
{
	struct blk_dev *bdev = dev;
222
	u64 data = 1;
223
	int r;
224

225
226
227
	r = write(bdev->io_efd, &data, sizeof(data));
	if (r < 0)
		return r;
228
229
230
231
232

	return 0;
}

static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
233
{
234
	struct blk_dev *bdev = dev;
235

236
237
238
239
240
	return bdev->vqs[vq].pfn;
}

static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
{
241
	/* FIXME: dynamic */
242
	return VIRTIO_BLK_QUEUE_SIZE;
243
244
}

245
246
247
248
249
250
static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
{
	/* FIXME: dynamic */
	return size;
}

251
252
253
254
255
static int get_vq_count(struct kvm *kvm, void *dev)
{
	return NUM_VIRT_QUEUES;
}

256
static struct virtio_ops blk_dev_virtio_ops = {
257
258
259
	.get_config		= get_config,
	.get_host_features	= get_host_features,
	.set_guest_features	= set_guest_features,
260
	.get_vq_count		= get_vq_count,
261
	.init_vq		= init_vq,
262
	.notify_status		= notify_status,
263
264
265
	.notify_vq		= notify_vq,
	.get_pfn_vq		= get_pfn_vq,
	.get_size_vq		= get_size_vq,
266
	.set_size_vq		= set_size_vq,
267
268
};

269
static int virtio_blk__init_one(struct kvm *kvm, struct disk_image *disk)
270
{
271
	struct blk_dev *bdev;
272
	unsigned int i;
273

274
	if (!disk)
275
		return -EINVAL;
276

277
278
	bdev = calloc(1, sizeof(struct blk_dev));
	if (bdev == NULL)
279
		return -ENOMEM;
280

281
	*bdev = (struct blk_dev) {
282
		.mutex			= MUTEX_INITIALIZER,
283
284
285
286
		.disk			= disk,
		.blk_config		= (struct virtio_blk_config) {
			.capacity	= disk->size / SECTOR_SIZE,
			.seg_max	= DISK_SEG_MAX,
287
		},
288
289
		.io_efd			= eventfd(0, 0),
		.kvm			= kvm,
290
	};
291

292
	virtio_init(kvm, bdev, &bdev->vdev, &blk_dev_virtio_ops,
293
		    VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_BLK,
294
		    VIRTIO_ID_BLOCK, PCI_CLASS_BLK);
295

296
	list_add_tail(&bdev->list, &bdevs);
297

298
	for (i = 0; i < ARRAY_SIZE(bdev->reqs); i++) {
299
300
		bdev->reqs[i].bdev = bdev;
		bdev->reqs[i].kvm = kvm;
301
	}
302

303
304
	disk_image__set_callback(bdev->disk, virtio_blk_complete);

305
	pthread_create(&bdev->io_thread, NULL, virtio_blk_thread, bdev);
Asias He's avatar
Asias He committed
306
	if (compat_id == -1)
307
		compat_id = virtio_compat_add_message("virtio-blk", "CONFIG_VIRTIO_BLK");
308

309
	return 0;
310
}
311

312
static int virtio_blk__exit_one(struct kvm *kvm, struct blk_dev *bdev)
313
{
314
315
	list_del(&bdev->list);
	free(bdev);
316

317
	return 0;
318
}
319

320
321
322
323
324
int virtio_blk__init(struct kvm *kvm)
{
	int i, r = 0;

	for (i = 0; i < kvm->nr_disks; i++) {
325
326
		if (kvm->disks[i]->wwpn)
			continue;
327
328
329
330
331
332
333
334
335
		r = virtio_blk__init_one(kvm, kvm->disks[i]);
		if (r < 0)
			goto cleanup;
	}

	return 0;
cleanup:
	return virtio_blk__exit(kvm);
}
336
virtio_dev_init(virtio_blk__init);
337
338

int virtio_blk__exit(struct kvm *kvm)
339
{
340
341
	while (!list_empty(&bdevs)) {
		struct blk_dev *bdev;
342

343
		bdev = list_first_entry(&bdevs, struct blk_dev, list);
344
		virtio_blk__exit_one(kvm, bdev);
345
	}
346
347

	return 0;
348
}
349
virtio_dev_exit(virtio_blk__exit);