Blame view

drivers/vhost/test.c 8.47 KB
7a338472f   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
71ccc212e   Michael S. Tsirkin   vhost test module
2
3
4
  /* Copyright (C) 2009 Red Hat, Inc.
   * Author: Michael S. Tsirkin <mst@redhat.com>
   *
71ccc212e   Michael S. Tsirkin   vhost test module
5
6
7
8
9
10
11
12
13
14
   * test virtio server in host kernel.
   */
  
  #include <linux/compat.h>
  #include <linux/eventfd.h>
  #include <linux/vhost.h>
  #include <linux/miscdevice.h>
  #include <linux/module.h>
  #include <linux/mutex.h>
  #include <linux/workqueue.h>
71ccc212e   Michael S. Tsirkin   vhost test module
15
16
17
18
  #include <linux/file.h>
  #include <linux/slab.h>
  
  #include "test.h"
6ac1afbf6   Asias He   vhost: Make vhost...
19
  #include "vhost.h"
71ccc212e   Michael S. Tsirkin   vhost test module
20
21
22
23
  
  /* Max number of bytes transferred before requeueing the job.
   * Using this limit prevents one virtqueue from starving others. */
  #define VHOST_TEST_WEIGHT 0x80000
264b563b8   Tiwei Bie   vhost/test: fix b...
24
25
26
27
28
  /* Max number of packets transferred before requeueing the job.
   * Using this limit prevents one virtqueue from starving others with
   * pkts.
   */
  #define VHOST_TEST_PKT_WEIGHT 256
