root/drivers/dma-buf/udmabuf.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. udmabuf_vm_fault
  2. mmap_udmabuf
  3. map_udmabuf
  4. unmap_udmabuf
  5. release_udmabuf
  6. kmap_udmabuf
  7. kunmap_udmabuf
  8. udmabuf_create
  9. udmabuf_ioctl_create
  10. udmabuf_ioctl_create_list
  11. udmabuf_ioctl
  12. udmabuf_dev_init
  13. udmabuf_dev_exit

   1 // SPDX-License-Identifier: GPL-2.0
   2 #include <linux/cred.h>
   3 #include <linux/device.h>
   4 #include <linux/dma-buf.h>
   5 #include <linux/highmem.h>
   6 #include <linux/init.h>
   7 #include <linux/kernel.h>
   8 #include <linux/memfd.h>
   9 #include <linux/miscdevice.h>
  10 #include <linux/module.h>
  11 #include <linux/shmem_fs.h>
  12 #include <linux/slab.h>
  13 #include <linux/udmabuf.h>
  14 
  15 static const u32    list_limit = 1024;  /* udmabuf_create_list->count limit */
  16 static const size_t size_limit_mb = 64; /* total dmabuf size, in megabytes  */
  17 
  18 struct udmabuf {
  19         pgoff_t pagecount;
  20         struct page **pages;
  21 };
  22 
  23 static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf)
  24 {
  25         struct vm_area_struct *vma = vmf->vma;
  26         struct udmabuf *ubuf = vma->vm_private_data;
  27 
  28         vmf->page = ubuf->pages[vmf->pgoff];
  29         get_page(vmf->page);
  30         return 0;
  31 }
  32 
  33 static const struct vm_operations_struct udmabuf_vm_ops = {
  34         .fault = udmabuf_vm_fault,
  35 };
  36 
  37 static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma)
  38 {
  39         struct udmabuf *ubuf = buf->priv;
  40 
  41         if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
  42                 return -EINVAL;
  43 
  44         vma->vm_ops = &udmabuf_vm_ops;
  45         vma->vm_private_data = ubuf;
  46         return 0;
  47 }
  48 
  49 static struct sg_table *map_udmabuf(struct dma_buf_attachment *at,
  50                                     enum dma_data_direction direction)
  51 {
  52         struct udmabuf *ubuf = at->dmabuf->priv;
  53         struct sg_table *sg;
  54         int ret;
  55 
  56         sg = kzalloc(sizeof(*sg), GFP_KERNEL);
  57         if (!sg)
  58                 return ERR_PTR(-ENOMEM);
  59         ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount,
  60                                         0, ubuf->pagecount << PAGE_SHIFT,
  61                                         GFP_KERNEL);
  62         if (ret < 0)
  63                 goto err;
  64         if (!dma_map_sg(at->dev, sg->sgl, sg->nents, direction)) {
  65                 ret = -EINVAL;
  66                 goto err;
  67         }
  68         return sg;
  69 
  70 err:
  71         sg_free_table(sg);
  72         kfree(sg);
  73         return ERR_PTR(ret);
  74 }
  75 
  76 static void unmap_udmabuf(struct dma_buf_attachment *at,
  77                           struct sg_table *sg,
  78                           enum dma_data_direction direction)
  79 {
  80         dma_unmap_sg(at->dev, sg->sgl, sg->nents, direction);
  81         sg_free_table(sg);
  82         kfree(sg);
  83 }
  84 
  85 static void release_udmabuf(struct dma_buf *buf)
  86 {
  87         struct udmabuf *ubuf = buf->priv;
  88         pgoff_t pg;
  89 
  90         for (pg = 0; pg < ubuf->pagecount; pg++)
  91                 put_page(ubuf->pages[pg]);
  92         kfree(ubuf->pages);
  93         kfree(ubuf);
  94 }
  95 
  96 static void *kmap_udmabuf(struct dma_buf *buf, unsigned long page_num)
  97 {
  98         struct udmabuf *ubuf = buf->priv;
  99         struct page *page = ubuf->pages[page_num];
 100 
 101         return kmap(page);
 102 }
 103 
 104 static void kunmap_udmabuf(struct dma_buf *buf, unsigned long page_num,
 105                            void *vaddr)
 106 {
 107         kunmap(vaddr);
 108 }
 109 
 110 static const struct dma_buf_ops udmabuf_ops = {
 111         .map_dma_buf      = map_udmabuf,
 112         .unmap_dma_buf    = unmap_udmabuf,
 113         .release          = release_udmabuf,
 114         .map              = kmap_udmabuf,
 115         .unmap            = kunmap_udmabuf,
 116         .mmap             = mmap_udmabuf,
 117 };
 118 
 119 #define SEALS_WANTED (F_SEAL_SHRINK)
 120 #define SEALS_DENIED (F_SEAL_WRITE)
 121 
 122 static long udmabuf_create(const struct udmabuf_create_list *head,
 123                            const struct udmabuf_create_item *list)
 124 {
 125         DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
 126         struct file *memfd = NULL;
 127         struct udmabuf *ubuf;
 128         struct dma_buf *buf;
 129         pgoff_t pgoff, pgcnt, pgidx, pgbuf = 0, pglimit;
 130         struct page *page;
 131         int seals, ret = -EINVAL;
 132         u32 i, flags;
 133 
 134         ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL);
 135         if (!ubuf)
 136                 return -ENOMEM;
 137 
 138         pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;
 139         for (i = 0; i < head->count; i++) {
 140                 if (!IS_ALIGNED(list[i].offset, PAGE_SIZE))
 141                         goto err;
 142                 if (!IS_ALIGNED(list[i].size, PAGE_SIZE))
 143                         goto err;
 144                 ubuf->pagecount += list[i].size >> PAGE_SHIFT;
 145                 if (ubuf->pagecount > pglimit)
 146                         goto err;
 147         }
 148         ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(*ubuf->pages),
 149                                     GFP_KERNEL);
 150         if (!ubuf->pages) {
 151                 ret = -ENOMEM;
 152                 goto err;
 153         }
 154 
 155         pgbuf = 0;
 156         for (i = 0; i < head->count; i++) {
 157                 ret = -EBADFD;
 158                 memfd = fget(list[i].memfd);
 159                 if (!memfd)
 160                         goto err;
 161                 if (!shmem_mapping(file_inode(memfd)->i_mapping))
 162                         goto err;
 163                 seals = memfd_fcntl(memfd, F_GET_SEALS, 0);
 164                 if (seals == -EINVAL)
 165                         goto err;
 166                 ret = -EINVAL;
 167                 if ((seals & SEALS_WANTED) != SEALS_WANTED ||
 168                     (seals & SEALS_DENIED) != 0)
 169                         goto err;
 170                 pgoff = list[i].offset >> PAGE_SHIFT;
 171                 pgcnt = list[i].size   >> PAGE_SHIFT;
 172                 for (pgidx = 0; pgidx < pgcnt; pgidx++) {
 173                         page = shmem_read_mapping_page(
 174                                 file_inode(memfd)->i_mapping, pgoff + pgidx);
 175                         if (IS_ERR(page)) {
 176                                 ret = PTR_ERR(page);
 177                                 goto err;
 178                         }
 179                         ubuf->pages[pgbuf++] = page;
 180                 }
 181                 fput(memfd);
 182                 memfd = NULL;
 183         }
 184 
 185         exp_info.ops  = &udmabuf_ops;
 186         exp_info.size = ubuf->pagecount << PAGE_SHIFT;
 187         exp_info.priv = ubuf;
 188         exp_info.flags = O_RDWR;
 189 
 190         buf = dma_buf_export(&exp_info);
 191         if (IS_ERR(buf)) {
 192                 ret = PTR_ERR(buf);
 193                 goto err;
 194         }
 195 
 196         flags = 0;
 197         if (head->flags & UDMABUF_FLAGS_CLOEXEC)
 198                 flags |= O_CLOEXEC;
 199         return dma_buf_fd(buf, flags);
 200 
 201 err:
 202         while (pgbuf > 0)
 203                 put_page(ubuf->pages[--pgbuf]);
 204         if (memfd)
 205                 fput(memfd);
 206         kfree(ubuf->pages);
 207         kfree(ubuf);
 208         return ret;
 209 }
 210 
 211 static long udmabuf_ioctl_create(struct file *filp, unsigned long arg)
 212 {
 213         struct udmabuf_create create;
 214         struct udmabuf_create_list head;
 215         struct udmabuf_create_item list;
 216 
 217         if (copy_from_user(&create, (void __user *)arg,
 218                            sizeof(create)))
 219                 return -EFAULT;
 220 
 221         head.flags  = create.flags;
 222         head.count  = 1;
 223         list.memfd  = create.memfd;
 224         list.offset = create.offset;
 225         list.size   = create.size;
 226 
 227         return udmabuf_create(&head, &list);
 228 }
 229 
 230 static long udmabuf_ioctl_create_list(struct file *filp, unsigned long arg)
 231 {
 232         struct udmabuf_create_list head;
 233         struct udmabuf_create_item *list;
 234         int ret = -EINVAL;
 235         u32 lsize;
 236 
 237         if (copy_from_user(&head, (void __user *)arg, sizeof(head)))
 238                 return -EFAULT;
 239         if (head.count > list_limit)
 240                 return -EINVAL;
 241         lsize = sizeof(struct udmabuf_create_item) * head.count;
 242         list = memdup_user((void __user *)(arg + sizeof(head)), lsize);
 243         if (IS_ERR(list))
 244                 return PTR_ERR(list);
 245 
 246         ret = udmabuf_create(&head, list);
 247         kfree(list);
 248         return ret;
 249 }
 250 
 251 static long udmabuf_ioctl(struct file *filp, unsigned int ioctl,
 252                           unsigned long arg)
 253 {
 254         long ret;
 255 
 256         switch (ioctl) {
 257         case UDMABUF_CREATE:
 258                 ret = udmabuf_ioctl_create(filp, arg);
 259                 break;
 260         case UDMABUF_CREATE_LIST:
 261                 ret = udmabuf_ioctl_create_list(filp, arg);
 262                 break;
 263         default:
 264                 ret = -ENOTTY;
 265                 break;
 266         }
 267         return ret;
 268 }
 269 
 270 static const struct file_operations udmabuf_fops = {
 271         .owner          = THIS_MODULE,
 272         .unlocked_ioctl = udmabuf_ioctl,
 273 };
 274 
 275 static struct miscdevice udmabuf_misc = {
 276         .minor          = MISC_DYNAMIC_MINOR,
 277         .name           = "udmabuf",
 278         .fops           = &udmabuf_fops,
 279 };
 280 
 281 static int __init udmabuf_dev_init(void)
 282 {
 283         return misc_register(&udmabuf_misc);
 284 }
 285 
 286 static void __exit udmabuf_dev_exit(void)
 287 {
 288         misc_deregister(&udmabuf_misc);
 289 }
 290 
 291 module_init(udmabuf_dev_init)
 292 module_exit(udmabuf_dev_exit)
 293 
 294 MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
 295 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */