···11+/* Copyright (C) 2009 Red Hat, Inc.22+ * Author: Michael S. Tsirkin <mst@redhat.com>33+ *44+ * This work is licensed under the terms of the GNU GPL, version 2.55+ *66+ * test virtio server in host kernel.77+ */88+99+#include <linux/compat.h>1010+#include <linux/eventfd.h>1111+#include <linux/vhost.h>1212+#include <linux/miscdevice.h>1313+#include <linux/module.h>1414+#include <linux/mutex.h>1515+#include <linux/workqueue.h>1616+#include <linux/rcupdate.h>1717+#include <linux/file.h>1818+#include <linux/slab.h>1919+2020+#include "test.h"2121+#include "vhost.c"2222+2323+/* Max number of bytes transferred before requeueing the job.2424+ * Using this limit prevents one virtqueue from starving others. */2525+#define VHOST_TEST_WEIGHT 0x800002626+2727+enum {2828+ VHOST_TEST_VQ = 0,2929+ VHOST_TEST_VQ_MAX = 1,3030+};3131+3232+struct vhost_test {3333+ struct vhost_dev dev;3434+ struct vhost_virtqueue vqs[VHOST_TEST_VQ_MAX];3535+};3636+3737+/* Expects to be always run from workqueue - which acts as3838+ * read-size critical section for our kind of RCU. */3939+static void handle_vq(struct vhost_test *n)4040+{4141+ struct vhost_virtqueue *vq = &n->dev.vqs[VHOST_TEST_VQ];4242+ unsigned out, in;4343+ int head;4444+ size_t len, total_len = 0;4545+ void *private;4646+4747+ private = rcu_dereference_check(vq->private_data, 1);4848+ if (!private)4949+ return;5050+5151+ mutex_lock(&vq->mutex);5252+ vhost_disable_notify(vq);5353+5454+ for (;;) {5555+ head = vhost_get_vq_desc(&n->dev, vq, vq->iov,5656+ ARRAY_SIZE(vq->iov),5757+ &out, &in,5858+ NULL, NULL);5959+ /* On error, stop handling until the next kick. */6060+ if (unlikely(head < 0))6161+ break;6262+ /* Nothing new? Wait for eventfd to tell us they refilled. */6363+ if (head == vq->num) {6464+ if (unlikely(vhost_enable_notify(vq))) {6565+ vhost_disable_notify(vq);6666+ continue;6767+ }6868+ break;6969+ }7070+ if (in) {7171+ vq_err(vq, "Unexpected descriptor format for TX: "7272+ "out %d, int %d\n", out, in);7373+ break;7474+ }7575+ len = iov_length(vq->iov, out);7676+ /* Sanity check */7777+ if (!len) {7878+ vq_err(vq, "Unexpected 0 len for TX\n");7979+ break;8080+ }8181+ vhost_add_used_and_signal(&n->dev, vq, head, 0);8282+ total_len += len;8383+ if (unlikely(total_len >= VHOST_TEST_WEIGHT)) {8484+ vhost_poll_queue(&vq->poll);8585+ break;8686+ }8787+ }8888+8989+ mutex_unlock(&vq->mutex);9090+}9191+9292+static void handle_vq_kick(struct vhost_work *work)9393+{9494+ struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,9595+ poll.work);9696+ struct vhost_test *n = container_of(vq->dev, struct vhost_test, dev);9797+9898+ handle_vq(n);9999+}100100+101101+static int vhost_test_open(struct inode *inode, struct file *f)102102+{103103+ struct vhost_test *n = kmalloc(sizeof *n, GFP_KERNEL);104104+ struct vhost_dev *dev;105105+ int r;106106+107107+ if (!n)108108+ return -ENOMEM;109109+110110+ dev = &n->dev;111111+ n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick;112112+ r = vhost_dev_init(dev, n->vqs, VHOST_TEST_VQ_MAX);113113+ if (r < 0) {114114+ kfree(n);115115+ return r;116116+ }117117+118118+ f->private_data = n;119119+120120+ return 0;121121+}122122+123123+static void *vhost_test_stop_vq(struct vhost_test *n,124124+ struct vhost_virtqueue *vq)125125+{126126+ void *private;127127+128128+ mutex_lock(&vq->mutex);129129+ private = rcu_dereference_protected(vq->private_data,130130+ lockdep_is_held(&vq->mutex));131131+ rcu_assign_pointer(vq->private_data, NULL);132132+ mutex_unlock(&vq->mutex);133133+ return private;134134+}135135+136136+static void vhost_test_stop(struct vhost_test *n, void **privatep)137137+{138138+ *privatep = vhost_test_stop_vq(n, n->vqs + VHOST_TEST_VQ);139139+}140140+141141+static void vhost_test_flush_vq(struct vhost_test *n, int index)142142+{143143+ vhost_poll_flush(&n->dev.vqs[index].poll);144144+}145145+146146+static void vhost_test_flush(struct vhost_test *n)147147+{148148+ vhost_test_flush_vq(n, VHOST_TEST_VQ);149149+}150150+151151+static int vhost_test_release(struct inode *inode, struct file *f)152152+{153153+ struct vhost_test *n = f->private_data;154154+ void *private;155155+156156+ vhost_test_stop(n, &private);157157+ vhost_test_flush(n);158158+ vhost_dev_cleanup(&n->dev);159159+ /* We do an extra flush before freeing memory,160160+ * since jobs can re-queue themselves. */161161+ vhost_test_flush(n);162162+ kfree(n);163163+ return 0;164164+}165165+166166+static long vhost_test_run(struct vhost_test *n, int test)167167+{168168+ void *priv, *oldpriv;169169+ struct vhost_virtqueue *vq;170170+ int r, index;171171+172172+ if (test < 0 || test > 1)173173+ return -EINVAL;174174+175175+ mutex_lock(&n->dev.mutex);176176+ r = vhost_dev_check_owner(&n->dev);177177+ if (r)178178+ goto err;179179+180180+ for (index = 0; index < n->dev.nvqs; ++index) {181181+ /* Verify that ring has been setup correctly. */182182+ if (!vhost_vq_access_ok(&n->vqs[index])) {183183+ r = -EFAULT;184184+ goto err;185185+ }186186+ }187187+188188+ for (index = 0; index < n->dev.nvqs; ++index) {189189+ vq = n->vqs + index;190190+ mutex_lock(&vq->mutex);191191+ priv = test ? n : NULL;192192+193193+ /* start polling new socket */194194+ oldpriv = rcu_dereference_protected(vq->private_data,195195+ lockdep_is_held(&vq->mutex));196196+ rcu_assign_pointer(vq->private_data, priv);197197+198198+ mutex_unlock(&vq->mutex);199199+200200+ if (oldpriv) {201201+ vhost_test_flush_vq(n, index);202202+ }203203+ }204204+205205+ mutex_unlock(&n->dev.mutex);206206+ return 0;207207+208208+err:209209+ mutex_unlock(&n->dev.mutex);210210+ return r;211211+}212212+213213+static long vhost_test_reset_owner(struct vhost_test *n)214214+{215215+ void *priv = NULL;216216+ long err;217217+ mutex_lock(&n->dev.mutex);218218+ err = vhost_dev_check_owner(&n->dev);219219+ if (err)220220+ goto done;221221+ vhost_test_stop(n, &priv);222222+ vhost_test_flush(n);223223+ err = vhost_dev_reset_owner(&n->dev);224224+done:225225+ mutex_unlock(&n->dev.mutex);226226+ return err;227227+}228228+229229+static int vhost_test_set_features(struct vhost_test *n, u64 features)230230+{231231+ mutex_lock(&n->dev.mutex);232232+ if ((features & (1 << VHOST_F_LOG_ALL)) &&233233+ !vhost_log_access_ok(&n->dev)) {234234+ mutex_unlock(&n->dev.mutex);235235+ return -EFAULT;236236+ }237237+ n->dev.acked_features = features;238238+ smp_wmb();239239+ vhost_test_flush(n);240240+ mutex_unlock(&n->dev.mutex);241241+ return 0;242242+}243243+244244+static long vhost_test_ioctl(struct file *f, unsigned int ioctl,245245+ unsigned long arg)246246+{247247+ struct vhost_test *n = f->private_data;248248+ void __user *argp = (void __user *)arg;249249+ u64 __user *featurep = argp;250250+ int test;251251+ u64 features;252252+ int r;253253+ switch (ioctl) {254254+ case VHOST_TEST_RUN:255255+ if (copy_from_user(&test, argp, sizeof test))256256+ return -EFAULT;257257+ return vhost_test_run(n, test);258258+ case VHOST_GET_FEATURES:259259+ features = VHOST_FEATURES;260260+ if (copy_to_user(featurep, &features, sizeof features))261261+ return -EFAULT;262262+ return 0;263263+ case VHOST_SET_FEATURES:264264+ if (copy_from_user(&features, featurep, sizeof features))265265+ return -EFAULT;266266+ if (features & ~VHOST_FEATURES)267267+ return -EOPNOTSUPP;268268+ return vhost_test_set_features(n, features);269269+ case VHOST_RESET_OWNER:270270+ return vhost_test_reset_owner(n);271271+ default:272272+ mutex_lock(&n->dev.mutex);273273+ r = vhost_dev_ioctl(&n->dev, ioctl, arg);274274+ vhost_test_flush(n);275275+ mutex_unlock(&n->dev.mutex);276276+ return r;277277+ }278278+}279279+280280+#ifdef CONFIG_COMPAT281281+static long vhost_test_compat_ioctl(struct file *f, unsigned int ioctl,282282+ unsigned long arg)283283+{284284+ return vhost_test_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));285285+}286286+#endif287287+288288+static const struct file_operations vhost_test_fops = {289289+ .owner = THIS_MODULE,290290+ .release = vhost_test_release,291291+ .unlocked_ioctl = vhost_test_ioctl,292292+#ifdef CONFIG_COMPAT293293+ .compat_ioctl = vhost_test_compat_ioctl,294294+#endif295295+ .open = vhost_test_open,296296+ .llseek = noop_llseek,297297+};298298+299299+static struct miscdevice vhost_test_misc = {300300+ MISC_DYNAMIC_MINOR,301301+ "vhost-test",302302+ &vhost_test_fops,303303+};304304+305305+static int vhost_test_init(void)306306+{307307+ return misc_register(&vhost_test_misc);308308+}309309+module_init(vhost_test_init);310310+311311+static void vhost_test_exit(void)312312+{313313+ misc_deregister(&vhost_test_misc);314314+}315315+module_exit(vhost_test_exit);316316+317317+MODULE_VERSION("0.0.1");318318+MODULE_LICENSE("GPL v2");319319+MODULE_AUTHOR("Michael S. Tsirkin");320320+MODULE_DESCRIPTION("Host kernel side for virtio simulator");
+7
drivers/vhost/test.h
···11+#ifndef LINUX_VHOST_TEST_H22+#define LINUX_VHOST_TEST_H33+44+/* Start a given test on the virtio null device. 0 stops all tests. */55+#define VHOST_TEST_RUN _IOW(VHOST_VIRTIO, 0x31, int)66+77+#endif