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 = vhost_vq_get_backend(vq);
  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, true, NULL);
 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 = vhost_vq_get_backend(vq);
 137        vhost_vq_set_backend(vq, 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 = vhost_vq_get_backend(vq);
 202                vhost_vq_set_backend(vq, 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_iotlb *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_set_backend(struct vhost_test *n, unsigned index, int fd)
 267{
 268        static void *backend;
 269
 270        const bool enable = fd != -1;
 271        struct vhost_virtqueue *vq;
 272        int r;
 273
 274        mutex_lock(&n->dev.mutex);
 275        r = vhost_dev_check_owner(&n->dev);
 276        if (r)
 277                goto err;
 278
 279        if (index >= VHOST_TEST_VQ_MAX) {
 280                r = -ENOBUFS;
 281                goto err;
 282        }
 283        vq = &n->vqs[index];
 284        mutex_lock(&vq->mutex);
 285
 286        /* Verify that ring has been setup correctly. */
 287        if (!vhost_vq_access_ok(vq)) {
 288                r = -EFAULT;
 289                goto err_vq;
 290        }
 291        if (!enable) {
 292                vhost_poll_stop(&vq->poll);
 293                backend = vhost_vq_get_backend(vq);
 294                vhost_vq_set_backend(vq, NULL);
 295        } else {
 296                vhost_vq_set_backend(vq, backend);
 297                r = vhost_vq_init_access(vq);
 298                if (r == 0)
 299                        r = vhost_poll_start(&vq->poll, vq->kick);
 300        }
 301
 302        mutex_unlock(&vq->mutex);
 303
 304        if (enable) {
 305                vhost_test_flush_vq(n, index);
 306        }
 307
 308        mutex_unlock(&n->dev.mutex);
 309        return 0;
 310
 311err_vq:
 312        mutex_unlock(&vq->mutex);
 313err:
 314        mutex_unlock(&n->dev.mutex);
 315        return r;
 316}
 317
 318static long vhost_test_ioctl(struct file *f, unsigned int ioctl,
 319                             unsigned long arg)
 320{
 321        struct vhost_vring_file backend;
 322        struct vhost_test *n = f->private_data;
 323        void __user *argp = (void __user *)arg;
 324        u64 __user *featurep = argp;
 325        int test;
 326        u64 features;
 327        int r;
 328        switch (ioctl) {
 329        case VHOST_TEST_RUN:
 330                if (copy_from_user(&test, argp, sizeof test))
 331                        return -EFAULT;
 332                return vhost_test_run(n, test);
 333        case VHOST_TEST_SET_BACKEND:
 334                if (copy_from_user(&backend, argp, sizeof backend))
 335                        return -EFAULT;
 336                return vhost_test_set_backend(n, backend.index, backend.fd);
 337        case VHOST_GET_FEATURES:
 338                features = VHOST_FEATURES;
 339                if (copy_to_user(featurep, &features, sizeof features))
 340                        return -EFAULT;
 341                return 0;
 342        case VHOST_SET_FEATURES:
 343                printk(KERN_ERR "1\n");
 344                if (copy_from_user(&features, featurep, sizeof features))
 345                        return -EFAULT;
 346                printk(KERN_ERR "2\n");
 347                if (features & ~VHOST_FEATURES)
 348                        return -EOPNOTSUPP;
 349                printk(KERN_ERR "3\n");
 350                return vhost_test_set_features(n, features);
 351        case VHOST_RESET_OWNER:
 352                return vhost_test_reset_owner(n);
 353        default:
 354                mutex_lock(&n->dev.mutex);
 355                r = vhost_dev_ioctl(&n->dev, ioctl, argp);
 356                if (r == -ENOIOCTLCMD)
 357                        r = vhost_vring_ioctl(&n->dev, ioctl, argp);
 358                vhost_test_flush(n);
 359                mutex_unlock(&n->dev.mutex);
 360                return r;
 361        }
 362}
 363
 364static const struct file_operations vhost_test_fops = {
 365        .owner          = THIS_MODULE,
 366        .release        = vhost_test_release,
 367        .unlocked_ioctl = vhost_test_ioctl,
 368        .compat_ioctl   = compat_ptr_ioctl,
 369        .open           = vhost_test_open,
 370        .llseek         = noop_llseek,
 371};
 372
 373static struct miscdevice vhost_test_misc = {
 374        MISC_DYNAMIC_MINOR,
 375        "vhost-test",
 376        &vhost_test_fops,
 377};
 378module_misc_device(vhost_test_misc);
 379
 380MODULE_VERSION("0.0.1");
 381MODULE_LICENSE("GPL v2");
 382MODULE_AUTHOR("Michael S. Tsirkin");
 383MODULE_DESCRIPTION("Host kernel side for virtio simulator");
 384