Blame view

tools/virtio/virtio_test.c 8.83 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
2
3
  #define _GNU_SOURCE
  #include <getopt.h>
633fae33d   Eugenio Pérez   tools/virtio: Add...
4
  #include <limits.h>
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
5
6
7
8
9
10
11
12
13
14
  #include <string.h>
  #include <poll.h>
  #include <sys/eventfd.h>
  #include <stdlib.h>
  #include <assert.h>
  #include <unistd.h>
  #include <sys/ioctl.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <fcntl.h>
61d0b5a4b   Rusty Russell   tools/virtio: sep...
15
  #include <stdbool.h>
2d7ce0e8a   Michael S. Tsirkin   tools/virtio: mor...
16
  #include <linux/virtio_types.h>
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
17
18
19
20
  #include <linux/vhost.h>
  #include <linux/virtio.h>
  #include <linux/virtio_ring.h>
  #include "../../drivers/vhost/test.h"
7add78b2a   Eugenio Pérez   tools/virtio: Add...
21
  #define RANDOM_BATCH -1
61d0b5a4b   Rusty Russell   tools/virtio: sep...
22
23
  /* Unused */
  void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  struct vq_info {
  	int kick;
  	int call;
  	int num;
  	int idx;
  	void *ring;
  	/* copy used for control */
  	struct vring vring;
  	struct virtqueue *vq;
  };
  
  struct vdev_info {
  	struct virtio_device vdev;
  	int control;
  	struct pollfd fds[1];
  	struct vq_info vqs[1];
  	int nvqs;
  	void *buf;
  	size_t buf_size;
  	struct vhost_memory *mem;
  };
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
45
46
  static const struct vhost_vring_file no_backend = { .fd = -1 },
  				     backend = { .fd = 1 };
1d8bf5c3a   Eugenio Pérez   tools/virtio: Res...
47
  static const struct vhost_vring_state null_state = {};
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
48

46f9c2b92   Heinz Graalfs   virtio_ring: chan...
49
  bool vq_notify(struct virtqueue *vq)
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
50
51
52
53
54
55
  {
  	struct vq_info *info = vq->priv;
  	unsigned long long v = 1;
  	int r;
  	r = write(info->kick, &v, sizeof v);
  	assert(r == sizeof v);
46f9c2b92   Heinz Graalfs   virtio_ring: chan...
56
  	return true;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
57
58
59
60
61
62
63
64
65
66
67
  }
  
  void vq_callback(struct virtqueue *vq)
  {
  }
  
  
  void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
  {
  	struct vhost_vring_state state = { .index = info->idx };
  	struct vhost_vring_file file = { .index = info->idx };
e16e12be3   Michael S. Tsirkin   virtio: use u32, ...
68
  	unsigned long long features = dev->vdev.features;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  	struct vhost_vring_addr addr = {
  		.index = info->idx,
  		.desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
  		.avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
  		.used_user_addr = (uint64_t)(unsigned long)info->vring.used,
  	};
  	int r;
  	r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
  	assert(r >= 0);
  	state.num = info->vring.num;
  	r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
  	assert(r >= 0);
  	state.num = 0;
  	r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
  	assert(r >= 0);
  	r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
  	assert(r >= 0);
  	file.fd = info->kick;
  	r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
  	assert(r >= 0);
  	file.fd = info->call;
  	r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
  	assert(r >= 0);
  }