71ccc212e   Michael S. Tsirkin   vhost test module
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  enum {
  	VHOST_TEST_VQ = 0,
  	VHOST_TEST_VQ_MAX = 1,
  };
  
  struct vhost_test {
  	struct vhost_dev dev;
  	struct vhost_virtqueue vqs[VHOST_TEST_VQ_MAX];
  };
  
  /* Expects to be always run from workqueue - which acts as
   * read-size critical section for our kind of RCU. */
  static void handle_vq(struct vhost_test *n)
  {
09a34c840   Michael S. Tsirkin   vhost/test: updat...
43
  	struct vhost_virtqueue *vq = &n->vqs[VHOST_TEST_VQ];
71ccc212e   Michael S. Tsirkin   vhost test module
44
45
46
47
  	unsigned out, in;
  	int head;
  	size_t len, total_len = 0;
  	void *private;
09a34c840   Michael S. Tsirkin   vhost/test: updat...
48
  	mutex_lock(&vq->mutex);
247643f85   Eugenio Pérez   vhost: Create acc...
49
  	private = vhost_vq_get_backend(vq);
09a34c840   Michael S. Tsirkin   vhost/test: updat...
50
51
  	if (!private) {
  		mutex_unlock(&vq->mutex);
71ccc212e   Michael S. Tsirkin   vhost test module
52
  		return;
09a34c840   Michael S. Tsirkin   vhost/test: updat...
53
  	}
71ccc212e   Michael S. Tsirkin   vhost test module
54

8ea8cf89e   Michael S. Tsirkin   vhost: support ev...
55
  	vhost_disable_notify(&n->dev, vq);
71ccc212e   Michael S. Tsirkin   vhost test module
56
57
  
  	for (;;) {
47283bef7   Michael S. Tsirkin   vhost: move memor...
58
  		head = vhost_get_vq_desc(vq, vq->iov,
71ccc212e   Michael S. Tsirkin   vhost test module
59
60
61
62
63
64
65
66
  					 ARRAY_SIZE(vq->iov),
  					 &out, &in,
  					 NULL, NULL);
  		/* On error, stop handling until the next kick. */
  		if (unlikely(head < 0))
  			break;
  		/* Nothing new?  Wait for eventfd to tell us they refilled. */
  		if (head == vq->num) {
8ea8cf89e   Michael S. Tsirkin   vhost: support ev...
67
68
  			if (unlikely(vhost_enable_notify(&n->dev, vq))) {
  				vhost_disable_notify(&n->dev, vq);
71ccc212e   Michael S. Tsirkin   vhost test module
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  				continue;
  			}
  			break;
  		}
  		if (in) {
  			vq_err(vq, "Unexpected descriptor format for TX: "
  			       "out %d, int %d
  ", out, in);
  			break;
  		}
  		len = iov_length(vq->iov, out);
  		/* Sanity check */
  		if (!len) {
  			vq_err(vq, "Unexpected 0 len for TX
  ");
  			break;
  		}
  		vhost_add_used_and_signal(&n->dev, vq, head, 0);
  		total_len += len;
264b563b8   Tiwei Bie   vhost/test: fix b...
88
  		if (unlikely(vhost_exceeds_weight(vq, 0, total_len)))
71ccc212e   Michael S. Tsirkin   vhost test module
89
  			break;
71ccc212e   Michael S. Tsirkin   vhost test module
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  	}
  
  	mutex_unlock(&vq->mutex);
  }
  
  static void handle_vq_kick(struct vhost_work *work)
  {
  	struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
  						  poll.work);
  	struct vhost_test *n = container_of(vq->dev, struct vhost_test, dev);
  
  	handle_vq(n);
  }
  
  static int vhost_test_open(struct inode *inode, struct file *f)
  {
  	struct vhost_test *n = kmalloc(sizeof *n, GFP_KERNEL);
  	struct vhost_dev *dev;
09a34c840   Michael S. Tsirkin   vhost/test: updat...
108
  	struct vhost_virtqueue **vqs;
71ccc212e   Michael S. Tsirkin   vhost test module
109
110
111
  
  	if (!n)
  		return -ENOMEM;
6da2ec560   Kees Cook   treewide: kmalloc...
112
  	vqs = kmalloc_array(VHOST_TEST_VQ_MAX, sizeof(*vqs), GFP_KERNEL);
09a34c840   Michael S. Tsirkin   vhost/test: updat...
113
114
115
116
  	if (!vqs) {
  		kfree(n);
  		return -ENOMEM;
  	}
71ccc212e   Michael S. Tsirkin   vhost test module
117
118
  
  	dev = &n->dev;
09a34c840   Michael S. Tsirkin   vhost/test: updat...
119
  	vqs[VHOST_TEST_VQ] = &n->vqs[VHOST_TEST_VQ];
71ccc212e   Michael S. Tsirkin   vhost test module
120
  	n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick;
264b563b8   Tiwei Bie   vhost/test: fix b...
121
  	vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX, UIO_MAXIOV,
044e4b092   Michael S. Tsirkin   vhost/test: fix u...
122
  		       VHOST_TEST_PKT_WEIGHT, VHOST_TEST_WEIGHT, true, NULL);
71ccc212e   Michael S. Tsirkin   vhost test module
123
124
125
126
127
128
129
130
131
132
133
134
  
  	f->private_data = n;
  
  	return 0;
  }
  
  static void *vhost_test_stop_vq(struct vhost_test *n,
  				struct vhost_virtqueue *vq)
  {
  	void *private;
  
  	mutex_lock(&vq->mutex);
247643f85   Eugenio Pérez   vhost: Create acc...
135
136
  	private = vhost_vq_get_backend(vq);
  	vhost_vq_set_backend(vq, NULL);
71ccc212e   Michael S. Tsirkin   vhost test module
137
138
139
140
141
142
143
144
145
146
147
  	mutex_unlock(&vq->mutex);
  	return private;
  }
  
  static void vhost_test_stop(struct vhost_test *n, void **privatep)
  {
  	*privatep = vhost_test_stop_vq(n, n->vqs + VHOST_TEST_VQ);
  }
  
  static void vhost_test_flush_vq(struct vhost_test *n, int index)
  {
09a34c840   Michael S. Tsirkin   vhost/test: updat...
148
  	vhost_poll_flush(&n->vqs[index].poll);
71ccc212e   Michael S. Tsirkin   vhost test module
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  }
  
  static void vhost_test_flush(struct vhost_test *n)
  {
  	vhost_test_flush_vq(n, VHOST_TEST_VQ);
  }
  
  static int vhost_test_release(struct inode *inode, struct file *f)
  {
  	struct vhost_test *n = f->private_data;
  	void  *private;
  
  	vhost_test_stop(n, &private);
  	vhost_test_flush(n);
245cdd9fb   Michael S. Tsirkin   vhost/test: stop ...
163
  	vhost_dev_stop(&n->dev);
f6f93f75a   夷则(Caspar)   vhost: remove unu...
164
  	vhost_dev_cleanup(&n->dev);
71ccc212e   Michael S. Tsirkin   vhost test module
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
  	/* We do an extra flush before freeing memory,
  	 * since jobs can re-queue themselves. */
  	vhost_test_flush(n);
  	kfree(n);
  	return 0;
  }
  
  static long vhost_test_run(struct vhost_test *n, int test)
  {
  	void *priv, *oldpriv;
  	struct vhost_virtqueue *vq;
  	int r, index;
  
  	if (test < 0 || test > 1)
  		return -EINVAL;
  
  	mutex_lock(&n->dev.mutex);
  	r = vhost_dev_check_owner(&n->dev);
  	if (r)
  		goto err;
  
  	for (index = 0; index < n->dev.nvqs; ++index) {
  		/* Verify that ring has been setup correctly. */
  		if (!vhost_vq_access_ok(&n->vqs[index])) {
  			r = -EFAULT;
  			goto err;
  		}
  	}
  
  	for (index = 0; index < n->dev.nvqs; ++index) {
  		vq = n->vqs + index;
  		mutex_lock(&vq->mutex);
  		priv = test ? n : NULL;
  
  		/* start polling new socket */
247643f85   Eugenio Pérez   vhost: Create acc...
200
201
  		oldpriv = vhost_vq_get_backend(vq);
  		vhost_vq_set_backend(vq, priv);
71ccc212e   Michael S. Tsirkin   vhost test module
202

80f7d0301   Greg Kurz   vhost: rename vho...
203
  		r = vhost_vq_init_access(&n->vqs[index]);
f59281daf   Jason Wang   vhost: init used ...
204

71ccc212e   Michael S. Tsirkin   vhost test module
205
  		mutex_unlock(&vq->mutex);
f59281daf   Jason Wang   vhost: init used ...
206
207
  		if (r)
  			goto err;
71ccc212e   Michael S. Tsirkin   vhost test module
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  		if (oldpriv) {
  			vhost_test_flush_vq(n, index);
  		}
  	}
  
  	mutex_unlock(&n->dev.mutex);
  	return 0;
  
  err:
  	mutex_unlock(&n->dev.mutex);
  	return r;
  }
  
  static long vhost_test_reset_owner(struct vhost_test *n)
  {
  	void *priv = NULL;
  	long err;
3302363a2   Michael S. Tsirkin   virtio/test: fix ...
225
  	struct vhost_iotlb *umem;
150b9e51a   Michael S. Tsirkin   vhost: fix error ...
226

71ccc212e   Michael S. Tsirkin   vhost test module
227
228
229
230
  	mutex_lock(&n->dev.mutex);
  	err = vhost_dev_check_owner(&n->dev);
  	if (err)
  		goto done;
446374d7c   Michael S. Tsirkin   vhost/test: fix a...
231
232
  	umem = vhost_dev_reset_owner_prepare();
  	if (!umem) {
150b9e51a   Michael S. Tsirkin   vhost: fix error ...
233
234
235
  		err = -ENOMEM;
  		goto done;
  	}
71ccc212e   Michael S. Tsirkin   vhost test module
236
237
  	vhost_test_stop(n, &priv);
  	vhost_test_flush(n);
245cdd9fb   Michael S. Tsirkin   vhost/test: stop ...
238
  	vhost_dev_stop(&n->dev);
446374d7c   Michael S. Tsirkin   vhost/test: fix a...
239
  	vhost_dev_reset_owner(&n->dev, umem);
71ccc212e   Michael S. Tsirkin   vhost test module
240
241
242
243
244
245
246
  done:
  	mutex_unlock(&n->dev.mutex);
  	return err;
  }
  
  static int vhost_test_set_features(struct vhost_test *n, u64 features)
  {
ea16c5143   Michael S. Tsirkin   vhost: move acked...
247
  	struct vhost_virtqueue *vq;
71ccc212e   Michael S. Tsirkin   vhost test module
248
249
250
251
252
253
  	mutex_lock(&n->dev.mutex);
  	if ((features & (1 << VHOST_F_LOG_ALL)) &&
  	    !vhost_log_access_ok(&n->dev)) {
  		mutex_unlock(&n->dev.mutex);
  		return -EFAULT;
  	}
ea16c5143   Michael S. Tsirkin   vhost: move acked...
254
255
256
257
  	vq = &n->vqs[VHOST_TEST_VQ];
  	mutex_lock(&vq->mutex);
  	vq->acked_features = features;
  	mutex_unlock(&vq->mutex);
71ccc212e   Michael S. Tsirkin   vhost test module
258
259
260
  	mutex_unlock(&n->dev.mutex);
  	return 0;
  }
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
  static long vhost_test_set_backend(struct vhost_test *n, unsigned index, int fd)
  {
  	static void *backend;
  
  	const bool enable = fd != -1;
  	struct vhost_virtqueue *vq;
  	int r;
  
  	mutex_lock(&n->dev.mutex);
  	r = vhost_dev_check_owner(&n->dev);
  	if (r)
  		goto err;
  
  	if (index >= VHOST_TEST_VQ_MAX) {
  		r = -ENOBUFS;
  		goto err;
  	}
  	vq = &n->vqs[index];
  	mutex_lock(&vq->mutex);
  
  	/* Verify that ring has been setup correctly. */
  	if (!vhost_vq_access_ok(vq)) {
  		r = -EFAULT;
  		goto err_vq;
  	}
  	if (!enable) {
  		vhost_poll_stop(&vq->poll);
  		backend = vhost_vq_get_backend(vq);
  		vhost_vq_set_backend(vq, NULL);
  	} else {
  		vhost_vq_set_backend(vq, backend);
  		r = vhost_vq_init_access(vq);
  		if (r == 0)
  			r = vhost_poll_start(&vq->poll, vq->kick);
  	}
  
  	mutex_unlock(&vq->mutex);
  
  	if (enable) {
  		vhost_test_flush_vq(n, index);
  	}
  
  	mutex_unlock(&n->dev.mutex);
  	return 0;
  
  err_vq:
  	mutex_unlock(&vq->mutex);
  err:
  	mutex_unlock(&n->dev.mutex);
  	return r;
  }
