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.
13 #include <linux/module.h>
15 #include <linux/sched.h>
16 #include <linux/slab.h>
17 #include <linux/vmalloc.h>
19 #include <media/videobuf2-core.h>
20 #include <media/videobuf2-memops.h>
22 struct vb2_vmalloc_buf
{
29 struct vb2_vmarea_handler handler
;
32 static void vb2_vmalloc_put(void *buf_priv
);
34 static void *vb2_vmalloc_alloc(void *alloc_ctx
, unsigned long size
)
36 struct vb2_vmalloc_buf
*buf
;
38 buf
= kzalloc(sizeof(*buf
), GFP_KERNEL
);
43 buf
->vaddr
= vmalloc_user(buf
->size
);
44 buf
->handler
.refcount
= &buf
->refcount
;
45 buf
->handler
.put
= vb2_vmalloc_put
;
46 buf
->handler
.arg
= buf
;
49 pr_debug("vmalloc of size %ld failed\n", buf
->size
);
54 atomic_inc(&buf
->refcount
);
58 static void vb2_vmalloc_put(void *buf_priv
)
60 struct vb2_vmalloc_buf
*buf
= buf_priv
;
62 if (atomic_dec_and_test(&buf
->refcount
)) {
68 static void *vb2_vmalloc_get_userptr(void *alloc_ctx
, unsigned long vaddr
,
69 unsigned long size
, int write
)
71 struct vb2_vmalloc_buf
*buf
;
72 unsigned long first
, last
;
75 buf
= kzalloc(sizeof(*buf
), GFP_KERNEL
);
80 offset
= vaddr
& ~PAGE_MASK
;
83 first
= vaddr
>> PAGE_SHIFT
;
84 last
= (vaddr
+ size
- 1) >> PAGE_SHIFT
;
85 buf
->n_pages
= last
- first
+ 1;
86 buf
->pages
= kzalloc(buf
->n_pages
* sizeof(struct page
*), GFP_KERNEL
);
88 goto fail_pages_array_alloc
;
90 /* current->mm->mmap_sem is taken by videobuf2 core */
91 n_pages
= get_user_pages(current
, current
->mm
, vaddr
& PAGE_MASK
,
92 buf
->n_pages
, write
, 1, /* force */
94 if (n_pages
!= buf
->n_pages
)
95 goto fail_get_user_pages
;
97 buf
->vaddr
= vm_map_ram(buf
->pages
, buf
->n_pages
, -1, PAGE_KERNEL
);
99 goto fail_get_user_pages
;
101 buf
->vaddr
+= offset
;
105 pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages
,
107 while (--n_pages
>= 0)
108 put_page(buf
->pages
[n_pages
]);
111 fail_pages_array_alloc
:
117 static void vb2_vmalloc_put_userptr(void *buf_priv
)
119 struct vb2_vmalloc_buf
*buf
= buf_priv
;
120 unsigned long vaddr
= (unsigned long)buf
->vaddr
& PAGE_MASK
;
124 vm_unmap_ram((void *)vaddr
, buf
->n_pages
);
125 for (i
= 0; i
< buf
->n_pages
; ++i
) {
127 set_page_dirty_lock(buf
->pages
[i
]);
128 put_page(buf
->pages
[i
]);
134 static void *vb2_vmalloc_vaddr(void *buf_priv
)
136 struct vb2_vmalloc_buf
*buf
= buf_priv
;
139 pr_err("Address of an unallocated plane requested "
140 "or cannot map user pointer\n");
147 static unsigned int vb2_vmalloc_num_users(void *buf_priv
)
149 struct vb2_vmalloc_buf
*buf
= buf_priv
;
150 return atomic_read(&buf
->refcount
);
153 static int vb2_vmalloc_mmap(void *buf_priv
, struct vm_area_struct
*vma
)
155 struct vb2_vmalloc_buf
*buf
= buf_priv
;
159 pr_err("No memory to map\n");
163 ret
= remap_vmalloc_range(vma
, buf
->vaddr
, 0);
165 pr_err("Remapping vmalloc memory, error: %d\n", ret
);
170 * Make sure that vm_areas for 2 buffers won't be merged together
172 vma
->vm_flags
|= VM_DONTEXPAND
;
175 * Use common vm_area operations to track buffer refcount.
177 vma
->vm_private_data
= &buf
->handler
;
178 vma
->vm_ops
= &vb2_common_vm_ops
;
180 vma
->vm_ops
->open(vma
);
185 const struct vb2_mem_ops vb2_vmalloc_memops
= {
186 .alloc
= vb2_vmalloc_alloc
,
187 .put
= vb2_vmalloc_put
,
188 .get_userptr
= vb2_vmalloc_get_userptr
,
189 .put_userptr
= vb2_vmalloc_put_userptr
,
190 .vaddr
= vb2_vmalloc_vaddr
,
191 .mmap
= vb2_vmalloc_mmap
,
192 .num_users
= vb2_vmalloc_num_users
,
194 EXPORT_SYMBOL_GPL(vb2_vmalloc_memops
);
196 MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
197 MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
198 MODULE_LICENSE("GPL");