xref: /openbmc/linux/drivers/misc/uacce/uacce.c (revision 282c22a8)
1015d239aSKenneth Lee // SPDX-License-Identifier: GPL-2.0-or-later
2015d239aSKenneth Lee #include <linux/compat.h>
3015d239aSKenneth Lee #include <linux/dma-mapping.h>
4015d239aSKenneth Lee #include <linux/iommu.h>
5015d239aSKenneth Lee #include <linux/module.h>
6015d239aSKenneth Lee #include <linux/poll.h>
77999096fSHerbert Xu #include <linux/slab.h>
8015d239aSKenneth Lee #include <linux/uacce.h>
9015d239aSKenneth Lee 
10015d239aSKenneth Lee static struct class *uacce_class;
11015d239aSKenneth Lee static dev_t uacce_devt;
12015d239aSKenneth Lee static DEFINE_XARRAY_ALLOC(uacce_xa);
13015d239aSKenneth Lee 
1480fc671bSJean-Philippe Brucker /*
1580fc671bSJean-Philippe Brucker  * If the parent driver or the device disappears, the queue state is invalid and
1680fc671bSJean-Philippe Brucker  * ops are not usable anymore.
1780fc671bSJean-Philippe Brucker  */
uacce_queue_is_valid(struct uacce_queue * q)1880fc671bSJean-Philippe Brucker static bool uacce_queue_is_valid(struct uacce_queue *q)
1980fc671bSJean-Philippe Brucker {
2080fc671bSJean-Philippe Brucker 	return q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED;
2180fc671bSJean-Philippe Brucker }
2280fc671bSJean-Philippe Brucker 
uacce_start_queue(struct uacce_queue * q)23015d239aSKenneth Lee static int uacce_start_queue(struct uacce_queue *q)
24015d239aSKenneth Lee {
2580fc671bSJean-Philippe Brucker 	int ret;
26015d239aSKenneth Lee 
2780fc671bSJean-Philippe Brucker 	if (q->state != UACCE_Q_INIT)
2880fc671bSJean-Philippe Brucker 		return -EINVAL;
29015d239aSKenneth Lee 
30015d239aSKenneth Lee 	if (q->uacce->ops->start_queue) {
31015d239aSKenneth Lee 		ret = q->uacce->ops->start_queue(q);
32015d239aSKenneth Lee 		if (ret < 0)
3380fc671bSJean-Philippe Brucker 			return ret;
34015d239aSKenneth Lee 	}
35015d239aSKenneth Lee 
36015d239aSKenneth Lee 	q->state = UACCE_Q_STARTED;
3780fc671bSJean-Philippe Brucker 	return 0;
38015d239aSKenneth Lee }
39015d239aSKenneth Lee 
uacce_put_queue(struct uacce_queue * q)40015d239aSKenneth Lee static int uacce_put_queue(struct uacce_queue *q)
41015d239aSKenneth Lee {
42015d239aSKenneth Lee 	struct uacce_device *uacce = q->uacce;
43015d239aSKenneth Lee 
44015d239aSKenneth Lee 	if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
45015d239aSKenneth Lee 		uacce->ops->stop_queue(q);
46015d239aSKenneth Lee 
47015d239aSKenneth Lee 	if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) &&
48015d239aSKenneth Lee 	     uacce->ops->put_queue)
49015d239aSKenneth Lee 		uacce->ops->put_queue(q);
50015d239aSKenneth Lee 
51015d239aSKenneth Lee 	q->state = UACCE_Q_ZOMBIE;
52015d239aSKenneth Lee 
53015d239aSKenneth Lee 	return 0;
54015d239aSKenneth Lee }
55015d239aSKenneth Lee 
uacce_fops_unl_ioctl(struct file * filep,unsigned int cmd,unsigned long arg)56015d239aSKenneth Lee static long uacce_fops_unl_ioctl(struct file *filep,
57015d239aSKenneth Lee 				 unsigned int cmd, unsigned long arg)
58015d239aSKenneth Lee {
59015d239aSKenneth Lee 	struct uacce_queue *q = filep->private_data;
60015d239aSKenneth Lee 	struct uacce_device *uacce = q->uacce;
6180fc671bSJean-Philippe Brucker 	long ret = -ENXIO;
6280fc671bSJean-Philippe Brucker 
6380fc671bSJean-Philippe Brucker 	/*
6480fc671bSJean-Philippe Brucker 	 * uacce->ops->ioctl() may take the mmap_lock when copying arg to/from
6580fc671bSJean-Philippe Brucker 	 * user. Avoid a circular lock dependency with uacce_fops_mmap(), which
6680fc671bSJean-Philippe Brucker 	 * gets called with mmap_lock held, by taking uacce->mutex instead of
6780fc671bSJean-Philippe Brucker 	 * q->mutex. Doing this in uacce_fops_mmap() is not possible because
6880fc671bSJean-Philippe Brucker 	 * uacce_fops_open() calls iommu_sva_bind_device(), which takes
6980fc671bSJean-Philippe Brucker 	 * mmap_lock, while holding uacce->mutex.
7080fc671bSJean-Philippe Brucker 	 */
7180fc671bSJean-Philippe Brucker 	mutex_lock(&uacce->mutex);
7280fc671bSJean-Philippe Brucker 	if (!uacce_queue_is_valid(q))
7380fc671bSJean-Philippe Brucker 		goto out_unlock;
74015d239aSKenneth Lee 
75015d239aSKenneth Lee 	switch (cmd) {
76015d239aSKenneth Lee 	case UACCE_CMD_START_Q:
7780fc671bSJean-Philippe Brucker 		ret = uacce_start_queue(q);
7880fc671bSJean-Philippe Brucker 		break;
79015d239aSKenneth Lee 	case UACCE_CMD_PUT_Q:
8080fc671bSJean-Philippe Brucker 		ret = uacce_put_queue(q);
8180fc671bSJean-Philippe Brucker 		break;
82015d239aSKenneth Lee 	default:
8380fc671bSJean-Philippe Brucker 		if (uacce->ops->ioctl)
8480fc671bSJean-Philippe Brucker 			ret = uacce->ops->ioctl(q, cmd, arg);
8580fc671bSJean-Philippe Brucker 		else
8680fc671bSJean-Philippe Brucker 			ret = -EINVAL;
87015d239aSKenneth Lee 	}
8880fc671bSJean-Philippe Brucker out_unlock:
8980fc671bSJean-Philippe Brucker 	mutex_unlock(&uacce->mutex);
9080fc671bSJean-Philippe Brucker 	return ret;
91015d239aSKenneth Lee }
92015d239aSKenneth Lee 
93015d239aSKenneth Lee #ifdef CONFIG_COMPAT
uacce_fops_compat_ioctl(struct file * filep,unsigned int cmd,unsigned long arg)94015d239aSKenneth Lee static long uacce_fops_compat_ioctl(struct file *filep,
95015d239aSKenneth Lee 				   unsigned int cmd, unsigned long arg)
96015d239aSKenneth Lee {
97015d239aSKenneth Lee 	arg = (unsigned long)compat_ptr(arg);
98015d239aSKenneth Lee 
99015d239aSKenneth Lee 	return uacce_fops_unl_ioctl(filep, cmd, arg);
100015d239aSKenneth Lee }
101015d239aSKenneth Lee #endif
102015d239aSKenneth Lee 
uacce_bind_queue(struct uacce_device * uacce,struct uacce_queue * q)103fb01562eSJean-Philippe Brucker static int uacce_bind_queue(struct uacce_device *uacce, struct uacce_queue *q)
104015d239aSKenneth Lee {
105c7b6bac9SFenghua Yu 	u32 pasid;
106fb01562eSJean-Philippe Brucker 	struct iommu_sva *handle;
107015d239aSKenneth Lee 
108fb01562eSJean-Philippe Brucker 	if (!(uacce->flags & UACCE_DEV_SVA))
109fb01562eSJean-Philippe Brucker 		return 0;
110015d239aSKenneth Lee 
111942fd543SLu Baolu 	handle = iommu_sva_bind_device(uacce->parent, current->mm);
112fb01562eSJean-Philippe Brucker 	if (IS_ERR(handle))
113fb01562eSJean-Philippe Brucker 		return PTR_ERR(handle);
114fb01562eSJean-Philippe Brucker 
115fb01562eSJean-Philippe Brucker 	pasid = iommu_sva_get_pasid(handle);
116fb01562eSJean-Philippe Brucker 	if (pasid == IOMMU_PASID_INVALID) {
117fb01562eSJean-Philippe Brucker 		iommu_sva_unbind_device(handle);
118fb01562eSJean-Philippe Brucker 		return -ENODEV;
119fb01562eSJean-Philippe Brucker 	}
120fb01562eSJean-Philippe Brucker 
121fb01562eSJean-Philippe Brucker 	q->handle = handle;
122fb01562eSJean-Philippe Brucker 	q->pasid = pasid;
123015d239aSKenneth Lee 	return 0;
124015d239aSKenneth Lee }
125015d239aSKenneth Lee 
uacce_unbind_queue(struct uacce_queue * q)126fb01562eSJean-Philippe Brucker static void uacce_unbind_queue(struct uacce_queue *q)
127015d239aSKenneth Lee {
128fb01562eSJean-Philippe Brucker 	if (!q->handle)
129fb01562eSJean-Philippe Brucker 		return;
130fb01562eSJean-Philippe Brucker 	iommu_sva_unbind_device(q->handle);
131fb01562eSJean-Philippe Brucker 	q->handle = NULL;
132015d239aSKenneth Lee }
133015d239aSKenneth Lee 
uacce_fops_open(struct inode * inode,struct file * filep)134015d239aSKenneth Lee static int uacce_fops_open(struct inode *inode, struct file *filep)
135015d239aSKenneth Lee {
136015d239aSKenneth Lee 	struct uacce_device *uacce;
137015d239aSKenneth Lee 	struct uacce_queue *q;
138f20b2c2aSKai Ye 	int ret;
139015d239aSKenneth Lee 
140015d239aSKenneth Lee 	uacce = xa_load(&uacce_xa, iminor(inode));
141015d239aSKenneth Lee 	if (!uacce)
142015d239aSKenneth Lee 		return -ENODEV;
143015d239aSKenneth Lee 
144015d239aSKenneth Lee 	q = kzalloc(sizeof(struct uacce_queue), GFP_KERNEL);
145015d239aSKenneth Lee 	if (!q)
146015d239aSKenneth Lee 		return -ENOMEM;
147015d239aSKenneth Lee 
14880fc671bSJean-Philippe Brucker 	mutex_lock(&uacce->mutex);
14980fc671bSJean-Philippe Brucker 
15080fc671bSJean-Philippe Brucker 	if (!uacce->parent) {
15180fc671bSJean-Philippe Brucker 		ret = -EINVAL;
15280fc671bSJean-Philippe Brucker 		goto out_with_mem;
15380fc671bSJean-Philippe Brucker 	}
15480fc671bSJean-Philippe Brucker 
155fb01562eSJean-Philippe Brucker 	ret = uacce_bind_queue(uacce, q);
156fb01562eSJean-Philippe Brucker 	if (ret)
157015d239aSKenneth Lee 		goto out_with_mem;
158015d239aSKenneth Lee 
159015d239aSKenneth Lee 	q->uacce = uacce;
160015d239aSKenneth Lee 
161015d239aSKenneth Lee 	if (uacce->ops->get_queue) {
162fb01562eSJean-Philippe Brucker 		ret = uacce->ops->get_queue(uacce, q->pasid, q);
163015d239aSKenneth Lee 		if (ret < 0)
164fb01562eSJean-Philippe Brucker 			goto out_with_bond;
165015d239aSKenneth Lee 	}
166015d239aSKenneth Lee 
167015d239aSKenneth Lee 	init_waitqueue_head(&q->wait);
168015d239aSKenneth Lee 	filep->private_data = q;
169015d239aSKenneth Lee 	q->state = UACCE_Q_INIT;
170df1b056dSZhangfei Gao 	q->mapping = filep->f_mapping;
17180fc671bSJean-Philippe Brucker 	mutex_init(&q->mutex);
172fb01562eSJean-Philippe Brucker 	list_add(&q->list, &uacce->queues);
17380fc671bSJean-Philippe Brucker 	mutex_unlock(&uacce->mutex);
174fb01562eSJean-Philippe Brucker 
175015d239aSKenneth Lee 	return 0;
176015d239aSKenneth Lee 
177fb01562eSJean-Philippe Brucker out_with_bond:
178fb01562eSJean-Philippe Brucker 	uacce_unbind_queue(q);
179015d239aSKenneth Lee out_with_mem:
180015d239aSKenneth Lee 	kfree(q);
18180fc671bSJean-Philippe Brucker 	mutex_unlock(&uacce->mutex);
182015d239aSKenneth Lee 	return ret;
183015d239aSKenneth Lee }
184015d239aSKenneth Lee 
uacce_fops_release(struct inode * inode,struct file * filep)185015d239aSKenneth Lee static int uacce_fops_release(struct inode *inode, struct file *filep)
186015d239aSKenneth Lee {
187015d239aSKenneth Lee 	struct uacce_queue *q = filep->private_data;
18880fc671bSJean-Philippe Brucker 	struct uacce_device *uacce = q->uacce;
189015d239aSKenneth Lee 
19080fc671bSJean-Philippe Brucker 	mutex_lock(&uacce->mutex);
191015d239aSKenneth Lee 	uacce_put_queue(q);
192fb01562eSJean-Philippe Brucker 	uacce_unbind_queue(q);
19380fc671bSJean-Philippe Brucker 	list_del(&q->list);
19480fc671bSJean-Philippe Brucker 	mutex_unlock(&uacce->mutex);
195015d239aSKenneth Lee 	kfree(q);
196015d239aSKenneth Lee 
197015d239aSKenneth Lee 	return 0;
198015d239aSKenneth Lee }
199015d239aSKenneth Lee 
uacce_vma_close(struct vm_area_struct * vma)200015d239aSKenneth Lee static void uacce_vma_close(struct vm_area_struct *vma)
201015d239aSKenneth Lee {
202015d239aSKenneth Lee 	struct uacce_queue *q = vma->vm_private_data;
203015d239aSKenneth Lee 
204*282c22a8SZhangfei Gao 	if (vma->vm_pgoff < UACCE_MAX_REGION) {
205*282c22a8SZhangfei Gao 		struct uacce_qfile_region *qfr = q->qfrs[vma->vm_pgoff];
206015d239aSKenneth Lee 
207*282c22a8SZhangfei Gao 		mutex_lock(&q->mutex);
208*282c22a8SZhangfei Gao 		q->qfrs[vma->vm_pgoff] = NULL;
209*282c22a8SZhangfei Gao 		mutex_unlock(&q->mutex);
210015d239aSKenneth Lee 		kfree(qfr);
211015d239aSKenneth Lee 	}
212*282c22a8SZhangfei Gao }
213015d239aSKenneth Lee 
214015d239aSKenneth Lee static const struct vm_operations_struct uacce_vm_ops = {
215015d239aSKenneth Lee 	.close = uacce_vma_close,
216015d239aSKenneth Lee };
217015d239aSKenneth Lee 
uacce_fops_mmap(struct file * filep,struct vm_area_struct * vma)218015d239aSKenneth Lee static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
219015d239aSKenneth Lee {
220015d239aSKenneth Lee 	struct uacce_queue *q = filep->private_data;
221015d239aSKenneth Lee 	struct uacce_device *uacce = q->uacce;
222015d239aSKenneth Lee 	struct uacce_qfile_region *qfr;
223015d239aSKenneth Lee 	enum uacce_qfrt type = UACCE_MAX_REGION;
224015d239aSKenneth Lee 	int ret = 0;
225015d239aSKenneth Lee 
226015d239aSKenneth Lee 	if (vma->vm_pgoff < UACCE_MAX_REGION)
227015d239aSKenneth Lee 		type = vma->vm_pgoff;
228015d239aSKenneth Lee 	else
229015d239aSKenneth Lee 		return -EINVAL;
230015d239aSKenneth Lee 
231015d239aSKenneth Lee 	qfr = kzalloc(sizeof(*qfr), GFP_KERNEL);
232015d239aSKenneth Lee 	if (!qfr)
233015d239aSKenneth Lee 		return -ENOMEM;
234015d239aSKenneth Lee 
2351c71222eSSuren Baghdasaryan 	vm_flags_set(vma, VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK);
236015d239aSKenneth Lee 	vma->vm_ops = &uacce_vm_ops;
237015d239aSKenneth Lee 	vma->vm_private_data = q;
238015d239aSKenneth Lee 	qfr->type = type;
239015d239aSKenneth Lee 
24080fc671bSJean-Philippe Brucker 	mutex_lock(&q->mutex);
24180fc671bSJean-Philippe Brucker 	if (!uacce_queue_is_valid(q)) {
24280fc671bSJean-Philippe Brucker 		ret = -ENXIO;
243015d239aSKenneth Lee 		goto out_with_lock;
244015d239aSKenneth Lee 	}
245015d239aSKenneth Lee 
246015d239aSKenneth Lee 	if (q->qfrs[type]) {
247015d239aSKenneth Lee 		ret = -EEXIST;
248015d239aSKenneth Lee 		goto out_with_lock;
249015d239aSKenneth Lee 	}
250015d239aSKenneth Lee 
251015d239aSKenneth Lee 	switch (type) {
252015d239aSKenneth Lee 	case UACCE_QFRT_MMIO:
253015d239aSKenneth Lee 	case UACCE_QFRT_DUS:
254015d239aSKenneth Lee 		if (!uacce->ops->mmap) {
255015d239aSKenneth Lee 			ret = -EINVAL;
256015d239aSKenneth Lee 			goto out_with_lock;
257015d239aSKenneth Lee 		}
258015d239aSKenneth Lee 
259015d239aSKenneth Lee 		ret = uacce->ops->mmap(q, vma, qfr);
260015d239aSKenneth Lee 		if (ret)
261015d239aSKenneth Lee 			goto out_with_lock;
262015d239aSKenneth Lee 		break;
263015d239aSKenneth Lee 
264015d239aSKenneth Lee 	default:
265015d239aSKenneth Lee 		ret = -EINVAL;
266015d239aSKenneth Lee 		goto out_with_lock;
267015d239aSKenneth Lee 	}
268015d239aSKenneth Lee 
269015d239aSKenneth Lee 	q->qfrs[type] = qfr;
27080fc671bSJean-Philippe Brucker 	mutex_unlock(&q->mutex);
271015d239aSKenneth Lee 
272015d239aSKenneth Lee 	return ret;
273015d239aSKenneth Lee 
274015d239aSKenneth Lee out_with_lock:
27580fc671bSJean-Philippe Brucker 	mutex_unlock(&q->mutex);
276015d239aSKenneth Lee 	kfree(qfr);
277015d239aSKenneth Lee 	return ret;
278015d239aSKenneth Lee }
279015d239aSKenneth Lee 
uacce_fops_poll(struct file * file,poll_table * wait)280015d239aSKenneth Lee static __poll_t uacce_fops_poll(struct file *file, poll_table *wait)
281015d239aSKenneth Lee {
282015d239aSKenneth Lee 	struct uacce_queue *q = file->private_data;
283015d239aSKenneth Lee 	struct uacce_device *uacce = q->uacce;
28480fc671bSJean-Philippe Brucker 	__poll_t ret = 0;
28580fc671bSJean-Philippe Brucker 
28680fc671bSJean-Philippe Brucker 	mutex_lock(&q->mutex);
28780fc671bSJean-Philippe Brucker 	if (!uacce_queue_is_valid(q))
28880fc671bSJean-Philippe Brucker 		goto out_unlock;
289015d239aSKenneth Lee 
290015d239aSKenneth Lee 	poll_wait(file, &q->wait, wait);
291015d239aSKenneth Lee 
29280fc671bSJean-Philippe Brucker 	if (uacce->ops->is_q_updated && uacce->ops->is_q_updated(q))
29380fc671bSJean-Philippe Brucker 		ret = EPOLLIN | EPOLLRDNORM;
29480fc671bSJean-Philippe Brucker 
29580fc671bSJean-Philippe Brucker out_unlock:
29680fc671bSJean-Philippe Brucker 	mutex_unlock(&q->mutex);
29780fc671bSJean-Philippe Brucker 	return ret;
298015d239aSKenneth Lee }
299015d239aSKenneth Lee 
300015d239aSKenneth Lee static const struct file_operations uacce_fops = {
301015d239aSKenneth Lee 	.owner		= THIS_MODULE,
302015d239aSKenneth Lee 	.open		= uacce_fops_open,
303015d239aSKenneth Lee 	.release	= uacce_fops_release,
304015d239aSKenneth Lee 	.unlocked_ioctl	= uacce_fops_unl_ioctl,
305015d239aSKenneth Lee #ifdef CONFIG_COMPAT
306015d239aSKenneth Lee 	.compat_ioctl	= uacce_fops_compat_ioctl,
307015d239aSKenneth Lee #endif
308015d239aSKenneth Lee 	.mmap		= uacce_fops_mmap,
309015d239aSKenneth Lee 	.poll		= uacce_fops_poll,
310015d239aSKenneth Lee };
311015d239aSKenneth Lee 
312015d239aSKenneth Lee #define to_uacce_device(dev) container_of(dev, struct uacce_device, dev)
313015d239aSKenneth Lee 
api_show(struct device * dev,struct device_attribute * attr,char * buf)314015d239aSKenneth Lee static ssize_t api_show(struct device *dev,
315015d239aSKenneth Lee 			struct device_attribute *attr, char *buf)
316015d239aSKenneth Lee {
317015d239aSKenneth Lee 	struct uacce_device *uacce = to_uacce_device(dev);
318015d239aSKenneth Lee 
3192d2802fbSKai Ye 	return sysfs_emit(buf, "%s\n", uacce->api_ver);
320015d239aSKenneth Lee }
321015d239aSKenneth Lee 
flags_show(struct device * dev,struct device_attribute * attr,char * buf)322015d239aSKenneth Lee static ssize_t flags_show(struct device *dev,
323015d239aSKenneth Lee 			  struct device_attribute *attr, char *buf)
324015d239aSKenneth Lee {
325015d239aSKenneth Lee 	struct uacce_device *uacce = to_uacce_device(dev);
326015d239aSKenneth Lee 
3272d2802fbSKai Ye 	return sysfs_emit(buf, "%u\n", uacce->flags);
328015d239aSKenneth Lee }
329015d239aSKenneth Lee 
available_instances_show(struct device * dev,struct device_attribute * attr,char * buf)330015d239aSKenneth Lee static ssize_t available_instances_show(struct device *dev,
331015d239aSKenneth Lee 					struct device_attribute *attr,
332015d239aSKenneth Lee 					char *buf)
333015d239aSKenneth Lee {
334015d239aSKenneth Lee 	struct uacce_device *uacce = to_uacce_device(dev);
335015d239aSKenneth Lee 
336015d239aSKenneth Lee 	if (!uacce->ops->get_available_instances)
337015d239aSKenneth Lee 		return -ENODEV;
338015d239aSKenneth Lee 
3392d2802fbSKai Ye 	return sysfs_emit(buf, "%d\n",
340015d239aSKenneth Lee 		       uacce->ops->get_available_instances(uacce));
341015d239aSKenneth Lee }
342015d239aSKenneth Lee 
algorithms_show(struct device * dev,struct device_attribute * attr,char * buf)343015d239aSKenneth Lee static ssize_t algorithms_show(struct device *dev,
344015d239aSKenneth Lee 			       struct device_attribute *attr, char *buf)
345015d239aSKenneth Lee {
346015d239aSKenneth Lee 	struct uacce_device *uacce = to_uacce_device(dev);
347015d239aSKenneth Lee 
3482d2802fbSKai Ye 	return sysfs_emit(buf, "%s\n", uacce->algs);
349015d239aSKenneth Lee }
350015d239aSKenneth Lee 
region_mmio_size_show(struct device * dev,struct device_attribute * attr,char * buf)351015d239aSKenneth Lee static ssize_t region_mmio_size_show(struct device *dev,
352015d239aSKenneth Lee 				     struct device_attribute *attr, char *buf)
353015d239aSKenneth Lee {
354015d239aSKenneth Lee 	struct uacce_device *uacce = to_uacce_device(dev);
355015d239aSKenneth Lee 
3562d2802fbSKai Ye 	return sysfs_emit(buf, "%lu\n",
357015d239aSKenneth Lee 		       uacce->qf_pg_num[UACCE_QFRT_MMIO] << PAGE_SHIFT);
358015d239aSKenneth Lee }
359015d239aSKenneth Lee 
region_dus_size_show(struct device * dev,struct device_attribute * attr,char * buf)360015d239aSKenneth Lee static ssize_t region_dus_size_show(struct device *dev,
361015d239aSKenneth Lee 				    struct device_attribute *attr, char *buf)
362015d239aSKenneth Lee {
363015d239aSKenneth Lee 	struct uacce_device *uacce = to_uacce_device(dev);
364015d239aSKenneth Lee 
3652d2802fbSKai Ye 	return sysfs_emit(buf, "%lu\n",
366015d239aSKenneth Lee 		       uacce->qf_pg_num[UACCE_QFRT_DUS] << PAGE_SHIFT);
367015d239aSKenneth Lee }
368015d239aSKenneth Lee 
isolate_show(struct device * dev,struct device_attribute * attr,char * buf)369e3e289fbSKai Ye static ssize_t isolate_show(struct device *dev,
370e3e289fbSKai Ye 			    struct device_attribute *attr, char *buf)
371e3e289fbSKai Ye {
372e3e289fbSKai Ye 	struct uacce_device *uacce = to_uacce_device(dev);
373e3e289fbSKai Ye 
374e3e289fbSKai Ye 	return sysfs_emit(buf, "%d\n", uacce->ops->get_isolate_state(uacce));
375e3e289fbSKai Ye }
376e3e289fbSKai Ye 
isolate_strategy_show(struct device * dev,struct device_attribute * attr,char * buf)377e3e289fbSKai Ye static ssize_t isolate_strategy_show(struct device *dev, struct device_attribute *attr, char *buf)
378e3e289fbSKai Ye {
379e3e289fbSKai Ye 	struct uacce_device *uacce = to_uacce_device(dev);
380e3e289fbSKai Ye 	u32 val;
381e3e289fbSKai Ye 
382e3e289fbSKai Ye 	val = uacce->ops->isolate_err_threshold_read(uacce);
383e3e289fbSKai Ye 
384e3e289fbSKai Ye 	return sysfs_emit(buf, "%u\n", val);
385e3e289fbSKai Ye }
386e3e289fbSKai Ye 
isolate_strategy_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)387e3e289fbSKai Ye static ssize_t isolate_strategy_store(struct device *dev, struct device_attribute *attr,
388e3e289fbSKai Ye 				   const char *buf, size_t count)
389e3e289fbSKai Ye {
390e3e289fbSKai Ye 	struct uacce_device *uacce = to_uacce_device(dev);
391e3e289fbSKai Ye 	unsigned long val;
392e3e289fbSKai Ye 	int ret;
393e3e289fbSKai Ye 
394e3e289fbSKai Ye 	if (kstrtoul(buf, 0, &val) < 0)
395e3e289fbSKai Ye 		return -EINVAL;
396e3e289fbSKai Ye 
397e3e289fbSKai Ye 	if (val > UACCE_MAX_ERR_THRESHOLD)
398e3e289fbSKai Ye 		return -EINVAL;
399e3e289fbSKai Ye 
400e3e289fbSKai Ye 	ret = uacce->ops->isolate_err_threshold_write(uacce, val);
401e3e289fbSKai Ye 	if (ret)
402e3e289fbSKai Ye 		return ret;
403e3e289fbSKai Ye 
404e3e289fbSKai Ye 	return count;
405e3e289fbSKai Ye }
406e3e289fbSKai Ye 
407015d239aSKenneth Lee static DEVICE_ATTR_RO(api);
408015d239aSKenneth Lee static DEVICE_ATTR_RO(flags);
409015d239aSKenneth Lee static DEVICE_ATTR_RO(available_instances);
410015d239aSKenneth Lee static DEVICE_ATTR_RO(algorithms);
411015d239aSKenneth Lee static DEVICE_ATTR_RO(region_mmio_size);
412015d239aSKenneth Lee static DEVICE_ATTR_RO(region_dus_size);
413e3e289fbSKai Ye static DEVICE_ATTR_RO(isolate);
414e3e289fbSKai Ye static DEVICE_ATTR_RW(isolate_strategy);
415015d239aSKenneth Lee 
416015d239aSKenneth Lee static struct attribute *uacce_dev_attrs[] = {
417015d239aSKenneth Lee 	&dev_attr_api.attr,
418015d239aSKenneth Lee 	&dev_attr_flags.attr,
419015d239aSKenneth Lee 	&dev_attr_available_instances.attr,
420015d239aSKenneth Lee 	&dev_attr_algorithms.attr,
421015d239aSKenneth Lee 	&dev_attr_region_mmio_size.attr,
422015d239aSKenneth Lee 	&dev_attr_region_dus_size.attr,
423e3e289fbSKai Ye 	&dev_attr_isolate.attr,
424e3e289fbSKai Ye 	&dev_attr_isolate_strategy.attr,
425015d239aSKenneth Lee 	NULL,
426015d239aSKenneth Lee };
427015d239aSKenneth Lee 
uacce_dev_is_visible(struct kobject * kobj,struct attribute * attr,int n)428015d239aSKenneth Lee static umode_t uacce_dev_is_visible(struct kobject *kobj,
429015d239aSKenneth Lee 				    struct attribute *attr, int n)
430015d239aSKenneth Lee {
431a4c3d756STian Tao 	struct device *dev = kobj_to_dev(kobj);
432015d239aSKenneth Lee 	struct uacce_device *uacce = to_uacce_device(dev);
433015d239aSKenneth Lee 
434015d239aSKenneth Lee 	if (((attr == &dev_attr_region_mmio_size.attr) &&
435015d239aSKenneth Lee 	    (!uacce->qf_pg_num[UACCE_QFRT_MMIO])) ||
436015d239aSKenneth Lee 	    ((attr == &dev_attr_region_dus_size.attr) &&
437015d239aSKenneth Lee 	    (!uacce->qf_pg_num[UACCE_QFRT_DUS])))
438015d239aSKenneth Lee 		return 0;
439015d239aSKenneth Lee 
440e3e289fbSKai Ye 	if (attr == &dev_attr_isolate_strategy.attr &&
441e3e289fbSKai Ye 	    (!uacce->ops->isolate_err_threshold_read &&
442e3e289fbSKai Ye 	     !uacce->ops->isolate_err_threshold_write))
443e3e289fbSKai Ye 		return 0;
444e3e289fbSKai Ye 
445e3e289fbSKai Ye 	if (attr == &dev_attr_isolate.attr && !uacce->ops->get_isolate_state)
446e3e289fbSKai Ye 		return 0;
447e3e289fbSKai Ye 
448015d239aSKenneth Lee 	return attr->mode;
449015d239aSKenneth Lee }
450015d239aSKenneth Lee 
451015d239aSKenneth Lee static struct attribute_group uacce_dev_group = {
452015d239aSKenneth Lee 	.is_visible	= uacce_dev_is_visible,
453015d239aSKenneth Lee 	.attrs		= uacce_dev_attrs,
454015d239aSKenneth Lee };
455015d239aSKenneth Lee 
456015d239aSKenneth Lee __ATTRIBUTE_GROUPS(uacce_dev);
457015d239aSKenneth Lee 
uacce_release(struct device * dev)458015d239aSKenneth Lee static void uacce_release(struct device *dev)
459015d239aSKenneth Lee {
460015d239aSKenneth Lee 	struct uacce_device *uacce = to_uacce_device(dev);
461015d239aSKenneth Lee 
462015d239aSKenneth Lee 	kfree(uacce);
463015d239aSKenneth Lee }
464015d239aSKenneth Lee 
uacce_enable_sva(struct device * parent,unsigned int flags)4650860788dSJean-Philippe Brucker static unsigned int uacce_enable_sva(struct device *parent, unsigned int flags)
4660860788dSJean-Philippe Brucker {
467762b296bSKai Ye 	int ret;
468762b296bSKai Ye 
4690860788dSJean-Philippe Brucker 	if (!(flags & UACCE_DEV_SVA))
4700860788dSJean-Philippe Brucker 		return flags;
4710860788dSJean-Philippe Brucker 
4720860788dSJean-Philippe Brucker 	flags &= ~UACCE_DEV_SVA;
4730860788dSJean-Philippe Brucker 
474762b296bSKai Ye 	ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_IOPF);
475762b296bSKai Ye 	if (ret) {
476762b296bSKai Ye 		dev_err(parent, "failed to enable IOPF feature! ret = %pe\n", ERR_PTR(ret));
4770860788dSJean-Philippe Brucker 		return flags;
478762b296bSKai Ye 	}
4790860788dSJean-Philippe Brucker 
480762b296bSKai Ye 	ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA);
481762b296bSKai Ye 	if (ret) {
482762b296bSKai Ye 		dev_err(parent, "failed to enable SVA feature! ret = %pe\n", ERR_PTR(ret));
4830860788dSJean-Philippe Brucker 		iommu_dev_disable_feature(parent, IOMMU_DEV_FEAT_IOPF);
4840860788dSJean-Philippe Brucker 		return flags;
4850860788dSJean-Philippe Brucker 	}
4860860788dSJean-Philippe Brucker 
4870860788dSJean-Philippe Brucker 	return flags | UACCE_DEV_SVA;
4880860788dSJean-Philippe Brucker }
4890860788dSJean-Philippe Brucker 
uacce_disable_sva(struct uacce_device * uacce)4900860788dSJean-Philippe Brucker static void uacce_disable_sva(struct uacce_device *uacce)
4910860788dSJean-Philippe Brucker {
4920860788dSJean-Philippe Brucker 	if (!(uacce->flags & UACCE_DEV_SVA))
4930860788dSJean-Philippe Brucker 		return;
4940860788dSJean-Philippe Brucker 
4950860788dSJean-Philippe Brucker 	iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
4960860788dSJean-Philippe Brucker 	iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_IOPF);
4970860788dSJean-Philippe Brucker }
4980860788dSJean-Philippe Brucker 
499015d239aSKenneth Lee /**
500015d239aSKenneth Lee  * uacce_alloc() - alloc an accelerator
501015d239aSKenneth Lee  * @parent: pointer of uacce parent device
502015d239aSKenneth Lee  * @interface: pointer of uacce_interface for register
503015d239aSKenneth Lee  *
504015d239aSKenneth Lee  * Returns uacce pointer if success and ERR_PTR if not
505015d239aSKenneth Lee  * Need check returned negotiated uacce->flags
506015d239aSKenneth Lee  */
uacce_alloc(struct device * parent,struct uacce_interface * interface)507015d239aSKenneth Lee struct uacce_device *uacce_alloc(struct device *parent,
508015d239aSKenneth Lee 				 struct uacce_interface *interface)
509015d239aSKenneth Lee {
510015d239aSKenneth Lee 	unsigned int flags = interface->flags;
511015d239aSKenneth Lee 	struct uacce_device *uacce;
512015d239aSKenneth Lee 	int ret;
513015d239aSKenneth Lee 
514015d239aSKenneth Lee 	uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL);
515015d239aSKenneth Lee 	if (!uacce)
516015d239aSKenneth Lee 		return ERR_PTR(-ENOMEM);
517015d239aSKenneth Lee 
5180860788dSJean-Philippe Brucker 	flags = uacce_enable_sva(parent, flags);
519015d239aSKenneth Lee 
520015d239aSKenneth Lee 	uacce->parent = parent;
521015d239aSKenneth Lee 	uacce->flags = flags;
522015d239aSKenneth Lee 	uacce->ops = interface->ops;
523015d239aSKenneth Lee 
524015d239aSKenneth Lee 	ret = xa_alloc(&uacce_xa, &uacce->dev_id, uacce, xa_limit_32b,
525015d239aSKenneth Lee 		       GFP_KERNEL);
526015d239aSKenneth Lee 	if (ret < 0)
527015d239aSKenneth Lee 		goto err_with_uacce;
528015d239aSKenneth Lee 
529fb01562eSJean-Philippe Brucker 	INIT_LIST_HEAD(&uacce->queues);
53080fc671bSJean-Philippe Brucker 	mutex_init(&uacce->mutex);
531015d239aSKenneth Lee 	device_initialize(&uacce->dev);
532015d239aSKenneth Lee 	uacce->dev.devt = MKDEV(MAJOR(uacce_devt), uacce->dev_id);
533015d239aSKenneth Lee 	uacce->dev.class = uacce_class;
534015d239aSKenneth Lee 	uacce->dev.groups = uacce_dev_groups;
535015d239aSKenneth Lee 	uacce->dev.parent = uacce->parent;
536015d239aSKenneth Lee 	uacce->dev.release = uacce_release;
537015d239aSKenneth Lee 	dev_set_name(&uacce->dev, "%s-%d", interface->name, uacce->dev_id);
538015d239aSKenneth Lee 
539015d239aSKenneth Lee 	return uacce;
540015d239aSKenneth Lee 
541015d239aSKenneth Lee err_with_uacce:
5420860788dSJean-Philippe Brucker 	uacce_disable_sva(uacce);
543015d239aSKenneth Lee 	kfree(uacce);
544015d239aSKenneth Lee 	return ERR_PTR(ret);
545015d239aSKenneth Lee }
546015d239aSKenneth Lee EXPORT_SYMBOL_GPL(uacce_alloc);
547015d239aSKenneth Lee 
548015d239aSKenneth Lee /**
549015d239aSKenneth Lee  * uacce_register() - add the accelerator to cdev and export to user space
550015d239aSKenneth Lee  * @uacce: The initialized uacce device
551015d239aSKenneth Lee  *
552015d239aSKenneth Lee  * Return 0 if register succeeded, or an error.
553015d239aSKenneth Lee  */
uacce_register(struct uacce_device * uacce)554015d239aSKenneth Lee int uacce_register(struct uacce_device *uacce)
555015d239aSKenneth Lee {
556015d239aSKenneth Lee 	if (!uacce)
557015d239aSKenneth Lee 		return -ENODEV;
558015d239aSKenneth Lee 
559015d239aSKenneth Lee 	uacce->cdev = cdev_alloc();
560015d239aSKenneth Lee 	if (!uacce->cdev)
561015d239aSKenneth Lee 		return -ENOMEM;
562015d239aSKenneth Lee 
563015d239aSKenneth Lee 	uacce->cdev->ops = &uacce_fops;
564015d239aSKenneth Lee 	uacce->cdev->owner = THIS_MODULE;
565015d239aSKenneth Lee 
566015d239aSKenneth Lee 	return cdev_device_add(uacce->cdev, &uacce->dev);
567015d239aSKenneth Lee }
568015d239aSKenneth Lee EXPORT_SYMBOL_GPL(uacce_register);
569015d239aSKenneth Lee 
570015d239aSKenneth Lee /**
571015d239aSKenneth Lee  * uacce_remove() - remove the accelerator
572015d239aSKenneth Lee  * @uacce: the accelerator to remove
573015d239aSKenneth Lee  */
uacce_remove(struct uacce_device * uacce)574015d239aSKenneth Lee void uacce_remove(struct uacce_device *uacce)
575015d239aSKenneth Lee {
576fb01562eSJean-Philippe Brucker 	struct uacce_queue *q, *next_q;
577015d239aSKenneth Lee 
578015d239aSKenneth Lee 	if (!uacce)
579015d239aSKenneth Lee 		return;
580015d239aSKenneth Lee 
58180fc671bSJean-Philippe Brucker 	/*
58280fc671bSJean-Philippe Brucker 	 * uacce_fops_open() may be running concurrently, even after we remove
58380fc671bSJean-Philippe Brucker 	 * the cdev. Holding uacce->mutex ensures that open() does not obtain a
58480fc671bSJean-Philippe Brucker 	 * removed uacce device.
58580fc671bSJean-Philippe Brucker 	 */
58680fc671bSJean-Philippe Brucker 	mutex_lock(&uacce->mutex);
587015d239aSKenneth Lee 	/* ensure no open queue remains */
588fb01562eSJean-Philippe Brucker 	list_for_each_entry_safe(q, next_q, &uacce->queues, list) {
58980fc671bSJean-Philippe Brucker 		/*
59080fc671bSJean-Philippe Brucker 		 * Taking q->mutex ensures that fops do not use the defunct
59180fc671bSJean-Philippe Brucker 		 * uacce->ops after the queue is disabled.
59280fc671bSJean-Philippe Brucker 		 */
59380fc671bSJean-Philippe Brucker 		mutex_lock(&q->mutex);
594015d239aSKenneth Lee 		uacce_put_queue(q);
59580fc671bSJean-Philippe Brucker 		mutex_unlock(&q->mutex);
596fb01562eSJean-Philippe Brucker 		uacce_unbind_queue(q);
597df1b056dSZhangfei Gao 
598df1b056dSZhangfei Gao 		/*
599df1b056dSZhangfei Gao 		 * unmap remaining mapping from user space, preventing user still
600df1b056dSZhangfei Gao 		 * access the mmaped area while parent device is already removed
601df1b056dSZhangfei Gao 		 */
602df1b056dSZhangfei Gao 		unmap_mapping_range(q->mapping, 0, 0, 1);
603015d239aSKenneth Lee 	}
604015d239aSKenneth Lee 
605015d239aSKenneth Lee 	/* disable sva now since no opened queues */
6060860788dSJean-Philippe Brucker 	uacce_disable_sva(uacce);
607015d239aSKenneth Lee 
608015d239aSKenneth Lee 	if (uacce->cdev)
609015d239aSKenneth Lee 		cdev_device_del(uacce->cdev, &uacce->dev);
610015d239aSKenneth Lee 	xa_erase(&uacce_xa, uacce->dev_id);
61180fc671bSJean-Philippe Brucker 	/*
61280fc671bSJean-Philippe Brucker 	 * uacce exists as long as there are open fds, but ops will be freed
61380fc671bSJean-Philippe Brucker 	 * now. Ensure that bugs cause NULL deref rather than use-after-free.
61480fc671bSJean-Philippe Brucker 	 */
61580fc671bSJean-Philippe Brucker 	uacce->ops = NULL;
61680fc671bSJean-Philippe Brucker 	uacce->parent = NULL;
61780fc671bSJean-Philippe Brucker 	mutex_unlock(&uacce->mutex);
618015d239aSKenneth Lee 	put_device(&uacce->dev);
619015d239aSKenneth Lee }
620015d239aSKenneth Lee EXPORT_SYMBOL_GPL(uacce_remove);
621015d239aSKenneth Lee 
uacce_init(void)622015d239aSKenneth Lee static int __init uacce_init(void)
623015d239aSKenneth Lee {
624015d239aSKenneth Lee 	int ret;
625015d239aSKenneth Lee 
6261aaba11dSGreg Kroah-Hartman 	uacce_class = class_create(UACCE_NAME);
627015d239aSKenneth Lee 	if (IS_ERR(uacce_class))
628015d239aSKenneth Lee 		return PTR_ERR(uacce_class);
629015d239aSKenneth Lee 
630015d239aSKenneth Lee 	ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME);
631015d239aSKenneth Lee 	if (ret)
632015d239aSKenneth Lee 		class_destroy(uacce_class);
633015d239aSKenneth Lee 
634015d239aSKenneth Lee 	return ret;
635015d239aSKenneth Lee }
636015d239aSKenneth Lee 
uacce_exit(void)637015d239aSKenneth Lee static __exit void uacce_exit(void)
638015d239aSKenneth Lee {
639015d239aSKenneth Lee 	unregister_chrdev_region(uacce_devt, MINORMASK);
640015d239aSKenneth Lee 	class_destroy(uacce_class);
641015d239aSKenneth Lee }
642015d239aSKenneth Lee 
643015d239aSKenneth Lee subsys_initcall(uacce_init);
644015d239aSKenneth Lee module_exit(uacce_exit);
645015d239aSKenneth Lee 
646015d239aSKenneth Lee MODULE_LICENSE("GPL");
647385997dcSKai Ye MODULE_AUTHOR("HiSilicon Tech. Co., Ltd.");
648015d239aSKenneth Lee MODULE_DESCRIPTION("Accelerator interface for Userland applications");
649