LCOV - code coverage report
Current view: top level - drivers/dma-buf - dma-heap.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_qemu_modules_combined.info Lines: 34 94 36.2 %
Date: 2020-09-30 20:25:01 Functions: 3 7 42.9 %
Branches: 13 56 23.2 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : /*
       3                 :            :  * Framework for userspace DMA-BUF allocations
       4                 :            :  *
       5                 :            :  * Copyright (C) 2011 Google, Inc.
       6                 :            :  * Copyright (C) 2019 Linaro Ltd.
       7                 :            :  */
       8                 :            : 
       9                 :            : #include <linux/cdev.h>
      10                 :            : #include <linux/debugfs.h>
      11                 :            : #include <linux/device.h>
      12                 :            : #include <linux/dma-buf.h>
      13                 :            : #include <linux/err.h>
      14                 :            : #include <linux/xarray.h>
      15                 :            : #include <linux/list.h>
      16                 :            : #include <linux/slab.h>
      17                 :            : #include <linux/uaccess.h>
      18                 :            : #include <linux/syscalls.h>
      19                 :            : #include <linux/dma-heap.h>
      20                 :            : #include <uapi/linux/dma-heap.h>
      21                 :            : 
      22                 :            : #define DEVNAME "dma_heap"
      23                 :            : 
      24                 :            : #define NUM_HEAP_MINORS 128
      25                 :            : 
      26                 :            : /**
      27                 :            :  * struct dma_heap - represents a dmabuf heap in the system
      28                 :            :  * @name:               used for debugging/device-node name
      29                 :            :  * @ops:                ops struct for this heap
      30                 :            :  * @heap_devt           heap device node
      31                 :            :  * @list                list head connecting to list of heaps
      32                 :            :  * @heap_cdev           heap char device
      33                 :            :  *
      34                 :            :  * Represents a heap of memory from which buffers can be made.
      35                 :            :  */
      36                 :            : struct dma_heap {
      37                 :            :         const char *name;
      38                 :            :         const struct dma_heap_ops *ops;
      39                 :            :         void *priv;
      40                 :            :         dev_t heap_devt;
      41                 :            :         struct list_head list;
      42                 :            :         struct cdev heap_cdev;
      43                 :            : };
      44                 :            : 
      45                 :            : static LIST_HEAD(heap_list);
      46                 :            : static DEFINE_MUTEX(heap_list_lock);
      47                 :            : static dev_t dma_heap_devt;
      48                 :            : static struct class *dma_heap_class;
      49                 :            : static DEFINE_XARRAY_ALLOC(dma_heap_minors);
      50                 :            : 
      51                 :            : static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
      52                 :            :                                  unsigned int fd_flags,
      53                 :            :                                  unsigned int heap_flags)
      54                 :            : {
      55                 :            :         /*
      56                 :            :          * Allocations from all heaps have to begin
      57                 :            :          * and end on page boundaries.
      58                 :            :          */
      59                 :          0 :         len = PAGE_ALIGN(len);
      60         [ #  # ]:          0 :         if (!len)
      61                 :            :                 return -EINVAL;
      62                 :            : 
      63                 :          0 :         return heap->ops->allocate(heap, len, fd_flags, heap_flags);
      64                 :            : }
      65                 :            : 
      66                 :          0 : static int dma_heap_open(struct inode *inode, struct file *file)
      67                 :            : {
      68                 :            :         struct dma_heap *heap;
      69                 :            : 
      70                 :          0 :         heap = xa_load(&dma_heap_minors, iminor(inode));
      71         [ #  # ]:          0 :         if (!heap) {
      72                 :          0 :                 pr_err("dma_heap: minor %d unknown.\n", iminor(inode));
      73                 :          0 :                 return -ENODEV;
      74                 :            :         }
      75                 :            : 
      76                 :            :         /* instance data as context */
      77                 :          0 :         file->private_data = heap;
      78                 :          0 :         nonseekable_open(inode, file);
      79                 :            : 
      80                 :          0 :         return 0;
      81                 :            : }
      82                 :            : 
      83                 :          0 : static long dma_heap_ioctl_allocate(struct file *file, void *data)
      84                 :            : {
      85                 :            :         struct dma_heap_allocation_data *heap_allocation = data;
      86                 :          0 :         struct dma_heap *heap = file->private_data;
      87                 :            :         int fd;
      88                 :            : 
      89         [ #  # ]:          0 :         if (heap_allocation->fd)
      90                 :            :                 return -EINVAL;
      91                 :            : 
      92         [ #  # ]:          0 :         if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
      93                 :            :                 return -EINVAL;
      94                 :            : 
      95         [ #  # ]:          0 :         if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
      96                 :            :                 return -EINVAL;
      97                 :            : 
      98                 :          0 :         fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
      99                 :            :                                    heap_allocation->fd_flags,
     100                 :            :                                    heap_allocation->heap_flags);
     101         [ #  # ]:          0 :         if (fd < 0)
     102                 :            :                 return fd;
     103                 :            : 
     104                 :          0 :         heap_allocation->fd = fd;
     105                 :            : 
     106                 :          0 :         return 0;
     107                 :            : }
     108                 :            : 
     109                 :            : static unsigned int dma_heap_ioctl_cmds[] = {
     110                 :            :         DMA_HEAP_IOCTL_ALLOC,
     111                 :            : };
     112                 :            : 
     113                 :          0 : static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
     114                 :            :                            unsigned long arg)
     115                 :            : {
     116                 :            :         char stack_kdata[128];
     117                 :            :         char *kdata = stack_kdata;
     118                 :            :         unsigned int kcmd;
     119                 :            :         unsigned int in_size, out_size, drv_size, ksize;
     120                 :          0 :         int nr = _IOC_NR(ucmd);
     121                 :            :         int ret = 0;
     122                 :            : 
     123         [ #  # ]:          0 :         if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
     124                 :            :                 return -EINVAL;
     125                 :            : 
     126                 :            :         /* Get the kernel ioctl cmd that matches */
     127                 :          0 :         kcmd = dma_heap_ioctl_cmds[nr];
     128                 :            : 
     129                 :            :         /* Figure out the delta between user cmd size and kernel cmd size */
     130                 :          0 :         drv_size = _IOC_SIZE(kcmd);
     131                 :          0 :         out_size = _IOC_SIZE(ucmd);
     132                 :            :         in_size = out_size;
     133         [ #  # ]:          0 :         if ((ucmd & kcmd & IOC_IN) == 0)
     134                 :            :                 in_size = 0;
     135         [ #  # ]:          0 :         if ((ucmd & kcmd & IOC_OUT) == 0)
     136                 :            :                 out_size = 0;
     137                 :          0 :         ksize = max(max(in_size, out_size), drv_size);
     138                 :            : 
     139                 :            :         /* If necessary, allocate buffer for ioctl argument */
     140         [ #  # ]:          0 :         if (ksize > sizeof(stack_kdata)) {
     141                 :            :                 kdata = kmalloc(ksize, GFP_KERNEL);
     142         [ #  # ]:          0 :                 if (!kdata)
     143                 :            :                         return -ENOMEM;
     144                 :            :         }
     145                 :            : 
     146         [ #  # ]:          0 :         if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
     147                 :            :                 ret = -EFAULT;
     148                 :            :                 goto err;
     149                 :            :         }
     150                 :            : 
     151                 :            :         /* zero out any difference between the kernel/user structure size */
     152         [ #  # ]:          0 :         if (ksize > in_size)
     153                 :          0 :                 memset(kdata + in_size, 0, ksize - in_size);
     154                 :            : 
     155         [ #  # ]:          0 :         switch (kcmd) {
     156                 :            :         case DMA_HEAP_IOCTL_ALLOC:
     157                 :          0 :                 ret = dma_heap_ioctl_allocate(file, kdata);
     158                 :            :                 break;
     159                 :            :         default:
     160                 :            :                 ret = -ENOTTY;
     161                 :            :                 goto err;
     162                 :            :         }
     163                 :            : 
     164         [ #  # ]:          0 :         if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
     165                 :            :                 ret = -EFAULT;
     166                 :            : err:
     167         [ #  # ]:          0 :         if (kdata != stack_kdata)
     168                 :          0 :                 kfree(kdata);
     169                 :          0 :         return ret;
     170                 :            : }
     171                 :            : 
     172                 :            : static const struct file_operations dma_heap_fops = {
     173                 :            :         .owner          = THIS_MODULE,
     174                 :            :         .open           = dma_heap_open,
     175                 :            :         .unlocked_ioctl = dma_heap_ioctl,
     176                 :            : #ifdef CONFIG_COMPAT
     177                 :            :         .compat_ioctl   = dma_heap_ioctl,
     178                 :            : #endif
     179                 :            : };
     180                 :            : 
     181                 :            : /**
     182                 :            :  * dma_heap_get_drvdata() - get per-subdriver data for the heap
     183                 :            :  * @heap: DMA-Heap to retrieve private data for
     184                 :            :  *
     185                 :            :  * Returns:
     186                 :            :  * The per-subdriver data for the heap.
     187                 :            :  */
     188                 :          0 : void *dma_heap_get_drvdata(struct dma_heap *heap)
     189                 :            : {
     190                 :          0 :         return heap->priv;
     191                 :            : }
     192                 :            : 
     193                 :        808 : struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
     194                 :            : {
     195                 :            :         struct dma_heap *heap, *h, *err_ret;
     196                 :            :         struct device *dev_ret;
     197                 :            :         unsigned int minor;
     198                 :            :         int ret;
     199                 :            : 
     200   [ +  -  -  + ]:        808 :         if (!exp_info->name || !strcmp(exp_info->name, "")) {
     201                 :          0 :                 pr_err("dma_heap: Cannot add heap without a name\n");
     202                 :          0 :                 return ERR_PTR(-EINVAL);
     203                 :            :         }
     204                 :            : 
     205   [ +  -  -  + ]:        808 :         if (!exp_info->ops || !exp_info->ops->allocate) {
     206                 :          0 :                 pr_err("dma_heap: Cannot add heap with invalid ops struct\n");
     207                 :          0 :                 return ERR_PTR(-EINVAL);
     208                 :            :         }
     209                 :            : 
     210                 :            :         /* check the name is unique */
     211                 :        808 :         mutex_lock(&heap_list_lock);
     212         [ +  + ]:       1212 :         list_for_each_entry(h, &heap_list, list) {
     213         [ -  + ]:        404 :                 if (!strcmp(h->name, exp_info->name)) {
     214                 :          0 :                         mutex_unlock(&heap_list_lock);
     215                 :          0 :                         pr_err("dma_heap: Already registered heap named %s\n",
     216                 :            :                                exp_info->name);
     217                 :          0 :                         return ERR_PTR(-EINVAL);
     218                 :            :                 }
     219                 :            :         }
     220                 :        808 :         mutex_unlock(&heap_list_lock);
     221                 :            : 
     222                 :        808 :         heap = kzalloc(sizeof(*heap), GFP_KERNEL);
     223         [ +  - ]:        808 :         if (!heap)
     224                 :            :                 return ERR_PTR(-ENOMEM);
     225                 :            : 
     226                 :        808 :         heap->name = exp_info->name;
     227                 :        808 :         heap->ops = exp_info->ops;
     228                 :        808 :         heap->priv = exp_info->priv;
     229                 :            : 
     230                 :            :         /* Find unused minor number */
     231                 :        808 :         ret = xa_alloc(&dma_heap_minors, &minor, heap,
     232                 :        808 :                        XA_LIMIT(0, NUM_HEAP_MINORS - 1), GFP_KERNEL);
     233         [ -  + ]:        808 :         if (ret < 0) {
     234                 :          0 :                 pr_err("dma_heap: Unable to get minor number for heap\n");
     235                 :            :                 err_ret = ERR_PTR(ret);
     236                 :          0 :                 goto err0;
     237                 :            :         }
     238                 :            : 
     239                 :            :         /* Create device */
     240                 :        808 :         heap->heap_devt = MKDEV(MAJOR(dma_heap_devt), minor);
     241                 :            : 
     242                 :        808 :         cdev_init(&heap->heap_cdev, &dma_heap_fops);
     243                 :        808 :         ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1);
     244         [ -  + ]:        808 :         if (ret < 0) {
     245                 :          0 :                 pr_err("dma_heap: Unable to add char device\n");
     246                 :            :                 err_ret = ERR_PTR(ret);
     247                 :          0 :                 goto err1;
     248                 :            :         }
     249                 :            : 
     250                 :        808 :         dev_ret = device_create(dma_heap_class,
     251                 :            :                                 NULL,
     252                 :            :                                 heap->heap_devt,
     253                 :            :                                 NULL,
     254                 :            :                                 heap->name);
     255         [ -  + ]:        808 :         if (IS_ERR(dev_ret)) {
     256                 :          0 :                 pr_err("dma_heap: Unable to create device\n");
     257                 :            :                 err_ret = ERR_CAST(dev_ret);
     258                 :            :                 goto err2;
     259                 :            :         }
     260                 :            :         /* Add heap to the list */
     261                 :        808 :         mutex_lock(&heap_list_lock);
     262                 :        808 :         list_add(&heap->list, &heap_list);
     263                 :        808 :         mutex_unlock(&heap_list_lock);
     264                 :            : 
     265                 :        808 :         return heap;
     266                 :            : 
     267                 :            : err2:
     268                 :          0 :         cdev_del(&heap->heap_cdev);
     269                 :            : err1:
     270                 :          0 :         xa_erase(&dma_heap_minors, minor);
     271                 :            : err0:
     272                 :          0 :         kfree(heap);
     273                 :          0 :         return err_ret;
     274                 :            : }
     275                 :            : 
     276                 :       4044 : static char *dma_heap_devnode(struct device *dev, umode_t *mode)
     277                 :            : {
     278                 :       4044 :         return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev));
     279                 :            : }
     280                 :            : 
     281                 :        404 : static int dma_heap_init(void)
     282                 :            : {
     283                 :            :         int ret;
     284                 :            : 
     285                 :        404 :         ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
     286         [ +  - ]:        404 :         if (ret)
     287                 :            :                 return ret;
     288                 :            : 
     289                 :        404 :         dma_heap_class = class_create(THIS_MODULE, DEVNAME);
     290         [ -  + ]:        404 :         if (IS_ERR(dma_heap_class)) {
     291                 :          0 :                 unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
     292                 :          0 :                 return PTR_ERR(dma_heap_class);
     293                 :            :         }
     294                 :        404 :         dma_heap_class->devnode = dma_heap_devnode;
     295                 :            : 
     296                 :        404 :         return 0;
     297                 :            : }
     298                 :            : subsys_initcall(dma_heap_init);

Generated by: LCOV version 1.14