linux/drivers/vhost/test.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Copyright (C) 2009 Red Hat, Inc.
   3 * Author: Michael S. Tsirkin <mst@redhat.com>
   4 *
   5 * test virtio server in host kernel.
   6 */
   7
   8#include <linux/compat.h>
   9#include <linux/eventfd.h>
  10#include <linux/vhost.h>
  11#include <linux/miscdevice.h>
  12#include <linux/module.h>
  13#include <linux/mutex.h>
  14#include <linux/workqueue.h>
  15#include <linux/file.h>
  16#include <linux/slab.h>
  17
  18#include "test.h"
  19#include "vhost.h"
  20
  21/* Max number of bytes transferred before requeueing the job.
  22 * Using this limit prevents one virtqueue from starving others. */
  23#define VHOST_TEST_WEIGHT 0x80000
  24
  25/* Max number of packets transferred before requeueing the job.
  26 * Using this limit prevents one virtqueue from starving others with
  27 * pkts.
  28 */
  29#define VHOST_TEST_PKT_WEIGHT 256
  30
  31enum {
  32        VHOST_TEST_VQ = 0,
  33        VHOST_TEST_VQ_MAX = 1,
  34};
  35
  36struct vhost_test {
  37        struct vhost_dev dev;
  38        struct vhost_virtqueue vqs[VHOST_TEST_VQ_MAX];
  39};
  40
  41/* Expects to be always run from workqueue - which acts as
  42 * read-size critical section for our kind of RCU. */
  43static void handle_vq(struct vhost_test *n)
  44{
  45        struct vhost_virtqueue *vq = &n->vqs[VHOST_TEST_VQ];
  46        unsigned out, in;
  47        int head;
  48        size_t len, total_len = 0;
  49        void *private;
  50
  51        mutex_lock(&vq->mutex);
  52        private = vq->private_data;
  53        if (!private) {
  54                mutex_unlock(&vq->mutex);
  55                return;
  56        }
  57
  58        vhost_disable_notify(&n->dev, vq);
  59
  60        for (;;) {
  61                head = vhost_get_vq_desc(vq, vq->iov,
  62                                         ARRAY_SIZE(vq->iov),
  63                                         &out, &in,
  64                                         NULL, NULL);
  65                /* On error, stop handling until the next kick. */
  66                if (unlikely(head < 0))
  67                        break;
  68                /* Nothing new?  Wait for eventfd to tell us they refilled. */
  69                if (head == vq->num) {
  70                        if (unlikely(vhost_enable_notify(&n->dev, vq))) {
  71                                vhost_disable_notify(&n->dev, vq);
  72                                continue;
  73                        }
  74                        break;
  75                }
  76                if (in) {
  77                        vq_err(vq, "Unexpected descriptor format for TX: "
  78                               "out %d, int %d\n", out, in);
  79                        break;
  80                }
  81                len = iov_length(vq->iov, out);
  82                /* Sanity check */
  83                if (!len) {
  84                        vq_err(vq, "Unexpected 0 len for TX\n");
  85                        break;
  86                }
  87                vhost_add_used_and_signal(&n->dev, vq, head, 0);
  88                total_len += len;
  89                if (unlikely(vhost_exceeds_weight(vq, 0, total_len)))
  90                        break;
  91        }
  92
  93        mutex_unlock(&vq->mutex);
  94}
  95
  96static void handle_vq_kick(struct vhost_work *work)
  97{
  98        struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
  99                                                  poll.work);
 100        struct vhost_test *n = container_of(vq->dev, struct vhost_test, dev);
 101
 102        handle_vq(n);
 103}
 104
 105static int vhost_test_open(struct inode *inode, struct file *f)
 106{
 107        struct vhost_test *n = kmalloc(sizeof *n, GFP_KERNEL);
 108        struct vhost_dev *dev;
 109        struct vhost_virtqueue **vqs;
 110
 111        if (!n)
 112                return -ENOMEM;
 113        vqs = kmalloc_array(VHOST_TEST_VQ_MAX, sizeof(*vqs), GFP_KERNEL);
 114        if (!vqs) {
 115                kfree(n);
 116                return -ENOMEM;
 117        }
 118
 119        dev = &n->dev;
 120        vqs[VHOST_TEST_VQ] = &n->vqs[VHOST_TEST_VQ];
 121        n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick;
 122        vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX, UIO_MAXIOV,
 123                       VHOST_TEST_PKT_WEIGHT, VHOST_TEST_WEIGHT);
 124
 125        f->private_data = n;
 126
 127        return 0;
 128}
 129
 130static void *vhost_test_stop_vq(struct vhost_test *n,
 131                                struct vhost_virtqueue *vq)
 132{
 133        void *private;
 134
 135        mutex_lock(&vq->mutex);
 136        private = vq->private_data;
 137        vq->private_data = NULL;
 138        mutex_unlock(&vq->mutex);
 139        return private;
 140}
 141
 142static void vhost_test_stop(struct vhost_test *n, void **privatep)
 143{
 144        *privatep = vhost_test_stop_vq(n, n->vqs + VHOST_TEST_VQ);
 145}
 146
 147static void vhost_test_flush_vq(struct vhost_test *n, int index)
 148{
 149        vhost_poll_flush(&n->vqs[index].poll);
 150}
 151
 152static void vhost_test_flush(struct vhost_test *n)
 153{
 154        vhost_test_flush_vq(n, VHOST_TEST_VQ);
 155}
 156
 157static int vhost_test_release(struct inode *inode, struct file *f)
 158{
 159        struct vhost_test *n = f->private_data;
 160        void  *private;
 161
 162        vhost_test_stop(n, &private);
 163        vhost_test_flush(n);
 164        vhost_dev_stop(&n->dev);
 165        vhost_dev_cleanup(&n->dev);
 166        /* We do an extra flush before freeing memory,
 167         * since jobs can re-queue themselves. */
 168        vhost_test_flush(n);
 169        kfree(n);
 170        return 0;
 171}
 172
 173static long vhost_test_run(struct vhost_test *n, int test)
 174{
 175        void *priv, *oldpriv;
 176        struct vhost_virtqueue *vq;
 177        int r, index;
 178
 179        if (test < 0 || test > 1)
 180                return -EINVAL;
 181
 182        mutex_lock(&n->dev.mutex);
 183        r = vhost_dev_check_owner(&n->dev);
 184        if (r)
 185                goto err;
 186
 187        for (index = 0; index < n->dev.nvqs; ++index) {
 188                /* Verify that ring has been setup correctly. */
 189                if (!vhost_vq_access_ok(&n->vqs[index])) {
 190                        r = -EFAULT;
 191                        goto err;
 192                }
 193        }
 194
 195        for (index = 0; index < n->dev.nvqs; ++index) {
 196                vq = n->vqs + index;
 197                mutex_lock(&vq->mutex);
 198                priv = test ? n : NULL;
 199
 200                /* start polling new socket */
 201                oldpriv = vq->private_data;
 202                vq->private_data = priv;
 203
 204                r = vhost_vq_init_access(&n->vqs[index]);
 205
 206                mutex_unlock(&vq->mutex);
 207
 208                if (r)
 209                        goto err;
 210
 211                if (oldpriv) {
 212                        vhost_test_flush_vq(n, index);
 213                }
 214        }
 215
 216        mutex_unlock(&n->dev.mutex);
 217        return 0;
 218
 219err:
 220        mutex_unlock(&n->dev.mutex);
 221        return r;
 222}
 223
 224static long vhost_test_reset_owner(struct vhost_test *n)
 225{
 226        void *priv = NULL;
 227        long err;
 228        struct vhost_umem *umem;
 229
 230        mutex_lock(&n->dev.mutex);
 231        err = vhost_dev_check_owner(&n->dev);
 232        if (err)
 233                goto done;
 234        umem = vhost_dev_reset_owner_prepare();
 235        if (!umem) {
 236                err = -ENOMEM;
 237                goto done;
 238        }
 239        vhost_test_stop(n, &priv);
 240        vhost_test_flush(n);
 241        vhost_dev_stop(&n->dev);
 242        vhost_dev_reset_owner(&n->dev, umem);
 243done:
 244        mutex_unlock(&n->dev.mutex);
 245        return err;
 246}
 247
 248static int vhost_test_set_features(struct vhost_test *n, u64 features)
 249{
 250        struct vhost_virtqueue *vq;
 251
 252        mutex_lock(&n->dev.mutex);
 253        if ((features & (1 << VHOST_F_LOG_ALL)) &&
 254            !vhost_log_access_ok(&n->dev)) {
 255                mutex_unlock(&n->dev.mutex);
 256                return -EFAULT;
 257        }
 258        vq = &n->vqs[VHOST_TEST_VQ];
 259        mutex_lock(&vq->mutex);
 260        vq->acked_features = features;
 261        mutex_unlock(&vq->mutex);
 262        mutex_unlock(&n->dev.mutex);
 263        return 0;
 264}
 265
 266static long vhost_test_ioctl(struct file *f, unsigned int ioctl,
 267                             unsigned long arg)
 268{
 269        struct vhost_test *n = f->private_data;
 270        void __user *argp = (void __user *)arg;
 271        u64 __user *featurep = argp;
 272        int test;
 273        u64 features;
 274        int r;
 275        switch (ioctl) {
 276        case VHOST_TEST_RUN:
 277                if (copy_from_user(&test, argp, sizeof test))
 278                        return -EFAULT;
 279                return vhost_test_run(n, test);
 280        case VHOST_GET_FEATURES:
 281                features = VHOST_FEATURES;
 282                if (copy_to_user(featurep, &features, sizeof features))
 283                        return -EFAULT;
 284                return 0;
 285        case VHOST_SET_FEATURES:
 286                printk(KERN_ERR "1\n");
 287                if (copy_from_user(&features, featurep, sizeof features))
 288                        return -EFAULT;
 289                printk(KERN_ERR "2\n");
 290                if (features & ~VHOST_FEATURES)
 291                        return -EOPNOTSUPP;
 292                printk(KERN_ERR "3\n");
 293                return vhost_test_set_features(n, features);
 294        case VHOST_RESET_OWNER:
 295                return vhost_test_reset_owner(n);
 296        default:
 297                mutex_lock(&n->dev.mutex);
 298                r = vhost_dev_ioctl(&n->dev, ioctl, argp);
 299                if (r == -ENOIOCTLCMD)
 300                        r = vhost_vring_ioctl(&n->dev, ioctl, argp);
 301                vhost_test_flush(n);
 302                mutex_unlock(&n->dev.mutex);
 303                return r;
 304        }
 305}
 306
 307static const struct file_operations vhost_test_fops = {
 308        .owner          = THIS_MODULE,
 309        .release        = vhost_test_release,
 310        .unlocked_ioctl = vhost_test_ioctl,
 311        .compat_ioctl   = compat_ptr_ioctl,
 312        .open           = vhost_test_open,
 313        .llseek         = noop_llseek,
 314};
 315
 316static struct miscdevice vhost_test_misc = {
 317        MISC_DYNAMIC_MINOR,
 318        "vhost-test",
 319        &vhost_test_fops,
 320};
 321module_misc_device(vhost_test_misc);
 322
 323MODULE_VERSION("0.0.1");
 324MODULE_LICENSE("GPL v2");
 325MODULE_AUTHOR("Michael S. Tsirkin");
 326MODULE_DESCRIPTION("Host kernel side for virtio simulator");
 327