Skip to content

Commit

Permalink
[media] vmalloc_sg: make sure all pages in vmalloc area are really DM…
Browse files Browse the repository at this point in the history
…A-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>
  • Loading branch information
James Harper authored and Mauro Carvalho Chehab committed Jul 26, 2014
1 parent b601fe5 commit 7b4eeed
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 4 deletions.
62 changes: 58 additions & 4 deletions drivers/media/v4l2-core/videobuf-dma-sg.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
int nr_pages)
{
int i;

dprintk(1, "init kernel [%d pages]\n", nr_pages);

dma->direction = direction;
dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages),
GFP_KERNEL);
if (!dma->vaddr_pages)
return -ENOMEM;

dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL);
if (!dma->dma_addr) {
kfree(dma->vaddr_pages);
return -ENOMEM;
}
for (i = 0; i < nr_pages; i++) {
void *addr;

addr = dma_alloc_coherent(dma->dev, PAGE_SIZE,
&(dma->dma_addr[i]), GFP_KERNEL);
if (addr == NULL)
goto out_free_pages;

dma->vaddr_pages[i] = virt_to_page(addr);
}
dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP,
PAGE_KERNEL);
if (NULL == dma->vaddr) {
dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
return -ENOMEM;
goto out_free_pages;
}

dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
Expand All @@ -228,6 +251,19 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
dma->nr_pages = nr_pages;

return 0;
out_free_pages:
while (i > 0) {
void *addr = page_address(dma->vaddr_pages[i]);
dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]);
i--;
}
kfree(dma->dma_addr);
dma->dma_addr = NULL;
kfree(dma->vaddr_pages);
dma->vaddr_pages = NULL;

return -ENOMEM;

}
EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);

Expand Down Expand Up @@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
dma->pages = NULL;
}

vfree(dma->vaddr);
dma->vaddr = NULL;
if (dma->dma_addr) {
for (i = 0; i < dma->nr_pages; i++) {
void *addr;

addr = page_address(dma->vaddr_pages[i]);
dma_free_coherent(dma->dev, PAGE_SIZE, addr,
dma->dma_addr[i]);
}
kfree(dma->dma_addr);
dma->dma_addr = NULL;
kfree(dma->vaddr_pages);
dma->vaddr_pages = NULL;
vunmap(dma->vaddr);
dma->vaddr = NULL;
}

if (dma->bus_addr)
dma->bus_addr = 0;
Expand Down Expand Up @@ -461,6 +510,11 @@ static int __videobuf_iolock(struct videobuf_queue *q,

MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);

if (!mem->dma.dev)
mem->dma.dev = q->dev;
else
WARN_ON(mem->dma.dev != q->dev);

switch (vb->memory) {
case V4L2_MEMORY_MMAP:
case V4L2_MEMORY_USERPTR:
Expand Down
3 changes: 3 additions & 0 deletions include/media/videobuf-dma-sg.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ struct videobuf_dmabuf {

/* for kernel buffers */
void *vaddr;
struct page **vaddr_pages;
dma_addr_t *dma_addr;
struct device *dev;

/* for overlay buffers (pci-pci dma) */
dma_addr_t bus_addr;
Expand Down

0 comments on commit 7b4eeed

Please sign in to comment.