674123926   Eugenio Pérez   tools/virtio: Ext...
93
94
95
96
97
98
99
100
101
102
103
104
  static void vq_reset(struct vq_info *info, int num, struct virtio_device *vdev)
  {
  	if (info->vq)
  		vring_del_virtqueue(info->vq);
  
  	memset(info->ring, 0, vring_size(num, 4096));
  	vring_init(&info->vring, num, info->ring, 4096);
  	info->vq = __vring_new_virtqueue(info->idx, info->vring, vdev, true,
  					 false, vq_notify, vq_callback, "test");
  	assert(info->vq);
  	info->vq->priv = info;
  }
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
105
106
107
108
109
110
111
112
113
  static void vq_info_add(struct vdev_info *dev, int num)
  {
  	struct vq_info *info = &dev->vqs[dev->nvqs];
  	int r;
  	info->idx = dev->nvqs;
  	info->kick = eventfd(0, EFD_NONBLOCK);
  	info->call = eventfd(0, EFD_NONBLOCK);
  	r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
  	assert(r >= 0);
674123926   Eugenio Pérez   tools/virtio: Ext...
114
  	vq_reset(info, num, &dev->vdev);
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
115
116
117
118
119
120
121
122
123
124
  	vhost_vq_setup(dev, info);
  	dev->fds[info->idx].fd = info->call;
  	dev->fds[info->idx].events = POLLIN;
  	dev->nvqs++;
  }
  
  static void vdev_info_init(struct vdev_info* dev, unsigned long long features)
  {
  	int r;
  	memset(dev, 0, sizeof *dev);
e16e12be3   Michael S. Tsirkin   virtio: use u32, ...
125
  	dev->vdev.features = features;
cb91909e4   Eugenio Pérez   tools/virtio: Use...
126
  	INIT_LIST_HEAD(&dev->vdev.vqs);
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  	dev->buf_size = 1024;
  	dev->buf = malloc(dev->buf_size);
  	assert(dev->buf);
          dev->control = open("/dev/vhost-test", O_RDWR);
  	assert(dev->control >= 0);
  	r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
  	assert(r >= 0);
  	dev->mem = malloc(offsetof(struct vhost_memory, regions) +
  			  sizeof dev->mem->regions[0]);
  	assert(dev->mem);
  	memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
                            sizeof dev->mem->regions[0]);
  	dev->mem->nregions = 1;
  	dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
  	dev->mem->regions[0].userspace_addr = (long)dev->buf;
  	dev->mem->regions[0].memory_size = dev->buf_size;
  	r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
  	assert(r >= 0);
  }
  
  /* TODO: this is pretty bad: we get a cache line bounce
   * for the wait queue on poll and another one on read,
   * plus the read which is there just to clear the
   * current state. */
  static void wait_for_interrupt(struct vdev_info *dev)
  {
  	int i;
  	unsigned long long val;
  	poll(dev->fds, dev->nvqs, -1);
  	for (i = 0; i < dev->nvqs; ++i)
  		if (dev->fds[i].revents & POLLIN) {
  			read(dev->fds[i].fd, &val, sizeof val);
  		}
  }
