Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.6-rc7 222 lines 5.1 kB view raw
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");