linux/drivers/media/video/videobuf2-vmalloc.c
<<
>>
Prefs
   1/*
   2 * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
   3 *
   4 * Copyright (C) 2010 Samsung Electronics
   5 *
   6 * Author: Pawel Osciak <pawel@osciak.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation.
  11 */
  12
  13#include <linux/io.h>
  14#include <linux/module.h>
  15#include <linux/mm.h>
  16#include <linux/sched.h>
  17#include <linux/slab.h>
  18#include <linux/vmalloc.h>
  19
  20#include <media/videobuf2-core.h>
  21#include <media/videobuf2-memops.h>
  22
  23struct vb2_vmalloc_buf {
  24        void                            *vaddr;
  25        struct page                     **pages;
  26        struct vm_area_struct           *vma;
  27        int                             write;
  28        unsigned long                   size;
  29        unsigned int                    n_pages;
  30        atomic_t                        refcount;
  31        struct vb2_vmarea_handler       handler;
  32};
  33
  34static void vb2_vmalloc_put(void *buf_priv);
  35
  36static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
  37{
  38        struct vb2_vmalloc_buf *buf;
  39
  40        buf = kzalloc(sizeof(*buf), GFP_KERNEL);
  41        if (!buf)
  42                return NULL;
  43
  44        buf->size = size;
  45        buf->vaddr = vmalloc_user(buf->size);
  46        buf->handler.refcount = &buf->refcount;
  47        buf->handler.put = vb2_vmalloc_put;
  48        buf->handler.arg = buf;
  49
  50        if (!buf->vaddr) {
  51                pr_debug("vmalloc of size %ld failed\n", buf->size);
  52                kfree(buf);
  53                return NULL;
  54        }
  55
  56        atomic_inc(&buf->refcount);
  57        return buf;
  58}
  59
  60static void vb2_vmalloc_put(void *buf_priv)
  61{
  62        struct vb2_vmalloc_buf *buf = buf_priv;
  63
  64        if (atomic_dec_and_test(&buf->refcount)) {
  65                vfree(buf->vaddr);
  66                kfree(buf);
  67        }
  68}
  69
  70static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
  71                                     unsigned long size, int write)
  72{
  73        struct vb2_vmalloc_buf *buf;
  74        unsigned long first, last;
  75        int n_pages, offset;
  76        struct vm_area_struct *vma;
  77        dma_addr_t physp;
  78
  79        buf = kzalloc(sizeof(*buf), GFP_KERNEL);
  80        if (!buf)
  81                return NULL;
  82
  83        buf->write = write;
  84        offset = vaddr & ~PAGE_MASK;
  85        buf->size = size;
  86
  87
  88        vma = find_vma(current->mm, vaddr);
  89        if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
  90                if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
  91                        goto fail_pages_array_alloc;
  92                buf->vma = vma;
  93                buf->vaddr = ioremap_nocache(physp, size);
  94                if (!buf->vaddr)
  95                        goto fail_pages_array_alloc;
  96        } else {
  97                first = vaddr >> PAGE_SHIFT;
  98                last  = (vaddr + size - 1) >> PAGE_SHIFT;
  99                buf->n_pages = last - first + 1;
 100                buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
 101                                     GFP_KERNEL);
 102                if (!buf->pages)
 103                        goto fail_pages_array_alloc;
 104
 105                /* current->mm->mmap_sem is taken by videobuf2 core */
 106                n_pages = get_user_pages(current, current->mm,
 107                                         vaddr & PAGE_MASK, buf->n_pages,
 108                                         write, 1, /* force */
 109                                         buf->pages, NULL);
 110                if (n_pages != buf->n_pages)
 111                        goto fail_get_user_pages;
 112
 113                buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
 114                                        PAGE_KERNEL);
 115                if (!buf->vaddr)
 116                        goto fail_get_user_pages;
 117        }
 118
 119        buf->vaddr += offset;
 120        return buf;
 121
 122fail_get_user_pages:
 123        pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
 124                 buf->n_pages);
 125        while (--n_pages >= 0)
 126                put_page(buf->pages[n_pages]);
 127        kfree(buf->pages);
 128
 129fail_pages_array_alloc:
 130        kfree(buf);
 131
 132        return NULL;
 133}
 134
 135static void vb2_vmalloc_put_userptr(void *buf_priv)
 136{
 137        struct vb2_vmalloc_buf *buf = buf_priv;
 138        unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
 139        unsigned int i;
 140
 141        if (buf->pages) {
 142                if (vaddr)
 143                        vm_unmap_ram((void *)vaddr, buf->n_pages);
 144                for (i = 0; i < buf->n_pages; ++i) {
 145                        if (buf->write)
 146                                set_page_dirty_lock(buf->pages[i]);
 147                        put_page(buf->pages[i]);
 148                }
 149                kfree(buf->pages);
 150        } else {
 151                if (buf->vma)
 152                        vb2_put_vma(buf->vma);
 153                iounmap(buf->vaddr);
 154        }
 155        kfree(buf);
 156}
 157
 158static void *vb2_vmalloc_vaddr(void *buf_priv)
 159{
 160        struct vb2_vmalloc_buf *buf = buf_priv;
 161
 162        if (!buf->vaddr) {
 163                pr_err("Address of an unallocated plane requested "
 164                       "or cannot map user pointer\n");
 165                return NULL;
 166        }
 167
 168        return buf->vaddr;
 169}
 170
 171static unsigned int vb2_vmalloc_num_users(void *buf_priv)
 172{
 173        struct vb2_vmalloc_buf *buf = buf_priv;
 174        return atomic_read(&buf->refcount);
 175}
 176
 177static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
 178{
 179        struct vb2_vmalloc_buf *buf = buf_priv;
 180        int ret;
 181
 182        if (!buf) {
 183                pr_err("No memory to map\n");
 184                return -EINVAL;
 185        }
 186
 187        ret = remap_vmalloc_range(vma, buf->vaddr, 0);
 188        if (ret) {
 189                pr_err("Remapping vmalloc memory, error: %d\n", ret);
 190                return ret;
 191        }
 192
 193        /*
 194         * Make sure that vm_areas for 2 buffers won't be merged together
 195         */
 196        vma->vm_flags           |= VM_DONTEXPAND;
 197
 198        /*
 199         * Use common vm_area operations to track buffer refcount.
 200         */
 201        vma->vm_private_data    = &buf->handler;
 202        vma->vm_ops             = &vb2_common_vm_ops;
 203
 204        vma->vm_ops->open(vma);
 205
 206        return 0;
 207}
 208
 209const struct vb2_mem_ops vb2_vmalloc_memops = {
 210        .alloc          = vb2_vmalloc_alloc,
 211        .put            = vb2_vmalloc_put,
 212        .get_userptr    = vb2_vmalloc_get_userptr,
 213        .put_userptr    = vb2_vmalloc_put_userptr,
 214        .vaddr          = vb2_vmalloc_vaddr,
 215        .mmap           = vb2_vmalloc_mmap,
 216        .num_users      = vb2_vmalloc_num_users,
 217};
 218EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
 219
 220MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
 221MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
 222MODULE_LICENSE("GPL");
 223