162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VDUSE: vDPA Device in Userspace 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020-2021 Bytedance Inc. and/or its affiliates. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Xie Yongji <xieyongji@bytedance.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/cdev.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/eventfd.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/wait.h> 1862306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 1962306a36Sopenharmony_ci#include <linux/poll.h> 2062306a36Sopenharmony_ci#include <linux/file.h> 2162306a36Sopenharmony_ci#include <linux/uio.h> 2262306a36Sopenharmony_ci#include <linux/vdpa.h> 2362306a36Sopenharmony_ci#include <linux/nospec.h> 2462306a36Sopenharmony_ci#include <linux/vmalloc.h> 2562306a36Sopenharmony_ci#include <linux/sched/mm.h> 2662306a36Sopenharmony_ci#include <uapi/linux/vduse.h> 2762306a36Sopenharmony_ci#include <uapi/linux/vdpa.h> 2862306a36Sopenharmony_ci#include <uapi/linux/virtio_config.h> 2962306a36Sopenharmony_ci#include <uapi/linux/virtio_ids.h> 3062306a36Sopenharmony_ci#include <uapi/linux/virtio_blk.h> 3162306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "iova_domain.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DRV_AUTHOR "Yongji Xie <xieyongji@bytedance.com>" 3662306a36Sopenharmony_ci#define DRV_DESC "vDPA Device in Userspace" 3762306a36Sopenharmony_ci#define DRV_LICENSE "GPL v2" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define VDUSE_DEV_MAX (1U << MINORBITS) 4062306a36Sopenharmony_ci#define VDUSE_MAX_BOUNCE_SIZE (1024 * 1024 * 1024) 4162306a36Sopenharmony_ci#define VDUSE_MIN_BOUNCE_SIZE (1024 * 1024) 4262306a36Sopenharmony_ci#define VDUSE_BOUNCE_SIZE (64 * 1024 * 1024) 4362306a36Sopenharmony_ci/* 128 MB reserved for virtqueue creation */ 4462306a36Sopenharmony_ci#define VDUSE_IOVA_SIZE (VDUSE_MAX_BOUNCE_SIZE + 128 * 1024 * 1024) 4562306a36Sopenharmony_ci#define VDUSE_MSG_DEFAULT_TIMEOUT 30 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define IRQ_UNBOUND -1 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct vduse_virtqueue { 5062306a36Sopenharmony_ci u16 index; 5162306a36Sopenharmony_ci u16 num_max; 5262306a36Sopenharmony_ci u32 num; 5362306a36Sopenharmony_ci u64 desc_addr; 5462306a36Sopenharmony_ci u64 driver_addr; 5562306a36Sopenharmony_ci u64 device_addr; 5662306a36Sopenharmony_ci struct vdpa_vq_state state; 5762306a36Sopenharmony_ci bool ready; 5862306a36Sopenharmony_ci bool kicked; 5962306a36Sopenharmony_ci spinlock_t kick_lock; 6062306a36Sopenharmony_ci spinlock_t irq_lock; 6162306a36Sopenharmony_ci struct eventfd_ctx *kickfd; 6262306a36Sopenharmony_ci struct vdpa_callback cb; 6362306a36Sopenharmony_ci struct work_struct inject; 6462306a36Sopenharmony_ci struct work_struct kick; 6562306a36Sopenharmony_ci int irq_effective_cpu; 6662306a36Sopenharmony_ci struct cpumask irq_affinity; 6762306a36Sopenharmony_ci struct kobject kobj; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct vduse_dev; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct vduse_vdpa { 7362306a36Sopenharmony_ci struct vdpa_device vdpa; 7462306a36Sopenharmony_ci struct vduse_dev *dev; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct vduse_umem { 7862306a36Sopenharmony_ci unsigned long iova; 7962306a36Sopenharmony_ci unsigned long npages; 8062306a36Sopenharmony_ci struct page **pages; 8162306a36Sopenharmony_ci struct mm_struct *mm; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct vduse_dev { 8562306a36Sopenharmony_ci struct vduse_vdpa *vdev; 8662306a36Sopenharmony_ci struct device *dev; 8762306a36Sopenharmony_ci struct vduse_virtqueue **vqs; 8862306a36Sopenharmony_ci struct vduse_iova_domain *domain; 8962306a36Sopenharmony_ci char *name; 9062306a36Sopenharmony_ci struct mutex lock; 9162306a36Sopenharmony_ci spinlock_t msg_lock; 9262306a36Sopenharmony_ci u64 msg_unique; 9362306a36Sopenharmony_ci u32 msg_timeout; 9462306a36Sopenharmony_ci wait_queue_head_t waitq; 9562306a36Sopenharmony_ci struct list_head send_list; 9662306a36Sopenharmony_ci struct list_head recv_list; 9762306a36Sopenharmony_ci struct vdpa_callback config_cb; 9862306a36Sopenharmony_ci struct work_struct inject; 9962306a36Sopenharmony_ci spinlock_t irq_lock; 10062306a36Sopenharmony_ci struct rw_semaphore rwsem; 10162306a36Sopenharmony_ci int minor; 10262306a36Sopenharmony_ci bool broken; 10362306a36Sopenharmony_ci bool connected; 10462306a36Sopenharmony_ci u64 api_version; 10562306a36Sopenharmony_ci u64 device_features; 10662306a36Sopenharmony_ci u64 driver_features; 10762306a36Sopenharmony_ci u32 device_id; 10862306a36Sopenharmony_ci u32 vendor_id; 10962306a36Sopenharmony_ci u32 generation; 11062306a36Sopenharmony_ci u32 config_size; 11162306a36Sopenharmony_ci void *config; 11262306a36Sopenharmony_ci u8 status; 11362306a36Sopenharmony_ci u32 vq_num; 11462306a36Sopenharmony_ci u32 vq_align; 11562306a36Sopenharmony_ci struct vduse_umem *umem; 11662306a36Sopenharmony_ci struct mutex mem_lock; 11762306a36Sopenharmony_ci unsigned int bounce_size; 11862306a36Sopenharmony_ci struct mutex domain_lock; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistruct vduse_dev_msg { 12262306a36Sopenharmony_ci struct vduse_dev_request req; 12362306a36Sopenharmony_ci struct vduse_dev_response resp; 12462306a36Sopenharmony_ci struct list_head list; 12562306a36Sopenharmony_ci wait_queue_head_t waitq; 12662306a36Sopenharmony_ci bool completed; 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistruct vduse_control { 13062306a36Sopenharmony_ci u64 api_version; 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic DEFINE_MUTEX(vduse_lock); 13462306a36Sopenharmony_cistatic DEFINE_IDR(vduse_idr); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic dev_t vduse_major; 13762306a36Sopenharmony_cistatic struct class *vduse_class; 13862306a36Sopenharmony_cistatic struct cdev vduse_ctrl_cdev; 13962306a36Sopenharmony_cistatic struct cdev vduse_cdev; 14062306a36Sopenharmony_cistatic struct workqueue_struct *vduse_irq_wq; 14162306a36Sopenharmony_cistatic struct workqueue_struct *vduse_irq_bound_wq; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic u32 allowed_device_id[] = { 14462306a36Sopenharmony_ci VIRTIO_ID_BLOCK, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic inline struct vduse_dev *vdpa_to_vduse(struct vdpa_device *vdpa) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct vduse_vdpa *vdev = container_of(vdpa, struct vduse_vdpa, vdpa); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return vdev->dev; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic inline struct vduse_dev *dev_to_vduse(struct device *dev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct vdpa_device *vdpa = dev_to_vdpa(dev); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return vdpa_to_vduse(vdpa); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic struct vduse_dev_msg *vduse_find_msg(struct list_head *head, 16262306a36Sopenharmony_ci uint32_t request_id) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct vduse_dev_msg *msg; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci list_for_each_entry(msg, head, list) { 16762306a36Sopenharmony_ci if (msg->req.request_id == request_id) { 16862306a36Sopenharmony_ci list_del(&msg->list); 16962306a36Sopenharmony_ci return msg; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return NULL; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct vduse_dev_msg *vduse_dequeue_msg(struct list_head *head) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct vduse_dev_msg *msg = NULL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (!list_empty(head)) { 18162306a36Sopenharmony_ci msg = list_first_entry(head, struct vduse_dev_msg, list); 18262306a36Sopenharmony_ci list_del(&msg->list); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return msg; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void vduse_enqueue_msg(struct list_head *head, 18962306a36Sopenharmony_ci struct vduse_dev_msg *msg) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci list_add_tail(&msg->list, head); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void vduse_dev_broken(struct vduse_dev *dev) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct vduse_dev_msg *msg, *tmp; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (unlikely(dev->broken)) 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci list_splice_init(&dev->recv_list, &dev->send_list); 20262306a36Sopenharmony_ci list_for_each_entry_safe(msg, tmp, &dev->send_list, list) { 20362306a36Sopenharmony_ci list_del(&msg->list); 20462306a36Sopenharmony_ci msg->completed = 1; 20562306a36Sopenharmony_ci msg->resp.result = VDUSE_REQ_RESULT_FAILED; 20662306a36Sopenharmony_ci wake_up(&msg->waitq); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci dev->broken = true; 20962306a36Sopenharmony_ci wake_up(&dev->waitq); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int vduse_dev_msg_sync(struct vduse_dev *dev, 21362306a36Sopenharmony_ci struct vduse_dev_msg *msg) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci int ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (unlikely(dev->broken)) 21862306a36Sopenharmony_ci return -EIO; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci init_waitqueue_head(&msg->waitq); 22162306a36Sopenharmony_ci spin_lock(&dev->msg_lock); 22262306a36Sopenharmony_ci if (unlikely(dev->broken)) { 22362306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 22462306a36Sopenharmony_ci return -EIO; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci msg->req.request_id = dev->msg_unique++; 22762306a36Sopenharmony_ci vduse_enqueue_msg(&dev->send_list, msg); 22862306a36Sopenharmony_ci wake_up(&dev->waitq); 22962306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 23062306a36Sopenharmony_ci if (dev->msg_timeout) 23162306a36Sopenharmony_ci ret = wait_event_killable_timeout(msg->waitq, msg->completed, 23262306a36Sopenharmony_ci (long)dev->msg_timeout * HZ); 23362306a36Sopenharmony_ci else 23462306a36Sopenharmony_ci ret = wait_event_killable(msg->waitq, msg->completed); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci spin_lock(&dev->msg_lock); 23762306a36Sopenharmony_ci if (!msg->completed) { 23862306a36Sopenharmony_ci list_del(&msg->list); 23962306a36Sopenharmony_ci msg->resp.result = VDUSE_REQ_RESULT_FAILED; 24062306a36Sopenharmony_ci /* Mark the device as malfunction when there is a timeout */ 24162306a36Sopenharmony_ci if (!ret) 24262306a36Sopenharmony_ci vduse_dev_broken(dev); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci ret = (msg->resp.result == VDUSE_REQ_RESULT_OK) ? 0 : -EIO; 24562306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int vduse_dev_get_vq_state_packed(struct vduse_dev *dev, 25162306a36Sopenharmony_ci struct vduse_virtqueue *vq, 25262306a36Sopenharmony_ci struct vdpa_vq_state_packed *packed) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct vduse_dev_msg msg = { 0 }; 25562306a36Sopenharmony_ci int ret; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci msg.req.type = VDUSE_GET_VQ_STATE; 25862306a36Sopenharmony_ci msg.req.vq_state.index = vq->index; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ret = vduse_dev_msg_sync(dev, &msg); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci packed->last_avail_counter = 26562306a36Sopenharmony_ci msg.resp.vq_state.packed.last_avail_counter & 0x0001; 26662306a36Sopenharmony_ci packed->last_avail_idx = 26762306a36Sopenharmony_ci msg.resp.vq_state.packed.last_avail_idx & 0x7FFF; 26862306a36Sopenharmony_ci packed->last_used_counter = 26962306a36Sopenharmony_ci msg.resp.vq_state.packed.last_used_counter & 0x0001; 27062306a36Sopenharmony_ci packed->last_used_idx = 27162306a36Sopenharmony_ci msg.resp.vq_state.packed.last_used_idx & 0x7FFF; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int vduse_dev_get_vq_state_split(struct vduse_dev *dev, 27762306a36Sopenharmony_ci struct vduse_virtqueue *vq, 27862306a36Sopenharmony_ci struct vdpa_vq_state_split *split) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct vduse_dev_msg msg = { 0 }; 28162306a36Sopenharmony_ci int ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci msg.req.type = VDUSE_GET_VQ_STATE; 28462306a36Sopenharmony_ci msg.req.vq_state.index = vq->index; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci ret = vduse_dev_msg_sync(dev, &msg); 28762306a36Sopenharmony_ci if (ret) 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci split->avail_index = msg.resp.vq_state.split.avail_index; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int vduse_dev_set_status(struct vduse_dev *dev, u8 status) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct vduse_dev_msg msg = { 0 }; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci msg.req.type = VDUSE_SET_STATUS; 30062306a36Sopenharmony_ci msg.req.s.status = status; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return vduse_dev_msg_sync(dev, &msg); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int vduse_dev_update_iotlb(struct vduse_dev *dev, 30662306a36Sopenharmony_ci u64 start, u64 last) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct vduse_dev_msg msg = { 0 }; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (last < start) 31162306a36Sopenharmony_ci return -EINVAL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci msg.req.type = VDUSE_UPDATE_IOTLB; 31462306a36Sopenharmony_ci msg.req.iova.start = start; 31562306a36Sopenharmony_ci msg.req.iova.last = last; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return vduse_dev_msg_sync(dev, &msg); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct file *file = iocb->ki_filp; 32362306a36Sopenharmony_ci struct vduse_dev *dev = file->private_data; 32462306a36Sopenharmony_ci struct vduse_dev_msg *msg; 32562306a36Sopenharmony_ci int size = sizeof(struct vduse_dev_request); 32662306a36Sopenharmony_ci ssize_t ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (iov_iter_count(to) < size) 32962306a36Sopenharmony_ci return -EINVAL; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci spin_lock(&dev->msg_lock); 33262306a36Sopenharmony_ci while (1) { 33362306a36Sopenharmony_ci msg = vduse_dequeue_msg(&dev->send_list); 33462306a36Sopenharmony_ci if (msg) 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci ret = -EAGAIN; 33862306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 33962306a36Sopenharmony_ci goto unlock; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 34262306a36Sopenharmony_ci ret = wait_event_interruptible_exclusive(dev->waitq, 34362306a36Sopenharmony_ci !list_empty(&dev->send_list)); 34462306a36Sopenharmony_ci if (ret) 34562306a36Sopenharmony_ci return ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci spin_lock(&dev->msg_lock); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 35062306a36Sopenharmony_ci ret = copy_to_iter(&msg->req, size, to); 35162306a36Sopenharmony_ci spin_lock(&dev->msg_lock); 35262306a36Sopenharmony_ci if (ret != size) { 35362306a36Sopenharmony_ci ret = -EFAULT; 35462306a36Sopenharmony_ci vduse_enqueue_msg(&dev->send_list, msg); 35562306a36Sopenharmony_ci goto unlock; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci vduse_enqueue_msg(&dev->recv_list, msg); 35862306a36Sopenharmony_ciunlock: 35962306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic bool is_mem_zero(const char *ptr, int size) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci int i; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci for (i = 0; i < size; i++) { 36962306a36Sopenharmony_ci if (ptr[i]) 37062306a36Sopenharmony_ci return false; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci return true; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic ssize_t vduse_dev_write_iter(struct kiocb *iocb, struct iov_iter *from) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct file *file = iocb->ki_filp; 37862306a36Sopenharmony_ci struct vduse_dev *dev = file->private_data; 37962306a36Sopenharmony_ci struct vduse_dev_response resp; 38062306a36Sopenharmony_ci struct vduse_dev_msg *msg; 38162306a36Sopenharmony_ci size_t ret; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = copy_from_iter(&resp, sizeof(resp), from); 38462306a36Sopenharmony_ci if (ret != sizeof(resp)) 38562306a36Sopenharmony_ci return -EINVAL; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!is_mem_zero((const char *)resp.reserved, sizeof(resp.reserved))) 38862306a36Sopenharmony_ci return -EINVAL; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci spin_lock(&dev->msg_lock); 39162306a36Sopenharmony_ci msg = vduse_find_msg(&dev->recv_list, resp.request_id); 39262306a36Sopenharmony_ci if (!msg) { 39362306a36Sopenharmony_ci ret = -ENOENT; 39462306a36Sopenharmony_ci goto unlock; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci memcpy(&msg->resp, &resp, sizeof(resp)); 39862306a36Sopenharmony_ci msg->completed = 1; 39962306a36Sopenharmony_ci wake_up(&msg->waitq); 40062306a36Sopenharmony_ciunlock: 40162306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic __poll_t vduse_dev_poll(struct file *file, poll_table *wait) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct vduse_dev *dev = file->private_data; 40962306a36Sopenharmony_ci __poll_t mask = 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci poll_wait(file, &dev->waitq, wait); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci spin_lock(&dev->msg_lock); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (unlikely(dev->broken)) 41662306a36Sopenharmony_ci mask |= EPOLLERR; 41762306a36Sopenharmony_ci if (!list_empty(&dev->send_list)) 41862306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 41962306a36Sopenharmony_ci if (!list_empty(&dev->recv_list)) 42062306a36Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return mask; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic void vduse_dev_reset(struct vduse_dev *dev) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci int i; 43062306a36Sopenharmony_ci struct vduse_iova_domain *domain = dev->domain; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* The coherent mappings are handled in vduse_dev_free_coherent() */ 43362306a36Sopenharmony_ci if (domain && domain->bounce_map) 43462306a36Sopenharmony_ci vduse_domain_reset_bounce_map(domain); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci down_write(&dev->rwsem); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci dev->status = 0; 43962306a36Sopenharmony_ci dev->driver_features = 0; 44062306a36Sopenharmony_ci dev->generation++; 44162306a36Sopenharmony_ci spin_lock(&dev->irq_lock); 44262306a36Sopenharmony_ci dev->config_cb.callback = NULL; 44362306a36Sopenharmony_ci dev->config_cb.private = NULL; 44462306a36Sopenharmony_ci spin_unlock(&dev->irq_lock); 44562306a36Sopenharmony_ci flush_work(&dev->inject); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci for (i = 0; i < dev->vq_num; i++) { 44862306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[i]; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci vq->ready = false; 45162306a36Sopenharmony_ci vq->desc_addr = 0; 45262306a36Sopenharmony_ci vq->driver_addr = 0; 45362306a36Sopenharmony_ci vq->device_addr = 0; 45462306a36Sopenharmony_ci vq->num = 0; 45562306a36Sopenharmony_ci memset(&vq->state, 0, sizeof(vq->state)); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci spin_lock(&vq->kick_lock); 45862306a36Sopenharmony_ci vq->kicked = false; 45962306a36Sopenharmony_ci if (vq->kickfd) 46062306a36Sopenharmony_ci eventfd_ctx_put(vq->kickfd); 46162306a36Sopenharmony_ci vq->kickfd = NULL; 46262306a36Sopenharmony_ci spin_unlock(&vq->kick_lock); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci spin_lock(&vq->irq_lock); 46562306a36Sopenharmony_ci vq->cb.callback = NULL; 46662306a36Sopenharmony_ci vq->cb.private = NULL; 46762306a36Sopenharmony_ci vq->cb.trigger = NULL; 46862306a36Sopenharmony_ci spin_unlock(&vq->irq_lock); 46962306a36Sopenharmony_ci flush_work(&vq->inject); 47062306a36Sopenharmony_ci flush_work(&vq->kick); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci up_write(&dev->rwsem); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int vduse_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 idx, 47762306a36Sopenharmony_ci u64 desc_area, u64 driver_area, 47862306a36Sopenharmony_ci u64 device_area) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 48162306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[idx]; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci vq->desc_addr = desc_area; 48462306a36Sopenharmony_ci vq->driver_addr = driver_area; 48562306a36Sopenharmony_ci vq->device_addr = device_area; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void vduse_vq_kick(struct vduse_virtqueue *vq) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci spin_lock(&vq->kick_lock); 49362306a36Sopenharmony_ci if (!vq->ready) 49462306a36Sopenharmony_ci goto unlock; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (vq->kickfd) 49762306a36Sopenharmony_ci eventfd_signal(vq->kickfd, 1); 49862306a36Sopenharmony_ci else 49962306a36Sopenharmony_ci vq->kicked = true; 50062306a36Sopenharmony_ciunlock: 50162306a36Sopenharmony_ci spin_unlock(&vq->kick_lock); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic void vduse_vq_kick_work(struct work_struct *work) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct vduse_virtqueue *vq = container_of(work, 50762306a36Sopenharmony_ci struct vduse_virtqueue, kick); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci vduse_vq_kick(vq); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic void vduse_vdpa_kick_vq(struct vdpa_device *vdpa, u16 idx) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 51562306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[idx]; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!eventfd_signal_allowed()) { 51862306a36Sopenharmony_ci schedule_work(&vq->kick); 51962306a36Sopenharmony_ci return; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci vduse_vq_kick(vq); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic void vduse_vdpa_set_vq_cb(struct vdpa_device *vdpa, u16 idx, 52562306a36Sopenharmony_ci struct vdpa_callback *cb) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 52862306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[idx]; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci spin_lock(&vq->irq_lock); 53162306a36Sopenharmony_ci vq->cb.callback = cb->callback; 53262306a36Sopenharmony_ci vq->cb.private = cb->private; 53362306a36Sopenharmony_ci vq->cb.trigger = cb->trigger; 53462306a36Sopenharmony_ci spin_unlock(&vq->irq_lock); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic void vduse_vdpa_set_vq_num(struct vdpa_device *vdpa, u16 idx, u32 num) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 54062306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[idx]; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci vq->num = num; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic void vduse_vdpa_set_vq_ready(struct vdpa_device *vdpa, 54662306a36Sopenharmony_ci u16 idx, bool ready) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 54962306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[idx]; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci vq->ready = ready; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic bool vduse_vdpa_get_vq_ready(struct vdpa_device *vdpa, u16 idx) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 55762306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[idx]; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return vq->ready; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int vduse_vdpa_set_vq_state(struct vdpa_device *vdpa, u16 idx, 56362306a36Sopenharmony_ci const struct vdpa_vq_state *state) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 56662306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[idx]; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (dev->driver_features & BIT_ULL(VIRTIO_F_RING_PACKED)) { 56962306a36Sopenharmony_ci vq->state.packed.last_avail_counter = 57062306a36Sopenharmony_ci state->packed.last_avail_counter; 57162306a36Sopenharmony_ci vq->state.packed.last_avail_idx = state->packed.last_avail_idx; 57262306a36Sopenharmony_ci vq->state.packed.last_used_counter = 57362306a36Sopenharmony_ci state->packed.last_used_counter; 57462306a36Sopenharmony_ci vq->state.packed.last_used_idx = state->packed.last_used_idx; 57562306a36Sopenharmony_ci } else 57662306a36Sopenharmony_ci vq->state.split.avail_index = state->split.avail_index; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic int vduse_vdpa_get_vq_state(struct vdpa_device *vdpa, u16 idx, 58262306a36Sopenharmony_ci struct vdpa_vq_state *state) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 58562306a36Sopenharmony_ci struct vduse_virtqueue *vq = dev->vqs[idx]; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (dev->driver_features & BIT_ULL(VIRTIO_F_RING_PACKED)) 58862306a36Sopenharmony_ci return vduse_dev_get_vq_state_packed(dev, vq, &state->packed); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return vduse_dev_get_vq_state_split(dev, vq, &state->split); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic u32 vduse_vdpa_get_vq_align(struct vdpa_device *vdpa) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return dev->vq_align; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic u64 vduse_vdpa_get_device_features(struct vdpa_device *vdpa) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return dev->device_features; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int vduse_vdpa_set_driver_features(struct vdpa_device *vdpa, u64 features) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci dev->driver_features = features; 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic u64 vduse_vdpa_get_driver_features(struct vdpa_device *vdpa) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return dev->driver_features; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic void vduse_vdpa_set_config_cb(struct vdpa_device *vdpa, 62362306a36Sopenharmony_ci struct vdpa_callback *cb) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci spin_lock(&dev->irq_lock); 62862306a36Sopenharmony_ci dev->config_cb.callback = cb->callback; 62962306a36Sopenharmony_ci dev->config_cb.private = cb->private; 63062306a36Sopenharmony_ci spin_unlock(&dev->irq_lock); 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic u16 vduse_vdpa_get_vq_num_max(struct vdpa_device *vdpa) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 63662306a36Sopenharmony_ci u16 num_max = 0; 63762306a36Sopenharmony_ci int i; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci for (i = 0; i < dev->vq_num; i++) 64062306a36Sopenharmony_ci if (num_max < dev->vqs[i]->num_max) 64162306a36Sopenharmony_ci num_max = dev->vqs[i]->num_max; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return num_max; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic u32 vduse_vdpa_get_device_id(struct vdpa_device *vdpa) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return dev->device_id; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic u32 vduse_vdpa_get_vendor_id(struct vdpa_device *vdpa) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return dev->vendor_id; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic u8 vduse_vdpa_get_status(struct vdpa_device *vdpa) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return dev->status; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic void vduse_vdpa_set_status(struct vdpa_device *vdpa, u8 status) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (vduse_dev_set_status(dev, status)) 67262306a36Sopenharmony_ci return; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci dev->status = status; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic size_t vduse_vdpa_get_config_size(struct vdpa_device *vdpa) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return dev->config_size; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic void vduse_vdpa_get_config(struct vdpa_device *vdpa, unsigned int offset, 68562306a36Sopenharmony_ci void *buf, unsigned int len) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* Initialize the buffer in case of partial copy. */ 69062306a36Sopenharmony_ci memset(buf, 0, len); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (offset > dev->config_size) 69362306a36Sopenharmony_ci return; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (len > dev->config_size - offset) 69662306a36Sopenharmony_ci len = dev->config_size - offset; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci memcpy(buf, dev->config + offset, len); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic void vduse_vdpa_set_config(struct vdpa_device *vdpa, unsigned int offset, 70262306a36Sopenharmony_ci const void *buf, unsigned int len) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci /* Now we only support read-only configuration space */ 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int vduse_vdpa_reset(struct vdpa_device *vdpa) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 71062306a36Sopenharmony_ci int ret = vduse_dev_set_status(dev, 0); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci vduse_dev_reset(dev); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return ret; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic u32 vduse_vdpa_get_generation(struct vdpa_device *vdpa) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci return dev->generation; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic int vduse_vdpa_set_vq_affinity(struct vdpa_device *vdpa, u16 idx, 72562306a36Sopenharmony_ci const struct cpumask *cpu_mask) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (cpu_mask) 73062306a36Sopenharmony_ci cpumask_copy(&dev->vqs[idx]->irq_affinity, cpu_mask); 73162306a36Sopenharmony_ci else 73262306a36Sopenharmony_ci cpumask_setall(&dev->vqs[idx]->irq_affinity); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return 0; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic const struct cpumask * 73862306a36Sopenharmony_civduse_vdpa_get_vq_affinity(struct vdpa_device *vdpa, u16 idx) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return &dev->vqs[idx]->irq_affinity; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int vduse_vdpa_set_map(struct vdpa_device *vdpa, 74662306a36Sopenharmony_ci unsigned int asid, 74762306a36Sopenharmony_ci struct vhost_iotlb *iotlb) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 75062306a36Sopenharmony_ci int ret; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ret = vduse_domain_set_map(dev->domain, iotlb); 75362306a36Sopenharmony_ci if (ret) 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci ret = vduse_dev_update_iotlb(dev, 0ULL, ULLONG_MAX); 75762306a36Sopenharmony_ci if (ret) { 75862306a36Sopenharmony_ci vduse_domain_clear_map(dev->domain, iotlb); 75962306a36Sopenharmony_ci return ret; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return 0; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic void vduse_vdpa_free(struct vdpa_device *vdpa) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct vduse_dev *dev = vdpa_to_vduse(vdpa); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci dev->vdev = NULL; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic const struct vdpa_config_ops vduse_vdpa_config_ops = { 77362306a36Sopenharmony_ci .set_vq_address = vduse_vdpa_set_vq_address, 77462306a36Sopenharmony_ci .kick_vq = vduse_vdpa_kick_vq, 77562306a36Sopenharmony_ci .set_vq_cb = vduse_vdpa_set_vq_cb, 77662306a36Sopenharmony_ci .set_vq_num = vduse_vdpa_set_vq_num, 77762306a36Sopenharmony_ci .set_vq_ready = vduse_vdpa_set_vq_ready, 77862306a36Sopenharmony_ci .get_vq_ready = vduse_vdpa_get_vq_ready, 77962306a36Sopenharmony_ci .set_vq_state = vduse_vdpa_set_vq_state, 78062306a36Sopenharmony_ci .get_vq_state = vduse_vdpa_get_vq_state, 78162306a36Sopenharmony_ci .get_vq_align = vduse_vdpa_get_vq_align, 78262306a36Sopenharmony_ci .get_device_features = vduse_vdpa_get_device_features, 78362306a36Sopenharmony_ci .set_driver_features = vduse_vdpa_set_driver_features, 78462306a36Sopenharmony_ci .get_driver_features = vduse_vdpa_get_driver_features, 78562306a36Sopenharmony_ci .set_config_cb = vduse_vdpa_set_config_cb, 78662306a36Sopenharmony_ci .get_vq_num_max = vduse_vdpa_get_vq_num_max, 78762306a36Sopenharmony_ci .get_device_id = vduse_vdpa_get_device_id, 78862306a36Sopenharmony_ci .get_vendor_id = vduse_vdpa_get_vendor_id, 78962306a36Sopenharmony_ci .get_status = vduse_vdpa_get_status, 79062306a36Sopenharmony_ci .set_status = vduse_vdpa_set_status, 79162306a36Sopenharmony_ci .get_config_size = vduse_vdpa_get_config_size, 79262306a36Sopenharmony_ci .get_config = vduse_vdpa_get_config, 79362306a36Sopenharmony_ci .set_config = vduse_vdpa_set_config, 79462306a36Sopenharmony_ci .get_generation = vduse_vdpa_get_generation, 79562306a36Sopenharmony_ci .set_vq_affinity = vduse_vdpa_set_vq_affinity, 79662306a36Sopenharmony_ci .get_vq_affinity = vduse_vdpa_get_vq_affinity, 79762306a36Sopenharmony_ci .reset = vduse_vdpa_reset, 79862306a36Sopenharmony_ci .set_map = vduse_vdpa_set_map, 79962306a36Sopenharmony_ci .free = vduse_vdpa_free, 80062306a36Sopenharmony_ci}; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic dma_addr_t vduse_dev_map_page(struct device *dev, struct page *page, 80362306a36Sopenharmony_ci unsigned long offset, size_t size, 80462306a36Sopenharmony_ci enum dma_data_direction dir, 80562306a36Sopenharmony_ci unsigned long attrs) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct vduse_dev *vdev = dev_to_vduse(dev); 80862306a36Sopenharmony_ci struct vduse_iova_domain *domain = vdev->domain; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci return vduse_domain_map_page(domain, page, offset, size, dir, attrs); 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic void vduse_dev_unmap_page(struct device *dev, dma_addr_t dma_addr, 81462306a36Sopenharmony_ci size_t size, enum dma_data_direction dir, 81562306a36Sopenharmony_ci unsigned long attrs) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct vduse_dev *vdev = dev_to_vduse(dev); 81862306a36Sopenharmony_ci struct vduse_iova_domain *domain = vdev->domain; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return vduse_domain_unmap_page(domain, dma_addr, size, dir, attrs); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void *vduse_dev_alloc_coherent(struct device *dev, size_t size, 82462306a36Sopenharmony_ci dma_addr_t *dma_addr, gfp_t flag, 82562306a36Sopenharmony_ci unsigned long attrs) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct vduse_dev *vdev = dev_to_vduse(dev); 82862306a36Sopenharmony_ci struct vduse_iova_domain *domain = vdev->domain; 82962306a36Sopenharmony_ci unsigned long iova; 83062306a36Sopenharmony_ci void *addr; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci *dma_addr = DMA_MAPPING_ERROR; 83362306a36Sopenharmony_ci addr = vduse_domain_alloc_coherent(domain, size, 83462306a36Sopenharmony_ci (dma_addr_t *)&iova, flag, attrs); 83562306a36Sopenharmony_ci if (!addr) 83662306a36Sopenharmony_ci return NULL; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci *dma_addr = (dma_addr_t)iova; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return addr; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic void vduse_dev_free_coherent(struct device *dev, size_t size, 84462306a36Sopenharmony_ci void *vaddr, dma_addr_t dma_addr, 84562306a36Sopenharmony_ci unsigned long attrs) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct vduse_dev *vdev = dev_to_vduse(dev); 84862306a36Sopenharmony_ci struct vduse_iova_domain *domain = vdev->domain; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci vduse_domain_free_coherent(domain, size, vaddr, dma_addr, attrs); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic size_t vduse_dev_max_mapping_size(struct device *dev) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct vduse_dev *vdev = dev_to_vduse(dev); 85662306a36Sopenharmony_ci struct vduse_iova_domain *domain = vdev->domain; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return domain->bounce_size; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic const struct dma_map_ops vduse_dev_dma_ops = { 86262306a36Sopenharmony_ci .map_page = vduse_dev_map_page, 86362306a36Sopenharmony_ci .unmap_page = vduse_dev_unmap_page, 86462306a36Sopenharmony_ci .alloc = vduse_dev_alloc_coherent, 86562306a36Sopenharmony_ci .free = vduse_dev_free_coherent, 86662306a36Sopenharmony_ci .max_mapping_size = vduse_dev_max_mapping_size, 86762306a36Sopenharmony_ci}; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic unsigned int perm_to_file_flags(u8 perm) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci unsigned int flags = 0; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci switch (perm) { 87462306a36Sopenharmony_ci case VDUSE_ACCESS_WO: 87562306a36Sopenharmony_ci flags |= O_WRONLY; 87662306a36Sopenharmony_ci break; 87762306a36Sopenharmony_ci case VDUSE_ACCESS_RO: 87862306a36Sopenharmony_ci flags |= O_RDONLY; 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci case VDUSE_ACCESS_RW: 88162306a36Sopenharmony_ci flags |= O_RDWR; 88262306a36Sopenharmony_ci break; 88362306a36Sopenharmony_ci default: 88462306a36Sopenharmony_ci WARN(1, "invalidate vhost IOTLB permission\n"); 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return flags; 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int vduse_kickfd_setup(struct vduse_dev *dev, 89262306a36Sopenharmony_ci struct vduse_vq_eventfd *eventfd) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct eventfd_ctx *ctx = NULL; 89562306a36Sopenharmony_ci struct vduse_virtqueue *vq; 89662306a36Sopenharmony_ci u32 index; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (eventfd->index >= dev->vq_num) 89962306a36Sopenharmony_ci return -EINVAL; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci index = array_index_nospec(eventfd->index, dev->vq_num); 90262306a36Sopenharmony_ci vq = dev->vqs[index]; 90362306a36Sopenharmony_ci if (eventfd->fd >= 0) { 90462306a36Sopenharmony_ci ctx = eventfd_ctx_fdget(eventfd->fd); 90562306a36Sopenharmony_ci if (IS_ERR(ctx)) 90662306a36Sopenharmony_ci return PTR_ERR(ctx); 90762306a36Sopenharmony_ci } else if (eventfd->fd != VDUSE_EVENTFD_DEASSIGN) 90862306a36Sopenharmony_ci return 0; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci spin_lock(&vq->kick_lock); 91162306a36Sopenharmony_ci if (vq->kickfd) 91262306a36Sopenharmony_ci eventfd_ctx_put(vq->kickfd); 91362306a36Sopenharmony_ci vq->kickfd = ctx; 91462306a36Sopenharmony_ci if (vq->ready && vq->kicked && vq->kickfd) { 91562306a36Sopenharmony_ci eventfd_signal(vq->kickfd, 1); 91662306a36Sopenharmony_ci vq->kicked = false; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci spin_unlock(&vq->kick_lock); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci return 0; 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cistatic bool vduse_dev_is_ready(struct vduse_dev *dev) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci int i; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci for (i = 0; i < dev->vq_num; i++) 92862306a36Sopenharmony_ci if (!dev->vqs[i]->num_max) 92962306a36Sopenharmony_ci return false; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return true; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic void vduse_dev_irq_inject(struct work_struct *work) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct vduse_dev *dev = container_of(work, struct vduse_dev, inject); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci spin_lock_bh(&dev->irq_lock); 93962306a36Sopenharmony_ci if (dev->config_cb.callback) 94062306a36Sopenharmony_ci dev->config_cb.callback(dev->config_cb.private); 94162306a36Sopenharmony_ci spin_unlock_bh(&dev->irq_lock); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic void vduse_vq_irq_inject(struct work_struct *work) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct vduse_virtqueue *vq = container_of(work, 94762306a36Sopenharmony_ci struct vduse_virtqueue, inject); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci spin_lock_bh(&vq->irq_lock); 95062306a36Sopenharmony_ci if (vq->ready && vq->cb.callback) 95162306a36Sopenharmony_ci vq->cb.callback(vq->cb.private); 95262306a36Sopenharmony_ci spin_unlock_bh(&vq->irq_lock); 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic bool vduse_vq_signal_irqfd(struct vduse_virtqueue *vq) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci bool signal = false; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (!vq->cb.trigger) 96062306a36Sopenharmony_ci return false; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci spin_lock_irq(&vq->irq_lock); 96362306a36Sopenharmony_ci if (vq->ready && vq->cb.trigger) { 96462306a36Sopenharmony_ci eventfd_signal(vq->cb.trigger, 1); 96562306a36Sopenharmony_ci signal = true; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci spin_unlock_irq(&vq->irq_lock); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci return signal; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int vduse_dev_queue_irq_work(struct vduse_dev *dev, 97362306a36Sopenharmony_ci struct work_struct *irq_work, 97462306a36Sopenharmony_ci int irq_effective_cpu) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci int ret = -EINVAL; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci down_read(&dev->rwsem); 97962306a36Sopenharmony_ci if (!(dev->status & VIRTIO_CONFIG_S_DRIVER_OK)) 98062306a36Sopenharmony_ci goto unlock; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci ret = 0; 98362306a36Sopenharmony_ci if (irq_effective_cpu == IRQ_UNBOUND) 98462306a36Sopenharmony_ci queue_work(vduse_irq_wq, irq_work); 98562306a36Sopenharmony_ci else 98662306a36Sopenharmony_ci queue_work_on(irq_effective_cpu, 98762306a36Sopenharmony_ci vduse_irq_bound_wq, irq_work); 98862306a36Sopenharmony_ciunlock: 98962306a36Sopenharmony_ci up_read(&dev->rwsem); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci return ret; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int vduse_dev_dereg_umem(struct vduse_dev *dev, 99562306a36Sopenharmony_ci u64 iova, u64 size) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci int ret; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci mutex_lock(&dev->mem_lock); 100062306a36Sopenharmony_ci ret = -ENOENT; 100162306a36Sopenharmony_ci if (!dev->umem) 100262306a36Sopenharmony_ci goto unlock; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci ret = -EINVAL; 100562306a36Sopenharmony_ci if (!dev->domain) 100662306a36Sopenharmony_ci goto unlock; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (dev->umem->iova != iova || size != dev->domain->bounce_size) 100962306a36Sopenharmony_ci goto unlock; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci vduse_domain_remove_user_bounce_pages(dev->domain); 101262306a36Sopenharmony_ci unpin_user_pages_dirty_lock(dev->umem->pages, 101362306a36Sopenharmony_ci dev->umem->npages, true); 101462306a36Sopenharmony_ci atomic64_sub(dev->umem->npages, &dev->umem->mm->pinned_vm); 101562306a36Sopenharmony_ci mmdrop(dev->umem->mm); 101662306a36Sopenharmony_ci vfree(dev->umem->pages); 101762306a36Sopenharmony_ci kfree(dev->umem); 101862306a36Sopenharmony_ci dev->umem = NULL; 101962306a36Sopenharmony_ci ret = 0; 102062306a36Sopenharmony_ciunlock: 102162306a36Sopenharmony_ci mutex_unlock(&dev->mem_lock); 102262306a36Sopenharmony_ci return ret; 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic int vduse_dev_reg_umem(struct vduse_dev *dev, 102662306a36Sopenharmony_ci u64 iova, u64 uaddr, u64 size) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci struct page **page_list = NULL; 102962306a36Sopenharmony_ci struct vduse_umem *umem = NULL; 103062306a36Sopenharmony_ci long pinned = 0; 103162306a36Sopenharmony_ci unsigned long npages, lock_limit; 103262306a36Sopenharmony_ci int ret; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (!dev->domain || !dev->domain->bounce_map || 103562306a36Sopenharmony_ci size != dev->domain->bounce_size || 103662306a36Sopenharmony_ci iova != 0 || uaddr & ~PAGE_MASK) 103762306a36Sopenharmony_ci return -EINVAL; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci mutex_lock(&dev->mem_lock); 104062306a36Sopenharmony_ci ret = -EEXIST; 104162306a36Sopenharmony_ci if (dev->umem) 104262306a36Sopenharmony_ci goto unlock; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci ret = -ENOMEM; 104562306a36Sopenharmony_ci npages = size >> PAGE_SHIFT; 104662306a36Sopenharmony_ci page_list = __vmalloc(array_size(npages, sizeof(struct page *)), 104762306a36Sopenharmony_ci GFP_KERNEL_ACCOUNT); 104862306a36Sopenharmony_ci umem = kzalloc(sizeof(*umem), GFP_KERNEL); 104962306a36Sopenharmony_ci if (!page_list || !umem) 105062306a36Sopenharmony_ci goto unlock; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci mmap_read_lock(current->mm); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci lock_limit = PFN_DOWN(rlimit(RLIMIT_MEMLOCK)); 105562306a36Sopenharmony_ci if (npages + atomic64_read(¤t->mm->pinned_vm) > lock_limit) 105662306a36Sopenharmony_ci goto out; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci pinned = pin_user_pages(uaddr, npages, FOLL_LONGTERM | FOLL_WRITE, 105962306a36Sopenharmony_ci page_list); 106062306a36Sopenharmony_ci if (pinned != npages) { 106162306a36Sopenharmony_ci ret = pinned < 0 ? pinned : -ENOMEM; 106262306a36Sopenharmony_ci goto out; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci ret = vduse_domain_add_user_bounce_pages(dev->domain, 106662306a36Sopenharmony_ci page_list, pinned); 106762306a36Sopenharmony_ci if (ret) 106862306a36Sopenharmony_ci goto out; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci atomic64_add(npages, ¤t->mm->pinned_vm); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci umem->pages = page_list; 107362306a36Sopenharmony_ci umem->npages = pinned; 107462306a36Sopenharmony_ci umem->iova = iova; 107562306a36Sopenharmony_ci umem->mm = current->mm; 107662306a36Sopenharmony_ci mmgrab(current->mm); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci dev->umem = umem; 107962306a36Sopenharmony_ciout: 108062306a36Sopenharmony_ci if (ret && pinned > 0) 108162306a36Sopenharmony_ci unpin_user_pages(page_list, pinned); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci mmap_read_unlock(current->mm); 108462306a36Sopenharmony_ciunlock: 108562306a36Sopenharmony_ci if (ret) { 108662306a36Sopenharmony_ci vfree(page_list); 108762306a36Sopenharmony_ci kfree(umem); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci mutex_unlock(&dev->mem_lock); 109062306a36Sopenharmony_ci return ret; 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic void vduse_vq_update_effective_cpu(struct vduse_virtqueue *vq) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci int curr_cpu = vq->irq_effective_cpu; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci while (true) { 109862306a36Sopenharmony_ci curr_cpu = cpumask_next(curr_cpu, &vq->irq_affinity); 109962306a36Sopenharmony_ci if (cpu_online(curr_cpu)) 110062306a36Sopenharmony_ci break; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (curr_cpu >= nr_cpu_ids) 110362306a36Sopenharmony_ci curr_cpu = IRQ_UNBOUND; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci vq->irq_effective_cpu = curr_cpu; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic long vduse_dev_ioctl(struct file *file, unsigned int cmd, 111062306a36Sopenharmony_ci unsigned long arg) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci struct vduse_dev *dev = file->private_data; 111362306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 111462306a36Sopenharmony_ci int ret; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (unlikely(dev->broken)) 111762306a36Sopenharmony_ci return -EPERM; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci switch (cmd) { 112062306a36Sopenharmony_ci case VDUSE_IOTLB_GET_FD: { 112162306a36Sopenharmony_ci struct vduse_iotlb_entry entry; 112262306a36Sopenharmony_ci struct vhost_iotlb_map *map; 112362306a36Sopenharmony_ci struct vdpa_map_file *map_file; 112462306a36Sopenharmony_ci struct file *f = NULL; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci ret = -EFAULT; 112762306a36Sopenharmony_ci if (copy_from_user(&entry, argp, sizeof(entry))) 112862306a36Sopenharmony_ci break; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci ret = -EINVAL; 113162306a36Sopenharmony_ci if (entry.start > entry.last) 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci mutex_lock(&dev->domain_lock); 113562306a36Sopenharmony_ci if (!dev->domain) { 113662306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 113762306a36Sopenharmony_ci break; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci spin_lock(&dev->domain->iotlb_lock); 114062306a36Sopenharmony_ci map = vhost_iotlb_itree_first(dev->domain->iotlb, 114162306a36Sopenharmony_ci entry.start, entry.last); 114262306a36Sopenharmony_ci if (map) { 114362306a36Sopenharmony_ci map_file = (struct vdpa_map_file *)map->opaque; 114462306a36Sopenharmony_ci f = get_file(map_file->file); 114562306a36Sopenharmony_ci entry.offset = map_file->offset; 114662306a36Sopenharmony_ci entry.start = map->start; 114762306a36Sopenharmony_ci entry.last = map->last; 114862306a36Sopenharmony_ci entry.perm = map->perm; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci spin_unlock(&dev->domain->iotlb_lock); 115162306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 115262306a36Sopenharmony_ci ret = -EINVAL; 115362306a36Sopenharmony_ci if (!f) 115462306a36Sopenharmony_ci break; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci ret = -EFAULT; 115762306a36Sopenharmony_ci if (copy_to_user(argp, &entry, sizeof(entry))) { 115862306a36Sopenharmony_ci fput(f); 115962306a36Sopenharmony_ci break; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci ret = receive_fd(f, perm_to_file_flags(entry.perm)); 116262306a36Sopenharmony_ci fput(f); 116362306a36Sopenharmony_ci break; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci case VDUSE_DEV_GET_FEATURES: 116662306a36Sopenharmony_ci /* 116762306a36Sopenharmony_ci * Just mirror what driver wrote here. 116862306a36Sopenharmony_ci * The driver is expected to check FEATURE_OK later. 116962306a36Sopenharmony_ci */ 117062306a36Sopenharmony_ci ret = put_user(dev->driver_features, (u64 __user *)argp); 117162306a36Sopenharmony_ci break; 117262306a36Sopenharmony_ci case VDUSE_DEV_SET_CONFIG: { 117362306a36Sopenharmony_ci struct vduse_config_data config; 117462306a36Sopenharmony_ci unsigned long size = offsetof(struct vduse_config_data, 117562306a36Sopenharmony_ci buffer); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci ret = -EFAULT; 117862306a36Sopenharmony_ci if (copy_from_user(&config, argp, size)) 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci ret = -EINVAL; 118262306a36Sopenharmony_ci if (config.offset > dev->config_size || 118362306a36Sopenharmony_ci config.length == 0 || 118462306a36Sopenharmony_ci config.length > dev->config_size - config.offset) 118562306a36Sopenharmony_ci break; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci ret = -EFAULT; 118862306a36Sopenharmony_ci if (copy_from_user(dev->config + config.offset, argp + size, 118962306a36Sopenharmony_ci config.length)) 119062306a36Sopenharmony_ci break; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci ret = 0; 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci case VDUSE_DEV_INJECT_CONFIG_IRQ: 119662306a36Sopenharmony_ci ret = vduse_dev_queue_irq_work(dev, &dev->inject, IRQ_UNBOUND); 119762306a36Sopenharmony_ci break; 119862306a36Sopenharmony_ci case VDUSE_VQ_SETUP: { 119962306a36Sopenharmony_ci struct vduse_vq_config config; 120062306a36Sopenharmony_ci u32 index; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci ret = -EFAULT; 120362306a36Sopenharmony_ci if (copy_from_user(&config, argp, sizeof(config))) 120462306a36Sopenharmony_ci break; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci ret = -EINVAL; 120762306a36Sopenharmony_ci if (config.index >= dev->vq_num) 120862306a36Sopenharmony_ci break; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (!is_mem_zero((const char *)config.reserved, 121162306a36Sopenharmony_ci sizeof(config.reserved))) 121262306a36Sopenharmony_ci break; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci index = array_index_nospec(config.index, dev->vq_num); 121562306a36Sopenharmony_ci dev->vqs[index]->num_max = config.max_size; 121662306a36Sopenharmony_ci ret = 0; 121762306a36Sopenharmony_ci break; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci case VDUSE_VQ_GET_INFO: { 122062306a36Sopenharmony_ci struct vduse_vq_info vq_info; 122162306a36Sopenharmony_ci struct vduse_virtqueue *vq; 122262306a36Sopenharmony_ci u32 index; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci ret = -EFAULT; 122562306a36Sopenharmony_ci if (copy_from_user(&vq_info, argp, sizeof(vq_info))) 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci ret = -EINVAL; 122962306a36Sopenharmony_ci if (vq_info.index >= dev->vq_num) 123062306a36Sopenharmony_ci break; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci index = array_index_nospec(vq_info.index, dev->vq_num); 123362306a36Sopenharmony_ci vq = dev->vqs[index]; 123462306a36Sopenharmony_ci vq_info.desc_addr = vq->desc_addr; 123562306a36Sopenharmony_ci vq_info.driver_addr = vq->driver_addr; 123662306a36Sopenharmony_ci vq_info.device_addr = vq->device_addr; 123762306a36Sopenharmony_ci vq_info.num = vq->num; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (dev->driver_features & BIT_ULL(VIRTIO_F_RING_PACKED)) { 124062306a36Sopenharmony_ci vq_info.packed.last_avail_counter = 124162306a36Sopenharmony_ci vq->state.packed.last_avail_counter; 124262306a36Sopenharmony_ci vq_info.packed.last_avail_idx = 124362306a36Sopenharmony_ci vq->state.packed.last_avail_idx; 124462306a36Sopenharmony_ci vq_info.packed.last_used_counter = 124562306a36Sopenharmony_ci vq->state.packed.last_used_counter; 124662306a36Sopenharmony_ci vq_info.packed.last_used_idx = 124762306a36Sopenharmony_ci vq->state.packed.last_used_idx; 124862306a36Sopenharmony_ci } else 124962306a36Sopenharmony_ci vq_info.split.avail_index = 125062306a36Sopenharmony_ci vq->state.split.avail_index; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci vq_info.ready = vq->ready; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci ret = -EFAULT; 125562306a36Sopenharmony_ci if (copy_to_user(argp, &vq_info, sizeof(vq_info))) 125662306a36Sopenharmony_ci break; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci ret = 0; 125962306a36Sopenharmony_ci break; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci case VDUSE_VQ_SETUP_KICKFD: { 126262306a36Sopenharmony_ci struct vduse_vq_eventfd eventfd; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci ret = -EFAULT; 126562306a36Sopenharmony_ci if (copy_from_user(&eventfd, argp, sizeof(eventfd))) 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci ret = vduse_kickfd_setup(dev, &eventfd); 126962306a36Sopenharmony_ci break; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci case VDUSE_VQ_INJECT_IRQ: { 127262306a36Sopenharmony_ci u32 index; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci ret = -EFAULT; 127562306a36Sopenharmony_ci if (get_user(index, (u32 __user *)argp)) 127662306a36Sopenharmony_ci break; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci ret = -EINVAL; 127962306a36Sopenharmony_ci if (index >= dev->vq_num) 128062306a36Sopenharmony_ci break; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci ret = 0; 128362306a36Sopenharmony_ci index = array_index_nospec(index, dev->vq_num); 128462306a36Sopenharmony_ci if (!vduse_vq_signal_irqfd(dev->vqs[index])) { 128562306a36Sopenharmony_ci vduse_vq_update_effective_cpu(dev->vqs[index]); 128662306a36Sopenharmony_ci ret = vduse_dev_queue_irq_work(dev, 128762306a36Sopenharmony_ci &dev->vqs[index]->inject, 128862306a36Sopenharmony_ci dev->vqs[index]->irq_effective_cpu); 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci break; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci case VDUSE_IOTLB_REG_UMEM: { 129362306a36Sopenharmony_ci struct vduse_iova_umem umem; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci ret = -EFAULT; 129662306a36Sopenharmony_ci if (copy_from_user(&umem, argp, sizeof(umem))) 129762306a36Sopenharmony_ci break; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci ret = -EINVAL; 130062306a36Sopenharmony_ci if (!is_mem_zero((const char *)umem.reserved, 130162306a36Sopenharmony_ci sizeof(umem.reserved))) 130262306a36Sopenharmony_ci break; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci mutex_lock(&dev->domain_lock); 130562306a36Sopenharmony_ci ret = vduse_dev_reg_umem(dev, umem.iova, 130662306a36Sopenharmony_ci umem.uaddr, umem.size); 130762306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 130862306a36Sopenharmony_ci break; 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci case VDUSE_IOTLB_DEREG_UMEM: { 131162306a36Sopenharmony_ci struct vduse_iova_umem umem; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci ret = -EFAULT; 131462306a36Sopenharmony_ci if (copy_from_user(&umem, argp, sizeof(umem))) 131562306a36Sopenharmony_ci break; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci ret = -EINVAL; 131862306a36Sopenharmony_ci if (!is_mem_zero((const char *)umem.reserved, 131962306a36Sopenharmony_ci sizeof(umem.reserved))) 132062306a36Sopenharmony_ci break; 132162306a36Sopenharmony_ci mutex_lock(&dev->domain_lock); 132262306a36Sopenharmony_ci ret = vduse_dev_dereg_umem(dev, umem.iova, 132362306a36Sopenharmony_ci umem.size); 132462306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 132562306a36Sopenharmony_ci break; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci case VDUSE_IOTLB_GET_INFO: { 132862306a36Sopenharmony_ci struct vduse_iova_info info; 132962306a36Sopenharmony_ci struct vhost_iotlb_map *map; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci ret = -EFAULT; 133262306a36Sopenharmony_ci if (copy_from_user(&info, argp, sizeof(info))) 133362306a36Sopenharmony_ci break; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci ret = -EINVAL; 133662306a36Sopenharmony_ci if (info.start > info.last) 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (!is_mem_zero((const char *)info.reserved, 134062306a36Sopenharmony_ci sizeof(info.reserved))) 134162306a36Sopenharmony_ci break; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci mutex_lock(&dev->domain_lock); 134462306a36Sopenharmony_ci if (!dev->domain) { 134562306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 134662306a36Sopenharmony_ci break; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci spin_lock(&dev->domain->iotlb_lock); 134962306a36Sopenharmony_ci map = vhost_iotlb_itree_first(dev->domain->iotlb, 135062306a36Sopenharmony_ci info.start, info.last); 135162306a36Sopenharmony_ci if (map) { 135262306a36Sopenharmony_ci info.start = map->start; 135362306a36Sopenharmony_ci info.last = map->last; 135462306a36Sopenharmony_ci info.capability = 0; 135562306a36Sopenharmony_ci if (dev->domain->bounce_map && map->start == 0 && 135662306a36Sopenharmony_ci map->last == dev->domain->bounce_size - 1) 135762306a36Sopenharmony_ci info.capability |= VDUSE_IOVA_CAP_UMEM; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci spin_unlock(&dev->domain->iotlb_lock); 136062306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 136162306a36Sopenharmony_ci if (!map) 136262306a36Sopenharmony_ci break; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci ret = -EFAULT; 136562306a36Sopenharmony_ci if (copy_to_user(argp, &info, sizeof(info))) 136662306a36Sopenharmony_ci break; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci ret = 0; 136962306a36Sopenharmony_ci break; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci default: 137262306a36Sopenharmony_ci ret = -ENOIOCTLCMD; 137362306a36Sopenharmony_ci break; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci return ret; 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic int vduse_dev_release(struct inode *inode, struct file *file) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct vduse_dev *dev = file->private_data; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci mutex_lock(&dev->domain_lock); 138462306a36Sopenharmony_ci if (dev->domain) 138562306a36Sopenharmony_ci vduse_dev_dereg_umem(dev, 0, dev->domain->bounce_size); 138662306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 138762306a36Sopenharmony_ci spin_lock(&dev->msg_lock); 138862306a36Sopenharmony_ci /* Make sure the inflight messages can processed after reconncection */ 138962306a36Sopenharmony_ci list_splice_init(&dev->recv_list, &dev->send_list); 139062306a36Sopenharmony_ci spin_unlock(&dev->msg_lock); 139162306a36Sopenharmony_ci dev->connected = false; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci return 0; 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_cistatic struct vduse_dev *vduse_dev_get_from_minor(int minor) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct vduse_dev *dev; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci mutex_lock(&vduse_lock); 140162306a36Sopenharmony_ci dev = idr_find(&vduse_idr, minor); 140262306a36Sopenharmony_ci mutex_unlock(&vduse_lock); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci return dev; 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic int vduse_dev_open(struct inode *inode, struct file *file) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci int ret; 141062306a36Sopenharmony_ci struct vduse_dev *dev = vduse_dev_get_from_minor(iminor(inode)); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (!dev) 141362306a36Sopenharmony_ci return -ENODEV; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci ret = -EBUSY; 141662306a36Sopenharmony_ci mutex_lock(&dev->lock); 141762306a36Sopenharmony_ci if (dev->connected) 141862306a36Sopenharmony_ci goto unlock; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci ret = 0; 142162306a36Sopenharmony_ci dev->connected = true; 142262306a36Sopenharmony_ci file->private_data = dev; 142362306a36Sopenharmony_ciunlock: 142462306a36Sopenharmony_ci mutex_unlock(&dev->lock); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci return ret; 142762306a36Sopenharmony_ci} 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_cistatic const struct file_operations vduse_dev_fops = { 143062306a36Sopenharmony_ci .owner = THIS_MODULE, 143162306a36Sopenharmony_ci .open = vduse_dev_open, 143262306a36Sopenharmony_ci .release = vduse_dev_release, 143362306a36Sopenharmony_ci .read_iter = vduse_dev_read_iter, 143462306a36Sopenharmony_ci .write_iter = vduse_dev_write_iter, 143562306a36Sopenharmony_ci .poll = vduse_dev_poll, 143662306a36Sopenharmony_ci .unlocked_ioctl = vduse_dev_ioctl, 143762306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 143862306a36Sopenharmony_ci .llseek = noop_llseek, 143962306a36Sopenharmony_ci}; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cistatic ssize_t irq_cb_affinity_show(struct vduse_virtqueue *vq, char *buf) 144262306a36Sopenharmony_ci{ 144362306a36Sopenharmony_ci return sprintf(buf, "%*pb\n", cpumask_pr_args(&vq->irq_affinity)); 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic ssize_t irq_cb_affinity_store(struct vduse_virtqueue *vq, 144762306a36Sopenharmony_ci const char *buf, size_t count) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci cpumask_var_t new_value; 145062306a36Sopenharmony_ci int ret; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (!zalloc_cpumask_var(&new_value, GFP_KERNEL)) 145362306a36Sopenharmony_ci return -ENOMEM; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci ret = cpumask_parse(buf, new_value); 145662306a36Sopenharmony_ci if (ret) 145762306a36Sopenharmony_ci goto free_mask; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci ret = -EINVAL; 146062306a36Sopenharmony_ci if (!cpumask_intersects(new_value, cpu_online_mask)) 146162306a36Sopenharmony_ci goto free_mask; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci cpumask_copy(&vq->irq_affinity, new_value); 146462306a36Sopenharmony_ci ret = count; 146562306a36Sopenharmony_cifree_mask: 146662306a36Sopenharmony_ci free_cpumask_var(new_value); 146762306a36Sopenharmony_ci return ret; 146862306a36Sopenharmony_ci} 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_cistruct vq_sysfs_entry { 147162306a36Sopenharmony_ci struct attribute attr; 147262306a36Sopenharmony_ci ssize_t (*show)(struct vduse_virtqueue *vq, char *buf); 147362306a36Sopenharmony_ci ssize_t (*store)(struct vduse_virtqueue *vq, const char *buf, 147462306a36Sopenharmony_ci size_t count); 147562306a36Sopenharmony_ci}; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic struct vq_sysfs_entry irq_cb_affinity_attr = __ATTR_RW(irq_cb_affinity); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistatic struct attribute *vq_attrs[] = { 148062306a36Sopenharmony_ci &irq_cb_affinity_attr.attr, 148162306a36Sopenharmony_ci NULL, 148262306a36Sopenharmony_ci}; 148362306a36Sopenharmony_ciATTRIBUTE_GROUPS(vq); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_cistatic ssize_t vq_attr_show(struct kobject *kobj, struct attribute *attr, 148662306a36Sopenharmony_ci char *buf) 148762306a36Sopenharmony_ci{ 148862306a36Sopenharmony_ci struct vduse_virtqueue *vq = container_of(kobj, 148962306a36Sopenharmony_ci struct vduse_virtqueue, kobj); 149062306a36Sopenharmony_ci struct vq_sysfs_entry *entry = container_of(attr, 149162306a36Sopenharmony_ci struct vq_sysfs_entry, attr); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci if (!entry->show) 149462306a36Sopenharmony_ci return -EIO; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci return entry->show(vq, buf); 149762306a36Sopenharmony_ci} 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_cistatic ssize_t vq_attr_store(struct kobject *kobj, struct attribute *attr, 150062306a36Sopenharmony_ci const char *buf, size_t count) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci struct vduse_virtqueue *vq = container_of(kobj, 150362306a36Sopenharmony_ci struct vduse_virtqueue, kobj); 150462306a36Sopenharmony_ci struct vq_sysfs_entry *entry = container_of(attr, 150562306a36Sopenharmony_ci struct vq_sysfs_entry, attr); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci if (!entry->store) 150862306a36Sopenharmony_ci return -EIO; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci return entry->store(vq, buf, count); 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic const struct sysfs_ops vq_sysfs_ops = { 151462306a36Sopenharmony_ci .show = vq_attr_show, 151562306a36Sopenharmony_ci .store = vq_attr_store, 151662306a36Sopenharmony_ci}; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_cistatic void vq_release(struct kobject *kobj) 151962306a36Sopenharmony_ci{ 152062306a36Sopenharmony_ci struct vduse_virtqueue *vq = container_of(kobj, 152162306a36Sopenharmony_ci struct vduse_virtqueue, kobj); 152262306a36Sopenharmony_ci kfree(vq); 152362306a36Sopenharmony_ci} 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_cistatic const struct kobj_type vq_type = { 152662306a36Sopenharmony_ci .release = vq_release, 152762306a36Sopenharmony_ci .sysfs_ops = &vq_sysfs_ops, 152862306a36Sopenharmony_ci .default_groups = vq_groups, 152962306a36Sopenharmony_ci}; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_cistatic void vduse_dev_deinit_vqs(struct vduse_dev *dev) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci int i; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci if (!dev->vqs) 153662306a36Sopenharmony_ci return; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci for (i = 0; i < dev->vq_num; i++) 153962306a36Sopenharmony_ci kobject_put(&dev->vqs[i]->kobj); 154062306a36Sopenharmony_ci kfree(dev->vqs); 154162306a36Sopenharmony_ci} 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_cistatic int vduse_dev_init_vqs(struct vduse_dev *dev, u32 vq_align, u32 vq_num) 154462306a36Sopenharmony_ci{ 154562306a36Sopenharmony_ci int ret, i; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci dev->vq_align = vq_align; 154862306a36Sopenharmony_ci dev->vq_num = vq_num; 154962306a36Sopenharmony_ci dev->vqs = kcalloc(dev->vq_num, sizeof(*dev->vqs), GFP_KERNEL); 155062306a36Sopenharmony_ci if (!dev->vqs) 155162306a36Sopenharmony_ci return -ENOMEM; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci for (i = 0; i < vq_num; i++) { 155462306a36Sopenharmony_ci dev->vqs[i] = kzalloc(sizeof(*dev->vqs[i]), GFP_KERNEL); 155562306a36Sopenharmony_ci if (!dev->vqs[i]) { 155662306a36Sopenharmony_ci ret = -ENOMEM; 155762306a36Sopenharmony_ci goto err; 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci dev->vqs[i]->index = i; 156162306a36Sopenharmony_ci dev->vqs[i]->irq_effective_cpu = IRQ_UNBOUND; 156262306a36Sopenharmony_ci INIT_WORK(&dev->vqs[i]->inject, vduse_vq_irq_inject); 156362306a36Sopenharmony_ci INIT_WORK(&dev->vqs[i]->kick, vduse_vq_kick_work); 156462306a36Sopenharmony_ci spin_lock_init(&dev->vqs[i]->kick_lock); 156562306a36Sopenharmony_ci spin_lock_init(&dev->vqs[i]->irq_lock); 156662306a36Sopenharmony_ci cpumask_setall(&dev->vqs[i]->irq_affinity); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci kobject_init(&dev->vqs[i]->kobj, &vq_type); 156962306a36Sopenharmony_ci ret = kobject_add(&dev->vqs[i]->kobj, 157062306a36Sopenharmony_ci &dev->dev->kobj, "vq%d", i); 157162306a36Sopenharmony_ci if (ret) { 157262306a36Sopenharmony_ci kfree(dev->vqs[i]); 157362306a36Sopenharmony_ci goto err; 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci return 0; 157862306a36Sopenharmony_cierr: 157962306a36Sopenharmony_ci while (i--) 158062306a36Sopenharmony_ci kobject_put(&dev->vqs[i]->kobj); 158162306a36Sopenharmony_ci kfree(dev->vqs); 158262306a36Sopenharmony_ci dev->vqs = NULL; 158362306a36Sopenharmony_ci return ret; 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_cistatic struct vduse_dev *vduse_dev_create(void) 158762306a36Sopenharmony_ci{ 158862306a36Sopenharmony_ci struct vduse_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (!dev) 159162306a36Sopenharmony_ci return NULL; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci mutex_init(&dev->lock); 159462306a36Sopenharmony_ci mutex_init(&dev->mem_lock); 159562306a36Sopenharmony_ci mutex_init(&dev->domain_lock); 159662306a36Sopenharmony_ci spin_lock_init(&dev->msg_lock); 159762306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->send_list); 159862306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->recv_list); 159962306a36Sopenharmony_ci spin_lock_init(&dev->irq_lock); 160062306a36Sopenharmony_ci init_rwsem(&dev->rwsem); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci INIT_WORK(&dev->inject, vduse_dev_irq_inject); 160362306a36Sopenharmony_ci init_waitqueue_head(&dev->waitq); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci return dev; 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic void vduse_dev_destroy(struct vduse_dev *dev) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci kfree(dev); 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic struct vduse_dev *vduse_find_dev(const char *name) 161462306a36Sopenharmony_ci{ 161562306a36Sopenharmony_ci struct vduse_dev *dev; 161662306a36Sopenharmony_ci int id; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci idr_for_each_entry(&vduse_idr, dev, id) 161962306a36Sopenharmony_ci if (!strcmp(dev->name, name)) 162062306a36Sopenharmony_ci return dev; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci return NULL; 162362306a36Sopenharmony_ci} 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_cistatic int vduse_destroy_dev(char *name) 162662306a36Sopenharmony_ci{ 162762306a36Sopenharmony_ci struct vduse_dev *dev = vduse_find_dev(name); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci if (!dev) 163062306a36Sopenharmony_ci return -EINVAL; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci mutex_lock(&dev->lock); 163362306a36Sopenharmony_ci if (dev->vdev || dev->connected) { 163462306a36Sopenharmony_ci mutex_unlock(&dev->lock); 163562306a36Sopenharmony_ci return -EBUSY; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci dev->connected = true; 163862306a36Sopenharmony_ci mutex_unlock(&dev->lock); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci vduse_dev_reset(dev); 164162306a36Sopenharmony_ci device_destroy(vduse_class, MKDEV(MAJOR(vduse_major), dev->minor)); 164262306a36Sopenharmony_ci idr_remove(&vduse_idr, dev->minor); 164362306a36Sopenharmony_ci kvfree(dev->config); 164462306a36Sopenharmony_ci vduse_dev_deinit_vqs(dev); 164562306a36Sopenharmony_ci if (dev->domain) 164662306a36Sopenharmony_ci vduse_domain_destroy(dev->domain); 164762306a36Sopenharmony_ci kfree(dev->name); 164862306a36Sopenharmony_ci vduse_dev_destroy(dev); 164962306a36Sopenharmony_ci module_put(THIS_MODULE); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci return 0; 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic bool device_is_allowed(u32 device_id) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci int i; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(allowed_device_id); i++) 165962306a36Sopenharmony_ci if (allowed_device_id[i] == device_id) 166062306a36Sopenharmony_ci return true; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci return false; 166362306a36Sopenharmony_ci} 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_cistatic bool features_is_valid(u64 features) 166662306a36Sopenharmony_ci{ 166762306a36Sopenharmony_ci if (!(features & (1ULL << VIRTIO_F_ACCESS_PLATFORM))) 166862306a36Sopenharmony_ci return false; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci /* Now we only support read-only configuration space */ 167162306a36Sopenharmony_ci if (features & (1ULL << VIRTIO_BLK_F_CONFIG_WCE)) 167262306a36Sopenharmony_ci return false; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci return true; 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic bool vduse_validate_config(struct vduse_dev_config *config) 167862306a36Sopenharmony_ci{ 167962306a36Sopenharmony_ci if (!is_mem_zero((const char *)config->reserved, 168062306a36Sopenharmony_ci sizeof(config->reserved))) 168162306a36Sopenharmony_ci return false; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (config->vq_align > PAGE_SIZE) 168462306a36Sopenharmony_ci return false; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (config->config_size > PAGE_SIZE) 168762306a36Sopenharmony_ci return false; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci if (config->vq_num > 0xffff) 169062306a36Sopenharmony_ci return false; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (!config->name[0]) 169362306a36Sopenharmony_ci return false; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (!device_is_allowed(config->device_id)) 169662306a36Sopenharmony_ci return false; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (!features_is_valid(config->features)) 169962306a36Sopenharmony_ci return false; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci return true; 170262306a36Sopenharmony_ci} 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cistatic ssize_t msg_timeout_show(struct device *device, 170562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci struct vduse_dev *dev = dev_get_drvdata(device); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", dev->msg_timeout); 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_cistatic ssize_t msg_timeout_store(struct device *device, 171362306a36Sopenharmony_ci struct device_attribute *attr, 171462306a36Sopenharmony_ci const char *buf, size_t count) 171562306a36Sopenharmony_ci{ 171662306a36Sopenharmony_ci struct vduse_dev *dev = dev_get_drvdata(device); 171762306a36Sopenharmony_ci int ret; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &dev->msg_timeout); 172062306a36Sopenharmony_ci if (ret < 0) 172162306a36Sopenharmony_ci return ret; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci return count; 172462306a36Sopenharmony_ci} 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(msg_timeout); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_cistatic ssize_t bounce_size_show(struct device *device, 172962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 173062306a36Sopenharmony_ci{ 173162306a36Sopenharmony_ci struct vduse_dev *dev = dev_get_drvdata(device); 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", dev->bounce_size); 173462306a36Sopenharmony_ci} 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_cistatic ssize_t bounce_size_store(struct device *device, 173762306a36Sopenharmony_ci struct device_attribute *attr, 173862306a36Sopenharmony_ci const char *buf, size_t count) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci struct vduse_dev *dev = dev_get_drvdata(device); 174162306a36Sopenharmony_ci unsigned int bounce_size; 174262306a36Sopenharmony_ci int ret; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci ret = -EPERM; 174562306a36Sopenharmony_ci mutex_lock(&dev->domain_lock); 174662306a36Sopenharmony_ci if (dev->domain) 174762306a36Sopenharmony_ci goto unlock; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &bounce_size); 175062306a36Sopenharmony_ci if (ret < 0) 175162306a36Sopenharmony_ci goto unlock; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci ret = -EINVAL; 175462306a36Sopenharmony_ci if (bounce_size > VDUSE_MAX_BOUNCE_SIZE || 175562306a36Sopenharmony_ci bounce_size < VDUSE_MIN_BOUNCE_SIZE) 175662306a36Sopenharmony_ci goto unlock; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci dev->bounce_size = bounce_size & PAGE_MASK; 175962306a36Sopenharmony_ci ret = count; 176062306a36Sopenharmony_ciunlock: 176162306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 176262306a36Sopenharmony_ci return ret; 176362306a36Sopenharmony_ci} 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(bounce_size); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_cistatic struct attribute *vduse_dev_attrs[] = { 176862306a36Sopenharmony_ci &dev_attr_msg_timeout.attr, 176962306a36Sopenharmony_ci &dev_attr_bounce_size.attr, 177062306a36Sopenharmony_ci NULL 177162306a36Sopenharmony_ci}; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ciATTRIBUTE_GROUPS(vduse_dev); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_cistatic int vduse_create_dev(struct vduse_dev_config *config, 177662306a36Sopenharmony_ci void *config_buf, u64 api_version) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci int ret; 177962306a36Sopenharmony_ci struct vduse_dev *dev; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci ret = -EEXIST; 178262306a36Sopenharmony_ci if (vduse_find_dev(config->name)) 178362306a36Sopenharmony_ci goto err; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci ret = -ENOMEM; 178662306a36Sopenharmony_ci dev = vduse_dev_create(); 178762306a36Sopenharmony_ci if (!dev) 178862306a36Sopenharmony_ci goto err; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci dev->api_version = api_version; 179162306a36Sopenharmony_ci dev->device_features = config->features; 179262306a36Sopenharmony_ci dev->device_id = config->device_id; 179362306a36Sopenharmony_ci dev->vendor_id = config->vendor_id; 179462306a36Sopenharmony_ci dev->name = kstrdup(config->name, GFP_KERNEL); 179562306a36Sopenharmony_ci if (!dev->name) 179662306a36Sopenharmony_ci goto err_str; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci dev->bounce_size = VDUSE_BOUNCE_SIZE; 179962306a36Sopenharmony_ci dev->config = config_buf; 180062306a36Sopenharmony_ci dev->config_size = config->config_size; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci ret = idr_alloc(&vduse_idr, dev, 1, VDUSE_DEV_MAX, GFP_KERNEL); 180362306a36Sopenharmony_ci if (ret < 0) 180462306a36Sopenharmony_ci goto err_idr; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci dev->minor = ret; 180762306a36Sopenharmony_ci dev->msg_timeout = VDUSE_MSG_DEFAULT_TIMEOUT; 180862306a36Sopenharmony_ci dev->dev = device_create_with_groups(vduse_class, NULL, 180962306a36Sopenharmony_ci MKDEV(MAJOR(vduse_major), dev->minor), 181062306a36Sopenharmony_ci dev, vduse_dev_groups, "%s", config->name); 181162306a36Sopenharmony_ci if (IS_ERR(dev->dev)) { 181262306a36Sopenharmony_ci ret = PTR_ERR(dev->dev); 181362306a36Sopenharmony_ci goto err_dev; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci ret = vduse_dev_init_vqs(dev, config->vq_align, config->vq_num); 181762306a36Sopenharmony_ci if (ret) 181862306a36Sopenharmony_ci goto err_vqs; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci __module_get(THIS_MODULE); 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci return 0; 182362306a36Sopenharmony_cierr_vqs: 182462306a36Sopenharmony_ci device_destroy(vduse_class, MKDEV(MAJOR(vduse_major), dev->minor)); 182562306a36Sopenharmony_cierr_dev: 182662306a36Sopenharmony_ci idr_remove(&vduse_idr, dev->minor); 182762306a36Sopenharmony_cierr_idr: 182862306a36Sopenharmony_ci kfree(dev->name); 182962306a36Sopenharmony_cierr_str: 183062306a36Sopenharmony_ci vduse_dev_destroy(dev); 183162306a36Sopenharmony_cierr: 183262306a36Sopenharmony_ci return ret; 183362306a36Sopenharmony_ci} 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_cistatic long vduse_ioctl(struct file *file, unsigned int cmd, 183662306a36Sopenharmony_ci unsigned long arg) 183762306a36Sopenharmony_ci{ 183862306a36Sopenharmony_ci int ret; 183962306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 184062306a36Sopenharmony_ci struct vduse_control *control = file->private_data; 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci mutex_lock(&vduse_lock); 184362306a36Sopenharmony_ci switch (cmd) { 184462306a36Sopenharmony_ci case VDUSE_GET_API_VERSION: 184562306a36Sopenharmony_ci ret = put_user(control->api_version, (u64 __user *)argp); 184662306a36Sopenharmony_ci break; 184762306a36Sopenharmony_ci case VDUSE_SET_API_VERSION: { 184862306a36Sopenharmony_ci u64 api_version; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci ret = -EFAULT; 185162306a36Sopenharmony_ci if (get_user(api_version, (u64 __user *)argp)) 185262306a36Sopenharmony_ci break; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci ret = -EINVAL; 185562306a36Sopenharmony_ci if (api_version > VDUSE_API_VERSION) 185662306a36Sopenharmony_ci break; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci ret = 0; 185962306a36Sopenharmony_ci control->api_version = api_version; 186062306a36Sopenharmony_ci break; 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci case VDUSE_CREATE_DEV: { 186362306a36Sopenharmony_ci struct vduse_dev_config config; 186462306a36Sopenharmony_ci unsigned long size = offsetof(struct vduse_dev_config, config); 186562306a36Sopenharmony_ci void *buf; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci ret = -EFAULT; 186862306a36Sopenharmony_ci if (copy_from_user(&config, argp, size)) 186962306a36Sopenharmony_ci break; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci ret = -EINVAL; 187262306a36Sopenharmony_ci if (vduse_validate_config(&config) == false) 187362306a36Sopenharmony_ci break; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci buf = vmemdup_user(argp + size, config.config_size); 187662306a36Sopenharmony_ci if (IS_ERR(buf)) { 187762306a36Sopenharmony_ci ret = PTR_ERR(buf); 187862306a36Sopenharmony_ci break; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci config.name[VDUSE_NAME_MAX - 1] = '\0'; 188162306a36Sopenharmony_ci ret = vduse_create_dev(&config, buf, control->api_version); 188262306a36Sopenharmony_ci if (ret) 188362306a36Sopenharmony_ci kvfree(buf); 188462306a36Sopenharmony_ci break; 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci case VDUSE_DESTROY_DEV: { 188762306a36Sopenharmony_ci char name[VDUSE_NAME_MAX]; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci ret = -EFAULT; 189062306a36Sopenharmony_ci if (copy_from_user(name, argp, VDUSE_NAME_MAX)) 189162306a36Sopenharmony_ci break; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci name[VDUSE_NAME_MAX - 1] = '\0'; 189462306a36Sopenharmony_ci ret = vduse_destroy_dev(name); 189562306a36Sopenharmony_ci break; 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci default: 189862306a36Sopenharmony_ci ret = -EINVAL; 189962306a36Sopenharmony_ci break; 190062306a36Sopenharmony_ci } 190162306a36Sopenharmony_ci mutex_unlock(&vduse_lock); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci return ret; 190462306a36Sopenharmony_ci} 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_cistatic int vduse_release(struct inode *inode, struct file *file) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct vduse_control *control = file->private_data; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci kfree(control); 191162306a36Sopenharmony_ci return 0; 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic int vduse_open(struct inode *inode, struct file *file) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci struct vduse_control *control; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci control = kmalloc(sizeof(struct vduse_control), GFP_KERNEL); 191962306a36Sopenharmony_ci if (!control) 192062306a36Sopenharmony_ci return -ENOMEM; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci control->api_version = VDUSE_API_VERSION; 192362306a36Sopenharmony_ci file->private_data = control; 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci return 0; 192662306a36Sopenharmony_ci} 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic const struct file_operations vduse_ctrl_fops = { 192962306a36Sopenharmony_ci .owner = THIS_MODULE, 193062306a36Sopenharmony_ci .open = vduse_open, 193162306a36Sopenharmony_ci .release = vduse_release, 193262306a36Sopenharmony_ci .unlocked_ioctl = vduse_ioctl, 193362306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 193462306a36Sopenharmony_ci .llseek = noop_llseek, 193562306a36Sopenharmony_ci}; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic char *vduse_devnode(const struct device *dev, umode_t *mode) 193862306a36Sopenharmony_ci{ 193962306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "vduse/%s", dev_name(dev)); 194062306a36Sopenharmony_ci} 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_cistruct vduse_mgmt_dev { 194362306a36Sopenharmony_ci struct vdpa_mgmt_dev mgmt_dev; 194462306a36Sopenharmony_ci struct device dev; 194562306a36Sopenharmony_ci}; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_cistatic struct vduse_mgmt_dev *vduse_mgmt; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_cistatic int vduse_dev_init_vdpa(struct vduse_dev *dev, const char *name) 195062306a36Sopenharmony_ci{ 195162306a36Sopenharmony_ci struct vduse_vdpa *vdev; 195262306a36Sopenharmony_ci int ret; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci if (dev->vdev) 195562306a36Sopenharmony_ci return -EEXIST; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci vdev = vdpa_alloc_device(struct vduse_vdpa, vdpa, dev->dev, 195862306a36Sopenharmony_ci &vduse_vdpa_config_ops, 1, 1, name, true); 195962306a36Sopenharmony_ci if (IS_ERR(vdev)) 196062306a36Sopenharmony_ci return PTR_ERR(vdev); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci dev->vdev = vdev; 196362306a36Sopenharmony_ci vdev->dev = dev; 196462306a36Sopenharmony_ci vdev->vdpa.dev.dma_mask = &vdev->vdpa.dev.coherent_dma_mask; 196562306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&vdev->vdpa.dev, DMA_BIT_MASK(64)); 196662306a36Sopenharmony_ci if (ret) { 196762306a36Sopenharmony_ci put_device(&vdev->vdpa.dev); 196862306a36Sopenharmony_ci return ret; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci set_dma_ops(&vdev->vdpa.dev, &vduse_dev_dma_ops); 197162306a36Sopenharmony_ci vdev->vdpa.dma_dev = &vdev->vdpa.dev; 197262306a36Sopenharmony_ci vdev->vdpa.mdev = &vduse_mgmt->mgmt_dev; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci return 0; 197562306a36Sopenharmony_ci} 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_cistatic int vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, 197862306a36Sopenharmony_ci const struct vdpa_dev_set_config *config) 197962306a36Sopenharmony_ci{ 198062306a36Sopenharmony_ci struct vduse_dev *dev; 198162306a36Sopenharmony_ci int ret; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci mutex_lock(&vduse_lock); 198462306a36Sopenharmony_ci dev = vduse_find_dev(name); 198562306a36Sopenharmony_ci if (!dev || !vduse_dev_is_ready(dev)) { 198662306a36Sopenharmony_ci mutex_unlock(&vduse_lock); 198762306a36Sopenharmony_ci return -EINVAL; 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci ret = vduse_dev_init_vdpa(dev, name); 199062306a36Sopenharmony_ci mutex_unlock(&vduse_lock); 199162306a36Sopenharmony_ci if (ret) 199262306a36Sopenharmony_ci return ret; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci mutex_lock(&dev->domain_lock); 199562306a36Sopenharmony_ci if (!dev->domain) 199662306a36Sopenharmony_ci dev->domain = vduse_domain_create(VDUSE_IOVA_SIZE - 1, 199762306a36Sopenharmony_ci dev->bounce_size); 199862306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 199962306a36Sopenharmony_ci if (!dev->domain) { 200062306a36Sopenharmony_ci put_device(&dev->vdev->vdpa.dev); 200162306a36Sopenharmony_ci return -ENOMEM; 200262306a36Sopenharmony_ci } 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci ret = _vdpa_register_device(&dev->vdev->vdpa, dev->vq_num); 200562306a36Sopenharmony_ci if (ret) { 200662306a36Sopenharmony_ci put_device(&dev->vdev->vdpa.dev); 200762306a36Sopenharmony_ci mutex_lock(&dev->domain_lock); 200862306a36Sopenharmony_ci vduse_domain_destroy(dev->domain); 200962306a36Sopenharmony_ci dev->domain = NULL; 201062306a36Sopenharmony_ci mutex_unlock(&dev->domain_lock); 201162306a36Sopenharmony_ci return ret; 201262306a36Sopenharmony_ci } 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci return 0; 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_cistatic void vdpa_dev_del(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev) 201862306a36Sopenharmony_ci{ 201962306a36Sopenharmony_ci _vdpa_unregister_device(dev); 202062306a36Sopenharmony_ci} 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_cistatic const struct vdpa_mgmtdev_ops vdpa_dev_mgmtdev_ops = { 202362306a36Sopenharmony_ci .dev_add = vdpa_dev_add, 202462306a36Sopenharmony_ci .dev_del = vdpa_dev_del, 202562306a36Sopenharmony_ci}; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_cistatic struct virtio_device_id id_table[] = { 202862306a36Sopenharmony_ci { VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID }, 202962306a36Sopenharmony_ci { 0 }, 203062306a36Sopenharmony_ci}; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_cistatic void vduse_mgmtdev_release(struct device *dev) 203362306a36Sopenharmony_ci{ 203462306a36Sopenharmony_ci struct vduse_mgmt_dev *mgmt_dev; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci mgmt_dev = container_of(dev, struct vduse_mgmt_dev, dev); 203762306a36Sopenharmony_ci kfree(mgmt_dev); 203862306a36Sopenharmony_ci} 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_cistatic int vduse_mgmtdev_init(void) 204162306a36Sopenharmony_ci{ 204262306a36Sopenharmony_ci int ret; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci vduse_mgmt = kzalloc(sizeof(*vduse_mgmt), GFP_KERNEL); 204562306a36Sopenharmony_ci if (!vduse_mgmt) 204662306a36Sopenharmony_ci return -ENOMEM; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci ret = dev_set_name(&vduse_mgmt->dev, "vduse"); 204962306a36Sopenharmony_ci if (ret) { 205062306a36Sopenharmony_ci kfree(vduse_mgmt); 205162306a36Sopenharmony_ci return ret; 205262306a36Sopenharmony_ci } 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci vduse_mgmt->dev.release = vduse_mgmtdev_release; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci ret = device_register(&vduse_mgmt->dev); 205762306a36Sopenharmony_ci if (ret) 205862306a36Sopenharmony_ci goto dev_reg_err; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci vduse_mgmt->mgmt_dev.id_table = id_table; 206162306a36Sopenharmony_ci vduse_mgmt->mgmt_dev.ops = &vdpa_dev_mgmtdev_ops; 206262306a36Sopenharmony_ci vduse_mgmt->mgmt_dev.device = &vduse_mgmt->dev; 206362306a36Sopenharmony_ci ret = vdpa_mgmtdev_register(&vduse_mgmt->mgmt_dev); 206462306a36Sopenharmony_ci if (ret) 206562306a36Sopenharmony_ci device_unregister(&vduse_mgmt->dev); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci return ret; 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_cidev_reg_err: 207062306a36Sopenharmony_ci put_device(&vduse_mgmt->dev); 207162306a36Sopenharmony_ci return ret; 207262306a36Sopenharmony_ci} 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_cistatic void vduse_mgmtdev_exit(void) 207562306a36Sopenharmony_ci{ 207662306a36Sopenharmony_ci vdpa_mgmtdev_unregister(&vduse_mgmt->mgmt_dev); 207762306a36Sopenharmony_ci device_unregister(&vduse_mgmt->dev); 207862306a36Sopenharmony_ci} 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_cistatic int vduse_init(void) 208162306a36Sopenharmony_ci{ 208262306a36Sopenharmony_ci int ret; 208362306a36Sopenharmony_ci struct device *dev; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci vduse_class = class_create("vduse"); 208662306a36Sopenharmony_ci if (IS_ERR(vduse_class)) 208762306a36Sopenharmony_ci return PTR_ERR(vduse_class); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci vduse_class->devnode = vduse_devnode; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci ret = alloc_chrdev_region(&vduse_major, 0, VDUSE_DEV_MAX, "vduse"); 209262306a36Sopenharmony_ci if (ret) 209362306a36Sopenharmony_ci goto err_chardev_region; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci /* /dev/vduse/control */ 209662306a36Sopenharmony_ci cdev_init(&vduse_ctrl_cdev, &vduse_ctrl_fops); 209762306a36Sopenharmony_ci vduse_ctrl_cdev.owner = THIS_MODULE; 209862306a36Sopenharmony_ci ret = cdev_add(&vduse_ctrl_cdev, vduse_major, 1); 209962306a36Sopenharmony_ci if (ret) 210062306a36Sopenharmony_ci goto err_ctrl_cdev; 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci dev = device_create(vduse_class, NULL, vduse_major, NULL, "control"); 210362306a36Sopenharmony_ci if (IS_ERR(dev)) { 210462306a36Sopenharmony_ci ret = PTR_ERR(dev); 210562306a36Sopenharmony_ci goto err_device; 210662306a36Sopenharmony_ci } 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci /* /dev/vduse/$DEVICE */ 210962306a36Sopenharmony_ci cdev_init(&vduse_cdev, &vduse_dev_fops); 211062306a36Sopenharmony_ci vduse_cdev.owner = THIS_MODULE; 211162306a36Sopenharmony_ci ret = cdev_add(&vduse_cdev, MKDEV(MAJOR(vduse_major), 1), 211262306a36Sopenharmony_ci VDUSE_DEV_MAX - 1); 211362306a36Sopenharmony_ci if (ret) 211462306a36Sopenharmony_ci goto err_cdev; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci ret = -ENOMEM; 211762306a36Sopenharmony_ci vduse_irq_wq = alloc_workqueue("vduse-irq", 211862306a36Sopenharmony_ci WQ_HIGHPRI | WQ_SYSFS | WQ_UNBOUND, 0); 211962306a36Sopenharmony_ci if (!vduse_irq_wq) 212062306a36Sopenharmony_ci goto err_wq; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci vduse_irq_bound_wq = alloc_workqueue("vduse-irq-bound", WQ_HIGHPRI, 0); 212362306a36Sopenharmony_ci if (!vduse_irq_bound_wq) 212462306a36Sopenharmony_ci goto err_bound_wq; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci ret = vduse_domain_init(); 212762306a36Sopenharmony_ci if (ret) 212862306a36Sopenharmony_ci goto err_domain; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci ret = vduse_mgmtdev_init(); 213162306a36Sopenharmony_ci if (ret) 213262306a36Sopenharmony_ci goto err_mgmtdev; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci return 0; 213562306a36Sopenharmony_cierr_mgmtdev: 213662306a36Sopenharmony_ci vduse_domain_exit(); 213762306a36Sopenharmony_cierr_domain: 213862306a36Sopenharmony_ci destroy_workqueue(vduse_irq_bound_wq); 213962306a36Sopenharmony_cierr_bound_wq: 214062306a36Sopenharmony_ci destroy_workqueue(vduse_irq_wq); 214162306a36Sopenharmony_cierr_wq: 214262306a36Sopenharmony_ci cdev_del(&vduse_cdev); 214362306a36Sopenharmony_cierr_cdev: 214462306a36Sopenharmony_ci device_destroy(vduse_class, vduse_major); 214562306a36Sopenharmony_cierr_device: 214662306a36Sopenharmony_ci cdev_del(&vduse_ctrl_cdev); 214762306a36Sopenharmony_cierr_ctrl_cdev: 214862306a36Sopenharmony_ci unregister_chrdev_region(vduse_major, VDUSE_DEV_MAX); 214962306a36Sopenharmony_cierr_chardev_region: 215062306a36Sopenharmony_ci class_destroy(vduse_class); 215162306a36Sopenharmony_ci return ret; 215262306a36Sopenharmony_ci} 215362306a36Sopenharmony_cimodule_init(vduse_init); 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_cistatic void vduse_exit(void) 215662306a36Sopenharmony_ci{ 215762306a36Sopenharmony_ci vduse_mgmtdev_exit(); 215862306a36Sopenharmony_ci vduse_domain_exit(); 215962306a36Sopenharmony_ci destroy_workqueue(vduse_irq_bound_wq); 216062306a36Sopenharmony_ci destroy_workqueue(vduse_irq_wq); 216162306a36Sopenharmony_ci cdev_del(&vduse_cdev); 216262306a36Sopenharmony_ci device_destroy(vduse_class, vduse_major); 216362306a36Sopenharmony_ci cdev_del(&vduse_ctrl_cdev); 216462306a36Sopenharmony_ci unregister_chrdev_region(vduse_major, VDUSE_DEV_MAX); 216562306a36Sopenharmony_ci class_destroy(vduse_class); 216662306a36Sopenharmony_ci} 216762306a36Sopenharmony_cimodule_exit(vduse_exit); 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ciMODULE_LICENSE(DRV_LICENSE); 217062306a36Sopenharmony_ciMODULE_AUTHOR(DRV_AUTHOR); 217162306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC); 2172