64d098886   Michael S. Tsirkin   virtio/tools: add...
161
  static void run_test(struct vdev_info *dev, struct vq_info *vq,
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
162
  		     bool delayed, int batch, int reset_n, int bufs)
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
163
164
  {
  	struct scatterlist sl;
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
165
  	long started = 0, completed = 0, next_reset = reset_n;
633fae33d   Eugenio Pérez   tools/virtio: Add...
166
  	long completed_before, started_before;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
167
168
169
  	int r, test = 1;
  	unsigned len;
  	long long spurious = 0;
7add78b2a   Eugenio Pérez   tools/virtio: Add...
170
  	const bool random_batch = batch == RANDOM_BATCH;
1d8bf5c3a   Eugenio Pérez   tools/virtio: Res...
171

4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
172
173
  	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
  	assert(r >= 0);
1d8bf5c3a   Eugenio Pérez   tools/virtio: Res...
174
175
176
  	if (!reset_n) {
  		next_reset = INT_MAX;
  	}
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
177
178
179
  	for (;;) {
  		virtqueue_disable_cb(vq->vq);
  		completed_before = completed;
633fae33d   Eugenio Pérez   tools/virtio: Add...
180
  		started_before = started;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
181
  		do {
1d8bf5c3a   Eugenio Pérez   tools/virtio: Res...
182
  			const bool reset = completed > next_reset;
7add78b2a   Eugenio Pérez   tools/virtio: Add...
183
184
  			if (random_batch)
  				batch = (random() % vq->vring.num) + 1;
633fae33d   Eugenio Pérez   tools/virtio: Add...
185
186
  			while (started < bufs &&
  			       (started - completed) < batch) {
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
187
  				sg_init_one(&sl, dev->buf, dev->buf_size);
cf994e0af   Rusty Russell   tools/virtio: rem...
188
189
190
  				r = virtqueue_add_outbuf(vq->vq, &sl, 1,
  							 dev->buf + started,
  							 GFP_ATOMIC);
633fae33d   Eugenio Pérez   tools/virtio: Add...
191
192
193
194
195
  				if (unlikely(r != 0)) {
  					if (r == -ENOSPC &&
  					    started > started_before)
  						r = 0;
  					else
53c18c990   Heinz Graalfs   virtio_test: veri...
196
  						r = -1;
633fae33d   Eugenio Pérez   tools/virtio: Add...
197
  					break;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
198
  				}
633fae33d   Eugenio Pérez   tools/virtio: Add...
199
200
201
202
203
204
205
206
207
208
  
  				++started;
  
  				if (unlikely(!virtqueue_kick(vq->vq))) {
  					r = -1;
  					break;
  				}
  			}
  
  			if (started >= bufs)
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
209
  				r = -1;
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
210
211
212
213
214
  			if (reset) {
  				r = ioctl(dev->control, VHOST_TEST_SET_BACKEND,
  					  &no_backend);
  				assert(!r);
  			}
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
215
  			/* Flush out completed bufs if any */
633fae33d   Eugenio Pérez   tools/virtio: Add...
216
  			while (virtqueue_get_buf(vq->vq, &len)) {
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
217
218
219
  				++completed;
  				r = 0;
  			}
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
220
  			if (reset) {
1d8bf5c3a   Eugenio Pérez   tools/virtio: Res...
221
222
223
224
225
226
227
228
229
230
231
232
  				struct vhost_vring_state s = { .index = 0 };
  
  				vq_reset(vq, vq->vring.num, &dev->vdev);
  
  				r = ioctl(dev->control, VHOST_GET_VRING_BASE,
  					  &s);
  				assert(!r);
  
  				s.num = 0;
  				r = ioctl(dev->control, VHOST_SET_VRING_BASE,
  					  &null_state);
  				assert(!r);
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
233
234
235
  				r = ioctl(dev->control, VHOST_TEST_SET_BACKEND,
  					  &backend);
  				assert(!r);
1d8bf5c3a   Eugenio Pérez   tools/virtio: Res...
236
  				started = completed;
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
237
238
239
  				while (completed > next_reset)
  					next_reset += completed;
  			}
de929b044   Rusty Russell   virtio: tools: ma...
240
  		} while (r == 0);
633fae33d   Eugenio Pérez   tools/virtio: Add...
241
  		if (completed == completed_before && started == started_before)
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
242
243
244
245
246
  			++spurious;
  		assert(completed <= bufs);
  		assert(started <= bufs);
  		if (completed == bufs)
  			break;
64d098886   Michael S. Tsirkin   virtio/tools: add...
247
248
249
250
251
252
  		if (delayed) {
  			if (virtqueue_enable_cb_delayed(vq->vq))
  				wait_for_interrupt(dev);
  		} else {
  			if (virtqueue_enable_cb(vq->vq))
  				wait_for_interrupt(dev);
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
253
254
255
256
257
  		}
  	}
  	test = 0;
  	r = ioctl(dev->control, VHOST_TEST_RUN, &test);
  	assert(r >= 0);
1d8bf5c3a   Eugenio Pérez   tools/virtio: Res...
258
259
260
261
  	fprintf(stderr,
  		"spurious wakeups: 0x%llx started=0x%lx completed=0x%lx
  ",
  		spurious, started, completed);
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
262
263
264
265
266
267
268
269
270
  }
  
  const char optstring[] = "h";
  const struct option longopts[] = {
  	{
  		.name = "help",
  		.val = 'h',
  	},
  	{
4423fe40b   Michael S. Tsirkin   virtio_test: supp...
271
272
273
274
275
276
277
278
  		.name = "event-idx",
  		.val = 'E',
  	},
  	{
  		.name = "no-event-idx",
  		.val = 'e',
  	},
  	{
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
279
280
281
282
283
284
285
286
  		.name = "indirect",
  		.val = 'I',
  	},
  	{
  		.name = "no-indirect",
  		.val = 'i',
  	},
  	{
43b09122c   Michael S. Tsirkin   tools/virtio: add...
287
288
289
290
291
292
293
294
  		.name = "virtio-1",
  		.val = '1',
  	},
  	{
  		.name = "no-virtio-1",
  		.val = '0',
  	},
  	{
64d098886   Michael S. Tsirkin   virtio/tools: add...
295
296
297
298
299
300
301
302
  		.name = "delayed-interrupt",
  		.val = 'D',
  	},
  	{
  		.name = "no-delayed-interrupt",
  		.val = 'd',
  	},
  	{
633fae33d   Eugenio Pérez   tools/virtio: Add...
303
304
305
306
307
  		.name = "batch",
  		.val = 'b',
  		.has_arg = required_argument,
  	},
  	{
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
308
309
310
311
312
  		.name = "reset",
  		.val = 'r',
  		.has_arg = optional_argument,
  	},
  	{
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
313
314
  	}
  };
