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(&current->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, &current->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