Skip to content

Commit 7b4eeed

Browse files
James Harpermchehab
authored andcommitted
[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>
1 parent b601fe5 commit 7b4eeed

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed

drivers/media/v4l2-core/videobuf-dma-sg.c

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
211211
int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
212212
int nr_pages)
213213
{
214+
int i;
215+
214216
dprintk(1, "init kernel [%d pages]\n", nr_pages);
215217

216218
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);
218241
if (NULL == dma->vaddr) {
219242
dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
220-
return -ENOMEM;
243+
goto out_free_pages;
221244
}
222245

223246
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,
228251
dma->nr_pages = nr_pages;
229252

230253
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+
231267
}
232268
EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
233269

@@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
322358
dma->pages = NULL;
323359
}
324360

325-
vfree(dma->vaddr);
326-
dma->vaddr = NULL;
361+
if (dma->dma_addr) {
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+
}
327376

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

462511
MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
463512

513+
if (!mem->dma.dev)
514+
mem->dma.dev = q->dev;
515+
else
516+
WARN_ON(mem->dma.dev != q->dev);
517+
464518
switch (vb->memory) {
465519
case V4L2_MEMORY_MMAP:
466520
case V4L2_MEMORY_USERPTR:

include/media/videobuf-dma-sg.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ struct videobuf_dmabuf {
5353

5454
/* for kernel buffers */
5555
void *vaddr;
56+
struct page **vaddr_pages;
57+
dma_addr_t *dma_addr;
58+
struct device *dev;
5659

5760
/* for overlay buffers (pci-pci dma) */
5861
dma_addr_t bus_addr;

0 commit comments

Comments
 (0)