[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>
This commit is contained in:
parent
b601fe5688
commit
7b4eeed174
@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
|
|||||||
int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
|
int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
|
||||||
int nr_pages)
|
int nr_pages)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
dprintk(1, "init kernel [%d pages]\n", nr_pages);
|
dprintk(1, "init kernel [%d pages]\n", nr_pages);
|
||||||
|
|
||||||
dma->direction = direction;
|
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) {
|
if (NULL == dma->vaddr) {
|
||||||
dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
|
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",
|
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,
|
|||||||
dma->nr_pages = nr_pages;
|
dma->nr_pages = nr_pages;
|
||||||
|
|
||||||
return 0;
|
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);
|
EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
|
||||||
|
|
||||||
@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
|
|||||||
dma->pages = NULL;
|
dma->pages = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
vfree(dma->vaddr);
|
if (dma->dma_addr) {
|
||||||
dma->vaddr = NULL;
|
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)
|
if (dma->bus_addr)
|
||||||
dma->bus_addr = 0;
|
dma->bus_addr = 0;
|
||||||
@ -461,6 +510,11 @@ static int __videobuf_iolock(struct videobuf_queue *q,
|
|||||||
|
|
||||||
MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
|
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) {
|
switch (vb->memory) {
|
||||||
case V4L2_MEMORY_MMAP:
|
case V4L2_MEMORY_MMAP:
|
||||||
case V4L2_MEMORY_USERPTR:
|
case V4L2_MEMORY_USERPTR:
|
||||||
|
@ -53,6 +53,9 @@ struct videobuf_dmabuf {
|
|||||||
|
|
||||||
/* for kernel buffers */
|
/* for kernel buffers */
|
||||||
void *vaddr;
|
void *vaddr;
|
||||||
|
struct page **vaddr_pages;
|
||||||
|
dma_addr_t *dma_addr;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
/* for overlay buffers (pci-pci dma) */
|
/* for overlay buffers (pci-pci dma) */
|
||||||
dma_addr_t bus_addr;
|
dma_addr_t bus_addr;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user