diff options
Diffstat (limited to 'drivers/media/video/videobuf2-vmalloc.c')
-rw-r--r-- | drivers/media/video/videobuf2-vmalloc.c | 90 |
1 files changed, 78 insertions, 12 deletions
diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c index a3a884234059..4e789a178f8a 100644 --- a/drivers/media/video/videobuf2-vmalloc.c +++ b/drivers/media/video/videobuf2-vmalloc.c | |||
@@ -12,6 +12,7 @@ | |||
12 | 12 | ||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/mm.h> | 14 | #include <linux/mm.h> |
15 | #include <linux/sched.h> | ||
15 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
16 | #include <linux/vmalloc.h> | 17 | #include <linux/vmalloc.h> |
17 | 18 | ||
@@ -20,7 +21,10 @@ | |||
20 | 21 | ||
21 | struct vb2_vmalloc_buf { | 22 | struct vb2_vmalloc_buf { |
22 | void *vaddr; | 23 | void *vaddr; |
24 | struct page **pages; | ||
25 | int write; | ||
23 | unsigned long size; | 26 | unsigned long size; |
27 | unsigned int n_pages; | ||
24 | atomic_t refcount; | 28 | atomic_t refcount; |
25 | struct vb2_vmarea_handler handler; | 29 | struct vb2_vmarea_handler handler; |
26 | }; | 30 | }; |
@@ -31,7 +35,7 @@ static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size) | |||
31 | { | 35 | { |
32 | struct vb2_vmalloc_buf *buf; | 36 | struct vb2_vmalloc_buf *buf; |
33 | 37 | ||
34 | buf = kzalloc(sizeof *buf, GFP_KERNEL); | 38 | buf = kzalloc(sizeof(*buf), GFP_KERNEL); |
35 | if (!buf) | 39 | if (!buf) |
36 | return NULL; | 40 | return NULL; |
37 | 41 | ||
@@ -42,15 +46,12 @@ static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size) | |||
42 | buf->handler.arg = buf; | 46 | buf->handler.arg = buf; |
43 | 47 | ||
44 | if (!buf->vaddr) { | 48 | if (!buf->vaddr) { |
45 | printk(KERN_ERR "vmalloc of size %ld failed\n", buf->size); | 49 | pr_debug("vmalloc of size %ld failed\n", buf->size); |
46 | kfree(buf); | 50 | kfree(buf); |
47 | return NULL; | 51 | return NULL; |
48 | } | 52 | } |
49 | 53 | ||
50 | atomic_inc(&buf->refcount); | 54 | atomic_inc(&buf->refcount); |
51 | printk(KERN_DEBUG "Allocated vmalloc buffer of size %ld at vaddr=%p\n", | ||
52 | buf->size, buf->vaddr); | ||
53 | |||
54 | return buf; | 55 | return buf; |
55 | } | 56 | } |
56 | 57 | ||
@@ -59,21 +60,84 @@ static void vb2_vmalloc_put(void *buf_priv) | |||
59 | struct vb2_vmalloc_buf *buf = buf_priv; | 60 | struct vb2_vmalloc_buf *buf = buf_priv; |
60 | 61 | ||
61 | if (atomic_dec_and_test(&buf->refcount)) { | 62 | if (atomic_dec_and_test(&buf->refcount)) { |
62 | printk(KERN_DEBUG "%s: Freeing vmalloc mem at vaddr=%p\n", | ||
63 | __func__, buf->vaddr); | ||
64 | vfree(buf->vaddr); | 63 | vfree(buf->vaddr); |
65 | kfree(buf); | 64 | kfree(buf); |
66 | } | 65 | } |
67 | } | 66 | } |
68 | 67 | ||
69 | static void *vb2_vmalloc_vaddr(void *buf_priv) | 68 | static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, |
69 | unsigned long size, int write) | ||
70 | { | ||
71 | struct vb2_vmalloc_buf *buf; | ||
72 | unsigned long first, last; | ||
73 | int n_pages, offset; | ||
74 | |||
75 | buf = kzalloc(sizeof(*buf), GFP_KERNEL); | ||
76 | if (!buf) | ||
77 | return NULL; | ||
78 | |||
79 | buf->write = write; | ||
80 | offset = vaddr & ~PAGE_MASK; | ||
81 | buf->size = size; | ||
82 | |||
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); | ||
87 | if (!buf->pages) | ||
88 | goto fail_pages_array_alloc; | ||
89 | |||
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 */ | ||
93 | buf->pages, NULL); | ||
94 | if (n_pages != buf->n_pages) | ||
95 | goto fail_get_user_pages; | ||
96 | |||
97 | buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, PAGE_KERNEL); | ||
98 | if (!buf->vaddr) | ||
99 | goto fail_get_user_pages; | ||
100 | |||
101 | buf->vaddr += offset; | ||
102 | return buf; | ||
103 | |||
104 | fail_get_user_pages: | ||
105 | pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, | ||
106 | buf->n_pages); | ||
107 | while (--n_pages >= 0) | ||
108 | put_page(buf->pages[n_pages]); | ||
109 | kfree(buf->pages); | ||
110 | |||
111 | fail_pages_array_alloc: | ||
112 | kfree(buf); | ||
113 | |||
114 | return NULL; | ||
115 | } | ||
116 | |||
117 | static void vb2_vmalloc_put_userptr(void *buf_priv) | ||
70 | { | 118 | { |
71 | struct vb2_vmalloc_buf *buf = buf_priv; | 119 | struct vb2_vmalloc_buf *buf = buf_priv; |
120 | unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; | ||
121 | unsigned int i; | ||
122 | |||
123 | if (vaddr) | ||
124 | vm_unmap_ram((void *)vaddr, buf->n_pages); | ||
125 | for (i = 0; i < buf->n_pages; ++i) { | ||
126 | if (buf->write) | ||
127 | set_page_dirty_lock(buf->pages[i]); | ||
128 | put_page(buf->pages[i]); | ||
129 | } | ||
130 | kfree(buf->pages); | ||
131 | kfree(buf); | ||
132 | } | ||
72 | 133 | ||
73 | BUG_ON(!buf); | 134 | static void *vb2_vmalloc_vaddr(void *buf_priv) |
135 | { | ||
136 | struct vb2_vmalloc_buf *buf = buf_priv; | ||
74 | 137 | ||
75 | if (!buf->vaddr) { | 138 | if (!buf->vaddr) { |
76 | printk(KERN_ERR "Address of an unallocated plane requested\n"); | 139 | pr_err("Address of an unallocated plane requested " |
140 | "or cannot map user pointer\n"); | ||
77 | return NULL; | 141 | return NULL; |
78 | } | 142 | } |
79 | 143 | ||
@@ -92,13 +156,13 @@ static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma) | |||
92 | int ret; | 156 | int ret; |
93 | 157 | ||
94 | if (!buf) { | 158 | if (!buf) { |
95 | printk(KERN_ERR "No memory to map\n"); | 159 | pr_err("No memory to map\n"); |
96 | return -EINVAL; | 160 | return -EINVAL; |
97 | } | 161 | } |
98 | 162 | ||
99 | ret = remap_vmalloc_range(vma, buf->vaddr, 0); | 163 | ret = remap_vmalloc_range(vma, buf->vaddr, 0); |
100 | if (ret) { | 164 | if (ret) { |
101 | printk(KERN_ERR "Remapping vmalloc memory, error: %d\n", ret); | 165 | pr_err("Remapping vmalloc memory, error: %d\n", ret); |
102 | return ret; | 166 | return ret; |
103 | } | 167 | } |
104 | 168 | ||
@@ -121,6 +185,8 @@ static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma) | |||
121 | const struct vb2_mem_ops vb2_vmalloc_memops = { | 185 | const struct vb2_mem_ops vb2_vmalloc_memops = { |
122 | .alloc = vb2_vmalloc_alloc, | 186 | .alloc = vb2_vmalloc_alloc, |
123 | .put = vb2_vmalloc_put, | 187 | .put = vb2_vmalloc_put, |
188 | .get_userptr = vb2_vmalloc_get_userptr, | ||
189 | .put_userptr = vb2_vmalloc_put_userptr, | ||
124 | .vaddr = vb2_vmalloc_vaddr, | 190 | .vaddr = vb2_vmalloc_vaddr, |
125 | .mmap = vb2_vmalloc_mmap, | 191 | .mmap = vb2_vmalloc_mmap, |
126 | .num_users = vb2_vmalloc_num_users, | 192 | .num_users = vb2_vmalloc_num_users, |