18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (C) 2009 Red Hat, Inc. 38c2ecf20Sopenharmony_ci * Author: Michael S. Tsirkin <mst@redhat.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * test virtio server in host kernel. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/compat.h> 98c2ecf20Sopenharmony_ci#include <linux/eventfd.h> 108c2ecf20Sopenharmony_ci#include <linux/vhost.h> 118c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 158c2ecf20Sopenharmony_ci#include <linux/file.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "test.h" 198c2ecf20Sopenharmony_ci#include "vhost.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Max number of bytes transferred before requeueing the job. 228c2ecf20Sopenharmony_ci * Using this limit prevents one virtqueue from starving others. */ 238c2ecf20Sopenharmony_ci#define VHOST_TEST_WEIGHT 0x80000 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Max number of packets transferred before requeueing the job. 268c2ecf20Sopenharmony_ci * Using this limit prevents one virtqueue from starving others with 278c2ecf20Sopenharmony_ci * pkts. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci#define VHOST_TEST_PKT_WEIGHT 256 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cienum { 328c2ecf20Sopenharmony_ci VHOST_TEST_VQ = 0, 338c2ecf20Sopenharmony_ci VHOST_TEST_VQ_MAX = 1, 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct vhost_test { 378c2ecf20Sopenharmony_ci struct vhost_dev dev; 388c2ecf20Sopenharmony_ci struct vhost_virtqueue vqs[VHOST_TEST_VQ_MAX]; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* Expects to be always run from workqueue - which acts as 428c2ecf20Sopenharmony_ci * read-size critical section for our kind of RCU. */ 438c2ecf20Sopenharmony_cistatic void handle_vq(struct vhost_test *n) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = &n->vqs[VHOST_TEST_VQ]; 468c2ecf20Sopenharmony_ci unsigned out, in; 478c2ecf20Sopenharmony_ci int head; 488c2ecf20Sopenharmony_ci size_t len, total_len = 0; 498c2ecf20Sopenharmony_ci void *private; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 528c2ecf20Sopenharmony_ci private = vhost_vq_get_backend(vq); 538c2ecf20Sopenharmony_ci if (!private) { 548c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 558c2ecf20Sopenharmony_ci return; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci vhost_disable_notify(&n->dev, vq); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci for (;;) { 618c2ecf20Sopenharmony_ci head = vhost_get_vq_desc(vq, vq->iov, 628c2ecf20Sopenharmony_ci ARRAY_SIZE(vq->iov), 638c2ecf20Sopenharmony_ci &out, &in, 648c2ecf20Sopenharmony_ci NULL, NULL); 658c2ecf20Sopenharmony_ci /* On error, stop handling until the next kick. */ 668c2ecf20Sopenharmony_ci if (unlikely(head < 0)) 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci /* Nothing new? Wait for eventfd to tell us they refilled. */ 698c2ecf20Sopenharmony_ci if (head == vq->num) { 708c2ecf20Sopenharmony_ci if (unlikely(vhost_enable_notify(&n->dev, vq))) { 718c2ecf20Sopenharmony_ci vhost_disable_notify(&n->dev, vq); 728c2ecf20Sopenharmony_ci continue; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci if (in) { 778c2ecf20Sopenharmony_ci vq_err(vq, "Unexpected descriptor format for TX: " 788c2ecf20Sopenharmony_ci "out %d, int %d\n", out, in); 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci len = iov_length(vq->iov, out); 828c2ecf20Sopenharmony_ci /* Sanity check */ 838c2ecf20Sopenharmony_ci if (!len) { 848c2ecf20Sopenharmony_ci vq_err(vq, "Unexpected 0 len for TX\n"); 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci vhost_add_used_and_signal(&n->dev, vq, head, 0); 888c2ecf20Sopenharmony_ci total_len += len; 898c2ecf20Sopenharmony_ci if (unlikely(vhost_exceeds_weight(vq, 0, total_len))) 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void handle_vq_kick(struct vhost_work *work) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, 998c2ecf20Sopenharmony_ci poll.work); 1008c2ecf20Sopenharmony_ci struct vhost_test *n = container_of(vq->dev, struct vhost_test, dev); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci handle_vq(n); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int vhost_test_open(struct inode *inode, struct file *f) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct vhost_test *n = kmalloc(sizeof *n, GFP_KERNEL); 1088c2ecf20Sopenharmony_ci struct vhost_dev *dev; 1098c2ecf20Sopenharmony_ci struct vhost_virtqueue **vqs; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!n) 1128c2ecf20Sopenharmony_ci return -ENOMEM; 1138c2ecf20Sopenharmony_ci vqs = kmalloc_array(VHOST_TEST_VQ_MAX, sizeof(*vqs), GFP_KERNEL); 1148c2ecf20Sopenharmony_ci if (!vqs) { 1158c2ecf20Sopenharmony_ci kfree(n); 1168c2ecf20Sopenharmony_ci return -ENOMEM; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci dev = &n->dev; 1208c2ecf20Sopenharmony_ci vqs[VHOST_TEST_VQ] = &n->vqs[VHOST_TEST_VQ]; 1218c2ecf20Sopenharmony_ci n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick; 1228c2ecf20Sopenharmony_ci vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX, UIO_MAXIOV, 1238c2ecf20Sopenharmony_ci VHOST_TEST_PKT_WEIGHT, VHOST_TEST_WEIGHT, true, NULL); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci f->private_data = n; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void *vhost_test_stop_vq(struct vhost_test *n, 1318c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci void *private; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 1368c2ecf20Sopenharmony_ci private = vhost_vq_get_backend(vq); 1378c2ecf20Sopenharmony_ci vhost_vq_set_backend(vq, NULL); 1388c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 1398c2ecf20Sopenharmony_ci return private; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void vhost_test_stop(struct vhost_test *n, void **privatep) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci *privatep = vhost_test_stop_vq(n, n->vqs + VHOST_TEST_VQ); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void vhost_test_flush_vq(struct vhost_test *n, int index) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci vhost_poll_flush(&n->vqs[index].poll); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void vhost_test_flush(struct vhost_test *n) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci vhost_test_flush_vq(n, VHOST_TEST_VQ); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int vhost_test_release(struct inode *inode, struct file *f) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct vhost_test *n = f->private_data; 1608c2ecf20Sopenharmony_ci void *private; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci vhost_test_stop(n, &private); 1638c2ecf20Sopenharmony_ci vhost_test_flush(n); 1648c2ecf20Sopenharmony_ci vhost_dev_stop(&n->dev); 1658c2ecf20Sopenharmony_ci vhost_dev_cleanup(&n->dev); 1668c2ecf20Sopenharmony_ci /* We do an extra flush before freeing memory, 1678c2ecf20Sopenharmony_ci * since jobs can re-queue themselves. */ 1688c2ecf20Sopenharmony_ci vhost_test_flush(n); 1698c2ecf20Sopenharmony_ci kfree(n); 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic long vhost_test_run(struct vhost_test *n, int test) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci void *priv, *oldpriv; 1768c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 1778c2ecf20Sopenharmony_ci int r, index; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (test < 0 || test > 1) 1808c2ecf20Sopenharmony_ci return -EINVAL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mutex_lock(&n->dev.mutex); 1838c2ecf20Sopenharmony_ci r = vhost_dev_check_owner(&n->dev); 1848c2ecf20Sopenharmony_ci if (r) 1858c2ecf20Sopenharmony_ci goto err; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci for (index = 0; index < n->dev.nvqs; ++index) { 1888c2ecf20Sopenharmony_ci /* Verify that ring has been setup correctly. */ 1898c2ecf20Sopenharmony_ci if (!vhost_vq_access_ok(&n->vqs[index])) { 1908c2ecf20Sopenharmony_ci r = -EFAULT; 1918c2ecf20Sopenharmony_ci goto err; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci for (index = 0; index < n->dev.nvqs; ++index) { 1968c2ecf20Sopenharmony_ci vq = n->vqs + index; 1978c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 1988c2ecf20Sopenharmony_ci priv = test ? n : NULL; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* start polling new socket */ 2018c2ecf20Sopenharmony_ci oldpriv = vhost_vq_get_backend(vq); 2028c2ecf20Sopenharmony_ci vhost_vq_set_backend(vq, priv); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci r = vhost_vq_init_access(&n->vqs[index]); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (r) 2098c2ecf20Sopenharmony_ci goto err; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (oldpriv) { 2128c2ecf20Sopenharmony_ci vhost_test_flush_vq(n, index); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci mutex_unlock(&n->dev.mutex); 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cierr: 2208c2ecf20Sopenharmony_ci mutex_unlock(&n->dev.mutex); 2218c2ecf20Sopenharmony_ci return r; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic long vhost_test_reset_owner(struct vhost_test *n) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci void *priv = NULL; 2278c2ecf20Sopenharmony_ci long err; 2288c2ecf20Sopenharmony_ci struct vhost_iotlb *umem; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mutex_lock(&n->dev.mutex); 2318c2ecf20Sopenharmony_ci err = vhost_dev_check_owner(&n->dev); 2328c2ecf20Sopenharmony_ci if (err) 2338c2ecf20Sopenharmony_ci goto done; 2348c2ecf20Sopenharmony_ci umem = vhost_dev_reset_owner_prepare(); 2358c2ecf20Sopenharmony_ci if (!umem) { 2368c2ecf20Sopenharmony_ci err = -ENOMEM; 2378c2ecf20Sopenharmony_ci goto done; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci vhost_test_stop(n, &priv); 2408c2ecf20Sopenharmony_ci vhost_test_flush(n); 2418c2ecf20Sopenharmony_ci vhost_dev_stop(&n->dev); 2428c2ecf20Sopenharmony_ci vhost_dev_reset_owner(&n->dev, umem); 2438c2ecf20Sopenharmony_cidone: 2448c2ecf20Sopenharmony_ci mutex_unlock(&n->dev.mutex); 2458c2ecf20Sopenharmony_ci return err; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int vhost_test_set_features(struct vhost_test *n, u64 features) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci mutex_lock(&n->dev.mutex); 2538c2ecf20Sopenharmony_ci if ((features & (1 << VHOST_F_LOG_ALL)) && 2548c2ecf20Sopenharmony_ci !vhost_log_access_ok(&n->dev)) { 2558c2ecf20Sopenharmony_ci mutex_unlock(&n->dev.mutex); 2568c2ecf20Sopenharmony_ci return -EFAULT; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci vq = &n->vqs[VHOST_TEST_VQ]; 2598c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 2608c2ecf20Sopenharmony_ci vq->acked_features = features; 2618c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 2628c2ecf20Sopenharmony_ci mutex_unlock(&n->dev.mutex); 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic long vhost_test_set_backend(struct vhost_test *n, unsigned index, int fd) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci static void *backend; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci const bool enable = fd != -1; 2718c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 2728c2ecf20Sopenharmony_ci int r; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci mutex_lock(&n->dev.mutex); 2758c2ecf20Sopenharmony_ci r = vhost_dev_check_owner(&n->dev); 2768c2ecf20Sopenharmony_ci if (r) 2778c2ecf20Sopenharmony_ci goto err; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (index >= VHOST_TEST_VQ_MAX) { 2808c2ecf20Sopenharmony_ci r = -ENOBUFS; 2818c2ecf20Sopenharmony_ci goto err; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci vq = &n->vqs[index]; 2848c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Verify that ring has been setup correctly. */ 2878c2ecf20Sopenharmony_ci if (!vhost_vq_access_ok(vq)) { 2888c2ecf20Sopenharmony_ci r = -EFAULT; 2898c2ecf20Sopenharmony_ci goto err_vq; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci if (!enable) { 2928c2ecf20Sopenharmony_ci vhost_poll_stop(&vq->poll); 2938c2ecf20Sopenharmony_ci backend = vhost_vq_get_backend(vq); 2948c2ecf20Sopenharmony_ci vhost_vq_set_backend(vq, NULL); 2958c2ecf20Sopenharmony_ci } else { 2968c2ecf20Sopenharmony_ci vhost_vq_set_backend(vq, backend); 2978c2ecf20Sopenharmony_ci r = vhost_vq_init_access(vq); 2988c2ecf20Sopenharmony_ci if (r == 0) 2998c2ecf20Sopenharmony_ci r = vhost_poll_start(&vq->poll, vq->kick); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (enable) { 3058c2ecf20Sopenharmony_ci vhost_test_flush_vq(n, index); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci mutex_unlock(&n->dev.mutex); 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cierr_vq: 3128c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 3138c2ecf20Sopenharmony_cierr: 3148c2ecf20Sopenharmony_ci mutex_unlock(&n->dev.mutex); 3158c2ecf20Sopenharmony_ci return r; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic long vhost_test_ioctl(struct file *f, unsigned int ioctl, 3198c2ecf20Sopenharmony_ci unsigned long arg) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct vhost_vring_file backend; 3228c2ecf20Sopenharmony_ci struct vhost_test *n = f->private_data; 3238c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 3248c2ecf20Sopenharmony_ci u64 __user *featurep = argp; 3258c2ecf20Sopenharmony_ci int test; 3268c2ecf20Sopenharmony_ci u64 features; 3278c2ecf20Sopenharmony_ci int r; 3288c2ecf20Sopenharmony_ci switch (ioctl) { 3298c2ecf20Sopenharmony_ci case VHOST_TEST_RUN: 3308c2ecf20Sopenharmony_ci if (copy_from_user(&test, argp, sizeof test)) 3318c2ecf20Sopenharmony_ci return -EFAULT; 3328c2ecf20Sopenharmony_ci return vhost_test_run(n, test); 3338c2ecf20Sopenharmony_ci case VHOST_TEST_SET_BACKEND: 3348c2ecf20Sopenharmony_ci if (copy_from_user(&backend, argp, sizeof backend)) 3358c2ecf20Sopenharmony_ci return -EFAULT; 3368c2ecf20Sopenharmony_ci return vhost_test_set_backend(n, backend.index, backend.fd); 3378c2ecf20Sopenharmony_ci case VHOST_GET_FEATURES: 3388c2ecf20Sopenharmony_ci features = VHOST_FEATURES; 3398c2ecf20Sopenharmony_ci if (copy_to_user(featurep, &features, sizeof features)) 3408c2ecf20Sopenharmony_ci return -EFAULT; 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci case VHOST_SET_FEATURES: 3438c2ecf20Sopenharmony_ci printk(KERN_ERR "1\n"); 3448c2ecf20Sopenharmony_ci if (copy_from_user(&features, featurep, sizeof features)) 3458c2ecf20Sopenharmony_ci return -EFAULT; 3468c2ecf20Sopenharmony_ci printk(KERN_ERR "2\n"); 3478c2ecf20Sopenharmony_ci if (features & ~VHOST_FEATURES) 3488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3498c2ecf20Sopenharmony_ci printk(KERN_ERR "3\n"); 3508c2ecf20Sopenharmony_ci return vhost_test_set_features(n, features); 3518c2ecf20Sopenharmony_ci case VHOST_RESET_OWNER: 3528c2ecf20Sopenharmony_ci return vhost_test_reset_owner(n); 3538c2ecf20Sopenharmony_ci default: 3548c2ecf20Sopenharmony_ci mutex_lock(&n->dev.mutex); 3558c2ecf20Sopenharmony_ci r = vhost_dev_ioctl(&n->dev, ioctl, argp); 3568c2ecf20Sopenharmony_ci if (r == -ENOIOCTLCMD) 3578c2ecf20Sopenharmony_ci r = vhost_vring_ioctl(&n->dev, ioctl, argp); 3588c2ecf20Sopenharmony_ci vhost_test_flush(n); 3598c2ecf20Sopenharmony_ci mutex_unlock(&n->dev.mutex); 3608c2ecf20Sopenharmony_ci return r; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic const struct file_operations vhost_test_fops = { 3658c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3668c2ecf20Sopenharmony_ci .release = vhost_test_release, 3678c2ecf20Sopenharmony_ci .unlocked_ioctl = vhost_test_ioctl, 3688c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 3698c2ecf20Sopenharmony_ci .open = vhost_test_open, 3708c2ecf20Sopenharmony_ci .llseek = noop_llseek, 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic struct miscdevice vhost_test_misc = { 3748c2ecf20Sopenharmony_ci MISC_DYNAMIC_MINOR, 3758c2ecf20Sopenharmony_ci "vhost-test", 3768c2ecf20Sopenharmony_ci &vhost_test_fops, 3778c2ecf20Sopenharmony_ci}; 3788c2ecf20Sopenharmony_cimodule_misc_device(vhost_test_misc); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ciMODULE_VERSION("0.0.1"); 3818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael S. Tsirkin"); 3838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Host kernel side for virtio simulator"); 384