linux/drivers/dma-buf/dma-heap.c
<<
>>
Prefs
   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 */
  36struct 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
  45static LIST_HEAD(heap_list);
  46static DEFINE_MUTEX(heap_list_lock);
  47static dev_t dma_heap_devt;
  48static struct class *dma_heap_class;
  49static DEFINE_XARRAY_ALLOC(dma_heap_minors);
  50
  51static 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        len = PAGE_ALIGN(len);
  60        if (!len)
  61                return -EINVAL;
  62
  63        return heap->ops->allocate(heap, len, fd_flags, heap_flags);
  64}
  65
  66static int dma_heap_open(struct inode *inode, struct file *file)
  67{
  68        struct dma_heap *heap;
  69
  70        heap = xa_load(&dma_heap_minors, iminor(inode));
  71        if (!heap) {
  72                pr_err("dma_heap: minor %d unknown.\n", iminor(inode));
  73                return -ENODEV;
  74        }
  75
  76        /* instance data as context */
  77        file->private_data = heap;
  78        nonseekable_open(inode, file);
  79
  80        return 0;
  81}
  82
  83static long dma_heap_ioctl_allocate(struct file *file, void *data)
  84{
  85        struct dma_heap_allocation_data *heap_allocation = data;
  86        struct dma_heap *heap = file->private_data;
  87        int fd;
  88
  89        if (heap_allocation->fd)
  90                return -EINVAL;
  91
  92        if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
  93                return -EINVAL;
  94
  95        if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
  96                return -EINVAL;
  97
  98        fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
  99                                   heap_allocation->fd_flags,
 100                                   heap_allocation->heap_flags);
 101        if (fd < 0)
 102                return fd;
 103
 104        heap_allocation->fd = fd;
 105
 106        return 0;
 107}
 108
 109static unsigned int dma_heap_ioctl_cmds[] = {
 110        DMA_HEAP_IOCTL_ALLOC,
 111};
 112
 113static 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        int nr = _IOC_NR(ucmd);
 121        int ret = 0;
 122
 123        if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
 124                return -EINVAL;
 125
 126        /* Get the kernel ioctl cmd that matches */
 127        kcmd = dma_heap_ioctl_cmds[nr];
 128
 129        /* Figure out the delta between user cmd size and kernel cmd size */
 130        drv_size = _IOC_SIZE(kcmd);
 131        out_size = _IOC_SIZE(ucmd);
 132        in_size = out_size;
 133        if ((ucmd & kcmd & IOC_IN) == 0)
 134                in_size = 0;
 135        if ((ucmd & kcmd & IOC_OUT) == 0)
 136                out_size = 0;
 137        ksize = max(max(in_size, out_size), drv_size);
 138
 139        /* If necessary, allocate buffer for ioctl argument */
 140        if (ksize > sizeof(stack_kdata)) {
 141                kdata = kmalloc(ksize, GFP_KERNEL);
 142                if (!kdata)
 143                        return -ENOMEM;
 144        }
 145
 146        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        if (ksize > in_size)
 153                memset(kdata + in_size, 0, ksize - in_size);
 154
 155        switch (kcmd) {
 156        case DMA_HEAP_IOCTL_ALLOC:
 157                ret = dma_heap_ioctl_allocate(file, kdata);
 158                break;
 159        default:
 160                ret = -ENOTTY;
 161                goto err;
 162        }
 163
 164        if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
 165                ret = -EFAULT;
 166err:
 167        if (kdata != stack_kdata)
 168                kfree(kdata);
 169        return ret;
 170}
 171
 172static 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 */
 188void *dma_heap_get_drvdata(struct dma_heap *heap)
 189{
 190        return heap->priv;
 191}
 192
 193struct 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        if (!exp_info->name || !strcmp(exp_info->name, "")) {
 201                pr_err("dma_heap: Cannot add heap without a name\n");
 202                return ERR_PTR(-EINVAL);
 203        }
 204
 205        if (!exp_info->ops || !exp_info->ops->allocate) {
 206                pr_err("dma_heap: Cannot add heap with invalid ops struct\n");
 207                return ERR_PTR(-EINVAL);
 208        }
 209
 210        /* check the name is unique */
 211        mutex_lock(&heap_list_lock);
 212        list_for_each_entry(h, &heap_list, list) {
 213                if (!strcmp(h->name, exp_info->name)) {
 214                        mutex_unlock(&heap_list_lock);
 215                        pr_err("dma_heap: Already registered heap named %s\n",
 216                               exp_info->name);
 217                        return ERR_PTR(-EINVAL);
 218                }
 219        }
 220        mutex_unlock(&heap_list_lock);
 221
 222        heap = kzalloc(sizeof(*heap), GFP_KERNEL);
 223        if (!heap)
 224                return ERR_PTR(-ENOMEM);
 225
 226        heap->name = exp_info->name;
 227        heap->ops = exp_info->ops;
 228        heap->priv = exp_info->priv;
 229
 230        /* Find unused minor number */
 231        ret = xa_alloc(&dma_heap_minors, &minor, heap,
 232                       XA_LIMIT(0, NUM_HEAP_MINORS - 1), GFP_KERNEL);
 233        if (ret < 0) {
 234                pr_err("dma_heap: Unable to get minor number for heap\n");
 235                err_ret = ERR_PTR(ret);
 236                goto err0;
 237        }
 238
 239        /* Create device */
 240        heap->heap_devt = MKDEV(MAJOR(dma_heap_devt), minor);
 241
 242        cdev_init(&heap->heap_cdev, &dma_heap_fops);
 243        ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1);
 244        if (ret < 0) {
 245                pr_err("dma_heap: Unable to add char device\n");
 246                err_ret = ERR_PTR(ret);
 247                goto err1;
 248        }
 249
 250        dev_ret = device_create(dma_heap_class,
 251                                NULL,
 252                                heap->heap_devt,
 253                                NULL,
 254                                heap->name);
 255        if (IS_ERR(dev_ret)) {
 256                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        mutex_lock(&heap_list_lock);
 262        list_add(&heap->list, &heap_list);
 263        mutex_unlock(&heap_list_lock);
 264
 265        return heap;
 266
 267err2:
 268        cdev_del(&heap->heap_cdev);
 269err1:
 270        xa_erase(&dma_heap_minors, minor);
 271err0:
 272        kfree(heap);
 273        return err_ret;
 274}
 275
 276static char *dma_heap_devnode(struct device *dev, umode_t *mode)
 277{
 278        return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev));
 279}
 280
 281static int dma_heap_init(void)
 282{
 283        int ret;
 284
 285        ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
 286        if (ret)
 287                return ret;
 288
 289        dma_heap_class = class_create(THIS_MODULE, DEVNAME);
 290        if (IS_ERR(dma_heap_class)) {
 291                unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
 292                return PTR_ERR(dma_heap_class);
 293        }
 294        dma_heap_class->devnode = dma_heap_devnode;
 295
 296        return 0;
 297}
 298subsys_initcall(dma_heap_init);
 299