2 * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
4 * Copyright (C) 2010 Samsung Electronics
6 * Author: Pawel Osciak <pawel@osciak.com>
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.
14 #include <linux/module.h>
16 #include <linux/sched.h>
17 #include <linux/slab.h>
18 #include <linux/vmalloc.h>
20 #include <media/videobuf2-core.h>
21 #include <media/videobuf2-memops.h>
23 struct vb2_vmalloc_buf
{
26 struct vm_area_struct
*vma
;
31 struct vb2_vmarea_handler handler
;
34 static void vb2_vmalloc_put(void *buf_priv
);
36 static void *vb2_vmalloc_alloc(void *alloc_ctx
, unsigned long size
)
38 struct vb2_vmalloc_buf
*buf
;
40 buf
= kzalloc(sizeof(*buf
), GFP_KERNEL
);
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
;
51 pr_debug("vmalloc of size %ld failed\n", buf
->size
);
56 atomic_inc(&buf
->refcount
);
60 static void vb2_vmalloc_put(void *buf_priv
)
62 struct vb2_vmalloc_buf
*buf
= buf_priv
;
64 if (atomic_dec_and_test(&buf
->refcount
)) {
70 static void *vb2_vmalloc_get_userptr(void *alloc_ctx
, unsigned long vaddr
,
71 unsigned long size
, int write
)
73 struct vb2_vmalloc_buf
*buf
;
74 unsigned long first
, last
;
76 struct vm_area_struct
*vma
;
79 buf
= kzalloc(sizeof(*buf
), GFP_KERNEL
);
84 offset
= vaddr
& ~PAGE_MASK
;
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
;
93 buf
->vaddr
= ioremap_nocache(physp
, size
);
95 goto fail_pages_array_alloc
;
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
*),
103 goto fail_pages_array_alloc
;
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 */
110 if (n_pages
!= buf
->n_pages
)
111 goto fail_get_user_pages
;
113 buf
->vaddr
= vm_map_ram(buf
->pages
, buf
->n_pages
, -1,
116 goto fail_get_user_pages
;
119 buf
->vaddr
+= offset
;
123 pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages
,
125 while (--n_pages
>= 0)
126 put_page(buf
->pages
[n_pages
]);
129 fail_pages_array_alloc
:
135 static void vb2_vmalloc_put_userptr(void *buf_priv
)
137 struct vb2_vmalloc_buf
*buf
= buf_priv
;
138 unsigned long vaddr
= (unsigned long)buf
->vaddr
& PAGE_MASK
;
143 vm_unmap_ram((void *)vaddr
, buf
->n_pages
);
144 for (i
= 0; i
< buf
->n_pages
; ++i
) {
146 set_page_dirty_lock(buf
->pages
[i
]);
147 put_page(buf
->pages
[i
]);
152 vb2_put_vma(buf
->vma
);
158 static void *vb2_vmalloc_vaddr(void *buf_priv
)
160 struct vb2_vmalloc_buf
*buf
= buf_priv
;
163 pr_err("Address of an unallocated plane requested "
164 "or cannot map user pointer\n");
171 static unsigned int vb2_vmalloc_num_users(void *buf_priv
)
173 struct vb2_vmalloc_buf
*buf
= buf_priv
;
174 return atomic_read(&buf
->refcount
);
177 static int vb2_vmalloc_mmap(void *buf_priv
, struct vm_area_struct
*vma
)
179 struct vb2_vmalloc_buf
*buf
= buf_priv
;
183 pr_err("No memory to map\n");
187 ret
= remap_vmalloc_range(vma
, buf
->vaddr
, 0);
189 pr_err("Remapping vmalloc memory, error: %d\n", ret
);
194 * Make sure that vm_areas for 2 buffers won't be merged together
196 vma
->vm_flags
|= VM_DONTEXPAND
;
199 * Use common vm_area operations to track buffer refcount.
201 vma
->vm_private_data
= &buf
->handler
;
202 vma
->vm_ops
= &vb2_common_vm_ops
;
204 vma
->vm_ops
->open(vma
);
209 const 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
,
218 EXPORT_SYMBOL_GPL(vb2_vmalloc_memops
);
220 MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
221 MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
222 MODULE_LICENSE("GPL");