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