diff options
author | James Harper <james.harper@ejbdigital.com.au> | 2014-06-12 05:53:38 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-07-26 10:33:15 -0400 |
commit | 7b4eeed174b71c325705ff8c53f333bc79d0ee7a (patch) | |
tree | 3f23ad213911ab98dd30694109c6c0bcfe17afd7 | |
parent | b601fe5688ae285693c64e833003c14acb38378a (diff) |
[media] vmalloc_sg: make sure all pages in vmalloc area are really DMA-ready
Patch originally written by Konrad. Rebased on current linux media tree.
Under Xen, vmalloc_32() isn't guaranteed to return pages which are really
under 4G in machine physical addresses (only in virtual pseudo-physical
addresses). To work around this, implement a vmalloc variant which
allocates each page with dma_alloc_coherent() to guarantee that each
page is suitable for the device in question.
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: James Harper <james.harper@ejbdigital.com.au>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r-- | drivers/media/v4l2-core/videobuf-dma-sg.c | 62 | ||||
-rw-r--r-- | include/media/videobuf-dma-sg.h | 3 |
2 files changed, 61 insertions, 4 deletions
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 828e7c10bd70..3c8cc023a5a5 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c | |||
@@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user); | |||
211 | int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, | 211 | int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, |
212 | int nr_pages) | 212 | int nr_pages) |
213 | { | 213 | { |
214 | int i; | ||
215 | |||
214 | dprintk(1, "init kernel [%d pages]\n", nr_pages); | 216 | dprintk(1, "init kernel [%d pages]\n", nr_pages); |
215 | 217 | ||
216 | dma->direction = direction; | 218 | dma->direction = direction; |
217 | dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); | 219 | dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages), |
220 | GFP_KERNEL); | ||
221 | if (!dma->vaddr_pages) | ||
222 | return -ENOMEM; | ||
223 | |||
224 | dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL); | ||
225 | if (!dma->dma_addr) { | ||
226 | kfree(dma->vaddr_pages); | ||
227 | return -ENOMEM; | ||
228 | } | ||
229 | for (i = 0; i < nr_pages; i++) { | ||
230 | void *addr; | ||
231 | |||
232 | addr = dma_alloc_coherent(dma->dev, PAGE_SIZE, | ||
233 | &(dma->dma_addr[i]), GFP_KERNEL); | ||
234 | if (addr == NULL) | ||
235 | goto out_free_pages; | ||
236 | |||
237 | dma->vaddr_pages[i] = virt_to_page(addr); | ||
238 | } | ||
239 | dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP, | ||
240 | PAGE_KERNEL); | ||
218 | if (NULL == dma->vaddr) { | 241 | if (NULL == dma->vaddr) { |
219 | dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); | 242 | dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); |
220 | return -ENOMEM; | 243 | goto out_free_pages; |
221 | } | 244 | } |
222 | 245 | ||
223 | dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", | 246 | dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", |
@@ -228,6 +251,19 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, | |||
228 | dma->nr_pages = nr_pages; | 251 | dma->nr_pages = nr_pages; |
229 | 252 | ||
230 | return 0; | 253 | return 0; |
254 | out_free_pages: | ||
255 | while (i > 0) { | ||
256 | void *addr = page_address(dma->vaddr_pages[i]); | ||
257 | dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]); | ||
258 | i--; | ||
259 | } | ||
260 | kfree(dma->dma_addr); | ||
261 | dma->dma_addr = NULL; | ||
262 | kfree(dma->vaddr_pages); | ||
263 | dma->vaddr_pages = NULL; | ||
264 | |||
265 | return -ENOMEM; | ||
266 | |||
231 | } | 267 | } |
232 | EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); | 268 | EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); |
233 | 269 | ||
@@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) | |||
322 | dma->pages = NULL; | 358 | dma->pages = NULL; |
323 | } | 359 | } |
324 | 360 | ||
325 | vfree(dma->vaddr); | 361 | if (dma->dma_addr) { |
326 | dma->vaddr = NULL; | 362 | for (i = 0; i < dma->nr_pages; i++) { |
363 | void *addr; | ||
364 | |||
365 | addr = page_address(dma->vaddr_pages[i]); | ||
366 | dma_free_coherent(dma->dev, PAGE_SIZE, addr, | ||
367 | dma->dma_addr[i]); | ||
368 | } | ||
369 | kfree(dma->dma_addr); | ||
370 | dma->dma_addr = NULL; | ||
371 | kfree(dma->vaddr_pages); | ||
372 | dma->vaddr_pages = NULL; | ||
373 | vunmap(dma->vaddr); | ||
374 | dma->vaddr = NULL; | ||
375 | } | ||
327 | 376 | ||
328 | if (dma->bus_addr) | 377 | if (dma->bus_addr) |
329 | dma->bus_addr = 0; | 378 | dma->bus_addr = 0; |
@@ -461,6 +510,11 @@ static int __videobuf_iolock(struct videobuf_queue *q, | |||
461 | 510 | ||
462 | MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); | 511 | MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); |
463 | 512 | ||
513 | if (!mem->dma.dev) | ||
514 | mem->dma.dev = q->dev; | ||
515 | else | ||
516 | WARN_ON(mem->dma.dev != q->dev); | ||
517 | |||
464 | switch (vb->memory) { | 518 | switch (vb->memory) { |
465 | case V4L2_MEMORY_MMAP: | 519 | case V4L2_MEMORY_MMAP: |
466 | case V4L2_MEMORY_USERPTR: | 520 | case V4L2_MEMORY_USERPTR: |
diff --git a/include/media/videobuf-dma-sg.h b/include/media/videobuf-dma-sg.h index d8fb6012c10d..fb6fd4d8f4ed 100644 --- a/include/media/videobuf-dma-sg.h +++ b/include/media/videobuf-dma-sg.h | |||
@@ -53,6 +53,9 @@ struct videobuf_dmabuf { | |||
53 | 53 | ||
54 | /* for kernel buffers */ | 54 | /* for kernel buffers */ |
55 | void *vaddr; | 55 | void *vaddr; |
56 | struct page **vaddr_pages; | ||
57 | dma_addr_t *dma_addr; | ||
58 | struct device *dev; | ||
56 | 59 | ||
57 | /* for overlay buffers (pci-pci dma) */ | 60 | /* for overlay buffers (pci-pci dma) */ |
58 | dma_addr_t bus_addr; | 61 | dma_addr_t bus_addr; |