Merge tag 'xfs-5.13-merge-3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[platform/kernel/linux-rpi.git] / drivers / misc / uacce / uacce.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 #include <linux/compat.h>
3 #include <linux/dma-mapping.h>
4 #include <linux/iommu.h>
5 #include <linux/module.h>
6 #include <linux/poll.h>
7 #include <linux/slab.h>
8 #include <linux/uacce.h>
9
10 static struct class *uacce_class;
11 static dev_t uacce_devt;
12 static DEFINE_MUTEX(uacce_mutex);
13 static DEFINE_XARRAY_ALLOC(uacce_xa);
14
15 static int uacce_start_queue(struct uacce_queue *q)
16 {
17         int ret = 0;
18
19         mutex_lock(&uacce_mutex);
20
21         if (q->state != UACCE_Q_INIT) {
22                 ret = -EINVAL;
23                 goto out_with_lock;
24         }
25
26         if (q->uacce->ops->start_queue) {
27                 ret = q->uacce->ops->start_queue(q);
28                 if (ret < 0)
29                         goto out_with_lock;
30         }
31
32         q->state = UACCE_Q_STARTED;
33
34 out_with_lock:
35         mutex_unlock(&uacce_mutex);
36
37         return ret;
38 }
39
40 static int uacce_put_queue(struct uacce_queue *q)
41 {
42         struct uacce_device *uacce = q->uacce;
43
44         mutex_lock(&uacce_mutex);
45
46         if (q->state == UACCE_Q_ZOMBIE)
47                 goto out;
48
49         if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
50                 uacce->ops->stop_queue(q);
51
52         if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) &&
53              uacce->ops->put_queue)
54                 uacce->ops->put_queue(q);
55
56         q->state = UACCE_Q_ZOMBIE;
57 out:
58         mutex_unlock(&uacce_mutex);
59
60         return 0;
61 }
62
63 static long uacce_fops_unl_ioctl(struct file *filep,
64                                  unsigned int cmd, unsigned long arg)
65 {
66         struct uacce_queue *q = filep->private_data;
67         struct uacce_device *uacce = q->uacce;
68
69         switch (cmd) {
70         case UACCE_CMD_START_Q:
71                 return uacce_start_queue(q);
72
73         case UACCE_CMD_PUT_Q:
74                 return uacce_put_queue(q);
75
76         default:
77                 if (!uacce->ops->ioctl)
78                         return -EINVAL;
79
80                 return uacce->ops->ioctl(q, cmd, arg);
81         }
82 }
83
84 #ifdef CONFIG_COMPAT
85 static long uacce_fops_compat_ioctl(struct file *filep,
86                                    unsigned int cmd, unsigned long arg)
87 {
88         arg = (unsigned long)compat_ptr(arg);
89
90         return uacce_fops_unl_ioctl(filep, cmd, arg);
91 }
92 #endif
93
94 static int uacce_bind_queue(struct uacce_device *uacce, struct uacce_queue *q)
95 {
96         u32 pasid;
97         struct iommu_sva *handle;
98
99         if (!(uacce->flags & UACCE_DEV_SVA))
100                 return 0;
101
102         handle = iommu_sva_bind_device(uacce->parent, current->mm, NULL);
103         if (IS_ERR(handle))
104                 return PTR_ERR(handle);
105
106         pasid = iommu_sva_get_pasid(handle);
107         if (pasid == IOMMU_PASID_INVALID) {
108                 iommu_sva_unbind_device(handle);
109                 return -ENODEV;
110         }
111
112         q->handle = handle;
113         q->pasid = pasid;
114         return 0;
115 }
116
117 static void uacce_unbind_queue(struct uacce_queue *q)
118 {
119         if (!q->handle)
120                 return;
121         iommu_sva_unbind_device(q->handle);
122         q->handle = NULL;
123 }
124
125 static int uacce_fops_open(struct inode *inode, struct file *filep)
126 {
127         struct uacce_device *uacce;
128         struct uacce_queue *q;
129         int ret;
130
131         uacce = xa_load(&uacce_xa, iminor(inode));
132         if (!uacce)
133                 return -ENODEV;
134
135         q = kzalloc(sizeof(struct uacce_queue), GFP_KERNEL);
136         if (!q)
137                 return -ENOMEM;
138
139         ret = uacce_bind_queue(uacce, q);
140         if (ret)
141                 goto out_with_mem;
142
143         q->uacce = uacce;
144
145         if (uacce->ops->get_queue) {
146                 ret = uacce->ops->get_queue(uacce, q->pasid, q);
147                 if (ret < 0)
148                         goto out_with_bond;
149         }
150
151         init_waitqueue_head(&q->wait);
152         filep->private_data = q;
153         uacce->inode = inode;
154         q->state = UACCE_Q_INIT;
155
156         mutex_lock(&uacce->queues_lock);
157         list_add(&q->list, &uacce->queues);
158         mutex_unlock(&uacce->queues_lock);
159
160         return 0;
161
162 out_with_bond:
163         uacce_unbind_queue(q);
164 out_with_mem:
165         kfree(q);
166         return ret;
167 }
168
169 static int uacce_fops_release(struct inode *inode, struct file *filep)
170 {
171         struct uacce_queue *q = filep->private_data;
172
173         mutex_lock(&q->uacce->queues_lock);
174         list_del(&q->list);
175         mutex_unlock(&q->uacce->queues_lock);
176         uacce_put_queue(q);
177         uacce_unbind_queue(q);
178         kfree(q);
179
180         return 0;
181 }
182
183 static void uacce_vma_close(struct vm_area_struct *vma)
184 {
185         struct uacce_queue *q = vma->vm_private_data;
186         struct uacce_qfile_region *qfr = NULL;
187
188         if (vma->vm_pgoff < UACCE_MAX_REGION)
189                 qfr = q->qfrs[vma->vm_pgoff];
190
191         kfree(qfr);
192 }
193
194 static const struct vm_operations_struct uacce_vm_ops = {
195         .close = uacce_vma_close,
196 };
197
198 static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
199 {
200         struct uacce_queue *q = filep->private_data;
201         struct uacce_device *uacce = q->uacce;
202         struct uacce_qfile_region *qfr;
203         enum uacce_qfrt type = UACCE_MAX_REGION;
204         int ret = 0;
205
206         if (vma->vm_pgoff < UACCE_MAX_REGION)
207                 type = vma->vm_pgoff;
208         else
209                 return -EINVAL;
210
211         qfr = kzalloc(sizeof(*qfr), GFP_KERNEL);
212         if (!qfr)
213                 return -ENOMEM;
214
215         vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK;
216         vma->vm_ops = &uacce_vm_ops;
217         vma->vm_private_data = q;
218         qfr->type = type;
219
220         mutex_lock(&uacce_mutex);
221
222         if (q->state != UACCE_Q_INIT && q->state != UACCE_Q_STARTED) {
223                 ret = -EINVAL;
224                 goto out_with_lock;
225         }
226
227         if (q->qfrs[type]) {
228                 ret = -EEXIST;
229                 goto out_with_lock;
230         }
231
232         switch (type) {
233         case UACCE_QFRT_MMIO:
234         case UACCE_QFRT_DUS:
235                 if (!uacce->ops->mmap) {
236                         ret = -EINVAL;
237                         goto out_with_lock;
238                 }
239
240                 ret = uacce->ops->mmap(q, vma, qfr);
241                 if (ret)
242                         goto out_with_lock;
243                 break;
244
245         default:
246                 ret = -EINVAL;
247                 goto out_with_lock;
248         }
249
250         q->qfrs[type] = qfr;
251         mutex_unlock(&uacce_mutex);
252
253         return ret;
254
255 out_with_lock:
256         mutex_unlock(&uacce_mutex);
257         kfree(qfr);
258         return ret;
259 }
260
261 static __poll_t uacce_fops_poll(struct file *file, poll_table *wait)
262 {
263         struct uacce_queue *q = file->private_data;
264         struct uacce_device *uacce = q->uacce;
265
266         poll_wait(file, &q->wait, wait);
267         if (uacce->ops->is_q_updated && uacce->ops->is_q_updated(q))
268                 return EPOLLIN | EPOLLRDNORM;
269
270         return 0;
271 }
272
273 static const struct file_operations uacce_fops = {
274         .owner          = THIS_MODULE,
275         .open           = uacce_fops_open,
276         .release        = uacce_fops_release,
277         .unlocked_ioctl = uacce_fops_unl_ioctl,
278 #ifdef CONFIG_COMPAT
279         .compat_ioctl   = uacce_fops_compat_ioctl,
280 #endif
281         .mmap           = uacce_fops_mmap,
282         .poll           = uacce_fops_poll,
283 };
284
285 #define to_uacce_device(dev) container_of(dev, struct uacce_device, dev)
286
287 static ssize_t api_show(struct device *dev,
288                         struct device_attribute *attr, char *buf)
289 {
290         struct uacce_device *uacce = to_uacce_device(dev);
291
292         return sprintf(buf, "%s\n", uacce->api_ver);
293 }
294
295 static ssize_t flags_show(struct device *dev,
296                           struct device_attribute *attr, char *buf)
297 {
298         struct uacce_device *uacce = to_uacce_device(dev);
299
300         return sprintf(buf, "%u\n", uacce->flags);
301 }
302
303 static ssize_t available_instances_show(struct device *dev,
304                                         struct device_attribute *attr,
305                                         char *buf)
306 {
307         struct uacce_device *uacce = to_uacce_device(dev);
308
309         if (!uacce->ops->get_available_instances)
310                 return -ENODEV;
311
312         return sprintf(buf, "%d\n",
313                        uacce->ops->get_available_instances(uacce));
314 }
315
316 static ssize_t algorithms_show(struct device *dev,
317                                struct device_attribute *attr, char *buf)
318 {
319         struct uacce_device *uacce = to_uacce_device(dev);
320
321         return sprintf(buf, "%s\n", uacce->algs);
322 }
323
324 static ssize_t region_mmio_size_show(struct device *dev,
325                                      struct device_attribute *attr, char *buf)
326 {
327         struct uacce_device *uacce = to_uacce_device(dev);
328
329         return sprintf(buf, "%lu\n",
330                        uacce->qf_pg_num[UACCE_QFRT_MMIO] << PAGE_SHIFT);
331 }
332
333 static ssize_t region_dus_size_show(struct device *dev,
334                                     struct device_attribute *attr, char *buf)
335 {
336         struct uacce_device *uacce = to_uacce_device(dev);
337
338         return sprintf(buf, "%lu\n",
339                        uacce->qf_pg_num[UACCE_QFRT_DUS] << PAGE_SHIFT);
340 }
341
342 static DEVICE_ATTR_RO(api);
343 static DEVICE_ATTR_RO(flags);
344 static DEVICE_ATTR_RO(available_instances);
345 static DEVICE_ATTR_RO(algorithms);
346 static DEVICE_ATTR_RO(region_mmio_size);
347 static DEVICE_ATTR_RO(region_dus_size);
348
349 static struct attribute *uacce_dev_attrs[] = {
350         &dev_attr_api.attr,
351         &dev_attr_flags.attr,
352         &dev_attr_available_instances.attr,
353         &dev_attr_algorithms.attr,
354         &dev_attr_region_mmio_size.attr,
355         &dev_attr_region_dus_size.attr,
356         NULL,
357 };
358
359 static umode_t uacce_dev_is_visible(struct kobject *kobj,
360                                     struct attribute *attr, int n)
361 {
362         struct device *dev = kobj_to_dev(kobj);
363         struct uacce_device *uacce = to_uacce_device(dev);
364
365         if (((attr == &dev_attr_region_mmio_size.attr) &&
366             (!uacce->qf_pg_num[UACCE_QFRT_MMIO])) ||
367             ((attr == &dev_attr_region_dus_size.attr) &&
368             (!uacce->qf_pg_num[UACCE_QFRT_DUS])))
369                 return 0;
370
371         return attr->mode;
372 }
373
374 static struct attribute_group uacce_dev_group = {
375         .is_visible     = uacce_dev_is_visible,
376         .attrs          = uacce_dev_attrs,
377 };
378
379 __ATTRIBUTE_GROUPS(uacce_dev);
380
381 static void uacce_release(struct device *dev)
382 {
383         struct uacce_device *uacce = to_uacce_device(dev);
384
385         kfree(uacce);
386 }
387
388 /**
389  * uacce_alloc() - alloc an accelerator
390  * @parent: pointer of uacce parent device
391  * @interface: pointer of uacce_interface for register
392  *
393  * Returns uacce pointer if success and ERR_PTR if not
394  * Need check returned negotiated uacce->flags
395  */
396 struct uacce_device *uacce_alloc(struct device *parent,
397                                  struct uacce_interface *interface)
398 {
399         unsigned int flags = interface->flags;
400         struct uacce_device *uacce;
401         int ret;
402
403         uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL);
404         if (!uacce)
405                 return ERR_PTR(-ENOMEM);
406
407         if (flags & UACCE_DEV_SVA) {
408                 ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA);
409                 if (ret)
410                         flags &= ~UACCE_DEV_SVA;
411         }
412
413         uacce->parent = parent;
414         uacce->flags = flags;
415         uacce->ops = interface->ops;
416
417         ret = xa_alloc(&uacce_xa, &uacce->dev_id, uacce, xa_limit_32b,
418                        GFP_KERNEL);
419         if (ret < 0)
420                 goto err_with_uacce;
421
422         INIT_LIST_HEAD(&uacce->queues);
423         mutex_init(&uacce->queues_lock);
424         device_initialize(&uacce->dev);
425         uacce->dev.devt = MKDEV(MAJOR(uacce_devt), uacce->dev_id);
426         uacce->dev.class = uacce_class;
427         uacce->dev.groups = uacce_dev_groups;
428         uacce->dev.parent = uacce->parent;
429         uacce->dev.release = uacce_release;
430         dev_set_name(&uacce->dev, "%s-%d", interface->name, uacce->dev_id);
431
432         return uacce;
433
434 err_with_uacce:
435         if (flags & UACCE_DEV_SVA)
436                 iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
437         kfree(uacce);
438         return ERR_PTR(ret);
439 }
440 EXPORT_SYMBOL_GPL(uacce_alloc);
441
442 /**
443  * uacce_register() - add the accelerator to cdev and export to user space
444  * @uacce: The initialized uacce device
445  *
446  * Return 0 if register succeeded, or an error.
447  */
448 int uacce_register(struct uacce_device *uacce)
449 {
450         if (!uacce)
451                 return -ENODEV;
452
453         uacce->cdev = cdev_alloc();
454         if (!uacce->cdev)
455                 return -ENOMEM;
456
457         uacce->cdev->ops = &uacce_fops;
458         uacce->cdev->owner = THIS_MODULE;
459
460         return cdev_device_add(uacce->cdev, &uacce->dev);
461 }
462 EXPORT_SYMBOL_GPL(uacce_register);
463
464 /**
465  * uacce_remove() - remove the accelerator
466  * @uacce: the accelerator to remove
467  */
468 void uacce_remove(struct uacce_device *uacce)
469 {
470         struct uacce_queue *q, *next_q;
471
472         if (!uacce)
473                 return;
474         /*
475          * unmap remaining mapping from user space, preventing user still
476          * access the mmaped area while parent device is already removed
477          */
478         if (uacce->inode)
479                 unmap_mapping_range(uacce->inode->i_mapping, 0, 0, 1);
480
481         /* ensure no open queue remains */
482         mutex_lock(&uacce->queues_lock);
483         list_for_each_entry_safe(q, next_q, &uacce->queues, list) {
484                 uacce_put_queue(q);
485                 uacce_unbind_queue(q);
486         }
487         mutex_unlock(&uacce->queues_lock);
488
489         /* disable sva now since no opened queues */
490         if (uacce->flags & UACCE_DEV_SVA)
491                 iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
492
493         if (uacce->cdev)
494                 cdev_device_del(uacce->cdev, &uacce->dev);
495         xa_erase(&uacce_xa, uacce->dev_id);
496         put_device(&uacce->dev);
497 }
498 EXPORT_SYMBOL_GPL(uacce_remove);
499
500 static int __init uacce_init(void)
501 {
502         int ret;
503
504         uacce_class = class_create(THIS_MODULE, UACCE_NAME);
505         if (IS_ERR(uacce_class))
506                 return PTR_ERR(uacce_class);
507
508         ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME);
509         if (ret)
510                 class_destroy(uacce_class);
511
512         return ret;
513 }
514
515 static __exit void uacce_exit(void)
516 {
517         unregister_chrdev_region(uacce_devt, MINORMASK);
518         class_destroy(uacce_class);
519 }
520
521 subsys_initcall(uacce_init);
522 module_exit(uacce_exit);
523
524 MODULE_LICENSE("GPL");
525 MODULE_AUTHOR("HiSilicon Tech. Co., Ltd.");
526 MODULE_DESCRIPTION("Accelerator interface for Userland applications");