18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018-2020 Intel Corporation. 48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Red Hat, Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Tiwei Bie <tiwei.bie@intel.com> 78c2ecf20Sopenharmony_ci * Jason Wang <jasowang@redhat.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Thanks Michael S. Tsirkin for the valuable comments and 108c2ecf20Sopenharmony_ci * suggestions. And thanks to Cunming Liang and Zhihong Wang for all 118c2ecf20Sopenharmony_ci * their supports. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/cdev.h> 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/iommu.h> 208c2ecf20Sopenharmony_ci#include <linux/uuid.h> 218c2ecf20Sopenharmony_ci#include <linux/vdpa.h> 228c2ecf20Sopenharmony_ci#include <linux/nospec.h> 238c2ecf20Sopenharmony_ci#include <linux/vhost.h> 248c2ecf20Sopenharmony_ci#include <linux/virtio_net.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "vhost.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cienum { 298c2ecf20Sopenharmony_ci VHOST_VDPA_BACKEND_FEATURES = 308c2ecf20Sopenharmony_ci (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2) | 318c2ecf20Sopenharmony_ci (1ULL << VHOST_BACKEND_F_IOTLB_BATCH), 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define VHOST_VDPA_DEV_MAX (1U << MINORBITS) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct vhost_vdpa { 378c2ecf20Sopenharmony_ci struct vhost_dev vdev; 388c2ecf20Sopenharmony_ci struct iommu_domain *domain; 398c2ecf20Sopenharmony_ci struct vhost_virtqueue *vqs; 408c2ecf20Sopenharmony_ci struct completion completion; 418c2ecf20Sopenharmony_ci struct vdpa_device *vdpa; 428c2ecf20Sopenharmony_ci struct device dev; 438c2ecf20Sopenharmony_ci struct cdev cdev; 448c2ecf20Sopenharmony_ci atomic_t opened; 458c2ecf20Sopenharmony_ci int nvqs; 468c2ecf20Sopenharmony_ci int virtio_id; 478c2ecf20Sopenharmony_ci int minor; 488c2ecf20Sopenharmony_ci struct eventfd_ctx *config_ctx; 498c2ecf20Sopenharmony_ci int in_batch; 508c2ecf20Sopenharmony_ci struct vdpa_iova_range range; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic DEFINE_IDA(vhost_vdpa_ida); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic dev_t vhost_vdpa_major; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void handle_vq_kick(struct vhost_work *work) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, 608c2ecf20Sopenharmony_ci poll.work); 618c2ecf20Sopenharmony_ci struct vhost_vdpa *v = container_of(vq->dev, struct vhost_vdpa, vdev); 628c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = v->vdpa->config; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci ops->kick_vq(v->vdpa, vq - v->vqs); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic irqreturn_t vhost_vdpa_virtqueue_cb(void *private) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = private; 708c2ecf20Sopenharmony_ci struct eventfd_ctx *call_ctx = vq->call_ctx.ctx; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (call_ctx) 738c2ecf20Sopenharmony_ci eventfd_signal(call_ctx, 1); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return IRQ_HANDLED; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic irqreturn_t vhost_vdpa_config_cb(void *private) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct vhost_vdpa *v = private; 818c2ecf20Sopenharmony_ci struct eventfd_ctx *config_ctx = v->config_ctx; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (config_ctx) 848c2ecf20Sopenharmony_ci eventfd_signal(config_ctx, 1); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return IRQ_HANDLED; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void vhost_vdpa_setup_vq_irq(struct vhost_vdpa *v, u16 qid) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = &v->vqs[qid]; 928c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = v->vdpa->config; 938c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 948c2ecf20Sopenharmony_ci int ret, irq; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!ops->get_vq_irq) 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci irq = ops->get_vq_irq(vdpa, qid); 1008c2ecf20Sopenharmony_ci if (irq < 0) 1018c2ecf20Sopenharmony_ci return; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci irq_bypass_unregister_producer(&vq->call_ctx.producer); 1048c2ecf20Sopenharmony_ci if (!vq->call_ctx.ctx) 1058c2ecf20Sopenharmony_ci return; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci vq->call_ctx.producer.token = vq->call_ctx.ctx; 1088c2ecf20Sopenharmony_ci vq->call_ctx.producer.irq = irq; 1098c2ecf20Sopenharmony_ci ret = irq_bypass_register_producer(&vq->call_ctx.producer); 1108c2ecf20Sopenharmony_ci if (unlikely(ret)) 1118c2ecf20Sopenharmony_ci dev_info(&v->dev, "vq %u, irq bypass producer (token %p) registration fails, ret = %d\n", 1128c2ecf20Sopenharmony_ci qid, vq->call_ctx.producer.token, ret); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = &v->vqs[qid]; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci irq_bypass_unregister_producer(&vq->call_ctx.producer); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void vhost_vdpa_reset(struct vhost_vdpa *v) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci vdpa_reset(vdpa); 1278c2ecf20Sopenharmony_ci v->in_batch = 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic long vhost_vdpa_get_device_id(struct vhost_vdpa *v, u8 __user *argp) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 1338c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 1348c2ecf20Sopenharmony_ci u32 device_id; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci device_id = ops->get_device_id(vdpa); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (copy_to_user(argp, &device_id, sizeof(device_id))) 1398c2ecf20Sopenharmony_ci return -EFAULT; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic long vhost_vdpa_get_status(struct vhost_vdpa *v, u8 __user *statusp) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 1478c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 1488c2ecf20Sopenharmony_ci u8 status; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci status = ops->get_status(vdpa); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (copy_to_user(statusp, &status, sizeof(status))) 1538c2ecf20Sopenharmony_ci return -EFAULT; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic long vhost_vdpa_set_status(struct vhost_vdpa *v, u8 __user *statusp) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 1618c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 1628c2ecf20Sopenharmony_ci u8 status, status_old; 1638c2ecf20Sopenharmony_ci int nvqs = v->nvqs; 1648c2ecf20Sopenharmony_ci u16 i; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (copy_from_user(&status, statusp, sizeof(status))) 1678c2ecf20Sopenharmony_ci return -EFAULT; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci status_old = ops->get_status(vdpa); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * Userspace shouldn't remove status bits unless reset the 1738c2ecf20Sopenharmony_ci * status to 0. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci if (status != 0 && (ops->get_status(vdpa) & ~status) != 0) 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ops->set_status(vdpa, status); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if ((status & VIRTIO_CONFIG_S_DRIVER_OK) && !(status_old & VIRTIO_CONFIG_S_DRIVER_OK)) 1818c2ecf20Sopenharmony_ci for (i = 0; i < nvqs; i++) 1828c2ecf20Sopenharmony_ci vhost_vdpa_setup_vq_irq(v, i); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if ((status_old & VIRTIO_CONFIG_S_DRIVER_OK) && !(status & VIRTIO_CONFIG_S_DRIVER_OK)) 1858c2ecf20Sopenharmony_ci for (i = 0; i < nvqs; i++) 1868c2ecf20Sopenharmony_ci vhost_vdpa_unsetup_vq_irq(v, i); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int vhost_vdpa_config_validate(struct vhost_vdpa *v, 1928c2ecf20Sopenharmony_ci struct vhost_vdpa_config *c) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci long size = 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci switch (v->virtio_id) { 1978c2ecf20Sopenharmony_ci case VIRTIO_ID_NET: 1988c2ecf20Sopenharmony_ci size = sizeof(struct virtio_net_config); 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (c->len == 0 || c->off > size) 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (c->len > size - c->off) 2068c2ecf20Sopenharmony_ci return -E2BIG; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic long vhost_vdpa_get_config(struct vhost_vdpa *v, 2128c2ecf20Sopenharmony_ci struct vhost_vdpa_config __user *c) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 2158c2ecf20Sopenharmony_ci struct vhost_vdpa_config config; 2168c2ecf20Sopenharmony_ci unsigned long size = offsetof(struct vhost_vdpa_config, buf); 2178c2ecf20Sopenharmony_ci u8 *buf; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (copy_from_user(&config, c, size)) 2208c2ecf20Sopenharmony_ci return -EFAULT; 2218c2ecf20Sopenharmony_ci if (vhost_vdpa_config_validate(v, &config)) 2228c2ecf20Sopenharmony_ci return -EINVAL; 2238c2ecf20Sopenharmony_ci buf = kvzalloc(config.len, GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (!buf) 2258c2ecf20Sopenharmony_ci return -ENOMEM; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci vdpa_get_config(vdpa, config.off, buf, config.len); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (copy_to_user(c->buf, buf, config.len)) { 2308c2ecf20Sopenharmony_ci kvfree(buf); 2318c2ecf20Sopenharmony_ci return -EFAULT; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci kvfree(buf); 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic long vhost_vdpa_set_config(struct vhost_vdpa *v, 2398c2ecf20Sopenharmony_ci struct vhost_vdpa_config __user *c) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 2428c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 2438c2ecf20Sopenharmony_ci struct vhost_vdpa_config config; 2448c2ecf20Sopenharmony_ci unsigned long size = offsetof(struct vhost_vdpa_config, buf); 2458c2ecf20Sopenharmony_ci u8 *buf; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (copy_from_user(&config, c, size)) 2488c2ecf20Sopenharmony_ci return -EFAULT; 2498c2ecf20Sopenharmony_ci if (vhost_vdpa_config_validate(v, &config)) 2508c2ecf20Sopenharmony_ci return -EINVAL; 2518c2ecf20Sopenharmony_ci buf = kvzalloc(config.len, GFP_KERNEL); 2528c2ecf20Sopenharmony_ci if (!buf) 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (copy_from_user(buf, c->buf, config.len)) { 2568c2ecf20Sopenharmony_ci kvfree(buf); 2578c2ecf20Sopenharmony_ci return -EFAULT; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ops->set_config(vdpa, config.off, buf, config.len); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci kvfree(buf); 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic long vhost_vdpa_get_features(struct vhost_vdpa *v, u64 __user *featurep) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 2698c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 2708c2ecf20Sopenharmony_ci u64 features; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci features = ops->get_features(vdpa); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (copy_to_user(featurep, &features, sizeof(features))) 2758c2ecf20Sopenharmony_ci return -EFAULT; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic long vhost_vdpa_set_features(struct vhost_vdpa *v, u64 __user *featurep) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 2838c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 2848c2ecf20Sopenharmony_ci u64 features; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* 2878c2ecf20Sopenharmony_ci * It's not allowed to change the features after they have 2888c2ecf20Sopenharmony_ci * been negotiated. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci if (ops->get_status(vdpa) & VIRTIO_CONFIG_S_FEATURES_OK) 2918c2ecf20Sopenharmony_ci return -EBUSY; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (copy_from_user(&features, featurep, sizeof(features))) 2948c2ecf20Sopenharmony_ci return -EFAULT; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (vdpa_set_features(vdpa, features)) 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic long vhost_vdpa_get_vring_num(struct vhost_vdpa *v, u16 __user *argp) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 3058c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 3068c2ecf20Sopenharmony_ci u16 num; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci num = ops->get_vq_num_max(vdpa); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (copy_to_user(argp, &num, sizeof(num))) 3118c2ecf20Sopenharmony_ci return -EFAULT; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void vhost_vdpa_config_put(struct vhost_vdpa *v) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci if (v->config_ctx) { 3198c2ecf20Sopenharmony_ci eventfd_ctx_put(v->config_ctx); 3208c2ecf20Sopenharmony_ci v->config_ctx = NULL; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct vdpa_callback cb; 3278c2ecf20Sopenharmony_ci int fd; 3288c2ecf20Sopenharmony_ci struct eventfd_ctx *ctx; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci cb.callback = vhost_vdpa_config_cb; 3318c2ecf20Sopenharmony_ci cb.private = v; 3328c2ecf20Sopenharmony_ci if (copy_from_user(&fd, argp, sizeof(fd))) 3338c2ecf20Sopenharmony_ci return -EFAULT; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ctx = fd == VHOST_FILE_UNBIND ? NULL : eventfd_ctx_fdget(fd); 3368c2ecf20Sopenharmony_ci swap(ctx, v->config_ctx); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(ctx)) 3398c2ecf20Sopenharmony_ci eventfd_ctx_put(ctx); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (IS_ERR(v->config_ctx)) { 3428c2ecf20Sopenharmony_ci long ret = PTR_ERR(v->config_ctx); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci v->config_ctx = NULL; 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci v->vdpa->config->set_config_cb(v->vdpa, &cb); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic long vhost_vdpa_get_iova_range(struct vhost_vdpa *v, u32 __user *argp) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct vhost_vdpa_iova_range range = { 3568c2ecf20Sopenharmony_ci .first = v->range.first, 3578c2ecf20Sopenharmony_ci .last = v->range.last, 3588c2ecf20Sopenharmony_ci }; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (copy_to_user(argp, &range, sizeof(range))) 3618c2ecf20Sopenharmony_ci return -EFAULT; 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, 3668c2ecf20Sopenharmony_ci void __user *argp) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 3698c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 3708c2ecf20Sopenharmony_ci struct vdpa_vq_state vq_state; 3718c2ecf20Sopenharmony_ci struct vdpa_callback cb; 3728c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 3738c2ecf20Sopenharmony_ci struct vhost_vring_state s; 3748c2ecf20Sopenharmony_ci u32 idx; 3758c2ecf20Sopenharmony_ci long r; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci r = get_user(idx, (u32 __user *)argp); 3788c2ecf20Sopenharmony_ci if (r < 0) 3798c2ecf20Sopenharmony_ci return r; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (idx >= v->nvqs) 3828c2ecf20Sopenharmony_ci return -ENOBUFS; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci idx = array_index_nospec(idx, v->nvqs); 3858c2ecf20Sopenharmony_ci vq = &v->vqs[idx]; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci switch (cmd) { 3888c2ecf20Sopenharmony_ci case VHOST_VDPA_SET_VRING_ENABLE: 3898c2ecf20Sopenharmony_ci if (copy_from_user(&s, argp, sizeof(s))) 3908c2ecf20Sopenharmony_ci return -EFAULT; 3918c2ecf20Sopenharmony_ci ops->set_vq_ready(vdpa, idx, s.num); 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci case VHOST_GET_VRING_BASE: 3948c2ecf20Sopenharmony_ci r = ops->get_vq_state(v->vdpa, idx, &vq_state); 3958c2ecf20Sopenharmony_ci if (r) 3968c2ecf20Sopenharmony_ci return r; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci vq->last_avail_idx = vq_state.avail_index; 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci r = vhost_vring_ioctl(&v->vdev, cmd, argp); 4038c2ecf20Sopenharmony_ci if (r) 4048c2ecf20Sopenharmony_ci return r; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci switch (cmd) { 4078c2ecf20Sopenharmony_ci case VHOST_SET_VRING_ADDR: 4088c2ecf20Sopenharmony_ci if (ops->set_vq_address(vdpa, idx, 4098c2ecf20Sopenharmony_ci (u64)(uintptr_t)vq->desc, 4108c2ecf20Sopenharmony_ci (u64)(uintptr_t)vq->avail, 4118c2ecf20Sopenharmony_ci (u64)(uintptr_t)vq->used)) 4128c2ecf20Sopenharmony_ci r = -EINVAL; 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci case VHOST_SET_VRING_BASE: 4168c2ecf20Sopenharmony_ci vq_state.avail_index = vq->last_avail_idx; 4178c2ecf20Sopenharmony_ci if (ops->set_vq_state(vdpa, idx, &vq_state)) 4188c2ecf20Sopenharmony_ci r = -EINVAL; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci case VHOST_SET_VRING_CALL: 4228c2ecf20Sopenharmony_ci if (vq->call_ctx.ctx) { 4238c2ecf20Sopenharmony_ci cb.callback = vhost_vdpa_virtqueue_cb; 4248c2ecf20Sopenharmony_ci cb.private = vq; 4258c2ecf20Sopenharmony_ci } else { 4268c2ecf20Sopenharmony_ci cb.callback = NULL; 4278c2ecf20Sopenharmony_ci cb.private = NULL; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci ops->set_vq_cb(vdpa, idx, &cb); 4308c2ecf20Sopenharmony_ci vhost_vdpa_setup_vq_irq(v, idx); 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci case VHOST_SET_VRING_NUM: 4348c2ecf20Sopenharmony_ci ops->set_vq_num(vdpa, idx, vq->num); 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return r; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic long vhost_vdpa_unlocked_ioctl(struct file *filep, 4428c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct vhost_vdpa *v = filep->private_data; 4458c2ecf20Sopenharmony_ci struct vhost_dev *d = &v->vdev; 4468c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 4478c2ecf20Sopenharmony_ci u64 __user *featurep = argp; 4488c2ecf20Sopenharmony_ci u64 features; 4498c2ecf20Sopenharmony_ci long r = 0; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (cmd == VHOST_SET_BACKEND_FEATURES) { 4528c2ecf20Sopenharmony_ci if (copy_from_user(&features, featurep, sizeof(features))) 4538c2ecf20Sopenharmony_ci return -EFAULT; 4548c2ecf20Sopenharmony_ci if (features & ~VHOST_VDPA_BACKEND_FEATURES) 4558c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4568c2ecf20Sopenharmony_ci vhost_set_backend_features(&v->vdev, features); 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci mutex_lock(&d->mutex); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci switch (cmd) { 4638c2ecf20Sopenharmony_ci case VHOST_VDPA_GET_DEVICE_ID: 4648c2ecf20Sopenharmony_ci r = vhost_vdpa_get_device_id(v, argp); 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci case VHOST_VDPA_GET_STATUS: 4678c2ecf20Sopenharmony_ci r = vhost_vdpa_get_status(v, argp); 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci case VHOST_VDPA_SET_STATUS: 4708c2ecf20Sopenharmony_ci r = vhost_vdpa_set_status(v, argp); 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case VHOST_VDPA_GET_CONFIG: 4738c2ecf20Sopenharmony_ci r = vhost_vdpa_get_config(v, argp); 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci case VHOST_VDPA_SET_CONFIG: 4768c2ecf20Sopenharmony_ci r = vhost_vdpa_set_config(v, argp); 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci case VHOST_GET_FEATURES: 4798c2ecf20Sopenharmony_ci r = vhost_vdpa_get_features(v, argp); 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci case VHOST_SET_FEATURES: 4828c2ecf20Sopenharmony_ci r = vhost_vdpa_set_features(v, argp); 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci case VHOST_VDPA_GET_VRING_NUM: 4858c2ecf20Sopenharmony_ci r = vhost_vdpa_get_vring_num(v, argp); 4868c2ecf20Sopenharmony_ci break; 4878c2ecf20Sopenharmony_ci case VHOST_SET_LOG_BASE: 4888c2ecf20Sopenharmony_ci case VHOST_SET_LOG_FD: 4898c2ecf20Sopenharmony_ci r = -ENOIOCTLCMD; 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci case VHOST_VDPA_SET_CONFIG_CALL: 4928c2ecf20Sopenharmony_ci r = vhost_vdpa_set_config_call(v, argp); 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci case VHOST_GET_BACKEND_FEATURES: 4958c2ecf20Sopenharmony_ci features = VHOST_VDPA_BACKEND_FEATURES; 4968c2ecf20Sopenharmony_ci if (copy_to_user(featurep, &features, sizeof(features))) 4978c2ecf20Sopenharmony_ci r = -EFAULT; 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci case VHOST_VDPA_GET_IOVA_RANGE: 5008c2ecf20Sopenharmony_ci r = vhost_vdpa_get_iova_range(v, argp); 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci default: 5038c2ecf20Sopenharmony_ci r = vhost_dev_ioctl(&v->vdev, cmd, argp); 5048c2ecf20Sopenharmony_ci if (r == -ENOIOCTLCMD) 5058c2ecf20Sopenharmony_ci r = vhost_vdpa_vring_ioctl(v, cmd, argp); 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci mutex_unlock(&d->mutex); 5108c2ecf20Sopenharmony_ci return r; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void vhost_vdpa_iotlb_unmap(struct vhost_vdpa *v, u64 start, u64 last) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct vhost_dev *dev = &v->vdev; 5168c2ecf20Sopenharmony_ci struct vhost_iotlb *iotlb = dev->iotlb; 5178c2ecf20Sopenharmony_ci struct vhost_iotlb_map *map; 5188c2ecf20Sopenharmony_ci struct page *page; 5198c2ecf20Sopenharmony_ci unsigned long pfn, pinned; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci while ((map = vhost_iotlb_itree_first(iotlb, start, last)) != NULL) { 5228c2ecf20Sopenharmony_ci pinned = map->size >> PAGE_SHIFT; 5238c2ecf20Sopenharmony_ci for (pfn = map->addr >> PAGE_SHIFT; 5248c2ecf20Sopenharmony_ci pinned > 0; pfn++, pinned--) { 5258c2ecf20Sopenharmony_ci page = pfn_to_page(pfn); 5268c2ecf20Sopenharmony_ci if (map->perm & VHOST_ACCESS_WO) 5278c2ecf20Sopenharmony_ci set_page_dirty_lock(page); 5288c2ecf20Sopenharmony_ci unpin_user_page(page); 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci atomic64_sub(map->size >> PAGE_SHIFT, &dev->mm->pinned_vm); 5318c2ecf20Sopenharmony_ci vhost_iotlb_map_free(iotlb, map); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void vhost_vdpa_iotlb_free(struct vhost_vdpa *v) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct vhost_dev *dev = &v->vdev; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci vhost_vdpa_iotlb_unmap(v, 0ULL, 0ULL - 1); 5408c2ecf20Sopenharmony_ci kfree(dev->iotlb); 5418c2ecf20Sopenharmony_ci dev->iotlb = NULL; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int perm_to_iommu_flags(u32 perm) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci int flags = 0; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci switch (perm) { 5498c2ecf20Sopenharmony_ci case VHOST_ACCESS_WO: 5508c2ecf20Sopenharmony_ci flags |= IOMMU_WRITE; 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci case VHOST_ACCESS_RO: 5538c2ecf20Sopenharmony_ci flags |= IOMMU_READ; 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci case VHOST_ACCESS_RW: 5568c2ecf20Sopenharmony_ci flags |= (IOMMU_WRITE | IOMMU_READ); 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci default: 5598c2ecf20Sopenharmony_ci WARN(1, "invalidate vhost IOTLB permission\n"); 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci return flags | IOMMU_CACHE; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int vhost_vdpa_map(struct vhost_vdpa *v, 5678c2ecf20Sopenharmony_ci u64 iova, u64 size, u64 pa, u32 perm) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct vhost_dev *dev = &v->vdev; 5708c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 5718c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 5728c2ecf20Sopenharmony_ci int r = 0; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci r = vhost_iotlb_add_range(dev->iotlb, iova, iova + size - 1, 5758c2ecf20Sopenharmony_ci pa, perm); 5768c2ecf20Sopenharmony_ci if (r) 5778c2ecf20Sopenharmony_ci return r; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (ops->dma_map) { 5808c2ecf20Sopenharmony_ci r = ops->dma_map(vdpa, iova, size, pa, perm); 5818c2ecf20Sopenharmony_ci } else if (ops->set_map) { 5828c2ecf20Sopenharmony_ci if (!v->in_batch) 5838c2ecf20Sopenharmony_ci r = ops->set_map(vdpa, dev->iotlb); 5848c2ecf20Sopenharmony_ci } else { 5858c2ecf20Sopenharmony_ci r = iommu_map(v->domain, iova, pa, size, 5868c2ecf20Sopenharmony_ci perm_to_iommu_flags(perm)); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (r) 5908c2ecf20Sopenharmony_ci vhost_iotlb_del_range(dev->iotlb, iova, iova + size - 1); 5918c2ecf20Sopenharmony_ci else 5928c2ecf20Sopenharmony_ci atomic64_add(size >> PAGE_SHIFT, &dev->mm->pinned_vm); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return r; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic void vhost_vdpa_unmap(struct vhost_vdpa *v, u64 iova, u64 size) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct vhost_dev *dev = &v->vdev; 6008c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 6018c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci vhost_vdpa_iotlb_unmap(v, iova, iova + size - 1); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (ops->dma_map) { 6068c2ecf20Sopenharmony_ci ops->dma_unmap(vdpa, iova, size); 6078c2ecf20Sopenharmony_ci } else if (ops->set_map) { 6088c2ecf20Sopenharmony_ci if (!v->in_batch) 6098c2ecf20Sopenharmony_ci ops->set_map(vdpa, dev->iotlb); 6108c2ecf20Sopenharmony_ci } else { 6118c2ecf20Sopenharmony_ci iommu_unmap(v->domain, iova, size); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, 6168c2ecf20Sopenharmony_ci struct vhost_iotlb_msg *msg) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct vhost_dev *dev = &v->vdev; 6198c2ecf20Sopenharmony_ci struct vhost_iotlb *iotlb = dev->iotlb; 6208c2ecf20Sopenharmony_ci struct page **page_list; 6218c2ecf20Sopenharmony_ci unsigned long list_size = PAGE_SIZE / sizeof(struct page *); 6228c2ecf20Sopenharmony_ci unsigned int gup_flags = FOLL_LONGTERM; 6238c2ecf20Sopenharmony_ci unsigned long npages, cur_base, map_pfn, last_pfn = 0; 6248c2ecf20Sopenharmony_ci unsigned long lock_limit, sz2pin, nchunks, i; 6258c2ecf20Sopenharmony_ci u64 iova = msg->iova; 6268c2ecf20Sopenharmony_ci long pinned; 6278c2ecf20Sopenharmony_ci int ret = 0; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (msg->iova < v->range.first || !msg->size || 6308c2ecf20Sopenharmony_ci msg->iova > U64_MAX - msg->size + 1 || 6318c2ecf20Sopenharmony_ci msg->iova + msg->size - 1 > v->range.last) 6328c2ecf20Sopenharmony_ci return -EINVAL; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (vhost_iotlb_itree_first(iotlb, msg->iova, 6358c2ecf20Sopenharmony_ci msg->iova + msg->size - 1)) 6368c2ecf20Sopenharmony_ci return -EEXIST; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* Limit the use of memory for bookkeeping */ 6398c2ecf20Sopenharmony_ci page_list = (struct page **) __get_free_page(GFP_KERNEL); 6408c2ecf20Sopenharmony_ci if (!page_list) 6418c2ecf20Sopenharmony_ci return -ENOMEM; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (msg->perm & VHOST_ACCESS_WO) 6448c2ecf20Sopenharmony_ci gup_flags |= FOLL_WRITE; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci npages = PAGE_ALIGN(msg->size + (iova & ~PAGE_MASK)) >> PAGE_SHIFT; 6478c2ecf20Sopenharmony_ci if (!npages) { 6488c2ecf20Sopenharmony_ci ret = -EINVAL; 6498c2ecf20Sopenharmony_ci goto free; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci mmap_read_lock(dev->mm); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; 6558c2ecf20Sopenharmony_ci if (npages + atomic64_read(&dev->mm->pinned_vm) > lock_limit) { 6568c2ecf20Sopenharmony_ci ret = -ENOMEM; 6578c2ecf20Sopenharmony_ci goto unlock; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci cur_base = msg->uaddr & PAGE_MASK; 6618c2ecf20Sopenharmony_ci iova &= PAGE_MASK; 6628c2ecf20Sopenharmony_ci nchunks = 0; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci while (npages) { 6658c2ecf20Sopenharmony_ci sz2pin = min_t(unsigned long, npages, list_size); 6668c2ecf20Sopenharmony_ci pinned = pin_user_pages(cur_base, sz2pin, 6678c2ecf20Sopenharmony_ci gup_flags, page_list, NULL); 6688c2ecf20Sopenharmony_ci if (sz2pin != pinned) { 6698c2ecf20Sopenharmony_ci if (pinned < 0) { 6708c2ecf20Sopenharmony_ci ret = pinned; 6718c2ecf20Sopenharmony_ci } else { 6728c2ecf20Sopenharmony_ci unpin_user_pages(page_list, pinned); 6738c2ecf20Sopenharmony_ci ret = -ENOMEM; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci goto out; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci nchunks++; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (!last_pfn) 6808c2ecf20Sopenharmony_ci map_pfn = page_to_pfn(page_list[0]); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci for (i = 0; i < pinned; i++) { 6838c2ecf20Sopenharmony_ci unsigned long this_pfn = page_to_pfn(page_list[i]); 6848c2ecf20Sopenharmony_ci u64 csize; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (last_pfn && (this_pfn != last_pfn + 1)) { 6878c2ecf20Sopenharmony_ci /* Pin a contiguous chunk of memory */ 6888c2ecf20Sopenharmony_ci csize = (last_pfn - map_pfn + 1) << PAGE_SHIFT; 6898c2ecf20Sopenharmony_ci ret = vhost_vdpa_map(v, iova, csize, 6908c2ecf20Sopenharmony_ci map_pfn << PAGE_SHIFT, 6918c2ecf20Sopenharmony_ci msg->perm); 6928c2ecf20Sopenharmony_ci if (ret) { 6938c2ecf20Sopenharmony_ci /* 6948c2ecf20Sopenharmony_ci * Unpin the pages that are left unmapped 6958c2ecf20Sopenharmony_ci * from this point on in the current 6968c2ecf20Sopenharmony_ci * page_list. The remaining outstanding 6978c2ecf20Sopenharmony_ci * ones which may stride across several 6988c2ecf20Sopenharmony_ci * chunks will be covered in the common 6998c2ecf20Sopenharmony_ci * error path subsequently. 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_ci unpin_user_pages(&page_list[i], 7028c2ecf20Sopenharmony_ci pinned - i); 7038c2ecf20Sopenharmony_ci goto out; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci map_pfn = this_pfn; 7078c2ecf20Sopenharmony_ci iova += csize; 7088c2ecf20Sopenharmony_ci nchunks = 0; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci last_pfn = this_pfn; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci cur_base += pinned << PAGE_SHIFT; 7158c2ecf20Sopenharmony_ci npages -= pinned; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Pin the rest chunk */ 7198c2ecf20Sopenharmony_ci ret = vhost_vdpa_map(v, iova, (last_pfn - map_pfn + 1) << PAGE_SHIFT, 7208c2ecf20Sopenharmony_ci map_pfn << PAGE_SHIFT, msg->perm); 7218c2ecf20Sopenharmony_ciout: 7228c2ecf20Sopenharmony_ci if (ret) { 7238c2ecf20Sopenharmony_ci if (nchunks) { 7248c2ecf20Sopenharmony_ci unsigned long pfn; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* 7278c2ecf20Sopenharmony_ci * Unpin the outstanding pages which are yet to be 7288c2ecf20Sopenharmony_ci * mapped but haven't due to vdpa_map() or 7298c2ecf20Sopenharmony_ci * pin_user_pages() failure. 7308c2ecf20Sopenharmony_ci * 7318c2ecf20Sopenharmony_ci * Mapped pages are accounted in vdpa_map(), hence 7328c2ecf20Sopenharmony_ci * the corresponding unpinning will be handled by 7338c2ecf20Sopenharmony_ci * vdpa_unmap(). 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ci WARN_ON(!last_pfn); 7368c2ecf20Sopenharmony_ci for (pfn = map_pfn; pfn <= last_pfn; pfn++) 7378c2ecf20Sopenharmony_ci unpin_user_page(pfn_to_page(pfn)); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci vhost_vdpa_unmap(v, msg->iova, msg->size); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ciunlock: 7428c2ecf20Sopenharmony_ci mmap_read_unlock(dev->mm); 7438c2ecf20Sopenharmony_cifree: 7448c2ecf20Sopenharmony_ci free_page((unsigned long)page_list); 7458c2ecf20Sopenharmony_ci return ret; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int vhost_vdpa_process_iotlb_msg(struct vhost_dev *dev, 7498c2ecf20Sopenharmony_ci struct vhost_iotlb_msg *msg) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct vhost_vdpa *v = container_of(dev, struct vhost_vdpa, vdev); 7528c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 7538c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 7548c2ecf20Sopenharmony_ci int r = 0; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci mutex_lock(&dev->mutex); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci r = vhost_dev_check_owner(dev); 7598c2ecf20Sopenharmony_ci if (r) 7608c2ecf20Sopenharmony_ci goto unlock; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci switch (msg->type) { 7638c2ecf20Sopenharmony_ci case VHOST_IOTLB_UPDATE: 7648c2ecf20Sopenharmony_ci r = vhost_vdpa_process_iotlb_update(v, msg); 7658c2ecf20Sopenharmony_ci break; 7668c2ecf20Sopenharmony_ci case VHOST_IOTLB_INVALIDATE: 7678c2ecf20Sopenharmony_ci vhost_vdpa_unmap(v, msg->iova, msg->size); 7688c2ecf20Sopenharmony_ci break; 7698c2ecf20Sopenharmony_ci case VHOST_IOTLB_BATCH_BEGIN: 7708c2ecf20Sopenharmony_ci v->in_batch = true; 7718c2ecf20Sopenharmony_ci break; 7728c2ecf20Sopenharmony_ci case VHOST_IOTLB_BATCH_END: 7738c2ecf20Sopenharmony_ci if (v->in_batch && ops->set_map) 7748c2ecf20Sopenharmony_ci ops->set_map(vdpa, dev->iotlb); 7758c2ecf20Sopenharmony_ci v->in_batch = false; 7768c2ecf20Sopenharmony_ci break; 7778c2ecf20Sopenharmony_ci default: 7788c2ecf20Sopenharmony_ci r = -EINVAL; 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ciunlock: 7828c2ecf20Sopenharmony_ci mutex_unlock(&dev->mutex); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci return r; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic ssize_t vhost_vdpa_chr_write_iter(struct kiocb *iocb, 7888c2ecf20Sopenharmony_ci struct iov_iter *from) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct file *file = iocb->ki_filp; 7918c2ecf20Sopenharmony_ci struct vhost_vdpa *v = file->private_data; 7928c2ecf20Sopenharmony_ci struct vhost_dev *dev = &v->vdev; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci return vhost_chr_write_iter(dev, from); 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic int vhost_vdpa_alloc_domain(struct vhost_vdpa *v) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 8008c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 8018c2ecf20Sopenharmony_ci struct device *dma_dev = vdpa_get_dma_dev(vdpa); 8028c2ecf20Sopenharmony_ci struct bus_type *bus; 8038c2ecf20Sopenharmony_ci int ret; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* Device want to do DMA by itself */ 8068c2ecf20Sopenharmony_ci if (ops->set_map || ops->dma_map) 8078c2ecf20Sopenharmony_ci return 0; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci bus = dma_dev->bus; 8108c2ecf20Sopenharmony_ci if (!bus) 8118c2ecf20Sopenharmony_ci return -EFAULT; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (!iommu_capable(bus, IOMMU_CAP_CACHE_COHERENCY)) 8148c2ecf20Sopenharmony_ci return -ENOTSUPP; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci v->domain = iommu_domain_alloc(bus); 8178c2ecf20Sopenharmony_ci if (!v->domain) 8188c2ecf20Sopenharmony_ci return -EIO; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ret = iommu_attach_device(v->domain, dma_dev); 8218c2ecf20Sopenharmony_ci if (ret) 8228c2ecf20Sopenharmony_ci goto err_attach; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci return 0; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cierr_attach: 8278c2ecf20Sopenharmony_ci iommu_domain_free(v->domain); 8288c2ecf20Sopenharmony_ci return ret; 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic void vhost_vdpa_free_domain(struct vhost_vdpa *v) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 8348c2ecf20Sopenharmony_ci struct device *dma_dev = vdpa_get_dma_dev(vdpa); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (v->domain) { 8378c2ecf20Sopenharmony_ci iommu_detach_device(v->domain, dma_dev); 8388c2ecf20Sopenharmony_ci iommu_domain_free(v->domain); 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci v->domain = NULL; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic void vhost_vdpa_set_iova_range(struct vhost_vdpa *v) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct vdpa_iova_range *range = &v->range; 8478c2ecf20Sopenharmony_ci struct iommu_domain_geometry geo; 8488c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 8498c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (ops->get_iova_range) { 8528c2ecf20Sopenharmony_ci *range = ops->get_iova_range(vdpa); 8538c2ecf20Sopenharmony_ci } else if (v->domain && 8548c2ecf20Sopenharmony_ci !iommu_domain_get_attr(v->domain, 8558c2ecf20Sopenharmony_ci DOMAIN_ATTR_GEOMETRY, &geo) && 8568c2ecf20Sopenharmony_ci geo.force_aperture) { 8578c2ecf20Sopenharmony_ci range->first = geo.aperture_start; 8588c2ecf20Sopenharmony_ci range->last = geo.aperture_end; 8598c2ecf20Sopenharmony_ci } else { 8608c2ecf20Sopenharmony_ci range->first = 0; 8618c2ecf20Sopenharmony_ci range->last = ULLONG_MAX; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic int vhost_vdpa_open(struct inode *inode, struct file *filep) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct vhost_vdpa *v; 8688c2ecf20Sopenharmony_ci struct vhost_dev *dev; 8698c2ecf20Sopenharmony_ci struct vhost_virtqueue **vqs; 8708c2ecf20Sopenharmony_ci int nvqs, i, r, opened; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci v = container_of(inode->i_cdev, struct vhost_vdpa, cdev); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci opened = atomic_cmpxchg(&v->opened, 0, 1); 8758c2ecf20Sopenharmony_ci if (opened) 8768c2ecf20Sopenharmony_ci return -EBUSY; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci nvqs = v->nvqs; 8798c2ecf20Sopenharmony_ci vhost_vdpa_reset(v); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci vqs = kmalloc_array(nvqs, sizeof(*vqs), GFP_KERNEL); 8828c2ecf20Sopenharmony_ci if (!vqs) { 8838c2ecf20Sopenharmony_ci r = -ENOMEM; 8848c2ecf20Sopenharmony_ci goto err; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci dev = &v->vdev; 8888c2ecf20Sopenharmony_ci for (i = 0; i < nvqs; i++) { 8898c2ecf20Sopenharmony_ci vqs[i] = &v->vqs[i]; 8908c2ecf20Sopenharmony_ci vqs[i]->handle_kick = handle_vq_kick; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci vhost_dev_init(dev, vqs, nvqs, 0, 0, 0, false, 8938c2ecf20Sopenharmony_ci vhost_vdpa_process_iotlb_msg); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci dev->iotlb = vhost_iotlb_alloc(0, 0); 8968c2ecf20Sopenharmony_ci if (!dev->iotlb) { 8978c2ecf20Sopenharmony_ci r = -ENOMEM; 8988c2ecf20Sopenharmony_ci goto err_init_iotlb; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci r = vhost_vdpa_alloc_domain(v); 9028c2ecf20Sopenharmony_ci if (r) 9038c2ecf20Sopenharmony_ci goto err_init_iotlb; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci vhost_vdpa_set_iova_range(v); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci filep->private_data = v; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cierr_init_iotlb: 9128c2ecf20Sopenharmony_ci vhost_dev_cleanup(&v->vdev); 9138c2ecf20Sopenharmony_ci kfree(vqs); 9148c2ecf20Sopenharmony_cierr: 9158c2ecf20Sopenharmony_ci atomic_dec(&v->opened); 9168c2ecf20Sopenharmony_ci return r; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic void vhost_vdpa_clean_irq(struct vhost_vdpa *v) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci int i; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci for (i = 0; i < v->nvqs; i++) 9248c2ecf20Sopenharmony_ci vhost_vdpa_unsetup_vq_irq(v, i); 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic int vhost_vdpa_release(struct inode *inode, struct file *filep) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct vhost_vdpa *v = filep->private_data; 9308c2ecf20Sopenharmony_ci struct vhost_dev *d = &v->vdev; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci mutex_lock(&d->mutex); 9338c2ecf20Sopenharmony_ci filep->private_data = NULL; 9348c2ecf20Sopenharmony_ci vhost_vdpa_reset(v); 9358c2ecf20Sopenharmony_ci vhost_dev_stop(&v->vdev); 9368c2ecf20Sopenharmony_ci vhost_vdpa_iotlb_free(v); 9378c2ecf20Sopenharmony_ci vhost_vdpa_free_domain(v); 9388c2ecf20Sopenharmony_ci vhost_vdpa_config_put(v); 9398c2ecf20Sopenharmony_ci vhost_vdpa_clean_irq(v); 9408c2ecf20Sopenharmony_ci vhost_dev_cleanup(&v->vdev); 9418c2ecf20Sopenharmony_ci kfree(v->vdev.vqs); 9428c2ecf20Sopenharmony_ci mutex_unlock(&d->mutex); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci atomic_dec(&v->opened); 9458c2ecf20Sopenharmony_ci complete(&v->completion); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci return 0; 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 9518c2ecf20Sopenharmony_cistatic vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct vhost_vdpa *v = vmf->vma->vm_file->private_data; 9548c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 9558c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 9568c2ecf20Sopenharmony_ci struct vdpa_notification_area notify; 9578c2ecf20Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 9588c2ecf20Sopenharmony_ci u16 index = vma->vm_pgoff; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci notify = ops->get_vq_notification(vdpa, index); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 9638c2ecf20Sopenharmony_ci if (remap_pfn_range(vma, vmf->address & PAGE_MASK, 9648c2ecf20Sopenharmony_ci notify.addr >> PAGE_SHIFT, PAGE_SIZE, 9658c2ecf20Sopenharmony_ci vma->vm_page_prot)) 9668c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci return VM_FAULT_NOPAGE; 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic const struct vm_operations_struct vhost_vdpa_vm_ops = { 9728c2ecf20Sopenharmony_ci .fault = vhost_vdpa_fault, 9738c2ecf20Sopenharmony_ci}; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct vhost_vdpa *v = vma->vm_file->private_data; 9788c2ecf20Sopenharmony_ci struct vdpa_device *vdpa = v->vdpa; 9798c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 9808c2ecf20Sopenharmony_ci struct vdpa_notification_area notify; 9818c2ecf20Sopenharmony_ci unsigned long index = vma->vm_pgoff; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (vma->vm_end - vma->vm_start != PAGE_SIZE) 9848c2ecf20Sopenharmony_ci return -EINVAL; 9858c2ecf20Sopenharmony_ci if ((vma->vm_flags & VM_SHARED) == 0) 9868c2ecf20Sopenharmony_ci return -EINVAL; 9878c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_READ) 9888c2ecf20Sopenharmony_ci return -EINVAL; 9898c2ecf20Sopenharmony_ci if (index > 65535) 9908c2ecf20Sopenharmony_ci return -EINVAL; 9918c2ecf20Sopenharmony_ci if (!ops->get_vq_notification) 9928c2ecf20Sopenharmony_ci return -ENOTSUPP; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci /* To be safe and easily modelled by userspace, We only 9958c2ecf20Sopenharmony_ci * support the doorbell which sits on the page boundary and 9968c2ecf20Sopenharmony_ci * does not share the page with other registers. 9978c2ecf20Sopenharmony_ci */ 9988c2ecf20Sopenharmony_ci notify = ops->get_vq_notification(vdpa, index); 9998c2ecf20Sopenharmony_ci if (notify.addr & (PAGE_SIZE - 1)) 10008c2ecf20Sopenharmony_ci return -EINVAL; 10018c2ecf20Sopenharmony_ci if (vma->vm_end - vma->vm_start != notify.size) 10028c2ecf20Sopenharmony_ci return -ENOTSUPP; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; 10058c2ecf20Sopenharmony_ci vma->vm_ops = &vhost_vdpa_vm_ops; 10068c2ecf20Sopenharmony_ci return 0; 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */ 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cistatic const struct file_operations vhost_vdpa_fops = { 10118c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10128c2ecf20Sopenharmony_ci .open = vhost_vdpa_open, 10138c2ecf20Sopenharmony_ci .release = vhost_vdpa_release, 10148c2ecf20Sopenharmony_ci .write_iter = vhost_vdpa_chr_write_iter, 10158c2ecf20Sopenharmony_ci .unlocked_ioctl = vhost_vdpa_unlocked_ioctl, 10168c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 10178c2ecf20Sopenharmony_ci .mmap = vhost_vdpa_mmap, 10188c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */ 10198c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 10208c2ecf20Sopenharmony_ci}; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic void vhost_vdpa_release_dev(struct device *device) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct vhost_vdpa *v = 10258c2ecf20Sopenharmony_ci container_of(device, struct vhost_vdpa, dev); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci ida_simple_remove(&vhost_vdpa_ida, v->minor); 10288c2ecf20Sopenharmony_ci kfree(v->vqs); 10298c2ecf20Sopenharmony_ci kfree(v); 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic int vhost_vdpa_probe(struct vdpa_device *vdpa) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdpa->config; 10358c2ecf20Sopenharmony_ci struct vhost_vdpa *v; 10368c2ecf20Sopenharmony_ci int minor; 10378c2ecf20Sopenharmony_ci int r; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Currently, we only accept the network devices. */ 10408c2ecf20Sopenharmony_ci if (ops->get_device_id(vdpa) != VIRTIO_ID_NET) 10418c2ecf20Sopenharmony_ci return -ENOTSUPP; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci v = kzalloc(sizeof(*v), GFP_KERNEL | __GFP_RETRY_MAYFAIL); 10448c2ecf20Sopenharmony_ci if (!v) 10458c2ecf20Sopenharmony_ci return -ENOMEM; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci minor = ida_simple_get(&vhost_vdpa_ida, 0, 10488c2ecf20Sopenharmony_ci VHOST_VDPA_DEV_MAX, GFP_KERNEL); 10498c2ecf20Sopenharmony_ci if (minor < 0) { 10508c2ecf20Sopenharmony_ci kfree(v); 10518c2ecf20Sopenharmony_ci return minor; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci atomic_set(&v->opened, 0); 10558c2ecf20Sopenharmony_ci v->minor = minor; 10568c2ecf20Sopenharmony_ci v->vdpa = vdpa; 10578c2ecf20Sopenharmony_ci v->nvqs = vdpa->nvqs; 10588c2ecf20Sopenharmony_ci v->virtio_id = ops->get_device_id(vdpa); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci device_initialize(&v->dev); 10618c2ecf20Sopenharmony_ci v->dev.release = vhost_vdpa_release_dev; 10628c2ecf20Sopenharmony_ci v->dev.parent = &vdpa->dev; 10638c2ecf20Sopenharmony_ci v->dev.devt = MKDEV(MAJOR(vhost_vdpa_major), minor); 10648c2ecf20Sopenharmony_ci v->vqs = kmalloc_array(v->nvqs, sizeof(struct vhost_virtqueue), 10658c2ecf20Sopenharmony_ci GFP_KERNEL); 10668c2ecf20Sopenharmony_ci if (!v->vqs) { 10678c2ecf20Sopenharmony_ci r = -ENOMEM; 10688c2ecf20Sopenharmony_ci goto err; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci r = dev_set_name(&v->dev, "vhost-vdpa-%u", minor); 10728c2ecf20Sopenharmony_ci if (r) 10738c2ecf20Sopenharmony_ci goto err; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci cdev_init(&v->cdev, &vhost_vdpa_fops); 10768c2ecf20Sopenharmony_ci v->cdev.owner = THIS_MODULE; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci r = cdev_device_add(&v->cdev, &v->dev); 10798c2ecf20Sopenharmony_ci if (r) 10808c2ecf20Sopenharmony_ci goto err; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci init_completion(&v->completion); 10838c2ecf20Sopenharmony_ci vdpa_set_drvdata(vdpa, v); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci return 0; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cierr: 10888c2ecf20Sopenharmony_ci put_device(&v->dev); 10898c2ecf20Sopenharmony_ci return r; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic void vhost_vdpa_remove(struct vdpa_device *vdpa) 10938c2ecf20Sopenharmony_ci{ 10948c2ecf20Sopenharmony_ci struct vhost_vdpa *v = vdpa_get_drvdata(vdpa); 10958c2ecf20Sopenharmony_ci int opened; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci cdev_device_del(&v->cdev, &v->dev); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci do { 11008c2ecf20Sopenharmony_ci opened = atomic_cmpxchg(&v->opened, 0, 1); 11018c2ecf20Sopenharmony_ci if (!opened) 11028c2ecf20Sopenharmony_ci break; 11038c2ecf20Sopenharmony_ci wait_for_completion(&v->completion); 11048c2ecf20Sopenharmony_ci } while (1); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci put_device(&v->dev); 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic struct vdpa_driver vhost_vdpa_driver = { 11108c2ecf20Sopenharmony_ci .driver = { 11118c2ecf20Sopenharmony_ci .name = "vhost_vdpa", 11128c2ecf20Sopenharmony_ci }, 11138c2ecf20Sopenharmony_ci .probe = vhost_vdpa_probe, 11148c2ecf20Sopenharmony_ci .remove = vhost_vdpa_remove, 11158c2ecf20Sopenharmony_ci}; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int __init vhost_vdpa_init(void) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci int r; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci r = alloc_chrdev_region(&vhost_vdpa_major, 0, VHOST_VDPA_DEV_MAX, 11228c2ecf20Sopenharmony_ci "vhost-vdpa"); 11238c2ecf20Sopenharmony_ci if (r) 11248c2ecf20Sopenharmony_ci goto err_alloc_chrdev; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci r = vdpa_register_driver(&vhost_vdpa_driver); 11278c2ecf20Sopenharmony_ci if (r) 11288c2ecf20Sopenharmony_ci goto err_vdpa_register_driver; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci return 0; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cierr_vdpa_register_driver: 11338c2ecf20Sopenharmony_ci unregister_chrdev_region(vhost_vdpa_major, VHOST_VDPA_DEV_MAX); 11348c2ecf20Sopenharmony_cierr_alloc_chrdev: 11358c2ecf20Sopenharmony_ci return r; 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_cimodule_init(vhost_vdpa_init); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic void __exit vhost_vdpa_exit(void) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci vdpa_unregister_driver(&vhost_vdpa_driver); 11428c2ecf20Sopenharmony_ci unregister_chrdev_region(vhost_vdpa_major, VHOST_VDPA_DEV_MAX); 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_cimodule_exit(vhost_vdpa_exit); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ciMODULE_VERSION("0.0.1"); 11478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 11488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 11498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("vDPA-based vhost backend for virtio"); 1150