Skip to content

Commit

Permalink
[media] videobuf2-dc: Fix support for mappings without struct page in…
Browse files Browse the repository at this point in the history
… userptr mode

Earlier version of dma-contig allocator in user ptr mode assumed that in
all cases DMA address equals physical address. This was just a special case.
Commit e15dab7 introduced correct support
for converting userpage to dma address, but unfortunately it broke the
support for simple dma address = physical address for the case, when given
physical frame has no struct page associated with it (this happens if one
use for example dma_declare_coherent api or other reserved memory approach).
This commit restores support for such cases.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
[s.nawrocki@samsung.com: replaced #elsif with #elif]
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>

Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
  • Loading branch information
Marek Szyprowski authored and Mauro Carvalho Chehab committed Sep 24, 2013
1 parent 3c5c23c commit 774d230
Showing 1 changed file with 82 additions and 5 deletions.
87 changes: 82 additions & 5 deletions drivers/media/v4l2-core/videobuf2-dma-contig.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,39 @@ static inline int vma_is_io(struct vm_area_struct *vma)
return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
}

static int vb2_dc_get_user_pfn(unsigned long start, int n_pages,
struct vm_area_struct *vma, unsigned long *res)
{
unsigned long pfn, start_pfn, prev_pfn;
unsigned int i;
int ret;

if (!vma_is_io(vma))
return -EFAULT;

ret = follow_pfn(vma, start, &pfn);
if (ret)
return ret;

start_pfn = pfn;
start += PAGE_SIZE;

for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) {
prev_pfn = pfn;
ret = follow_pfn(vma, start, &pfn);

if (ret) {
pr_err("no page for address %lu\n", start);
return ret;
}
if (pfn != prev_pfn + 1)
return -EINVAL;
}

*res = start_pfn;
return 0;
}

static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
int n_pages, struct vm_area_struct *vma, int write)
{
Expand All @@ -433,6 +466,9 @@ static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
unsigned long pfn;
int ret = follow_pfn(vma, start, &pfn);

if (!pfn_valid(pfn))
return -EINVAL;

if (ret) {
pr_err("no page for address %lu\n", start);
return ret;
Expand Down Expand Up @@ -468,16 +504,49 @@ static void vb2_dc_put_userptr(void *buf_priv)
struct vb2_dc_buf *buf = buf_priv;
struct sg_table *sgt = buf->dma_sgt;

dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
if (!vma_is_io(buf->vma))
vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
if (sgt) {
dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
if (!vma_is_io(buf->vma))
vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);

sg_free_table(sgt);
kfree(sgt);
sg_free_table(sgt);
kfree(sgt);
}
vb2_put_vma(buf->vma);
kfree(buf);
}

/*
* For some kind of reserved memory there might be no struct page available,
* so all that can be done to support such 'pages' is to try to convert
* pfn to dma address or at the last resort just assume that
* dma address == physical address (like it has been assumed in earlier version
* of videobuf2-dma-contig
*/

#ifdef __arch_pfn_to_dma
static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
{
return (dma_addr_t)__arch_pfn_to_dma(dev, pfn);
}
#elif defined(__pfn_to_bus)
static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
{
return (dma_addr_t)__pfn_to_bus(pfn);
}
#elif defined(__pfn_to_phys)
static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
{
return (dma_addr_t)__pfn_to_phys(pfn);
}
#else
static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn)
{
/* really, we cannot do anything better at this point */
return (dma_addr_t)(pfn) << PAGE_SHIFT;
}
#endif

static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
unsigned long size, int write)
{
Expand Down Expand Up @@ -548,6 +617,14 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
/* extract page list from userspace mapping */
ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, write);
if (ret) {
unsigned long pfn;
if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) {
buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn);
buf->size = size;
kfree(pages);
return buf;
}

pr_err("failed to get user pages\n");
goto fail_vma;
}
Expand Down

0 comments on commit 774d230

Please sign in to comment.