162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (C) 2009 Red Hat, Inc. 362306a36Sopenharmony_ci * Author: Michael S. Tsirkin <mst@redhat.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * test virtio server in host kernel. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/compat.h> 962306a36Sopenharmony_ci#include <linux/eventfd.h> 1062306a36Sopenharmony_ci#include <linux/vhost.h> 1162306a36Sopenharmony_ci#include <linux/miscdevice.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci#include <linux/file.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "test.h" 1962306a36Sopenharmony_ci#include "vhost.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* Max number of bytes transferred before requeueing the job. 2262306a36Sopenharmony_ci * Using this limit prevents one virtqueue from starving others. */ 2362306a36Sopenharmony_ci#define VHOST_TEST_WEIGHT 0x80000 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Max number of packets transferred before requeueing the job. 2662306a36Sopenharmony_ci * Using this limit prevents one virtqueue from starving others with 2762306a36Sopenharmony_ci * pkts. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define VHOST_TEST_PKT_WEIGHT 256 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cienum { 3262306a36Sopenharmony_ci VHOST_TEST_VQ = 0, 3362306a36Sopenharmony_ci VHOST_TEST_VQ_MAX = 1, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct vhost_test { 3762306a36Sopenharmony_ci struct vhost_dev dev; 3862306a36Sopenharmony_ci struct vhost_virtqueue vqs[VHOST_TEST_VQ_MAX]; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Expects to be always run from workqueue - which acts as 4262306a36Sopenharmony_ci * read-size critical section for our kind of RCU. */ 4362306a36Sopenharmony_cistatic void handle_vq(struct vhost_test *n) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct vhost_virtqueue *vq = &n->vqs[VHOST_TEST_VQ]; 4662306a36Sopenharmony_ci unsigned out, in; 4762306a36Sopenharmony_ci int head; 4862306a36Sopenharmony_ci size_t len, total_len = 0; 4962306a36Sopenharmony_ci void *private; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci mutex_lock(&vq->mutex); 5262306a36Sopenharmony_ci private = vhost_vq_get_backend(vq); 5362306a36Sopenharmony_ci if (!private) { 5462306a36Sopenharmony_ci mutex_unlock(&vq->mutex); 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci vhost_disable_notify(&n->dev, vq); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci for (;;) { 6162306a36Sopenharmony_ci head = vhost_get_vq_desc(vq, vq->iov, 6262306a36Sopenharmony_ci ARRAY_SIZE(vq->iov), 6362306a36Sopenharmony_ci &out, &in, 6462306a36Sopenharmony_ci NULL, NULL); 6562306a36Sopenharmony_ci /* On error, stop handling until the next kick. */ 6662306a36Sopenharmony_ci if (unlikely(head < 0)) 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci /* Nothing new? Wait for eventfd to tell us they refilled. */ 6962306a36Sopenharmony_ci if (head == vq->num) { 7062306a36Sopenharmony_ci if (unlikely(vhost_enable_notify(&n->dev, vq))) { 7162306a36Sopenharmony_ci vhost_disable_notify(&n->dev, vq); 7262306a36Sopenharmony_ci continue; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci if (in) { 7762306a36Sopenharmony_ci vq_err(vq, "Unexpected descriptor format for TX: " 7862306a36Sopenharmony_ci "out %d, int %d\n", out, in); 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci len = iov_length(vq->iov, out); 8262306a36Sopenharmony_ci /* Sanity check */ 8362306a36Sopenharmony_ci if (!len) { 8462306a36Sopenharmony_ci vq_err(vq, "Unexpected 0 len for TX\n"); 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci vhost_add_used_and_signal(&n->dev, vq, head, 0); 8862306a36Sopenharmony_ci total_len += len; 8962306a36Sopenharmony_ci if (unlikely(vhost_exceeds_weight(vq, 0, total_len))) 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci mutex_unlock(&vq->mutex); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void handle_vq_kick(struct vhost_work *work) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, 9962306a36Sopenharmony_ci poll.work); 10062306a36Sopenharmony_ci struct vhost_test *n = container_of(vq->dev, struct vhost_test, dev); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci handle_vq(n); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int vhost_test_open(struct inode *inode, struct file *f) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct vhost_test *n = kmalloc(sizeof *n, GFP_KERNEL); 10862306a36Sopenharmony_ci struct vhost_dev *dev; 10962306a36Sopenharmony_ci struct vhost_virtqueue **vqs; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (!n) 11262306a36Sopenharmony_ci return -ENOMEM; 11362306a36Sopenharmony_ci vqs = kmalloc_array(VHOST_TEST_VQ_MAX, sizeof(*vqs), GFP_KERNEL); 11462306a36Sopenharmony_ci if (!vqs) { 11562306a36Sopenharmony_ci kfree(n); 11662306a36Sopenharmony_ci return -ENOMEM; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci dev = &n->dev; 12062306a36Sopenharmony_ci vqs[VHOST_TEST_VQ] = &n->vqs[VHOST_TEST_VQ]; 12162306a36Sopenharmony_ci n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick; 12262306a36Sopenharmony_ci vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX, UIO_MAXIOV, 12362306a36Sopenharmony_ci VHOST_TEST_PKT_WEIGHT, VHOST_TEST_WEIGHT, true, NULL); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci f->private_data = n; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void *vhost_test_stop_vq(struct vhost_test *n, 13162306a36Sopenharmony_ci struct vhost_virtqueue *vq) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci void *private; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci mutex_lock(&vq->mutex); 13662306a36Sopenharmony_ci private = vhost_vq_get_backend(vq); 13762306a36Sopenharmony_ci vhost_vq_set_backend(vq, NULL); 13862306a36Sopenharmony_ci mutex_unlock(&vq->mutex); 13962306a36Sopenharmony_ci return private; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void vhost_test_stop(struct vhost_test *n, void **privatep) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci *privatep = vhost_test_stop_vq(n, n->vqs + VHOST_TEST_VQ); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void vhost_test_flush(struct vhost_test *n) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci vhost_dev_flush(&n->dev); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int vhost_test_release(struct inode *inode, struct file *f) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct vhost_test *n = f->private_data; 15562306a36Sopenharmony_ci void *private; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci vhost_test_stop(n, &private); 15862306a36Sopenharmony_ci vhost_test_flush(n); 15962306a36Sopenharmony_ci vhost_dev_stop(&n->dev); 16062306a36Sopenharmony_ci vhost_dev_cleanup(&n->dev); 16162306a36Sopenharmony_ci kfree(n->dev.vqs); 16262306a36Sopenharmony_ci kfree(n); 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic long vhost_test_run(struct vhost_test *n, int test) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci void *priv, *oldpriv; 16962306a36Sopenharmony_ci struct vhost_virtqueue *vq; 17062306a36Sopenharmony_ci int r, index; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (test < 0 || test > 1) 17362306a36Sopenharmony_ci return -EINVAL; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci mutex_lock(&n->dev.mutex); 17662306a36Sopenharmony_ci r = vhost_dev_check_owner(&n->dev); 17762306a36Sopenharmony_ci if (r) 17862306a36Sopenharmony_ci goto err; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci for (index = 0; index < n->dev.nvqs; ++index) { 18162306a36Sopenharmony_ci /* Verify that ring has been setup correctly. */ 18262306a36Sopenharmony_ci if (!vhost_vq_access_ok(&n->vqs[index])) { 18362306a36Sopenharmony_ci r = -EFAULT; 18462306a36Sopenharmony_ci goto err; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (index = 0; index < n->dev.nvqs; ++index) { 18962306a36Sopenharmony_ci vq = n->vqs + index; 19062306a36Sopenharmony_ci mutex_lock(&vq->mutex); 19162306a36Sopenharmony_ci priv = test ? n : NULL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* start polling new socket */ 19462306a36Sopenharmony_ci oldpriv = vhost_vq_get_backend(vq); 19562306a36Sopenharmony_ci vhost_vq_set_backend(vq, priv); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci r = vhost_vq_init_access(&n->vqs[index]); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci mutex_unlock(&vq->mutex); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (r) 20262306a36Sopenharmony_ci goto err; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (oldpriv) { 20562306a36Sopenharmony_ci vhost_test_flush(n); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci mutex_unlock(&n->dev.mutex); 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cierr: 21362306a36Sopenharmony_ci mutex_unlock(&n->dev.mutex); 21462306a36Sopenharmony_ci return r; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic long vhost_test_reset_owner(struct vhost_test *n) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci void *priv = NULL; 22062306a36Sopenharmony_ci long err; 22162306a36Sopenharmony_ci struct vhost_iotlb *umem; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci mutex_lock(&n->dev.mutex); 22462306a36Sopenharmony_ci err = vhost_dev_check_owner(&n->dev); 22562306a36Sopenharmony_ci if (err) 22662306a36Sopenharmony_ci goto done; 22762306a36Sopenharmony_ci umem = vhost_dev_reset_owner_prepare(); 22862306a36Sopenharmony_ci if (!umem) { 22962306a36Sopenharmony_ci err = -ENOMEM; 23062306a36Sopenharmony_ci goto done; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci vhost_test_stop(n, &priv); 23362306a36Sopenharmony_ci vhost_test_flush(n); 23462306a36Sopenharmony_ci vhost_dev_stop(&n->dev); 23562306a36Sopenharmony_ci vhost_dev_reset_owner(&n->dev, umem); 23662306a36Sopenharmony_cidone: 23762306a36Sopenharmony_ci mutex_unlock(&n->dev.mutex); 23862306a36Sopenharmony_ci return err; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int vhost_test_set_features(struct vhost_test *n, u64 features) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct vhost_virtqueue *vq; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mutex_lock(&n->dev.mutex); 24662306a36Sopenharmony_ci if ((features & (1 << VHOST_F_LOG_ALL)) && 24762306a36Sopenharmony_ci !vhost_log_access_ok(&n->dev)) { 24862306a36Sopenharmony_ci mutex_unlock(&n->dev.mutex); 24962306a36Sopenharmony_ci return -EFAULT; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci vq = &n->vqs[VHOST_TEST_VQ]; 25262306a36Sopenharmony_ci mutex_lock(&vq->mutex); 25362306a36Sopenharmony_ci vq->acked_features = features; 25462306a36Sopenharmony_ci mutex_unlock(&vq->mutex); 25562306a36Sopenharmony_ci mutex_unlock(&n->dev.mutex); 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic long vhost_test_set_backend(struct vhost_test *n, unsigned index, int fd) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci static void *backend; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci const bool enable = fd != -1; 26462306a36Sopenharmony_ci struct vhost_virtqueue *vq; 26562306a36Sopenharmony_ci int r; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci mutex_lock(&n->dev.mutex); 26862306a36Sopenharmony_ci r = vhost_dev_check_owner(&n->dev); 26962306a36Sopenharmony_ci if (r) 27062306a36Sopenharmony_ci goto err; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (index >= VHOST_TEST_VQ_MAX) { 27362306a36Sopenharmony_ci r = -ENOBUFS; 27462306a36Sopenharmony_ci goto err; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci vq = &n->vqs[index]; 27762306a36Sopenharmony_ci mutex_lock(&vq->mutex); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Verify that ring has been setup correctly. */ 28062306a36Sopenharmony_ci if (!vhost_vq_access_ok(vq)) { 28162306a36Sopenharmony_ci r = -EFAULT; 28262306a36Sopenharmony_ci goto err_vq; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci if (!enable) { 28562306a36Sopenharmony_ci vhost_poll_stop(&vq->poll); 28662306a36Sopenharmony_ci backend = vhost_vq_get_backend(vq); 28762306a36Sopenharmony_ci vhost_vq_set_backend(vq, NULL); 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci vhost_vq_set_backend(vq, backend); 29062306a36Sopenharmony_ci r = vhost_vq_init_access(vq); 29162306a36Sopenharmony_ci if (r == 0) 29262306a36Sopenharmony_ci r = vhost_poll_start(&vq->poll, vq->kick); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci mutex_unlock(&vq->mutex); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (enable) { 29862306a36Sopenharmony_ci vhost_test_flush(n); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci mutex_unlock(&n->dev.mutex); 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cierr_vq: 30562306a36Sopenharmony_ci mutex_unlock(&vq->mutex); 30662306a36Sopenharmony_cierr: 30762306a36Sopenharmony_ci mutex_unlock(&n->dev.mutex); 30862306a36Sopenharmony_ci return r; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic long vhost_test_ioctl(struct file *f, unsigned int ioctl, 31262306a36Sopenharmony_ci unsigned long arg) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct vhost_vring_file backend; 31562306a36Sopenharmony_ci struct vhost_test *n = f->private_data; 31662306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 31762306a36Sopenharmony_ci u64 __user *featurep = argp; 31862306a36Sopenharmony_ci int test; 31962306a36Sopenharmony_ci u64 features; 32062306a36Sopenharmony_ci int r; 32162306a36Sopenharmony_ci switch (ioctl) { 32262306a36Sopenharmony_ci case VHOST_TEST_RUN: 32362306a36Sopenharmony_ci if (copy_from_user(&test, argp, sizeof test)) 32462306a36Sopenharmony_ci return -EFAULT; 32562306a36Sopenharmony_ci return vhost_test_run(n, test); 32662306a36Sopenharmony_ci case VHOST_TEST_SET_BACKEND: 32762306a36Sopenharmony_ci if (copy_from_user(&backend, argp, sizeof backend)) 32862306a36Sopenharmony_ci return -EFAULT; 32962306a36Sopenharmony_ci return vhost_test_set_backend(n, backend.index, backend.fd); 33062306a36Sopenharmony_ci case VHOST_GET_FEATURES: 33162306a36Sopenharmony_ci features = VHOST_FEATURES; 33262306a36Sopenharmony_ci if (copy_to_user(featurep, &features, sizeof features)) 33362306a36Sopenharmony_ci return -EFAULT; 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci case VHOST_SET_FEATURES: 33662306a36Sopenharmony_ci if (copy_from_user(&features, featurep, sizeof features)) 33762306a36Sopenharmony_ci return -EFAULT; 33862306a36Sopenharmony_ci if (features & ~VHOST_FEATURES) 33962306a36Sopenharmony_ci return -EOPNOTSUPP; 34062306a36Sopenharmony_ci return vhost_test_set_features(n, features); 34162306a36Sopenharmony_ci case VHOST_RESET_OWNER: 34262306a36Sopenharmony_ci return vhost_test_reset_owner(n); 34362306a36Sopenharmony_ci default: 34462306a36Sopenharmony_ci mutex_lock(&n->dev.mutex); 34562306a36Sopenharmony_ci r = vhost_dev_ioctl(&n->dev, ioctl, argp); 34662306a36Sopenharmony_ci if (r == -ENOIOCTLCMD) 34762306a36Sopenharmony_ci r = vhost_vring_ioctl(&n->dev, ioctl, argp); 34862306a36Sopenharmony_ci vhost_test_flush(n); 34962306a36Sopenharmony_ci mutex_unlock(&n->dev.mutex); 35062306a36Sopenharmony_ci return r; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic const struct file_operations vhost_test_fops = { 35562306a36Sopenharmony_ci .owner = THIS_MODULE, 35662306a36Sopenharmony_ci .release = vhost_test_release, 35762306a36Sopenharmony_ci .unlocked_ioctl = vhost_test_ioctl, 35862306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 35962306a36Sopenharmony_ci .open = vhost_test_open, 36062306a36Sopenharmony_ci .llseek = noop_llseek, 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic struct miscdevice vhost_test_misc = { 36462306a36Sopenharmony_ci MISC_DYNAMIC_MINOR, 36562306a36Sopenharmony_ci "vhost-test", 36662306a36Sopenharmony_ci &vhost_test_fops, 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_cimodule_misc_device(vhost_test_misc); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciMODULE_VERSION("0.0.1"); 37162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 37262306a36Sopenharmony_ciMODULE_AUTHOR("Michael S. Tsirkin"); 37362306a36Sopenharmony_ciMODULE_DESCRIPTION("Host kernel side for virtio simulator"); 374