71ccc212e   Michael S. Tsirkin   vhost test module
312
313
314
  static long vhost_test_ioctl(struct file *f, unsigned int ioctl,
  			     unsigned long arg)
  {
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
315
  	struct vhost_vring_file backend;
71ccc212e   Michael S. Tsirkin   vhost test module
316
317
318
319
320
321
322
323
324
325
326
  	struct vhost_test *n = f->private_data;
  	void __user *argp = (void __user *)arg;
  	u64 __user *featurep = argp;
  	int test;
  	u64 features;
  	int r;
  	switch (ioctl) {
  	case VHOST_TEST_RUN:
  		if (copy_from_user(&test, argp, sizeof test))
  			return -EFAULT;
  		return vhost_test_run(n, test);
264ee5aa8   Eugenio Pérez   tools/virtio: Add...
327
328
329
330
  	case VHOST_TEST_SET_BACKEND:
  		if (copy_from_user(&backend, argp, sizeof backend))
  			return -EFAULT;
  		return vhost_test_set_backend(n, backend.index, backend.fd);
71ccc212e   Michael S. Tsirkin   vhost test module
331
  	case VHOST_GET_FEATURES:
09a34c840   Michael S. Tsirkin   vhost/test: updat...
332
  		features = VHOST_FEATURES;
71ccc212e   Michael S. Tsirkin   vhost test module
333
334
335
336
  		if (copy_to_user(featurep, &features, sizeof features))
  			return -EFAULT;
  		return 0;
  	case VHOST_SET_FEATURES:
4e9fa50c6   Michael S. Tsirkin   vhost: move featu...
337
338
  		printk(KERN_ERR "1
  ");
71ccc212e   Michael S. Tsirkin   vhost test module
339
340
  		if (copy_from_user(&features, featurep, sizeof features))
  			return -EFAULT;
4e9fa50c6   Michael S. Tsirkin   vhost: move featu...
341
342
  		printk(KERN_ERR "2
  ");
09a34c840   Michael S. Tsirkin   vhost/test: updat...
343
  		if (features & ~VHOST_FEATURES)
71ccc212e   Michael S. Tsirkin   vhost test module
344
  			return -EOPNOTSUPP;
4e9fa50c6   Michael S. Tsirkin   vhost: move featu...
345
346
  		printk(KERN_ERR "3
  ");
71ccc212e   Michael S. Tsirkin   vhost test module
347
348
349
350
351
  		return vhost_test_set_features(n, features);
  	case VHOST_RESET_OWNER:
  		return vhost_test_reset_owner(n);
  	default:
  		mutex_lock(&n->dev.mutex);
73640c991   Michael S. Tsirkin   tools/virtio: fix...
352
353
354
  		r = vhost_dev_ioctl(&n->dev, ioctl, argp);
                  if (r == -ENOIOCTLCMD)
                          r = vhost_vring_ioctl(&n->dev, ioctl, argp);
71ccc212e   Michael S. Tsirkin   vhost test module
355
356
357
358
359
  		vhost_test_flush(n);
  		mutex_unlock(&n->dev.mutex);
  		return r;
  	}
  }
71ccc212e   Michael S. Tsirkin   vhost test module
360
361
362
363
  static const struct file_operations vhost_test_fops = {
  	.owner          = THIS_MODULE,
  	.release        = vhost_test_release,
  	.unlocked_ioctl = vhost_test_ioctl,
407e9ef72   Arnd Bergmann   compat_ioctl: mov...
364
  	.compat_ioctl   = compat_ptr_ioctl,
71ccc212e   Michael S. Tsirkin   vhost test module
365
366
367
368
369
370
371
372
373
  	.open           = vhost_test_open,
  	.llseek		= noop_llseek,
  };
  
  static struct miscdevice vhost_test_misc = {
  	MISC_DYNAMIC_MINOR,
  	"vhost-test",
  	&vhost_test_fops,
  };
ca75d601b   PrasannaKumar Muralidharan   miscdevice: Add h...
374
  module_misc_device(vhost_test_misc);
71ccc212e   Michael S. Tsirkin   vhost test module
375
376
377
378
379
  
  MODULE_VERSION("0.0.1");
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("Michael S. Tsirkin");
  MODULE_DESCRIPTION("Host kernel side for virtio simulator");