4a7d6455b   Cong Ding   tools:virtio: fix...
315
  static void help(void)
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
316
  {
4423fe40b   Michael S. Tsirkin   virtio_test: supp...
317
318
319
  	fprintf(stderr, "Usage: virtio_test [--help]"
  		" [--no-indirect]"
  		" [--no-event-idx]"
43b09122c   Michael S. Tsirkin   tools/virtio: add...
320
  		" [--no-virtio-1]"
64d098886   Michael S. Tsirkin   virtio/tools: add...
321
  		" [--delayed-interrupt]"
7add78b2a   Eugenio Pérez   tools/virtio: Add...
322
  		" [--batch=random/N]"
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
323
  		" [--reset=N]"
4423fe40b   Michael S. Tsirkin   virtio_test: supp...
324
325
  		"
  ");
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
326
327
328
329
330
  }
  
  int main(int argc, char **argv)
  {
  	struct vdev_info dev;
4423fe40b   Michael S. Tsirkin   virtio_test: supp...
331
  	unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
43b09122c   Michael S. Tsirkin   tools/virtio: add...
332
  		(1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1);
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
333
  	long batch = 1, reset = 0;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
334
  	int o;
64d098886   Michael S. Tsirkin   virtio/tools: add...
335
  	bool delayed = false;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
336
337
338
339
340
341
342
343
344
  
  	for (;;) {
  		o = getopt_long(argc, argv, optstring, longopts, NULL);
  		switch (o) {
  		case -1:
  			goto done;
  		case '?':
  			help();
  			exit(2);
4423fe40b   Michael S. Tsirkin   virtio_test: supp...
345
346
347
  		case 'e':
  			features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX);
  			break;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
348
349
350
351
352
353
  		case 'h':
  			help();
  			goto done;
  		case 'i':
  			features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
  			break;
43b09122c   Michael S. Tsirkin   tools/virtio: add...
354
355
356
  		case '0':
  			features &= ~(1ULL << VIRTIO_F_VERSION_1);
  			break;
64d098886   Michael S. Tsirkin   virtio/tools: add...
357
358
359
  		case 'D':
  			delayed = true;
  			break;
633fae33d   Eugenio Pérez   tools/virtio: Add...
360
  		case 'b':
7add78b2a   Eugenio Pérez   tools/virtio: Add...
361
362
363
364
365
366
367
  			if (0 == strcmp(optarg, "random")) {
  				batch = RANDOM_BATCH;
  			} else {
  				batch = strtol(optarg, NULL, 10);
  				assert(batch > 0);
  				assert(batch < (long)INT_MAX + 1);
  			}
633fae33d   Eugenio Pérez   tools/virtio: Add...
368
  			break;
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
369
370
371
372
373
374
375
376
377
  		case 'r':
  			if (!optarg) {
  				reset = 1;
  			} else {
  				reset = strtol(optarg, NULL, 10);
  				assert(reset > 0);
  				assert(reset < (long)INT_MAX + 1);
  			}
  			break;
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
378
379
380
381
382
383
384
385
386
  		default:
  			assert(0);
  			break;
  		}
  	}
  
  done:
  	vdev_info_init(&dev, features);
  	vq_info_add(&dev, 256);
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
387
  	run_test(&dev, &dev.vqs[0], delayed, batch, reset, 0x100000);
4e53f78e5   Michael S. Tsirkin   tools/virtio: vir...
388
389
  	return 0;
  }