@@ -29,6 +29,7 @@ struct vb2_dma_sg_buf {
struct vb2_dma_sg_desc sg_desc;
atomic_t refcount;
struct vb2_vmarea_handler handler;
+ struct vm_area_struct *vma;
};
static void vb2_dma_sg_put(void *buf_priv);
@@ -150,15 +151,9 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
if (!buf->pages)
goto userptr_fail_pages_array_alloc;
- down_read(¤t->mm->mmap_sem);
- num_pages_from_user = get_user_pages(current, current->mm,
- vaddr & PAGE_MASK,
- buf->sg_desc.num_pages,
- write,
- 1, /* force */
- buf->pages,
- NULL);
- up_read(¤t->mm->mmap_sem);
+ num_pages_from_user = vb2_get_user_pages(vaddr, buf->sg_desc.num_pages,
+ buf->pages, write, &buf->vma);
+
if (num_pages_from_user != buf->sg_desc.num_pages)
goto userptr_fail_get_user_pages;
@@ -177,6 +172,8 @@ userptr_fail_get_user_pages:
num_pages_from_user, buf->sg_desc.num_pages);
while (--num_pages_from_user >= 0)
put_page(buf->pages[num_pages_from_user]);
+ if (buf->vma)
+ vb2_put_vma(buf->vma);
kfree(buf->pages);
userptr_fail_pages_array_alloc:
@@ -200,6 +197,8 @@ static void vb2_dma_sg_put_userptr(void *buf_priv)
__func__, buf->sg_desc.num_pages);
if (buf->vaddr)
vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages);
+ if (buf->vma)
+ vb2_put_vma(buf->vma);
while (--i >= 0) {
if (buf->write)
set_page_dirty_lock(buf->pages[i]);
@@ -236,28 +235,16 @@ static unsigned int vb2_dma_sg_num_users(void *buf_priv)
static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma)
{
struct vb2_dma_sg_buf *buf = buf_priv;
- unsigned long uaddr = vma->vm_start;
- unsigned long usize = vma->vm_end - vma->vm_start;
- int i = 0;
+ int ret;
if (!buf) {
printk(KERN_ERR "No memory to map\n");
return -EINVAL;
}
- do {
- int ret;
-
- ret = vm_insert_page(vma, uaddr, buf->pages[i++]);
- if (ret) {
- printk(KERN_ERR "Remapping memory, error: %d\n", ret);
- return ret;
- }
-
- uaddr += PAGE_SIZE;
- usize -= PAGE_SIZE;
- } while (usize > 0);
-
+ ret = vb2_insert_pages(vma, buf->pages);
+ if (ret)
+ return ret;
/*
* Use common vm_area operations to track buffer refcount.
@@ -185,6 +185,82 @@ int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range);
/**
+ * vb2_get_user_pages() - pin user pages
+ * @vaddr: virtual address from which to start
+ * @num_pages: number of pages to pin
+ * @pages: table of pointers to struct pages to pin
+ * @write: if 0, the pages must not be written to
+ * @vma: output parameter, copy of the vma or NULL
+ * if get_user_pages fails
+ *
+ * This function just forwards invocation to get_user_pages, but eases using
+ * the latter in videobuf2 allocators.
+ */
+int vb2_get_user_pages(unsigned long vaddr, unsigned int num_pages,
+ struct page **pages, int write, struct vm_area_struct **vma)
+{
+ struct vm_area_struct *found_vma;
+ struct mm_struct *mm = current->mm;
+ int ret = -EFAULT;
+
+ down_read(¤t->mm->mmap_sem);
+
+ found_vma = find_vma(mm, vaddr);
+ if (NULL == found_vma || found_vma->vm_end < (vaddr + num_pages * PAGE_SIZE))
+ goto done;
+
+ *vma = vb2_get_vma(found_vma);
+ if (NULL == *vma) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = get_user_pages(current, current->mm, vaddr & PAGE_MASK, num_pages,
+ write, 1 /* force */, pages, NULL);
+
+ if (ret != num_pages) {
+ vb2_put_vma(*vma);
+ *vma = NULL;
+ }
+
+done:
+ up_read(¤t->mm->mmap_sem);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_get_user_pages);
+
+/**
+ * vb2_insert_pages - insert pages into user vma
+ * @vma: virtual memory region for the mapping
+ * @pages: table of pointers to struct pages to be inserted
+ *
+ * This function for each page to be inserted performs vm_insert_page.
+ */
+int vb2_insert_pages(struct vm_area_struct *vma, struct page **pages)
+{
+ unsigned long uaddr = vma->vm_start;
+ unsigned long usize = vma->vm_end - vma->vm_start;
+ int i = 0;
+
+ do {
+ int ret;
+
+ ret = vm_insert_page(vma, uaddr, pages[i++]);
+ if (ret) {
+ printk(KERN_ERR "Remapping memory, error: %d\n", ret);
+ return ret;
+ }
+
+ uaddr += PAGE_SIZE;
+ usize -= PAGE_SIZE;
+ } while (usize > 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_insert_pages);
+
+/**
* vb2_common_vm_open() - increase refcount of the vma
* @vma: virtual memory region for the mapping
*
@@ -41,5 +41,10 @@ int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma);
void vb2_put_vma(struct vm_area_struct *vma);
+int vb2_get_user_pages(unsigned long vaddr, unsigned int num_pages,
+ struct page **pages, int write,
+ struct vm_area_struct **vma);
+
+int vb2_insert_pages(struct vm_area_struct *vma, struct page **pages);
#endif