All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] Helper to abstract vma handling in media layer
@ 2014-03-17 19:49 ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

  Hello,

  The following patch series is my first stab at abstracting vma handling
from the various media drivers. After this patch set drivers have to know
much less details about vmas, their types, and locking. My motivation for
the series is that I want to change get_user_pages() locking and I want
to handle subtle locking details in as few places as possible.

The core of the series is the new helper get_vaddr_pfns() which is given a
virtual address and it fills in PFNs into provided array. If PFNs correspond to
normal pages it also grabs references to these pages. The difference from
get_user_pages() is that this function can also deal with pfnmap, mixed, and io
mappings which is what the media drivers need.

The patches are just compile tested (since I don't have any of the hardware
I'm afraid I won't be able to do any more testing anyway) so please handle
with care. I'm grateful for any comments.

								Honza

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [RFC] Helper to abstract vma handling in media layer
@ 2014-03-17 19:49 ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

  Hello,

  The following patch series is my first stab at abstracting vma handling
from the various media drivers. After this patch set drivers have to know
much less details about vmas, their types, and locking. My motivation for
the series is that I want to change get_user_pages() locking and I want
to handle subtle locking details in as few places as possible.

The core of the series is the new helper get_vaddr_pfns() which is given a
virtual address and it fills in PFNs into provided array. If PFNs correspond to
normal pages it also grabs references to these pages. The difference from
get_user_pages() is that this function can also deal with pfnmap, mixed, and io
mappings which is what the media drivers need.

The patches are just compile tested (since I don't have any of the hardware
I'm afraid I won't be able to do any more testing anyway) so please handle
with care. I'm grateful for any comments.

								Honza

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Provide new function get_vaddr_pfns().  This function maps virtual
addresses from given start and fills given array with page frame numbers of
the corresponding pages. If given start belongs to a normal vma, the function
grabs reference to each of the pages to pin them in memory. If start
belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller
should make sure pfns aren't reused for anything else while he is using
them.

This function is created for various drivers to simplify handling of
their buffers.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/linux/mm.h |  46 +++++++++++++++
 mm/memory.c        | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 211 insertions(+)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index da8ad480bea7..b3bd29cc40dd 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -18,6 +18,8 @@
 #include <linux/pfn.h>
 #include <linux/bit_spinlock.h>
 #include <linux/shrinker.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
 
 struct mempolicy;
 struct anon_vma;
@@ -1180,6 +1182,50 @@ get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
 	return ret;
 }
 
+/* Container for pinned pfns / pages */
+struct pinned_pfns {
+	unsigned int nr_allocated_pfns;	/* Number of pfns we have space for */
+	unsigned int nr_pfns;		/* Number of pfns stored in pfns array */
+	unsigned int got_ref:1;		/* Did we pin pfns by getting page ref? */
+	unsigned int is_pages:1;	/* Does array contain pages or pfns? */
+	void *ptrs[0];			/* Array of pinned pfns / pages.
+					 * Use pfns_vector_pages() or
+					 * pfns_vector_pfns() for access */
+};
+
+struct pinned_pfns *pfns_vector_create(int nr_pfns);
+static inline void pfns_vector_destroy(struct pinned_pfns *pfns)
+{
+	if (!is_vmalloc_addr(pfns))
+		kfree(pfns);
+	else
+		vfree(pfns);
+}
+int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
+		   struct pinned_pfns *pfns);
+void put_vaddr_pfns(struct pinned_pfns *pfns);
+int pfns_vector_to_pages(struct pinned_pfns *pfns);
+
+static inline int pfns_vector_count(struct pinned_pfns *pfns)
+{
+	return pfns->nr_pfns;
+}
+
+static inline struct page **pfns_vector_pages(struct pinned_pfns *pfns)
+{
+	if (!pfns->is_pages)
+		return NULL;
+	return (struct page **)(pfns->ptrs);
+}
+
+static inline unsigned long *pfns_vector_pfns(struct pinned_pfns *pfns)
+{
+	if (pfns->is_pages)
+		return NULL;
+	return (unsigned long *)(pfns->ptrs);
+}
+
+
 struct kvec;
 int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
 			struct page **pages);
diff --git a/mm/memory.c b/mm/memory.c
index 22dfa617bddb..87bebcfb8911 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2024,6 +2024,171 @@ long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
 EXPORT_SYMBOL(get_user_pages);
 
 /**
+ * get_vaddr_pfns() - map virtual addresses to pfns
+ * @start:	starting user address
+ * @nr_pfns:	number of pfns from start to map
+ * @write:	whether pages will be written to by the caller
+ * @force:	whether to force write access even if user mapping is
+ *		readonly. This will result in the page being COWed even
+ *		in MAP_SHARED mappings. You do not want this.
+ * @pfns:	structure which receives pfns of the pages mapped.
+ *		It should have space for at least nr_pfns pfns. 
+ *
+ * This function maps virtual addresses from @start and fills @pfns structure
+ * with page frame numbers of corresponding pages. If @start belongs to a
+ * normal vma, the function grabs reference to each of the pages to pin them in
+ * memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't touch page
+ * structures. Caller should make sure pfns aren't reused for anything else
+ * while he is using them.
+ *
+ * This function takes care of grabbing mmap_sem as necessary.
+ */
+int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
+		   struct pinned_pfns *pfns)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	int ret = 0;
+	int err;
+
+	if (nr_pfns <= 0)
+		return 0;
+
+	if (nr_pfns > pfns->nr_allocated_pfns)
+		nr_pfns = pfns->nr_allocated_pfns;
+
+	down_read(&mm->mmap_sem);
+	vma = find_vma_intersection(mm, start, start + 1);
+	if (!vma) {
+		ret = -EFAULT;
+		goto out;
+	}
+	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
+		pfns->got_ref = 1;
+		pfns->is_pages = 1;
+		ret = get_user_pages(current, mm, start, nr_pfns, write, force,
+				     pfns_vector_pages(pfns), NULL);
+		goto out;
+	}
+
+	pfns->got_ref = 0;
+	pfns->is_pages = 0;
+	do {
+		unsigned long *nums = pfns_vector_pfns(pfns);
+
+		while (ret < nr_pfns && start + PAGE_SIZE <= vma->vm_end) {
+			err = follow_pfn(vma, start, &nums[ret]);
+			if (err) {
+				if (ret == 0)
+					ret = err;
+				goto out;
+			}
+			start += PAGE_SIZE;
+			ret++;
+		}
+		/*
+		 * We stop if we have enough pages or if VMA doesn't completely
+		 * cover the tail page.
+		 */
+		if (ret >= nr_pfns || start < vma->vm_end)
+			break;
+		vma = find_vma_intersection(mm, start, start + 1);
+	} while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP));
+out:
+	up_read(&mm->mmap_sem);
+	if (!ret)
+		ret = -EFAULT;
+	if (ret > 0)
+		pfns->nr_pfns = ret;
+	return ret;
+}
+EXPORT_SYMBOL(get_vaddr_pfns);
+
+/**
+ * put_vaddr_pfns() - drop references to pages if get_vaddr_pfns() acquired them
+ * @pfns:     structure with pfns we pinned
+ *
+ * Drop references to pages if get_vaddr_pfns() acquired them. We also
+ * invalidate the array of pfns so that it is prepared for the next call into
+ * get_vaddr_pfns().
+ */
+void put_vaddr_pfns(struct pinned_pfns *pfns)
+{
+	int i;
+
+	if (!pfns->got_ref)
+		goto out;
+	if (pfns->is_pages) {
+		struct page **pages = pfns_vector_pages(pfns);
+
+		for (i = 0; i < pfns->nr_pfns; i++)
+			put_page(pages[i]);
+	} else {
+		unsigned long *nums = pfns_vector_pfns(pfns);
+
+		for (i = 0; i < pfns->nr_pfns; i++)
+			put_page(pfn_to_page(nums[i]));
+	}
+	pfns->got_ref = 0;
+out:
+	pfns->nr_pfns = 0;
+}
+EXPORT_SYMBOL(put_vaddr_pfns);
+
+/**
+ * pfns_vector_to_pages - convert vector of pfns to vector of page pointers
+ * @pfns:	structure with pinned pfns
+ *
+ * Convert @pfns to not contain array of pfns but array of page pointers.
+ * If the conversion is successful, return 0. Otherwise return an error.
+ */
+int pfns_vector_to_pages(struct pinned_pfns *pfns)
+{
+	int i;
+	unsigned long *nums;
+	struct page **pages;
+
+	if (pfns->is_pages)
+		return 0;
+	nums = pfns_vector_pfns(pfns);
+	for (i = 0; i < pfns->nr_pfns; i++)
+		if (!pfn_valid(nums[i]))
+			return -EINVAL;
+	pages = (struct page **)nums;
+	for (i = 0; i < pfns->nr_pfns; i++)
+		pages[i] = pfn_to_page(nums[i]);
+	pfns->is_pages = 1;
+	return 0;
+}
+EXPORT_SYMBOL(pfns_vector_to_pages);
+
+/**
+ * pfns_vector_create() - allocate & initialize structure for pinned pfns
+ * @nr_pfns:	number of pfns slots we should reserve
+ *
+ * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
+ * pfns.
+ */
+struct pinned_pfns *pfns_vector_create(int nr_pfns)
+{
+	struct pinned_pfns *pfns;
+	int size = sizeof(struct pinned_pfns) + sizeof(unsigned long) * nr_pfns;
+
+	if (WARN_ON_ONCE(nr_pfns <= 0))
+		return NULL;
+	if (size <= PAGE_SIZE)
+		pfns = kmalloc(size, GFP_KERNEL);
+	else
+		pfns = vmalloc(size);
+	if (!pfns)
+		return NULL;
+	pfns->nr_allocated_pfns = nr_pfns;
+	pfns->nr_pfns = 0;
+	return 0;
+}
+EXPORT_SYMBOL(pfns_vector_create);
+
+/**
  * get_dump_page() - pin user page in memory while writing it to core dump
  * @addr: user address
  *
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Provide new function get_vaddr_pfns().  This function maps virtual
addresses from given start and fills given array with page frame numbers of
the corresponding pages. If given start belongs to a normal vma, the function
grabs reference to each of the pages to pin them in memory. If start
belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller
should make sure pfns aren't reused for anything else while he is using
them.

This function is created for various drivers to simplify handling of
their buffers.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 include/linux/mm.h |  46 +++++++++++++++
 mm/memory.c        | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 211 insertions(+)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index da8ad480bea7..b3bd29cc40dd 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -18,6 +18,8 @@
 #include <linux/pfn.h>
 #include <linux/bit_spinlock.h>
 #include <linux/shrinker.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
 
 struct mempolicy;
 struct anon_vma;
@@ -1180,6 +1182,50 @@ get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
 	return ret;
 }
 
+/* Container for pinned pfns / pages */
+struct pinned_pfns {
+	unsigned int nr_allocated_pfns;	/* Number of pfns we have space for */
+	unsigned int nr_pfns;		/* Number of pfns stored in pfns array */
+	unsigned int got_ref:1;		/* Did we pin pfns by getting page ref? */
+	unsigned int is_pages:1;	/* Does array contain pages or pfns? */
+	void *ptrs[0];			/* Array of pinned pfns / pages.
+					 * Use pfns_vector_pages() or
+					 * pfns_vector_pfns() for access */
+};
+
+struct pinned_pfns *pfns_vector_create(int nr_pfns);
+static inline void pfns_vector_destroy(struct pinned_pfns *pfns)
+{
+	if (!is_vmalloc_addr(pfns))
+		kfree(pfns);
+	else
+		vfree(pfns);
+}
+int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
+		   struct pinned_pfns *pfns);
+void put_vaddr_pfns(struct pinned_pfns *pfns);
+int pfns_vector_to_pages(struct pinned_pfns *pfns);
+
+static inline int pfns_vector_count(struct pinned_pfns *pfns)
+{
+	return pfns->nr_pfns;
+}
+
+static inline struct page **pfns_vector_pages(struct pinned_pfns *pfns)
+{
+	if (!pfns->is_pages)
+		return NULL;
+	return (struct page **)(pfns->ptrs);
+}
+
+static inline unsigned long *pfns_vector_pfns(struct pinned_pfns *pfns)
+{
+	if (pfns->is_pages)
+		return NULL;
+	return (unsigned long *)(pfns->ptrs);
+}
+
+
 struct kvec;
 int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
 			struct page **pages);
diff --git a/mm/memory.c b/mm/memory.c
index 22dfa617bddb..87bebcfb8911 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2024,6 +2024,171 @@ long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
 EXPORT_SYMBOL(get_user_pages);
 
 /**
+ * get_vaddr_pfns() - map virtual addresses to pfns
+ * @start:	starting user address
+ * @nr_pfns:	number of pfns from start to map
+ * @write:	whether pages will be written to by the caller
+ * @force:	whether to force write access even if user mapping is
+ *		readonly. This will result in the page being COWed even
+ *		in MAP_SHARED mappings. You do not want this.
+ * @pfns:	structure which receives pfns of the pages mapped.
+ *		It should have space for at least nr_pfns pfns. 
+ *
+ * This function maps virtual addresses from @start and fills @pfns structure
+ * with page frame numbers of corresponding pages. If @start belongs to a
+ * normal vma, the function grabs reference to each of the pages to pin them in
+ * memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't touch page
+ * structures. Caller should make sure pfns aren't reused for anything else
+ * while he is using them.
+ *
+ * This function takes care of grabbing mmap_sem as necessary.
+ */
+int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
+		   struct pinned_pfns *pfns)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	int ret = 0;
+	int err;
+
+	if (nr_pfns <= 0)
+		return 0;
+
+	if (nr_pfns > pfns->nr_allocated_pfns)
+		nr_pfns = pfns->nr_allocated_pfns;
+
+	down_read(&mm->mmap_sem);
+	vma = find_vma_intersection(mm, start, start + 1);
+	if (!vma) {
+		ret = -EFAULT;
+		goto out;
+	}
+	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
+		pfns->got_ref = 1;
+		pfns->is_pages = 1;
+		ret = get_user_pages(current, mm, start, nr_pfns, write, force,
+				     pfns_vector_pages(pfns), NULL);
+		goto out;
+	}
+
+	pfns->got_ref = 0;
+	pfns->is_pages = 0;
+	do {
+		unsigned long *nums = pfns_vector_pfns(pfns);
+
+		while (ret < nr_pfns && start + PAGE_SIZE <= vma->vm_end) {
+			err = follow_pfn(vma, start, &nums[ret]);
+			if (err) {
+				if (ret == 0)
+					ret = err;
+				goto out;
+			}
+			start += PAGE_SIZE;
+			ret++;
+		}
+		/*
+		 * We stop if we have enough pages or if VMA doesn't completely
+		 * cover the tail page.
+		 */
+		if (ret >= nr_pfns || start < vma->vm_end)
+			break;
+		vma = find_vma_intersection(mm, start, start + 1);
+	} while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP));
+out:
+	up_read(&mm->mmap_sem);
+	if (!ret)
+		ret = -EFAULT;
+	if (ret > 0)
+		pfns->nr_pfns = ret;
+	return ret;
+}
+EXPORT_SYMBOL(get_vaddr_pfns);
+
+/**
+ * put_vaddr_pfns() - drop references to pages if get_vaddr_pfns() acquired them
+ * @pfns:     structure with pfns we pinned
+ *
+ * Drop references to pages if get_vaddr_pfns() acquired them. We also
+ * invalidate the array of pfns so that it is prepared for the next call into
+ * get_vaddr_pfns().
+ */
+void put_vaddr_pfns(struct pinned_pfns *pfns)
+{
+	int i;
+
+	if (!pfns->got_ref)
+		goto out;
+	if (pfns->is_pages) {
+		struct page **pages = pfns_vector_pages(pfns);
+
+		for (i = 0; i < pfns->nr_pfns; i++)
+			put_page(pages[i]);
+	} else {
+		unsigned long *nums = pfns_vector_pfns(pfns);
+
+		for (i = 0; i < pfns->nr_pfns; i++)
+			put_page(pfn_to_page(nums[i]));
+	}
+	pfns->got_ref = 0;
+out:
+	pfns->nr_pfns = 0;
+}
+EXPORT_SYMBOL(put_vaddr_pfns);
+
+/**
+ * pfns_vector_to_pages - convert vector of pfns to vector of page pointers
+ * @pfns:	structure with pinned pfns
+ *
+ * Convert @pfns to not contain array of pfns but array of page pointers.
+ * If the conversion is successful, return 0. Otherwise return an error.
+ */
+int pfns_vector_to_pages(struct pinned_pfns *pfns)
+{
+	int i;
+	unsigned long *nums;
+	struct page **pages;
+
+	if (pfns->is_pages)
+		return 0;
+	nums = pfns_vector_pfns(pfns);
+	for (i = 0; i < pfns->nr_pfns; i++)
+		if (!pfn_valid(nums[i]))
+			return -EINVAL;
+	pages = (struct page **)nums;
+	for (i = 0; i < pfns->nr_pfns; i++)
+		pages[i] = pfn_to_page(nums[i]);
+	pfns->is_pages = 1;
+	return 0;
+}
+EXPORT_SYMBOL(pfns_vector_to_pages);
+
+/**
+ * pfns_vector_create() - allocate & initialize structure for pinned pfns
+ * @nr_pfns:	number of pfns slots we should reserve
+ *
+ * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
+ * pfns.
+ */
+struct pinned_pfns *pfns_vector_create(int nr_pfns)
+{
+	struct pinned_pfns *pfns;
+	int size = sizeof(struct pinned_pfns) + sizeof(unsigned long) * nr_pfns;
+
+	if (WARN_ON_ONCE(nr_pfns <= 0))
+		return NULL;
+	if (size <= PAGE_SIZE)
+		pfns = kmalloc(size, GFP_KERNEL);
+	else
+		pfns = vmalloc(size);
+	if (!pfns)
+		return NULL;
+	pfns->nr_allocated_pfns = nr_pfns;
+	pfns->nr_pfns = 0;
+	return 0;
+}
+EXPORT_SYMBOL(pfns_vector_create);
+
+/**
  * get_dump_page() - pin user page in memory while writing it to core dump
  * @addr: user address
  *
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 2/9] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns()
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() instead of
hand made mapping of virtual address to physical address. Also the
function leaked page reference from get_user_pages() so fix that by
properly release the reference when omap_vout_buffer_release() is
called.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/platform/omap/omap_vout.c | 63 +++++++++++++++------------------
 1 file changed, 29 insertions(+), 34 deletions(-)

diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index dfd0a21a0658..8c5803e4ce15 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -199,43 +199,31 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix)
  * omap_vout_uservirt_to_phys: This inline function is used to convert user
  * space virtual address to physical address.
  */
-static u32 omap_vout_uservirt_to_phys(u32 virtp)
+static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp,
+				 u32 *physp)
 {
-	unsigned long physp = 0;
-	struct vm_area_struct *vma;
-	struct mm_struct *mm = current->mm;
+	struct pinned_pfns *pfns;
+	int ret;
 
 	/* For kernel direct-mapped memory, take the easy way */
-	if (virtp >= PAGE_OFFSET)
-		return virt_to_phys((void *) virtp);
-
-	down_read(&current->mm->mmap_sem);
-	vma = find_vma(mm, virtp);
-	if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
-		/* this will catch, kernel-allocated, mmaped-to-usermode
-		   addresses */
-		physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
-		up_read(&current->mm->mmap_sem);
-	} else {
-		/* otherwise, use get_user_pages() for general userland pages */
-		int res, nr_pages = 1;
-		struct page *pages;
+	if (virtp >= PAGE_OFFSET) {
+		*physp = virt_to_phys((void *)virtp);
+		return 0;
+	}
 
-		res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
-				0, &pages, NULL);
-		up_read(&current->mm->mmap_sem);
+	pfns = pfns_vector_create(1);
+	if (!pfns)
+		return -ENOMEM;
 
-		if (res == nr_pages) {
-			physp =  __pa(page_address(&pages[0]) +
-					(virtp & ~PAGE_MASK));
-		} else {
-			printk(KERN_WARNING VOUT_NAME
-					"get_user_pages failed\n");
-			return 0;
-		}
+	ret = get_vaddr_pfns(virtp, 1, 1, 0, pfns);
+	if (ret != 1) {
+		pfns_vector_destroy(pfns);
+		return -EINVAL;
 	}
+	*physp = __pfn_to_phys(pfns_vector_pfns(pfns)[0]);
+	vb->priv = pfns;
 
-	return physp;
+	return 0;
 }
 
 /*
@@ -790,11 +778,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q,
 	 * address of the buffer
 	 */
 	if (V4L2_MEMORY_USERPTR == vb->memory) {
+		int ret;
+
 		if (0 == vb->baddr)
 			return -EINVAL;
 		/* Physical address */
-		vout->queued_buf_addr[vb->i] = (u8 *)
-			omap_vout_uservirt_to_phys(vb->baddr);
+		ret = omap_vout_get_userptr(vb, vb->baddr,
+				(u32 *)&vout->queued_buf_addr[vb->i]);
+		if (ret < 0)
+			return ret;
 	} else {
 		u32 addr, dma_addr;
 		unsigned long size;
@@ -843,9 +835,12 @@ static void omap_vout_buffer_release(struct videobuf_queue *q,
 	struct omap_vout_device *vout = q->priv_data;
 
 	vb->state = VIDEOBUF_NEEDS_INIT;
+	if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) {
+		struct pinned_pfns *pfns = vb->priv;
 
-	if (V4L2_MEMORY_MMAP != vout->memory)
-		return;
+		put_vaddr_pfns(pfns);
+		pfns_vector_destroy(pfns);
+	}
 }
 
 /*
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 2/9] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns()
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() instead of
hand made mapping of virtual address to physical address. Also the
function leaked page reference from get_user_pages() so fix that by
properly release the reference when omap_vout_buffer_release() is
called.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/platform/omap/omap_vout.c | 63 +++++++++++++++------------------
 1 file changed, 29 insertions(+), 34 deletions(-)

diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index dfd0a21a0658..8c5803e4ce15 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -199,43 +199,31 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix)
  * omap_vout_uservirt_to_phys: This inline function is used to convert user
  * space virtual address to physical address.
  */
-static u32 omap_vout_uservirt_to_phys(u32 virtp)
+static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp,
+				 u32 *physp)
 {
-	unsigned long physp = 0;
-	struct vm_area_struct *vma;
-	struct mm_struct *mm = current->mm;
+	struct pinned_pfns *pfns;
+	int ret;
 
 	/* For kernel direct-mapped memory, take the easy way */
-	if (virtp >= PAGE_OFFSET)
-		return virt_to_phys((void *) virtp);
-
-	down_read(&current->mm->mmap_sem);
-	vma = find_vma(mm, virtp);
-	if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
-		/* this will catch, kernel-allocated, mmaped-to-usermode
-		   addresses */
-		physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
-		up_read(&current->mm->mmap_sem);
-	} else {
-		/* otherwise, use get_user_pages() for general userland pages */
-		int res, nr_pages = 1;
-		struct page *pages;
+	if (virtp >= PAGE_OFFSET) {
+		*physp = virt_to_phys((void *)virtp);
+		return 0;
+	}
 
-		res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
-				0, &pages, NULL);
-		up_read(&current->mm->mmap_sem);
+	pfns = pfns_vector_create(1);
+	if (!pfns)
+		return -ENOMEM;
 
-		if (res == nr_pages) {
-			physp =  __pa(page_address(&pages[0]) +
-					(virtp & ~PAGE_MASK));
-		} else {
-			printk(KERN_WARNING VOUT_NAME
-					"get_user_pages failed\n");
-			return 0;
-		}
+	ret = get_vaddr_pfns(virtp, 1, 1, 0, pfns);
+	if (ret != 1) {
+		pfns_vector_destroy(pfns);
+		return -EINVAL;
 	}
+	*physp = __pfn_to_phys(pfns_vector_pfns(pfns)[0]);
+	vb->priv = pfns;
 
-	return physp;
+	return 0;
 }
 
 /*
@@ -790,11 +778,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q,
 	 * address of the buffer
 	 */
 	if (V4L2_MEMORY_USERPTR == vb->memory) {
+		int ret;
+
 		if (0 == vb->baddr)
 			return -EINVAL;
 		/* Physical address */
-		vout->queued_buf_addr[vb->i] = (u8 *)
-			omap_vout_uservirt_to_phys(vb->baddr);
+		ret = omap_vout_get_userptr(vb, vb->baddr,
+				(u32 *)&vout->queued_buf_addr[vb->i]);
+		if (ret < 0)
+			return ret;
 	} else {
 		u32 addr, dma_addr;
 		unsigned long size;
@@ -843,9 +835,12 @@ static void omap_vout_buffer_release(struct videobuf_queue *q,
 	struct omap_vout_device *vout = q->priv_data;
 
 	vb->state = VIDEOBUF_NEEDS_INIT;
+	if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) {
+		struct pinned_pfns *pfns = vb->priv;
 
-	if (V4L2_MEMORY_MMAP != vout->memory)
-		return;
+		put_vaddr_pfns(pfns);
+		pfns_vector_destroy(pfns);
+	}
 }
 
 /*
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 3/9] media: vb2: Teach vb2_queue_or_prepare_buf() to get pfns for user buffers
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Teach vb2_queue_or_prepare_buf() to get pfns underlying these buffers
and propagate them down to get_userptr callback. Thus each buffer
mapping method doesn't have to get pfns independently. Also this will
remove the knowledge about mmap_sem locking from videobuf2 core.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-core.c       | 121 ++++++++++++++++++++++++-
 drivers/media/v4l2-core/videobuf2-dma-contig.c |   5 +-
 drivers/media/v4l2-core/videobuf2-dma-sg.c     |   5 +-
 drivers/media/v4l2-core/videobuf2-vmalloc.c    |   5 +-
 include/media/videobuf2-core.h                 |   4 +-
 5 files changed, 128 insertions(+), 12 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index a127925c9d61..7cec08542fb5 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1033,7 +1033,8 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
 /**
  * __qbuf_userptr() - handle qbuf of a USERPTR buffer
  */
-static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b,
+			  struct pinned_pfns **ppfns)
 {
 	struct v4l2_plane planes[VIDEO_MAX_PLANES];
 	struct vb2_queue *q = vb->vb2_queue;
@@ -1075,7 +1076,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 
 		/* Acquire each plane's memory */
 		mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
-				      planes[plane].m.userptr,
+				      &ppfns[plane], planes[plane].m.userptr,
 				      planes[plane].length, write);
 		if (IS_ERR_OR_NULL(mem_priv)) {
 			dprintk(1, "qbuf: failed acquiring userspace "
@@ -1247,10 +1248,116 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 	q->ops->buf_queue(vb);
 }
 
+static struct pinned_pfns *vb2_create_one_pfnvec(struct v4l2_buffer *buf,
+				unsigned long vaddr,
+				unsigned int length)
+{
+	int ret;
+	unsigned long first, last;
+	unsigned long nr;
+	struct pinned_pfns *pfns;
+
+	first = vaddr >> PAGE_SHIFT;
+	last = (vaddr + length - 1) >> PAGE_SHIFT;
+	nr = last - first + 1;
+	pfns = pfns_vector_create(nr);
+	if (!pfns)
+		return ERR_PTR(-ENOMEM);
+	ret = get_vaddr_pfns(vaddr, nr, !V4L2_TYPE_IS_OUTPUT(buf->type), 1,
+			     pfns);
+	if (ret < 0)
+		goto out_destroy;
+	/* We accept only complete set of PFNs */
+	if (ret != nr) {
+		ret = -EFAULT;
+		goto out_release;
+	}
+	return pfns;
+out_release:
+	put_vaddr_pfns(pfns);
+out_destroy:
+	pfns_vector_destroy(pfns);
+	return ERR_PTR(ret);
+}
+
+/* Create PFN vecs for all provided user buffers. */
+static struct pinned_pfns **vb2_get_user_pfns(struct v4l2_buffer *buf,
+			    		      struct pinned_pfns **tmp_store)
+{
+	struct pinned_pfns **ppfns;
+	int count = 0;
+	int i;
+	struct pinned_pfns *ret;
+
+	if (V4L2_TYPE_IS_MULTIPLANAR(buf->type)) {
+		if (buf->length == 0)
+			return NULL;
+
+		count = buf->length;
+
+		ppfns = kzalloc(sizeof(struct pinned_pfns *) * count,
+				GFP_KERNEL);
+		if (!ppfns)
+			return ERR_PTR(-ENOMEM);
+
+		for (i = 0; i < count; i++) {
+			ret = vb2_create_one_pfnvec(buf,
+					buf->m.planes[i].m.userptr,
+					buf->m.planes[i].length);
+			if (IS_ERR(ret))
+				goto out_release;
+			ppfns[i] = ret;
+		}
+	} else {
+		count = 1;
+
+		/* Save one kmalloc for the simple case */
+		ppfns = tmp_store;
+		ppfns[0] = vb2_create_one_pfnvec(buf, buf->m.userptr,
+						 buf->length);
+		if (IS_ERR(ppfns[0]))
+			return ppfns[0];
+	}
+
+	return ppfns;
+out_release:
+	for (i = 0; i < count && ppfns[i]; i++) {
+		put_vaddr_pfns(ppfns[i]);
+		pfns_vector_destroy(ppfns[i]);
+	}
+	kfree(ppfns);
+	return ret;
+}
+
+/* Release PFN vecs the call did not consume */
+static void vb2_put_user_pfns(struct v4l2_buffer *buf,
+			      struct pinned_pfns **ppfns,
+			      struct pinned_pfns **tmp_store)
+{
+	int i;
+	int count;
+
+	if (V4L2_TYPE_IS_MULTIPLANAR(buf->type))
+		count = buf->length;
+	else
+		count = 1;
+
+	for (i = 0; i < count; i++)
+		if (ppfns[i]) {
+			put_vaddr_pfns(ppfns[i]);
+			pfns_vector_destroy(ppfns[i]);
+		}
+
+	if (ppfns != tmp_store)
+		kfree(ppfns);
+}
+
 static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
 	struct vb2_queue *q = vb->vb2_queue;
-	struct rw_semaphore *mmap_sem;
+	struct rw_semaphore *mmap_sem = NULL;
+	struct pinned_pfns *tmp_store;
+	struct pinned_pfns **ppfns = NULL;
 	int ret;
 
 	ret = __verify_length(vb, b);
@@ -1280,11 +1387,15 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		 */
 		mmap_sem = &current->mm->mmap_sem;
 		call_qop(q, wait_prepare, q);
+		ppfns = vb2_get_user_pfns(b, &tmp_store);
 		down_read(mmap_sem);
 		call_qop(q, wait_finish, q);
 
-		ret = __qbuf_userptr(vb, b);
-
+		if (!IS_ERR(ppfns)) {
+			ret = __qbuf_userptr(vb, b, ppfns);
+			vb2_put_user_pfns(b, ppfns, &tmp_store);
+		} else
+			ret = PTR_ERR(ppfns);
 		up_read(mmap_sem);
 		break;
 	case V4L2_MEMORY_DMABUF:
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 33d3871d1e13..c6378d943b89 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -547,8 +547,9 @@ static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn
 }
 #endif
 
-static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
-	unsigned long size, int write)
+static void *vb2_dc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
+				unsigned long vaddr, unsigned long size,
+				int write)
 {
 	struct vb2_dc_conf *conf = alloc_ctx;
 	struct vb2_dc_buf *buf;
diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index c779f210d2c6..ef0b3f765d8e 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -161,8 +161,9 @@ static inline int vma_is_io(struct vm_area_struct *vma)
 	return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
 }
 
-static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
-				    unsigned long size, int write)
+static void *vb2_dma_sg_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
+				    unsigned long vaddr, unsigned long size,
+				    int write)
 {
 	struct vb2_dma_sg_buf *buf;
 	unsigned long first, last;
diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index 313d9771b2bc..ab38e054d1a0 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -69,8 +69,9 @@ static void vb2_vmalloc_put(void *buf_priv)
 	}
 }
 
-static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
-				     unsigned long size, int write)
+static void *vb2_vmalloc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
+				     unsigned long vaddr, unsigned long size,
+				     int write)
 {
 	struct vb2_vmalloc_buf *buf;
 	unsigned long first, last;
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index bef53ce555d2..98c508cae09d 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -85,7 +85,9 @@ struct vb2_mem_ops {
 	void		(*put)(void *buf_priv);
 	struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);
 
-	void		*(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
+	void		*(*get_userptr)(void *alloc_ctx,
+					struct pinned_pfns **pfns,
+					unsigned long vaddr,
 					unsigned long size, int write);
 	void		(*put_userptr)(void *buf_priv);
 
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 3/9] media: vb2: Teach vb2_queue_or_prepare_buf() to get pfns for user buffers
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Teach vb2_queue_or_prepare_buf() to get pfns underlying these buffers
and propagate them down to get_userptr callback. Thus each buffer
mapping method doesn't have to get pfns independently. Also this will
remove the knowledge about mmap_sem locking from videobuf2 core.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-core.c       | 121 ++++++++++++++++++++++++-
 drivers/media/v4l2-core/videobuf2-dma-contig.c |   5 +-
 drivers/media/v4l2-core/videobuf2-dma-sg.c     |   5 +-
 drivers/media/v4l2-core/videobuf2-vmalloc.c    |   5 +-
 include/media/videobuf2-core.h                 |   4 +-
 5 files changed, 128 insertions(+), 12 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index a127925c9d61..7cec08542fb5 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1033,7 +1033,8 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
 /**
  * __qbuf_userptr() - handle qbuf of a USERPTR buffer
  */
-static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b,
+			  struct pinned_pfns **ppfns)
 {
 	struct v4l2_plane planes[VIDEO_MAX_PLANES];
 	struct vb2_queue *q = vb->vb2_queue;
@@ -1075,7 +1076,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 
 		/* Acquire each plane's memory */
 		mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
-				      planes[plane].m.userptr,
+				      &ppfns[plane], planes[plane].m.userptr,
 				      planes[plane].length, write);
 		if (IS_ERR_OR_NULL(mem_priv)) {
 			dprintk(1, "qbuf: failed acquiring userspace "
@@ -1247,10 +1248,116 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
 	q->ops->buf_queue(vb);
 }
 
+static struct pinned_pfns *vb2_create_one_pfnvec(struct v4l2_buffer *buf,
+				unsigned long vaddr,
+				unsigned int length)
+{
+	int ret;
+	unsigned long first, last;
+	unsigned long nr;
+	struct pinned_pfns *pfns;
+
+	first = vaddr >> PAGE_SHIFT;
+	last = (vaddr + length - 1) >> PAGE_SHIFT;
+	nr = last - first + 1;
+	pfns = pfns_vector_create(nr);
+	if (!pfns)
+		return ERR_PTR(-ENOMEM);
+	ret = get_vaddr_pfns(vaddr, nr, !V4L2_TYPE_IS_OUTPUT(buf->type), 1,
+			     pfns);
+	if (ret < 0)
+		goto out_destroy;
+	/* We accept only complete set of PFNs */
+	if (ret != nr) {
+		ret = -EFAULT;
+		goto out_release;
+	}
+	return pfns;
+out_release:
+	put_vaddr_pfns(pfns);
+out_destroy:
+	pfns_vector_destroy(pfns);
+	return ERR_PTR(ret);
+}
+
+/* Create PFN vecs for all provided user buffers. */
+static struct pinned_pfns **vb2_get_user_pfns(struct v4l2_buffer *buf,
+			    		      struct pinned_pfns **tmp_store)
+{
+	struct pinned_pfns **ppfns;
+	int count = 0;
+	int i;
+	struct pinned_pfns *ret;
+
+	if (V4L2_TYPE_IS_MULTIPLANAR(buf->type)) {
+		if (buf->length == 0)
+			return NULL;
+
+		count = buf->length;
+
+		ppfns = kzalloc(sizeof(struct pinned_pfns *) * count,
+				GFP_KERNEL);
+		if (!ppfns)
+			return ERR_PTR(-ENOMEM);
+
+		for (i = 0; i < count; i++) {
+			ret = vb2_create_one_pfnvec(buf,
+					buf->m.planes[i].m.userptr,
+					buf->m.planes[i].length);
+			if (IS_ERR(ret))
+				goto out_release;
+			ppfns[i] = ret;
+		}
+	} else {
+		count = 1;
+
+		/* Save one kmalloc for the simple case */
+		ppfns = tmp_store;
+		ppfns[0] = vb2_create_one_pfnvec(buf, buf->m.userptr,
+						 buf->length);
+		if (IS_ERR(ppfns[0]))
+			return ppfns[0];
+	}
+
+	return ppfns;
+out_release:
+	for (i = 0; i < count && ppfns[i]; i++) {
+		put_vaddr_pfns(ppfns[i]);
+		pfns_vector_destroy(ppfns[i]);
+	}
+	kfree(ppfns);
+	return ret;
+}
+
+/* Release PFN vecs the call did not consume */
+static void vb2_put_user_pfns(struct v4l2_buffer *buf,
+			      struct pinned_pfns **ppfns,
+			      struct pinned_pfns **tmp_store)
+{
+	int i;
+	int count;
+
+	if (V4L2_TYPE_IS_MULTIPLANAR(buf->type))
+		count = buf->length;
+	else
+		count = 1;
+
+	for (i = 0; i < count; i++)
+		if (ppfns[i]) {
+			put_vaddr_pfns(ppfns[i]);
+			pfns_vector_destroy(ppfns[i]);
+		}
+
+	if (ppfns != tmp_store)
+		kfree(ppfns);
+}
+
 static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
 	struct vb2_queue *q = vb->vb2_queue;
-	struct rw_semaphore *mmap_sem;
+	struct rw_semaphore *mmap_sem = NULL;
+	struct pinned_pfns *tmp_store;
+	struct pinned_pfns **ppfns = NULL;
 	int ret;
 
 	ret = __verify_length(vb, b);
@@ -1280,11 +1387,15 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		 */
 		mmap_sem = &current->mm->mmap_sem;
 		call_qop(q, wait_prepare, q);
+		ppfns = vb2_get_user_pfns(b, &tmp_store);
 		down_read(mmap_sem);
 		call_qop(q, wait_finish, q);
 
-		ret = __qbuf_userptr(vb, b);
-
+		if (!IS_ERR(ppfns)) {
+			ret = __qbuf_userptr(vb, b, ppfns);
+			vb2_put_user_pfns(b, ppfns, &tmp_store);
+		} else
+			ret = PTR_ERR(ppfns);
 		up_read(mmap_sem);
 		break;
 	case V4L2_MEMORY_DMABUF:
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 33d3871d1e13..c6378d943b89 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -547,8 +547,9 @@ static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn
 }
 #endif
 
-static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
-	unsigned long size, int write)
+static void *vb2_dc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
+				unsigned long vaddr, unsigned long size,
+				int write)
 {
 	struct vb2_dc_conf *conf = alloc_ctx;
 	struct vb2_dc_buf *buf;
diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index c779f210d2c6..ef0b3f765d8e 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -161,8 +161,9 @@ static inline int vma_is_io(struct vm_area_struct *vma)
 	return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
 }
 
-static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
-				    unsigned long size, int write)
+static void *vb2_dma_sg_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
+				    unsigned long vaddr, unsigned long size,
+				    int write)
 {
 	struct vb2_dma_sg_buf *buf;
 	unsigned long first, last;
diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index 313d9771b2bc..ab38e054d1a0 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -69,8 +69,9 @@ static void vb2_vmalloc_put(void *buf_priv)
 	}
 }
 
-static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
-				     unsigned long size, int write)
+static void *vb2_vmalloc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
+				     unsigned long vaddr, unsigned long size,
+				     int write)
 {
 	struct vb2_vmalloc_buf *buf;
 	unsigned long first, last;
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index bef53ce555d2..98c508cae09d 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -85,7 +85,9 @@ struct vb2_mem_ops {
 	void		(*put)(void *buf_priv);
 	struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);
 
-	void		*(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
+	void		*(*get_userptr)(void *alloc_ctx,
+					struct pinned_pfns **pfns,
+					unsigned long vaddr,
 					unsigned long size, int write);
 	void		(*put_userptr)(void *buf_priv);
 
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 4/9] media: vb2: Convert vb2_dma_sg_get_userptr() to use pinned pfns
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-dma-sg.c | 85 ++++++------------------------
 1 file changed, 15 insertions(+), 70 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index ef0b3f765d8e..a37ee0fa84d3 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -33,6 +33,7 @@ module_param(debug, int, 0644);
 struct vb2_dma_sg_buf {
 	void				*vaddr;
 	struct page			**pages;
+	struct pinned_pfns		*pfns;
 	int				write;
 	int				offset;
 	struct sg_table			sg_table;
@@ -166,9 +167,10 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 				    int write)
 {
 	struct vb2_dma_sg_buf *buf;
-	unsigned long first, last;
-	int num_pages_from_user;
-	struct vm_area_struct *vma;
+	struct pinned_pfns *pfns = *ppfn;
+
+	if (pfns_vector_to_pages(pfns))
+		return NULL;
 
 	buf = kzalloc(sizeof *buf, GFP_KERNEL);
 	if (!buf)
@@ -178,75 +180,20 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 	buf->write = write;
 	buf->offset = vaddr & ~PAGE_MASK;
 	buf->size = size;
-
-	first = (vaddr           & PAGE_MASK) >> PAGE_SHIFT;
-	last  = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
-	buf->num_pages = last - first + 1;
-
-	buf->pages = kzalloc(buf->num_pages * sizeof(struct page *),
-			     GFP_KERNEL);
-	if (!buf->pages)
-		goto userptr_fail_alloc_pages;
-
-	vma = find_vma(current->mm, vaddr);
-	if (!vma) {
-		dprintk(1, "no vma for address %lu\n", vaddr);
-		goto userptr_fail_find_vma;
-	}
-
-	if (vma->vm_end < vaddr + size) {
-		dprintk(1, "vma at %lu is too small for %lu bytes\n",
-			vaddr, size);
-		goto userptr_fail_find_vma;
-	}
-
-	buf->vma = vb2_get_vma(vma);
-	if (!buf->vma) {
-		dprintk(1, "failed to copy vma\n");
-		goto userptr_fail_find_vma;
-	}
-
-	if (vma_is_io(buf->vma)) {
-		for (num_pages_from_user = 0;
-		     num_pages_from_user < buf->num_pages;
-		     ++num_pages_from_user, vaddr += PAGE_SIZE) {
-			unsigned long pfn;
-
-			if (follow_pfn(buf->vma, vaddr, &pfn)) {
-				dprintk(1, "no page for address %lu\n", vaddr);
-				break;
-			}
-			buf->pages[num_pages_from_user] = pfn_to_page(pfn);
-		}
-	} else
-		num_pages_from_user = get_user_pages(current, current->mm,
-					     vaddr & PAGE_MASK,
-					     buf->num_pages,
-					     write,
-					     1, /* force */
-					     buf->pages,
-					     NULL);
-
-	if (num_pages_from_user != buf->num_pages)
-		goto userptr_fail_get_user_pages;
+	buf->pages = pfns_vector_pages(pfns);
+	buf->num_pages = pfns_vector_count(pfns);
 
 	if (sg_alloc_table_from_pages(&buf->sg_table, buf->pages,
 			buf->num_pages, buf->offset, size, 0))
-		goto userptr_fail_alloc_table_from_pages;
+		goto fail;
+
+	buf->pfns = pfns;
+	/* Clear *ppfn so that the caller doesn't free the vector */
+	*ppfn = NULL;
 
 	return buf;
 
-userptr_fail_alloc_table_from_pages:
-userptr_fail_get_user_pages:
-	dprintk(1, "get_user_pages requested/got: %d/%d]\n",
-		buf->num_pages, num_pages_from_user);
-	if (!vma_is_io(buf->vma))
-		while (--num_pages_from_user >= 0)
-			put_page(buf->pages[num_pages_from_user]);
-	vb2_put_vma(buf->vma);
-userptr_fail_find_vma:
-	kfree(buf->pages);
-userptr_fail_alloc_pages:
+fail:
 	kfree(buf);
 	return NULL;
 }
@@ -268,11 +215,9 @@ static void vb2_dma_sg_put_userptr(void *buf_priv)
 	while (--i >= 0) {
 		if (buf->write)
 			set_page_dirty_lock(buf->pages[i]);
-		if (!vma_is_io(buf->vma))
-			put_page(buf->pages[i]);
 	}
-	kfree(buf->pages);
-	vb2_put_vma(buf->vma);
+	put_vaddr_pfns(buf->pfns);
+	pfns_vector_destroy(buf->pfns);
 	kfree(buf);
 }
 
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 4/9] media: vb2: Convert vb2_dma_sg_get_userptr() to use pinned pfns
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-dma-sg.c | 85 ++++++------------------------
 1 file changed, 15 insertions(+), 70 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index ef0b3f765d8e..a37ee0fa84d3 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -33,6 +33,7 @@ module_param(debug, int, 0644);
 struct vb2_dma_sg_buf {
 	void				*vaddr;
 	struct page			**pages;
+	struct pinned_pfns		*pfns;
 	int				write;
 	int				offset;
 	struct sg_table			sg_table;
@@ -166,9 +167,10 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 				    int write)
 {
 	struct vb2_dma_sg_buf *buf;
-	unsigned long first, last;
-	int num_pages_from_user;
-	struct vm_area_struct *vma;
+	struct pinned_pfns *pfns = *ppfn;
+
+	if (pfns_vector_to_pages(pfns))
+		return NULL;
 
 	buf = kzalloc(sizeof *buf, GFP_KERNEL);
 	if (!buf)
@@ -178,75 +180,20 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 	buf->write = write;
 	buf->offset = vaddr & ~PAGE_MASK;
 	buf->size = size;
-
-	first = (vaddr           & PAGE_MASK) >> PAGE_SHIFT;
-	last  = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
-	buf->num_pages = last - first + 1;
-
-	buf->pages = kzalloc(buf->num_pages * sizeof(struct page *),
-			     GFP_KERNEL);
-	if (!buf->pages)
-		goto userptr_fail_alloc_pages;
-
-	vma = find_vma(current->mm, vaddr);
-	if (!vma) {
-		dprintk(1, "no vma for address %lu\n", vaddr);
-		goto userptr_fail_find_vma;
-	}
-
-	if (vma->vm_end < vaddr + size) {
-		dprintk(1, "vma at %lu is too small for %lu bytes\n",
-			vaddr, size);
-		goto userptr_fail_find_vma;
-	}
-
-	buf->vma = vb2_get_vma(vma);
-	if (!buf->vma) {
-		dprintk(1, "failed to copy vma\n");
-		goto userptr_fail_find_vma;
-	}
-
-	if (vma_is_io(buf->vma)) {
-		for (num_pages_from_user = 0;
-		     num_pages_from_user < buf->num_pages;
-		     ++num_pages_from_user, vaddr += PAGE_SIZE) {
-			unsigned long pfn;
-
-			if (follow_pfn(buf->vma, vaddr, &pfn)) {
-				dprintk(1, "no page for address %lu\n", vaddr);
-				break;
-			}
-			buf->pages[num_pages_from_user] = pfn_to_page(pfn);
-		}
-	} else
-		num_pages_from_user = get_user_pages(current, current->mm,
-					     vaddr & PAGE_MASK,
-					     buf->num_pages,
-					     write,
-					     1, /* force */
-					     buf->pages,
-					     NULL);
-
-	if (num_pages_from_user != buf->num_pages)
-		goto userptr_fail_get_user_pages;
+	buf->pages = pfns_vector_pages(pfns);
+	buf->num_pages = pfns_vector_count(pfns);
 
 	if (sg_alloc_table_from_pages(&buf->sg_table, buf->pages,
 			buf->num_pages, buf->offset, size, 0))
-		goto userptr_fail_alloc_table_from_pages;
+		goto fail;
+
+	buf->pfns = pfns;
+	/* Clear *ppfn so that the caller doesn't free the vector */
+	*ppfn = NULL;
 
 	return buf;
 
-userptr_fail_alloc_table_from_pages:
-userptr_fail_get_user_pages:
-	dprintk(1, "get_user_pages requested/got: %d/%d]\n",
-		buf->num_pages, num_pages_from_user);
-	if (!vma_is_io(buf->vma))
-		while (--num_pages_from_user >= 0)
-			put_page(buf->pages[num_pages_from_user]);
-	vb2_put_vma(buf->vma);
-userptr_fail_find_vma:
-	kfree(buf->pages);
-userptr_fail_alloc_pages:
+fail:
 	kfree(buf);
 	return NULL;
 }
@@ -268,11 +215,9 @@ static void vb2_dma_sg_put_userptr(void *buf_priv)
 	while (--i >= 0) {
 		if (buf->write)
 			set_page_dirty_lock(buf->pages[i]);
-		if (!vma_is_io(buf->vma))
-			put_page(buf->pages[i]);
 	}
-	kfree(buf->pages);
-	vb2_put_vma(buf->vma);
+	put_vaddr_pfns(buf->pfns);
+	pfns_vector_destroy(buf->pfns);
 	kfree(buf);
 }
 
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 5/9] media: vb2: Convert vb2_vmalloc_get_userptr() to use pfns vector
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert vb2_vmalloc_get_userptr() to use passed vector of pfns. When we
are doing that there's no need to allocate page array and some code can
be simplified.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-vmalloc.c | 86 +++++++++++------------------
 1 file changed, 33 insertions(+), 53 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index ab38e054d1a0..3b4e53bd97d7 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -23,11 +23,9 @@
 
 struct vb2_vmalloc_buf {
 	void				*vaddr;
-	struct page			**pages;
-	struct vm_area_struct		*vma;
+	struct pinned_pfns		*pfns;
 	int				write;
 	unsigned long			size;
-	unsigned int			n_pages;
 	atomic_t			refcount;
 	struct vb2_vmarea_handler	handler;
 	struct dma_buf			*dbuf;
@@ -74,10 +72,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 				     int write)
 {
 	struct vb2_vmalloc_buf *buf;
-	unsigned long first, last;
-	int n_pages, offset;
-	struct vm_area_struct *vma;
-	dma_addr_t physp;
+	struct pinned_pfns *pfns = *ppfn;
+	int n_pages, offset, i;
 
 	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
 	if (!buf)
@@ -87,49 +83,32 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 	offset = vaddr & ~PAGE_MASK;
 	buf->size = size;
 
-
-	vma = find_vma(current->mm, vaddr);
-	if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
-		if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
-			goto fail_pages_array_alloc;
-		buf->vma = vma;
-		buf->vaddr = ioremap_nocache(physp, size);
-		if (!buf->vaddr)
-			goto fail_pages_array_alloc;
+	n_pages = pfns_vector_count(pfns);
+	if (pfns_vector_to_pages(pfns) < 0) {
+		unsigned long *nums = pfns_vector_pfns(pfns);
+
+		/*
+		 * We cannot get page pointers for these pfns. Check memory is
+ 		 * physically contiguous and use direct mapping.
+		 */
+		for (i = 1; i < n_pages; i++)
+			if (nums[i-1] + 1 != nums[i])
+				goto out_buf;
+		buf->vaddr = ioremap_nocache(nums[0] << PAGE_SHIFT, size);
 	} else {
-		first = vaddr >> PAGE_SHIFT;
-		last  = (vaddr + size - 1) >> PAGE_SHIFT;
-		buf->n_pages = last - first + 1;
-		buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
-				     GFP_KERNEL);
-		if (!buf->pages)
-			goto fail_pages_array_alloc;
-
-		/* current->mm->mmap_sem is taken by videobuf2 core */
-		n_pages = get_user_pages(current, current->mm,
-					 vaddr & PAGE_MASK, buf->n_pages,
-					 write, 1, /* force */
-					 buf->pages, NULL);
-		if (n_pages != buf->n_pages)
-			goto fail_get_user_pages;
-
-		buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
+		buf->vaddr = vm_map_ram(pfns_vector_pages(pfns), n_pages, -1,
 					PAGE_KERNEL);
-		if (!buf->vaddr)
-			goto fail_get_user_pages;
 	}
 
+	if (!buf->vaddr)
+		goto out_buf;
+	buf->pfns = pfns;
+	/* Clear the pointer so that the vector isn't freed by the caller */
+	*ppfn = NULL;
 	buf->vaddr += offset;
 	return buf;
 
-fail_get_user_pages:
-	pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
-		 buf->n_pages);
-	while (--n_pages >= 0)
-		put_page(buf->pages[n_pages]);
-	kfree(buf->pages);
-
-fail_pages_array_alloc:
+out_buf:
 	kfree(buf);
 
 	return NULL;
@@ -140,21 +119,22 @@ static void vb2_vmalloc_put_userptr(void *buf_priv)
 	struct vb2_vmalloc_buf *buf = buf_priv;
 	unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
 	unsigned int i;
+	struct page **pages;
+	unsigned int n_pages;
 
-	if (buf->pages) {
+	if (buf->pfns->is_pages) {
+		n_pages = pfns_vector_count(buf->pfns);
+		pages = pfns_vector_pages(buf->pfns);
 		if (vaddr)
-			vm_unmap_ram((void *)vaddr, buf->n_pages);
-		for (i = 0; i < buf->n_pages; ++i) {
-			if (buf->write)
-				set_page_dirty_lock(buf->pages[i]);
-			put_page(buf->pages[i]);
-		}
-		kfree(buf->pages);
+			vm_unmap_ram((void *)vaddr, n_pages);
+		if (buf->write)
+			for (i = 0; i < n_pages; i++)
+				set_page_dirty_lock(pages[i]);
 	} else {
-		if (buf->vma)
-			vb2_put_vma(buf->vma);
 		iounmap(buf->vaddr);
 	}
+	put_vaddr_pfns(buf->pfns);
+	pfns_vector_destroy(buf->pfns);
 	kfree(buf);
 }
 
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 5/9] media: vb2: Convert vb2_vmalloc_get_userptr() to use pfns vector
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert vb2_vmalloc_get_userptr() to use passed vector of pfns. When we
are doing that there's no need to allocate page array and some code can
be simplified.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-vmalloc.c | 86 +++++++++++------------------
 1 file changed, 33 insertions(+), 53 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index ab38e054d1a0..3b4e53bd97d7 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -23,11 +23,9 @@
 
 struct vb2_vmalloc_buf {
 	void				*vaddr;
-	struct page			**pages;
-	struct vm_area_struct		*vma;
+	struct pinned_pfns		*pfns;
 	int				write;
 	unsigned long			size;
-	unsigned int			n_pages;
 	atomic_t			refcount;
 	struct vb2_vmarea_handler	handler;
 	struct dma_buf			*dbuf;
@@ -74,10 +72,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 				     int write)
 {
 	struct vb2_vmalloc_buf *buf;
-	unsigned long first, last;
-	int n_pages, offset;
-	struct vm_area_struct *vma;
-	dma_addr_t physp;
+	struct pinned_pfns *pfns = *ppfn;
+	int n_pages, offset, i;
 
 	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
 	if (!buf)
@@ -87,49 +83,32 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 	offset = vaddr & ~PAGE_MASK;
 	buf->size = size;
 
-
-	vma = find_vma(current->mm, vaddr);
-	if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
-		if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
-			goto fail_pages_array_alloc;
-		buf->vma = vma;
-		buf->vaddr = ioremap_nocache(physp, size);
-		if (!buf->vaddr)
-			goto fail_pages_array_alloc;
+	n_pages = pfns_vector_count(pfns);
+	if (pfns_vector_to_pages(pfns) < 0) {
+		unsigned long *nums = pfns_vector_pfns(pfns);
+
+		/*
+		 * We cannot get page pointers for these pfns. Check memory is
+ 		 * physically contiguous and use direct mapping.
+		 */
+		for (i = 1; i < n_pages; i++)
+			if (nums[i-1] + 1 != nums[i])
+				goto out_buf;
+		buf->vaddr = ioremap_nocache(nums[0] << PAGE_SHIFT, size);
 	} else {
-		first = vaddr >> PAGE_SHIFT;
-		last  = (vaddr + size - 1) >> PAGE_SHIFT;
-		buf->n_pages = last - first + 1;
-		buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
-				     GFP_KERNEL);
-		if (!buf->pages)
-			goto fail_pages_array_alloc;
-
-		/* current->mm->mmap_sem is taken by videobuf2 core */
-		n_pages = get_user_pages(current, current->mm,
-					 vaddr & PAGE_MASK, buf->n_pages,
-					 write, 1, /* force */
-					 buf->pages, NULL);
-		if (n_pages != buf->n_pages)
-			goto fail_get_user_pages;
-
-		buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
+		buf->vaddr = vm_map_ram(pfns_vector_pages(pfns), n_pages, -1,
 					PAGE_KERNEL);
-		if (!buf->vaddr)
-			goto fail_get_user_pages;
 	}
 
+	if (!buf->vaddr)
+		goto out_buf;
+	buf->pfns = pfns;
+	/* Clear the pointer so that the vector isn't freed by the caller */
+	*ppfn = NULL;
 	buf->vaddr += offset;
 	return buf;
 
-fail_get_user_pages:
-	pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
-		 buf->n_pages);
-	while (--n_pages >= 0)
-		put_page(buf->pages[n_pages]);
-	kfree(buf->pages);
-
-fail_pages_array_alloc:
+out_buf:
 	kfree(buf);
 
 	return NULL;
@@ -140,21 +119,22 @@ static void vb2_vmalloc_put_userptr(void *buf_priv)
 	struct vb2_vmalloc_buf *buf = buf_priv;
 	unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
 	unsigned int i;
+	struct page **pages;
+	unsigned int n_pages;
 
-	if (buf->pages) {
+	if (buf->pfns->is_pages) {
+		n_pages = pfns_vector_count(buf->pfns);
+		pages = pfns_vector_pages(buf->pfns);
 		if (vaddr)
-			vm_unmap_ram((void *)vaddr, buf->n_pages);
-		for (i = 0; i < buf->n_pages; ++i) {
-			if (buf->write)
-				set_page_dirty_lock(buf->pages[i]);
-			put_page(buf->pages[i]);
-		}
-		kfree(buf->pages);
+			vm_unmap_ram((void *)vaddr, n_pages);
+		if (buf->write)
+			for (i = 0; i < n_pages; i++)
+				set_page_dirty_lock(pages[i]);
 	} else {
-		if (buf->vma)
-			vb2_put_vma(buf->vma);
 		iounmap(buf->vaddr);
 	}
+	put_vaddr_pfns(buf->pfns);
+	pfns_vector_destroy(buf->pfns);
 	kfree(buf);
 }
 
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 6/9] media: vb2: Convert vb2_dc_get_userptr() to use pfns vector
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert vb2_dc_get_userptr() to use passed vector of pfns. When we are
doing that there's no need to allocate page array and some code can be
simplified.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-dma-contig.c | 203 ++++---------------------
 1 file changed, 30 insertions(+), 173 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index c6378d943b89..d445b62a51cf 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -32,15 +32,13 @@ struct vb2_dc_buf {
 	dma_addr_t			dma_addr;
 	enum dma_data_direction		dma_dir;
 	struct sg_table			*dma_sgt;
+	struct pinned_pfns		*pfns;
 
 	/* MMAP related */
 	struct vb2_vmarea_handler	handler;
 	atomic_t			refcount;
 	struct sg_table			*sgt_base;
 
-	/* USERPTR related */
-	struct vm_area_struct		*vma;
-
 	/* DMABUF related */
 	struct dma_buf_attachment	*db_attach;
 };
@@ -49,24 +47,6 @@ struct vb2_dc_buf {
 /*        scatterlist table functions        */
 /*********************************************/
 
-
-static void vb2_dc_sgt_foreach_page(struct sg_table *sgt,
-	void (*cb)(struct page *pg))
-{
-	struct scatterlist *s;
-	unsigned int i;
-
-	for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
-		struct page *page = sg_page(s);
-		unsigned int n_pages = PAGE_ALIGN(s->offset + s->length)
-			>> PAGE_SHIFT;
-		unsigned int j;
-
-		for (j = 0; j < n_pages; ++j, ++page)
-			cb(page);
-	}
-}
-
 static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt)
 {
 	struct scatterlist *s;
@@ -418,101 +398,24 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)
 /*       callbacks for USERPTR buffers       */
 /*********************************************/
 
-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)
-{
-	if (vma_is_io(vma)) {
-		unsigned int i;
-
-		for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) {
-			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;
-			}
-			pages[i] = pfn_to_page(pfn);
-		}
-	} else {
-		int n;
-
-		n = get_user_pages(current, current->mm, start & PAGE_MASK,
-			n_pages, write, 1, pages, NULL);
-		/* negative error means that no page was pinned */
-		n = max(n, 0);
-		if (n != n_pages) {
-			pr_err("got only %d of %d user pages\n", n, n_pages);
-			while (n)
-				put_page(pages[--n]);
-			return -EFAULT;
-		}
-	}
-
-	return 0;
-}
-
-static void vb2_dc_put_dirty_page(struct page *page)
-{
-	set_page_dirty_lock(page);
-	put_page(page);
-}
 
 static void vb2_dc_put_userptr(void *buf_priv)
 {
 	struct vb2_dc_buf *buf = buf_priv;
 	struct sg_table *sgt = buf->dma_sgt;
+	int i;
+	struct page **pages;
 
 	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);
-
+		pages = pfns_vector_pages(buf->pfns);
+		for (i = 0; i < pfns_vector_count(buf->pfns); i++)
+			set_page_dirty_lock(pages[i]);
 		sg_free_table(sgt);
 		kfree(sgt);
 	}
-	vb2_put_vma(buf->vma);
+	put_vaddr_pfns(buf->pfns);
+	pfns_vector_destroy(buf->pfns);
 	kfree(buf);
 }
 
@@ -553,13 +456,10 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 {
 	struct vb2_dc_conf *conf = alloc_ctx;
 	struct vb2_dc_buf *buf;
-	unsigned long start;
-	unsigned long end;
+	struct pinned_pfns *pfns = *ppfn;
 	unsigned long offset;
-	struct page **pages;
-	int n_pages;
+	int n_pages, i;
 	int ret = 0;
-	struct vm_area_struct *vma;
 	struct sg_table *sgt;
 	unsigned long contig_size;
 	unsigned long dma_align = dma_get_cache_alignment();
@@ -582,72 +482,38 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 	buf->dev = conf->dev;
 	buf->dma_dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
 
-	start = vaddr & PAGE_MASK;
 	offset = vaddr & ~PAGE_MASK;
-	end = PAGE_ALIGN(vaddr + size);
-	n_pages = (end - start) >> PAGE_SHIFT;
-
-	pages = kmalloc(n_pages * sizeof(pages[0]), GFP_KERNEL);
-	if (!pages) {
-		ret = -ENOMEM;
-		pr_err("failed to allocate pages table\n");
-		goto fail_buf;
-	}
-
-	/* current->mm->mmap_sem is taken by videobuf2 core */
-	vma = find_vma(current->mm, vaddr);
-	if (!vma) {
-		pr_err("no vma for address %lu\n", vaddr);
-		ret = -EFAULT;
-		goto fail_pages;
-	}
-
-	if (vma->vm_end < vaddr + size) {
-		pr_err("vma at %lu is too small for %lu bytes\n", vaddr, size);
-		ret = -EFAULT;
-		goto fail_pages;
-	}
+	n_pages = pfns_vector_count(pfns);
 
-	buf->vma = vb2_get_vma(vma);
-	if (!buf->vma) {
-		pr_err("failed to copy vma\n");
-		ret = -ENOMEM;
-		goto fail_pages;
-	}
+	ret = pfns_vector_to_pages(pfns);
+	if (ret < 0) {
+		unsigned long *nums = pfns_vector_pfns(pfns);
 
-	/* 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;
+		/*
+		 * Failed to convert to pages... Check the memory is physically
+ 		 * contiguous and use direct mapping
+		 */
+		for (i = 1; i < n_pages; i++)
+			if (nums[i-1] + 1 != nums[i])
+				goto fail_buf;
+		buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, nums[0]);
+		goto out;
 	}
 
 	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
 	if (!sgt) {
 		pr_err("failed to allocate sg table\n");
 		ret = -ENOMEM;
-		goto fail_get_user_pages;
+		goto fail_buf;
 	}
 
-	ret = sg_alloc_table_from_pages(sgt, pages, n_pages,
+	ret = sg_alloc_table_from_pages(sgt, pfns_vector_pages(pfns), n_pages,
 		offset, size, GFP_KERNEL);
 	if (ret) {
 		pr_err("failed to initialize sg table\n");
 		goto fail_sgt;
 	}
 
-	/* pages are no longer needed */
-	kfree(pages);
-	pages = NULL;
-
 	sgt->nents = dma_map_sg(buf->dev, sgt->sgl, sgt->orig_nents,
 		buf->dma_dir);
 	if (sgt->nents <= 0) {
@@ -665,8 +531,12 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 	}
 
 	buf->dma_addr = sg_dma_address(sgt->sgl);
-	buf->size = size;
 	buf->dma_sgt = sgt;
+out:
+	buf->size = size;
+	buf->pfns = pfns;
+	/* Clear *ppfn so that the caller doesn't free the pfn vector */
+	*ppfn = NULL;
 
 	return buf;
 
@@ -674,24 +544,11 @@ fail_map_sg:
 	dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
 
 fail_sgt_init:
-	if (!vma_is_io(buf->vma))
-		vb2_dc_sgt_foreach_page(sgt, put_page);
 	sg_free_table(sgt);
 
 fail_sgt:
 	kfree(sgt);
 
-fail_get_user_pages:
-	if (pages && !vma_is_io(buf->vma))
-		while (n_pages)
-			put_page(pages[--n_pages]);
-
-fail_vma:
-	vb2_put_vma(buf->vma);
-
-fail_pages:
-	kfree(pages); /* kfree is NULL-proof */
-
 fail_buf:
 	kfree(buf);
 
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 6/9] media: vb2: Convert vb2_dc_get_userptr() to use pfns vector
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert vb2_dc_get_userptr() to use passed vector of pfns. When we are
doing that there's no need to allocate page array and some code can be
simplified.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-dma-contig.c | 203 ++++---------------------
 1 file changed, 30 insertions(+), 173 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index c6378d943b89..d445b62a51cf 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -32,15 +32,13 @@ struct vb2_dc_buf {
 	dma_addr_t			dma_addr;
 	enum dma_data_direction		dma_dir;
 	struct sg_table			*dma_sgt;
+	struct pinned_pfns		*pfns;
 
 	/* MMAP related */
 	struct vb2_vmarea_handler	handler;
 	atomic_t			refcount;
 	struct sg_table			*sgt_base;
 
-	/* USERPTR related */
-	struct vm_area_struct		*vma;
-
 	/* DMABUF related */
 	struct dma_buf_attachment	*db_attach;
 };
@@ -49,24 +47,6 @@ struct vb2_dc_buf {
 /*        scatterlist table functions        */
 /*********************************************/
 
-
-static void vb2_dc_sgt_foreach_page(struct sg_table *sgt,
-	void (*cb)(struct page *pg))
-{
-	struct scatterlist *s;
-	unsigned int i;
-
-	for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
-		struct page *page = sg_page(s);
-		unsigned int n_pages = PAGE_ALIGN(s->offset + s->length)
-			>> PAGE_SHIFT;
-		unsigned int j;
-
-		for (j = 0; j < n_pages; ++j, ++page)
-			cb(page);
-	}
-}
-
 static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt)
 {
 	struct scatterlist *s;
@@ -418,101 +398,24 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)
 /*       callbacks for USERPTR buffers       */
 /*********************************************/
 
-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)
-{
-	if (vma_is_io(vma)) {
-		unsigned int i;
-
-		for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) {
-			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;
-			}
-			pages[i] = pfn_to_page(pfn);
-		}
-	} else {
-		int n;
-
-		n = get_user_pages(current, current->mm, start & PAGE_MASK,
-			n_pages, write, 1, pages, NULL);
-		/* negative error means that no page was pinned */
-		n = max(n, 0);
-		if (n != n_pages) {
-			pr_err("got only %d of %d user pages\n", n, n_pages);
-			while (n)
-				put_page(pages[--n]);
-			return -EFAULT;
-		}
-	}
-
-	return 0;
-}
-
-static void vb2_dc_put_dirty_page(struct page *page)
-{
-	set_page_dirty_lock(page);
-	put_page(page);
-}
 
 static void vb2_dc_put_userptr(void *buf_priv)
 {
 	struct vb2_dc_buf *buf = buf_priv;
 	struct sg_table *sgt = buf->dma_sgt;
+	int i;
+	struct page **pages;
 
 	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);
-
+		pages = pfns_vector_pages(buf->pfns);
+		for (i = 0; i < pfns_vector_count(buf->pfns); i++)
+			set_page_dirty_lock(pages[i]);
 		sg_free_table(sgt);
 		kfree(sgt);
 	}
-	vb2_put_vma(buf->vma);
+	put_vaddr_pfns(buf->pfns);
+	pfns_vector_destroy(buf->pfns);
 	kfree(buf);
 }
 
@@ -553,13 +456,10 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 {
 	struct vb2_dc_conf *conf = alloc_ctx;
 	struct vb2_dc_buf *buf;
-	unsigned long start;
-	unsigned long end;
+	struct pinned_pfns *pfns = *ppfn;
 	unsigned long offset;
-	struct page **pages;
-	int n_pages;
+	int n_pages, i;
 	int ret = 0;
-	struct vm_area_struct *vma;
 	struct sg_table *sgt;
 	unsigned long contig_size;
 	unsigned long dma_align = dma_get_cache_alignment();
@@ -582,72 +482,38 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 	buf->dev = conf->dev;
 	buf->dma_dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
 
-	start = vaddr & PAGE_MASK;
 	offset = vaddr & ~PAGE_MASK;
-	end = PAGE_ALIGN(vaddr + size);
-	n_pages = (end - start) >> PAGE_SHIFT;
-
-	pages = kmalloc(n_pages * sizeof(pages[0]), GFP_KERNEL);
-	if (!pages) {
-		ret = -ENOMEM;
-		pr_err("failed to allocate pages table\n");
-		goto fail_buf;
-	}
-
-	/* current->mm->mmap_sem is taken by videobuf2 core */
-	vma = find_vma(current->mm, vaddr);
-	if (!vma) {
-		pr_err("no vma for address %lu\n", vaddr);
-		ret = -EFAULT;
-		goto fail_pages;
-	}
-
-	if (vma->vm_end < vaddr + size) {
-		pr_err("vma at %lu is too small for %lu bytes\n", vaddr, size);
-		ret = -EFAULT;
-		goto fail_pages;
-	}
+	n_pages = pfns_vector_count(pfns);
 
-	buf->vma = vb2_get_vma(vma);
-	if (!buf->vma) {
-		pr_err("failed to copy vma\n");
-		ret = -ENOMEM;
-		goto fail_pages;
-	}
+	ret = pfns_vector_to_pages(pfns);
+	if (ret < 0) {
+		unsigned long *nums = pfns_vector_pfns(pfns);
 
-	/* 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;
+		/*
+		 * Failed to convert to pages... Check the memory is physically
+ 		 * contiguous and use direct mapping
+		 */
+		for (i = 1; i < n_pages; i++)
+			if (nums[i-1] + 1 != nums[i])
+				goto fail_buf;
+		buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, nums[0]);
+		goto out;
 	}
 
 	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
 	if (!sgt) {
 		pr_err("failed to allocate sg table\n");
 		ret = -ENOMEM;
-		goto fail_get_user_pages;
+		goto fail_buf;
 	}
 
-	ret = sg_alloc_table_from_pages(sgt, pages, n_pages,
+	ret = sg_alloc_table_from_pages(sgt, pfns_vector_pages(pfns), n_pages,
 		offset, size, GFP_KERNEL);
 	if (ret) {
 		pr_err("failed to initialize sg table\n");
 		goto fail_sgt;
 	}
 
-	/* pages are no longer needed */
-	kfree(pages);
-	pages = NULL;
-
 	sgt->nents = dma_map_sg(buf->dev, sgt->sgl, sgt->orig_nents,
 		buf->dma_dir);
 	if (sgt->nents <= 0) {
@@ -665,8 +531,12 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn,
 	}
 
 	buf->dma_addr = sg_dma_address(sgt->sgl);
-	buf->size = size;
 	buf->dma_sgt = sgt;
+out:
+	buf->size = size;
+	buf->pfns = pfns;
+	/* Clear *ppfn so that the caller doesn't free the pfn vector */
+	*ppfn = NULL;
 
 	return buf;
 
@@ -674,24 +544,11 @@ fail_map_sg:
 	dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
 
 fail_sgt_init:
-	if (!vma_is_io(buf->vma))
-		vb2_dc_sgt_foreach_page(sgt, put_page);
 	sg_free_table(sgt);
 
 fail_sgt:
 	kfree(sgt);
 
-fail_get_user_pages:
-	if (pages && !vma_is_io(buf->vma))
-		while (n_pages)
-			put_page(pages[--n_pages]);
-
-fail_vma:
-	vb2_put_vma(buf->vma);
-
-fail_pages:
-	kfree(pages); /* kfree is NULL-proof */
-
 fail_buf:
 	kfree(buf);
 
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 7/9] media: vb2: Remove unused functions
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Conversion to the use of pinned pfns made some functions unused. Remove
them. Also there's no need to lock mmap_sem in __buf_prepare() anymore.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-core.c   |   8 +-
 drivers/media/v4l2-core/videobuf2-memops.c | 114 -----------------------------
 include/media/videobuf2-memops.h           |   7 --
 3 files changed, 2 insertions(+), 127 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 7cec08542fb5..b77dfb3076e8 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1355,7 +1355,6 @@ static void vb2_put_user_pfns(struct v4l2_buffer *buf,
 static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
 	struct vb2_queue *q = vb->vb2_queue;
-	struct rw_semaphore *mmap_sem = NULL;
 	struct pinned_pfns *tmp_store;
 	struct pinned_pfns **ppfns = NULL;
 	int ret;
@@ -1374,8 +1373,8 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		break;
 	case V4L2_MEMORY_USERPTR:
 		/*
-		 * In case of user pointer buffers vb2 allocators need to get
-		 * direct access to userspace pages. This requires getting
+		 * In case of user pointer buffers vb2_get_user_pfns() needs to
+		 * get direct access to userspace pages. This requires getting
 		 * the mmap semaphore for read access in the current process
 		 * structure. The same semaphore is taken before calling mmap
 		 * operation, while both qbuf/prepare_buf and mmap are called
@@ -1385,10 +1384,8 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		 * the videobuf2 core releases the driver's lock, takes
 		 * mmap_sem and then takes the driver's lock again.
 		 */
-		mmap_sem = &current->mm->mmap_sem;
 		call_qop(q, wait_prepare, q);
 		ppfns = vb2_get_user_pfns(b, &tmp_store);
-		down_read(mmap_sem);
 		call_qop(q, wait_finish, q);
 
 		if (!IS_ERR(ppfns)) {
@@ -1396,7 +1393,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 			vb2_put_user_pfns(b, ppfns, &tmp_store);
 		} else
 			ret = PTR_ERR(ppfns);
-		up_read(mmap_sem);
 		break;
 	case V4L2_MEMORY_DMABUF:
 		ret = __qbuf_dmabuf(vb, b);
diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c
index 81c1ad8b2cf1..9b44e9af69ba 100644
--- a/drivers/media/v4l2-core/videobuf2-memops.c
+++ b/drivers/media/v4l2-core/videobuf2-memops.c
@@ -23,120 +23,6 @@
 #include <media/videobuf2-memops.h>
 
 /**
- * vb2_get_vma() - acquire and lock the virtual memory area
- * @vma:	given virtual memory area
- *
- * This function attempts to acquire an area mapped in the userspace for
- * the duration of a hardware operation. The area is "locked" by performing
- * the same set of operation that are done when process calls fork() and
- * memory areas are duplicated.
- *
- * Returns a copy of a virtual memory region on success or NULL.
- */
-struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma)
-{
-	struct vm_area_struct *vma_copy;
-
-	vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
-	if (vma_copy == NULL)
-		return NULL;
-
-	if (vma->vm_ops && vma->vm_ops->open)
-		vma->vm_ops->open(vma);
-
-	if (vma->vm_file)
-		get_file(vma->vm_file);
-
-	memcpy(vma_copy, vma, sizeof(*vma));
-
-	vma_copy->vm_mm = NULL;
-	vma_copy->vm_next = NULL;
-	vma_copy->vm_prev = NULL;
-
-	return vma_copy;
-}
-EXPORT_SYMBOL_GPL(vb2_get_vma);
-
-/**
- * vb2_put_userptr() - release a userspace virtual memory area
- * @vma:	virtual memory region associated with the area to be released
- *
- * This function releases the previously acquired memory area after a hardware
- * operation.
- */
-void vb2_put_vma(struct vm_area_struct *vma)
-{
-	if (!vma)
-		return;
-
-	if (vma->vm_ops && vma->vm_ops->close)
-		vma->vm_ops->close(vma);
-
-	if (vma->vm_file)
-		fput(vma->vm_file);
-
-	kfree(vma);
-}
-EXPORT_SYMBOL_GPL(vb2_put_vma);
-
-/**
- * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory
- * @vaddr:	starting virtual address of the area to be verified
- * @size:	size of the area
- * @res_paddr:	will return physical address for the given vaddr
- * @res_vma:	will return locked copy of struct vm_area for the given area
- *
- * This function will go through memory area of size @size mapped at @vaddr and
- * verify that the underlying physical pages are contiguous. If they are
- * contiguous the virtual memory area is locked and a @res_vma is filled with
- * the copy and @res_pa set to the physical address of the buffer.
- *
- * Returns 0 on success.
- */
-int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
-			   struct vm_area_struct **res_vma, dma_addr_t *res_pa)
-{
-	struct mm_struct *mm = current->mm;
-	struct vm_area_struct *vma;
-	unsigned long offset, start, end;
-	unsigned long this_pfn, prev_pfn;
-	dma_addr_t pa = 0;
-
-	start = vaddr;
-	offset = start & ~PAGE_MASK;
-	end = start + size;
-
-	vma = find_vma(mm, start);
-
-	if (vma == NULL || vma->vm_end < end)
-		return -EFAULT;
-
-	for (prev_pfn = 0; start < end; start += PAGE_SIZE) {
-		int ret = follow_pfn(vma, start, &this_pfn);
-		if (ret)
-			return ret;
-
-		if (prev_pfn == 0)
-			pa = this_pfn << PAGE_SHIFT;
-		else if (this_pfn != prev_pfn + 1)
-			return -EFAULT;
-
-		prev_pfn = this_pfn;
-	}
-
-	/*
-	 * Memory is contigous, lock vma and return to the caller
-	 */
-	*res_vma = vb2_get_vma(vma);
-	if (*res_vma == NULL)
-		return -ENOMEM;
-
-	*res_pa = pa + offset;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
-
-/**
  * vb2_common_vm_open() - increase refcount of the vma
  * @vma:	virtual memory region for the mapping
  *
diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h
index f05444ca8c0c..efab6dc53b52 100644
--- a/include/media/videobuf2-memops.h
+++ b/include/media/videobuf2-memops.h
@@ -30,11 +30,4 @@ struct vb2_vmarea_handler {
 
 extern const struct vm_operations_struct vb2_common_vm_ops;
 
-int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
-			   struct vm_area_struct **res_vma, dma_addr_t *res_pa);
-
-struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma);
-void vb2_put_vma(struct vm_area_struct *vma);
-
-
 #endif
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 7/9] media: vb2: Remove unused functions
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Conversion to the use of pinned pfns made some functions unused. Remove
them. Also there's no need to lock mmap_sem in __buf_prepare() anymore.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/media/v4l2-core/videobuf2-core.c   |   8 +-
 drivers/media/v4l2-core/videobuf2-memops.c | 114 -----------------------------
 include/media/videobuf2-memops.h           |   7 --
 3 files changed, 2 insertions(+), 127 deletions(-)

diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 7cec08542fb5..b77dfb3076e8 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -1355,7 +1355,6 @@ static void vb2_put_user_pfns(struct v4l2_buffer *buf,
 static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 {
 	struct vb2_queue *q = vb->vb2_queue;
-	struct rw_semaphore *mmap_sem = NULL;
 	struct pinned_pfns *tmp_store;
 	struct pinned_pfns **ppfns = NULL;
 	int ret;
@@ -1374,8 +1373,8 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		break;
 	case V4L2_MEMORY_USERPTR:
 		/*
-		 * In case of user pointer buffers vb2 allocators need to get
-		 * direct access to userspace pages. This requires getting
+		 * In case of user pointer buffers vb2_get_user_pfns() needs to
+		 * get direct access to userspace pages. This requires getting
 		 * the mmap semaphore for read access in the current process
 		 * structure. The same semaphore is taken before calling mmap
 		 * operation, while both qbuf/prepare_buf and mmap are called
@@ -1385,10 +1384,8 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 		 * the videobuf2 core releases the driver's lock, takes
 		 * mmap_sem and then takes the driver's lock again.
 		 */
-		mmap_sem = &current->mm->mmap_sem;
 		call_qop(q, wait_prepare, q);
 		ppfns = vb2_get_user_pfns(b, &tmp_store);
-		down_read(mmap_sem);
 		call_qop(q, wait_finish, q);
 
 		if (!IS_ERR(ppfns)) {
@@ -1396,7 +1393,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
 			vb2_put_user_pfns(b, ppfns, &tmp_store);
 		} else
 			ret = PTR_ERR(ppfns);
-		up_read(mmap_sem);
 		break;
 	case V4L2_MEMORY_DMABUF:
 		ret = __qbuf_dmabuf(vb, b);
diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c
index 81c1ad8b2cf1..9b44e9af69ba 100644
--- a/drivers/media/v4l2-core/videobuf2-memops.c
+++ b/drivers/media/v4l2-core/videobuf2-memops.c
@@ -23,120 +23,6 @@
 #include <media/videobuf2-memops.h>
 
 /**
- * vb2_get_vma() - acquire and lock the virtual memory area
- * @vma:	given virtual memory area
- *
- * This function attempts to acquire an area mapped in the userspace for
- * the duration of a hardware operation. The area is "locked" by performing
- * the same set of operation that are done when process calls fork() and
- * memory areas are duplicated.
- *
- * Returns a copy of a virtual memory region on success or NULL.
- */
-struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma)
-{
-	struct vm_area_struct *vma_copy;
-
-	vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
-	if (vma_copy == NULL)
-		return NULL;
-
-	if (vma->vm_ops && vma->vm_ops->open)
-		vma->vm_ops->open(vma);
-
-	if (vma->vm_file)
-		get_file(vma->vm_file);
-
-	memcpy(vma_copy, vma, sizeof(*vma));
-
-	vma_copy->vm_mm = NULL;
-	vma_copy->vm_next = NULL;
-	vma_copy->vm_prev = NULL;
-
-	return vma_copy;
-}
-EXPORT_SYMBOL_GPL(vb2_get_vma);
-
-/**
- * vb2_put_userptr() - release a userspace virtual memory area
- * @vma:	virtual memory region associated with the area to be released
- *
- * This function releases the previously acquired memory area after a hardware
- * operation.
- */
-void vb2_put_vma(struct vm_area_struct *vma)
-{
-	if (!vma)
-		return;
-
-	if (vma->vm_ops && vma->vm_ops->close)
-		vma->vm_ops->close(vma);
-
-	if (vma->vm_file)
-		fput(vma->vm_file);
-
-	kfree(vma);
-}
-EXPORT_SYMBOL_GPL(vb2_put_vma);
-
-/**
- * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory
- * @vaddr:	starting virtual address of the area to be verified
- * @size:	size of the area
- * @res_paddr:	will return physical address for the given vaddr
- * @res_vma:	will return locked copy of struct vm_area for the given area
- *
- * This function will go through memory area of size @size mapped at @vaddr and
- * verify that the underlying physical pages are contiguous. If they are
- * contiguous the virtual memory area is locked and a @res_vma is filled with
- * the copy and @res_pa set to the physical address of the buffer.
- *
- * Returns 0 on success.
- */
-int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
-			   struct vm_area_struct **res_vma, dma_addr_t *res_pa)
-{
-	struct mm_struct *mm = current->mm;
-	struct vm_area_struct *vma;
-	unsigned long offset, start, end;
-	unsigned long this_pfn, prev_pfn;
-	dma_addr_t pa = 0;
-
-	start = vaddr;
-	offset = start & ~PAGE_MASK;
-	end = start + size;
-
-	vma = find_vma(mm, start);
-
-	if (vma == NULL || vma->vm_end < end)
-		return -EFAULT;
-
-	for (prev_pfn = 0; start < end; start += PAGE_SIZE) {
-		int ret = follow_pfn(vma, start, &this_pfn);
-		if (ret)
-			return ret;
-
-		if (prev_pfn == 0)
-			pa = this_pfn << PAGE_SHIFT;
-		else if (this_pfn != prev_pfn + 1)
-			return -EFAULT;
-
-		prev_pfn = this_pfn;
-	}
-
-	/*
-	 * Memory is contigous, lock vma and return to the caller
-	 */
-	*res_vma = vb2_get_vma(vma);
-	if (*res_vma == NULL)
-		return -ENOMEM;
-
-	*res_pa = pa + offset;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
-
-/**
  * vb2_common_vm_open() - increase refcount of the vma
  * @vma:	virtual memory region for the mapping
  *
diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h
index f05444ca8c0c..efab6dc53b52 100644
--- a/include/media/videobuf2-memops.h
+++ b/include/media/videobuf2-memops.h
@@ -30,11 +30,4 @@ struct vb2_vmarea_handler {
 
 extern const struct vm_operations_struct vb2_common_vm_ops;
 
-int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
-			   struct vm_area_struct **res_vma, dma_addr_t *res_pa);
-
-struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma);
-void vb2_put_vma(struct vm_area_struct *vma);
-
-
 #endif
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 8/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_pfn()
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_pfn().
This removes the knowledge about vmas and mmap_sem locking from exynos
driver. Also it fixes a problem that the function has been mapping user
provided address without holding mmap_sem.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/gpu/drm/exynos/exynos_drm_g2d.c | 85 ++++++++++-------------------
 drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 ---------------------------------
 2 files changed, 30 insertions(+), 152 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index 6c1885eedfdf..ae16dc0aa56e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr {
 	dma_addr_t		dma_addr;
 	unsigned long		userptr;
 	unsigned long		size;
-	struct page		**pages;
-	unsigned int		npages;
+	struct pinned_pfns	*pfns;
 	struct sg_table		*sgt;
-	struct vm_area_struct	*vma;
 	atomic_t		refcount;
 	bool			in_pool;
 	bool			out_of_list;
@@ -360,6 +358,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev,
 {
 	struct g2d_cmdlist_userptr *g2d_userptr =
 					(struct g2d_cmdlist_userptr *)obj;
+	struct page **pages;
 
 	if (!obj)
 		return;
@@ -379,19 +378,21 @@ out:
 	exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt,
 					DMA_BIDIRECTIONAL);
 
-	exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
-					g2d_userptr->npages,
-					g2d_userptr->vma);
+	pages = pfns_vector_pages(g2d_userptr->pfns);
+	if (pages) {
+		int i;
 
-	exynos_gem_put_vma(g2d_userptr->vma);
+		for (i = 0; i < pfns_vector_count(g2d_userptr->pfns); i++)
+			set_page_dirty_lock(pages[i]);
+	}
+	put_vaddr_pfns(g2d_userptr->pfns);
+	pfns_vector_destroy(g2d_userptr->pfns);
 
 	if (!g2d_userptr->out_of_list)
 		list_del_init(&g2d_userptr->list);
 
 	sg_free_table(g2d_userptr->sgt);
 	kfree(g2d_userptr->sgt);
-
-	drm_free_large(g2d_userptr->pages);
 	kfree(g2d_userptr);
 }
 
@@ -410,6 +411,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
 	struct vm_area_struct *vma;
 	unsigned long start, end;
 	unsigned int npages, offset;
+	struct pinned_pfns *pfns;
 	int ret;
 
 	if (!size) {
@@ -453,59 +455,37 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
 		return ERR_PTR(-ENOMEM);
 
 	atomic_set(&g2d_userptr->refcount, 1);
+	g2d_userptr->size = size;
 
 	start = userptr & PAGE_MASK;
 	offset = userptr & ~PAGE_MASK;
 	end = PAGE_ALIGN(userptr + size);
 	npages = (end - start) >> PAGE_SHIFT;
-	g2d_userptr->npages = npages;
+	pfns = g2d_userptr->pfns = pfns_vector_create(npages);
+	if (!pfns)
+		goto out_free;
 
-	pages = drm_calloc_large(npages, sizeof(struct page *));
-	if (!pages) {
-		DRM_ERROR("failed to allocate pages.\n");
-		ret = -ENOMEM;
-		goto err_free;
-	}
-
-	vma = find_vma(current->mm, userptr);
-	if (!vma) {
-		DRM_ERROR("failed to get vm region.\n");
+	ret = get_vaddr_pfn(start, npages, 1, 1, pfns);
+	if (ret != npages) {
+		DRM_ERROR("failed to get user pages from userptr.\n");
+		if (ret < 0)
+			goto err_destroy_pfns;
 		ret = -EFAULT;
-		goto err_free_pages;
+		goto err_put_pfns;
 	}
-
-	if (vma->vm_end < userptr + size) {
-		DRM_ERROR("vma is too small.\n");
+	if (pfns_vector_to_pages(pfns) < 0) {
 		ret = -EFAULT;
-		goto err_free_pages;
+		goto err_put_pfns;
 	}
 
-	g2d_userptr->vma = exynos_gem_get_vma(vma);
-	if (!g2d_userptr->vma) {
-		DRM_ERROR("failed to copy vma.\n");
-		ret = -ENOMEM;
-		goto err_free_pages;
-	}
-
-	g2d_userptr->size = size;
-
-	ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK,
-						npages, pages, vma);
-	if (ret < 0) {
-		DRM_ERROR("failed to get user pages from userptr.\n");
-		goto err_put_vma;
-	}
-
-	g2d_userptr->pages = pages;
-
 	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
 	if (!sgt) {
 		ret = -ENOMEM;
-		goto err_free_userptr;
+		goto err_put_pfns;
 	}
 
-	ret = sg_alloc_table_from_pages(sgt, pages, npages, offset,
-					size, GFP_KERNEL);
+	ret = sg_alloc_table_from_pages(sgt, pfns_vector_pages(pfns), npages,
+					offset, size, GFP_KERNEL);
 	if (ret < 0) {
 		DRM_ERROR("failed to get sgt from pages.\n");
 		goto err_free_sgt;
@@ -540,16 +520,11 @@ err_sg_free_table:
 err_free_sgt:
 	kfree(sgt);
 
-err_free_userptr:
-	exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
-					g2d_userptr->npages,
-					g2d_userptr->vma);
-
-err_put_vma:
-	exynos_gem_put_vma(g2d_userptr->vma);
+err_put_pfns:
+	put_vaddr_pfns(pfns);
 
-err_free_pages:
-	drm_free_large(pages);
+err_destroy_pfns:
+	pfns_vector_destroy(pfns);
 
 err_free:
 	kfree(g2d_userptr);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 42d2904d88c7..e8df799e1684 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -459,103 +459,6 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
 	return 0;
 }
 
-struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma)
-{
-	struct vm_area_struct *vma_copy;
-
-	vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
-	if (!vma_copy)
-		return NULL;
-
-	if (vma->vm_ops && vma->vm_ops->open)
-		vma->vm_ops->open(vma);
-
-	if (vma->vm_file)
-		get_file(vma->vm_file);
-
-	memcpy(vma_copy, vma, sizeof(*vma));
-
-	vma_copy->vm_mm = NULL;
-	vma_copy->vm_next = NULL;
-	vma_copy->vm_prev = NULL;
-
-	return vma_copy;
-}
-
-void exynos_gem_put_vma(struct vm_area_struct *vma)
-{
-	if (!vma)
-		return;
-
-	if (vma->vm_ops && vma->vm_ops->close)
-		vma->vm_ops->close(vma);
-
-	if (vma->vm_file)
-		fput(vma->vm_file);
-
-	kfree(vma);
-}
-
-int exynos_gem_get_pages_from_userptr(unsigned long start,
-						unsigned int npages,
-						struct page **pages,
-						struct vm_area_struct *vma)
-{
-	int get_npages;
-
-	/* the memory region mmaped with VM_PFNMAP. */
-	if (vma_is_io(vma)) {
-		unsigned int i;
-
-		for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
-			unsigned long pfn;
-			int ret = follow_pfn(vma, start, &pfn);
-			if (ret)
-				return ret;
-
-			pages[i] = pfn_to_page(pfn);
-		}
-
-		if (i != npages) {
-			DRM_ERROR("failed to get user_pages.\n");
-			return -EINVAL;
-		}
-
-		return 0;
-	}
-
-	get_npages = get_user_pages(current, current->mm, start,
-					npages, 1, 1, pages, NULL);
-	get_npages = max(get_npages, 0);
-	if (get_npages != npages) {
-		DRM_ERROR("failed to get user_pages.\n");
-		while (get_npages)
-			put_page(pages[--get_npages]);
-		return -EFAULT;
-	}
-
-	return 0;
-}
-
-void exynos_gem_put_pages_to_userptr(struct page **pages,
-					unsigned int npages,
-					struct vm_area_struct *vma)
-{
-	if (!vma_is_io(vma)) {
-		unsigned int i;
-
-		for (i = 0; i < npages; i++) {
-			set_page_dirty_lock(pages[i]);
-
-			/*
-			 * undo the reference we took when populating
-			 * the table.
-			 */
-			put_page(pages[i]);
-		}
-	}
-}
-
 int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
 				struct sg_table *sgt,
 				enum dma_data_direction dir)
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 8/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_pfn()
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_pfn().
This removes the knowledge about vmas and mmap_sem locking from exynos
driver. Also it fixes a problem that the function has been mapping user
provided address without holding mmap_sem.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/gpu/drm/exynos/exynos_drm_g2d.c | 85 ++++++++++-------------------
 drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 ---------------------------------
 2 files changed, 30 insertions(+), 152 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index 6c1885eedfdf..ae16dc0aa56e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr {
 	dma_addr_t		dma_addr;
 	unsigned long		userptr;
 	unsigned long		size;
-	struct page		**pages;
-	unsigned int		npages;
+	struct pinned_pfns	*pfns;
 	struct sg_table		*sgt;
-	struct vm_area_struct	*vma;
 	atomic_t		refcount;
 	bool			in_pool;
 	bool			out_of_list;
@@ -360,6 +358,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev,
 {
 	struct g2d_cmdlist_userptr *g2d_userptr =
 					(struct g2d_cmdlist_userptr *)obj;
+	struct page **pages;
 
 	if (!obj)
 		return;
@@ -379,19 +378,21 @@ out:
 	exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt,
 					DMA_BIDIRECTIONAL);
 
-	exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
-					g2d_userptr->npages,
-					g2d_userptr->vma);
+	pages = pfns_vector_pages(g2d_userptr->pfns);
+	if (pages) {
+		int i;
 
-	exynos_gem_put_vma(g2d_userptr->vma);
+		for (i = 0; i < pfns_vector_count(g2d_userptr->pfns); i++)
+			set_page_dirty_lock(pages[i]);
+	}
+	put_vaddr_pfns(g2d_userptr->pfns);
+	pfns_vector_destroy(g2d_userptr->pfns);
 
 	if (!g2d_userptr->out_of_list)
 		list_del_init(&g2d_userptr->list);
 
 	sg_free_table(g2d_userptr->sgt);
 	kfree(g2d_userptr->sgt);
-
-	drm_free_large(g2d_userptr->pages);
 	kfree(g2d_userptr);
 }
 
@@ -410,6 +411,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
 	struct vm_area_struct *vma;
 	unsigned long start, end;
 	unsigned int npages, offset;
+	struct pinned_pfns *pfns;
 	int ret;
 
 	if (!size) {
@@ -453,59 +455,37 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
 		return ERR_PTR(-ENOMEM);
 
 	atomic_set(&g2d_userptr->refcount, 1);
+	g2d_userptr->size = size;
 
 	start = userptr & PAGE_MASK;
 	offset = userptr & ~PAGE_MASK;
 	end = PAGE_ALIGN(userptr + size);
 	npages = (end - start) >> PAGE_SHIFT;
-	g2d_userptr->npages = npages;
+	pfns = g2d_userptr->pfns = pfns_vector_create(npages);
+	if (!pfns)
+		goto out_free;
 
-	pages = drm_calloc_large(npages, sizeof(struct page *));
-	if (!pages) {
-		DRM_ERROR("failed to allocate pages.\n");
-		ret = -ENOMEM;
-		goto err_free;
-	}
-
-	vma = find_vma(current->mm, userptr);
-	if (!vma) {
-		DRM_ERROR("failed to get vm region.\n");
+	ret = get_vaddr_pfn(start, npages, 1, 1, pfns);
+	if (ret != npages) {
+		DRM_ERROR("failed to get user pages from userptr.\n");
+		if (ret < 0)
+			goto err_destroy_pfns;
 		ret = -EFAULT;
-		goto err_free_pages;
+		goto err_put_pfns;
 	}
-
-	if (vma->vm_end < userptr + size) {
-		DRM_ERROR("vma is too small.\n");
+	if (pfns_vector_to_pages(pfns) < 0) {
 		ret = -EFAULT;
-		goto err_free_pages;
+		goto err_put_pfns;
 	}
 
-	g2d_userptr->vma = exynos_gem_get_vma(vma);
-	if (!g2d_userptr->vma) {
-		DRM_ERROR("failed to copy vma.\n");
-		ret = -ENOMEM;
-		goto err_free_pages;
-	}
-
-	g2d_userptr->size = size;
-
-	ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK,
-						npages, pages, vma);
-	if (ret < 0) {
-		DRM_ERROR("failed to get user pages from userptr.\n");
-		goto err_put_vma;
-	}
-
-	g2d_userptr->pages = pages;
-
 	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
 	if (!sgt) {
 		ret = -ENOMEM;
-		goto err_free_userptr;
+		goto err_put_pfns;
 	}
 
-	ret = sg_alloc_table_from_pages(sgt, pages, npages, offset,
-					size, GFP_KERNEL);
+	ret = sg_alloc_table_from_pages(sgt, pfns_vector_pages(pfns), npages,
+					offset, size, GFP_KERNEL);
 	if (ret < 0) {
 		DRM_ERROR("failed to get sgt from pages.\n");
 		goto err_free_sgt;
@@ -540,16 +520,11 @@ err_sg_free_table:
 err_free_sgt:
 	kfree(sgt);
 
-err_free_userptr:
-	exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
-					g2d_userptr->npages,
-					g2d_userptr->vma);
-
-err_put_vma:
-	exynos_gem_put_vma(g2d_userptr->vma);
+err_put_pfns:
+	put_vaddr_pfns(pfns);
 
-err_free_pages:
-	drm_free_large(pages);
+err_destroy_pfns:
+	pfns_vector_destroy(pfns);
 
 err_free:
 	kfree(g2d_userptr);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 42d2904d88c7..e8df799e1684 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -459,103 +459,6 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
 	return 0;
 }
 
-struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma)
-{
-	struct vm_area_struct *vma_copy;
-
-	vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
-	if (!vma_copy)
-		return NULL;
-
-	if (vma->vm_ops && vma->vm_ops->open)
-		vma->vm_ops->open(vma);
-
-	if (vma->vm_file)
-		get_file(vma->vm_file);
-
-	memcpy(vma_copy, vma, sizeof(*vma));
-
-	vma_copy->vm_mm = NULL;
-	vma_copy->vm_next = NULL;
-	vma_copy->vm_prev = NULL;
-
-	return vma_copy;
-}
-
-void exynos_gem_put_vma(struct vm_area_struct *vma)
-{
-	if (!vma)
-		return;
-
-	if (vma->vm_ops && vma->vm_ops->close)
-		vma->vm_ops->close(vma);
-
-	if (vma->vm_file)
-		fput(vma->vm_file);
-
-	kfree(vma);
-}
-
-int exynos_gem_get_pages_from_userptr(unsigned long start,
-						unsigned int npages,
-						struct page **pages,
-						struct vm_area_struct *vma)
-{
-	int get_npages;
-
-	/* the memory region mmaped with VM_PFNMAP. */
-	if (vma_is_io(vma)) {
-		unsigned int i;
-
-		for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
-			unsigned long pfn;
-			int ret = follow_pfn(vma, start, &pfn);
-			if (ret)
-				return ret;
-
-			pages[i] = pfn_to_page(pfn);
-		}
-
-		if (i != npages) {
-			DRM_ERROR("failed to get user_pages.\n");
-			return -EINVAL;
-		}
-
-		return 0;
-	}
-
-	get_npages = get_user_pages(current, current->mm, start,
-					npages, 1, 1, pages, NULL);
-	get_npages = max(get_npages, 0);
-	if (get_npages != npages) {
-		DRM_ERROR("failed to get user_pages.\n");
-		while (get_npages)
-			put_page(pages[--get_npages]);
-		return -EFAULT;
-	}
-
-	return 0;
-}
-
-void exynos_gem_put_pages_to_userptr(struct page **pages,
-					unsigned int npages,
-					struct vm_area_struct *vma)
-{
-	if (!vma_is_io(vma)) {
-		unsigned int i;
-
-		for (i = 0; i < npages; i++) {
-			set_page_dirty_lock(pages[i]);
-
-			/*
-			 * undo the reference we took when populating
-			 * the table.
-			 */
-			put_page(pages[i]);
-		}
-	}
-}
-
 int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
 				struct sg_table *sgt,
 				enum dma_data_direction dir)
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 9/9] staging: tidspbridge: Convert to get_vaddr_pfns()
  2014-03-17 19:49 ` Jan Kara
@ 2014-03-17 19:49   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert the driver to use get_vaddr_pfns() instead of get_user_pages()
or a hand made page table walk. This removes knowledge about vmas
and mmap_sem locking from the driver.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/staging/tidspbridge/core/tiomap3430.c      | 261 ++++-----------------
 .../staging/tidspbridge/include/dspbridge/drv.h    |   3 +-
 .../tidspbridge/include/dspbridge/dspdefs.h        |   9 +-
 drivers/staging/tidspbridge/rmgr/proc.c            |  66 ++++--
 4 files changed, 91 insertions(+), 248 deletions(-)

diff --git a/drivers/staging/tidspbridge/core/tiomap3430.c b/drivers/staging/tidspbridge/core/tiomap3430.c
index b770b2281ce8..a7cbc2f90b60 100644
--- a/drivers/staging/tidspbridge/core/tiomap3430.c
+++ b/drivers/staging/tidspbridge/core/tiomap3430.c
@@ -100,9 +100,10 @@ static int bridge_brd_mem_write(struct bridge_dev_context *dev_ctxt,
 static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt,
 				  u32 ul_mpu_addr, u32 virt_addr,
 				  u32 ul_num_bytes, u32 ul_map_attr,
-				  struct page **mapped_pages);
+				  struct pinned_pfns *pfns);
 static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt,
-				     u32 virt_addr, u32 ul_num_bytes);
+				     u32 virt_addr, u32 ul_num_bytes,
+				     struct pinned_pfns *pfns);
 static int bridge_dev_create(struct bridge_dev_context
 					**dev_cntxt,
 					struct dev_object *hdev_obj,
@@ -1122,24 +1123,18 @@ static int bridge_brd_mem_write(struct bridge_dev_context *dev_ctxt,
  *  TODO: Disable MMU while updating the page tables (but that'll stall DSP)
  */
 static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt,
-				  u32 ul_mpu_addr, u32 virt_addr,
-				  u32 ul_num_bytes, u32 ul_map_attr,
-				  struct page **mapped_pages)
+			      u32 ul_mpu_addr, u32 virt_addr, u32 ul_num_bytes,
+			      u32 ul_map_attr, struct pinned_pfns *pfns)
 {
 	u32 attrs;
 	int status = 0;
 	struct bridge_dev_context *dev_context = dev_ctxt;
 	struct hw_mmu_map_attrs_t hw_attrs;
-	struct vm_area_struct *vma;
-	struct mm_struct *mm = current->mm;
 	u32 write = 0;
-	u32 num_usr_pgs = 0;
-	struct page *mapped_page, *pg;
-	s32 pg_num;
+	int num_usr_pgs, i;
 	u32 va = virt_addr;
-	struct task_struct *curr_task = current;
-	u32 pg_i = 0;
-	u32 mpu_addr, pa;
+	u32 pa;
+	struct page **pages;
 
 	dev_dbg(bridge,
 		"%s hDevCtxt %p, pa %x, va %x, size %x, ul_map_attr %x\n",
@@ -1201,128 +1196,36 @@ static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt,
 	if ((attrs & DSP_MAPPHYSICALADDR)) {
 		status = pte_update(dev_context, ul_mpu_addr, virt_addr,
 				    ul_num_bytes, &hw_attrs);
-		goto func_cont;
+		goto out;
 	}
 
-	/*
-	 * Important Note: ul_mpu_addr is mapped from user application process
-	 * to current process - it must lie completely within the current
-	 * virtual memory address space in order to be of use to us here!
-	 */
-	down_read(&mm->mmap_sem);
-	vma = find_vma(mm, ul_mpu_addr);
-	if (vma)
-		dev_dbg(bridge,
-			"VMAfor UserBuf: ul_mpu_addr=%x, ul_num_bytes=%x, "
-			"vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", ul_mpu_addr,
-			ul_num_bytes, vma->vm_start, vma->vm_end,
-			vma->vm_flags);
-
-	/*
-	 * It is observed that under some circumstances, the user buffer is
-	 * spread across several VMAs. So loop through and check if the entire
-	 * user buffer is covered
-	 */
-	while ((vma) && (ul_mpu_addr + ul_num_bytes > vma->vm_end)) {
-		/* jump to the next VMA region */
-		vma = find_vma(mm, vma->vm_end + 1);
-		dev_dbg(bridge,
-			"VMA for UserBuf ul_mpu_addr=%x ul_num_bytes=%x, "
-			"vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", ul_mpu_addr,
-			ul_num_bytes, vma->vm_start, vma->vm_end,
-			vma->vm_flags);
-	}
-	if (!vma) {
-		pr_err("%s: Failed to get VMA region for 0x%x (%d)\n",
-		       __func__, ul_mpu_addr, ul_num_bytes);
-		status = -EINVAL;
-		up_read(&mm->mmap_sem);
-		goto func_cont;
+	BUG_ON(!pfns);
+	num_usr_pgs = ul_num_bytes / PG_SIZE4K;
+	status = get_vaddr_pfns(ul_mpu_addr, num_usr_pgs, write, 1, pfns);
+	if (status < 0)
+		goto out;
+	if (status != num_usr_pgs) {
+		status = -EFAULT;
+		goto put_pfns;
 	}
 
-	if (vma->vm_flags & VM_IO) {
-		num_usr_pgs = ul_num_bytes / PG_SIZE4K;
-		mpu_addr = ul_mpu_addr;
-
-		/* Get the physical addresses for user buffer */
-		for (pg_i = 0; pg_i < num_usr_pgs; pg_i++) {
-			pa = user_va2_pa(mm, mpu_addr);
-			if (!pa) {
-				status = -EPERM;
-				pr_err("DSPBRIDGE: VM_IO mapping physical"
-				       "address is invalid\n");
-				break;
-			}
-			if (pfn_valid(__phys_to_pfn(pa))) {
-				pg = PHYS_TO_PAGE(pa);
-				get_page(pg);
-				if (page_count(pg) < 1) {
-					pr_err("Bad page in VM_IO buffer\n");
-					bad_page_dump(pa, pg);
-				}
-			}
-			status = pte_set(dev_context->pt_attrs, pa,
-					 va, HW_PAGE_SIZE4KB, &hw_attrs);
-			if (status)
-				break;
-
-			va += HW_PAGE_SIZE4KB;
-			mpu_addr += HW_PAGE_SIZE4KB;
-			pa += HW_PAGE_SIZE4KB;
-		}
-	} else {
-		num_usr_pgs = ul_num_bytes / PG_SIZE4K;
-		if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE))
-			write = 1;
-
-		for (pg_i = 0; pg_i < num_usr_pgs; pg_i++) {
-			pg_num = get_user_pages(curr_task, mm, ul_mpu_addr, 1,
-						write, 1, &mapped_page, NULL);
-			if (pg_num > 0) {
-				if (page_count(mapped_page) < 1) {
-					pr_err("Bad page count after doing"
-					       "get_user_pages on"
-					       "user buffer\n");
-					bad_page_dump(page_to_phys(mapped_page),
-						      mapped_page);
-				}
-				status = pte_set(dev_context->pt_attrs,
-						 page_to_phys(mapped_page), va,
-						 HW_PAGE_SIZE4KB, &hw_attrs);
-				if (status)
-					break;
-
-				if (mapped_pages)
-					mapped_pages[pg_i] = mapped_page;
-
-				va += HW_PAGE_SIZE4KB;
-				ul_mpu_addr += HW_PAGE_SIZE4KB;
-			} else {
-				pr_err("DSPBRIDGE: get_user_pages FAILED,"
-				       "MPU addr = 0x%x,"
-				       "vma->vm_flags = 0x%lx,"
-				       "get_user_pages Err"
-				       "Value = %d, Buffer"
-				       "size=0x%x\n", ul_mpu_addr,
-				       vma->vm_flags, pg_num, ul_num_bytes);
-				status = -EPERM;
-				break;
-			}
-		}
-	}
-	up_read(&mm->mmap_sem);
-func_cont:
-	if (status) {
-		/*
-		 * Roll out the mapped pages incase it failed in middle of
-		 * mapping
-		 */
-		if (pg_i) {
-			bridge_brd_mem_un_map(dev_context, virt_addr,
-					   (pg_i * PG_SIZE4K));
-		}
-		status = -EPERM;
+	status = pfns_vector_to_pages(pfns);
+	if (status < 0)
+		goto put_pfns;
+
+	pages = pfns_vector_pages(pfns);
+	for (i = 0; i < num_usr_pgs; i++) {
+		status = pte_set(dev_context->pt_attrs, page_to_phys(pages[i]),
+				 va, HW_PAGE_SIZE4KB, &hw_attrs);
+		if (status)
+			goto put_pfns;
+		va += HW_PAGE_SIZE4KB;
 	}
+	status = 0;
+	goto out;
+put_pfns:
+	put_vaddr_pfns(pfns);
+out:
 	/*
 	 * In any case, flush the TLB
 	 * This is called from here instead from pte_update to avoid unnecessary
@@ -1343,7 +1246,8 @@ func_cont:
  *      we clear consecutive PTEs until we unmap all the bytes
  */
 static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt,
-				     u32 virt_addr, u32 ul_num_bytes)
+				 u32 virt_addr, u32 ul_num_bytes,
+				 struct pinned_pfns *pfns)
 {
 	u32 l1_base_va;
 	u32 l2_base_va;
@@ -1357,13 +1261,10 @@ static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt,
 	u32 rem_bytes;
 	u32 rem_bytes_l2;
 	u32 va_curr;
-	struct page *pg = NULL;
 	int status = 0;
 	struct bridge_dev_context *dev_context = dev_ctxt;
 	struct pg_table_attrs *pt = dev_context->pt_attrs;
-	u32 temp;
-	u32 paddr;
-	u32 numof4k_pages = 0;
+	struct page **pages;
 
 	va_curr = virt_addr;
 	rem_bytes = ul_num_bytes;
@@ -1423,30 +1324,6 @@ static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt,
 				break;
 			}
 
-			/* Collect Physical addresses from VA */
-			paddr = (pte_val & ~(pte_size - 1));
-			if (pte_size == HW_PAGE_SIZE64KB)
-				numof4k_pages = 16;
-			else
-				numof4k_pages = 1;
-			temp = 0;
-			while (temp++ < numof4k_pages) {
-				if (!pfn_valid(__phys_to_pfn(paddr))) {
-					paddr += HW_PAGE_SIZE4KB;
-					continue;
-				}
-				pg = PHYS_TO_PAGE(paddr);
-				if (page_count(pg) < 1) {
-					pr_info("DSPBRIDGE: UNMAP function: "
-						"COUNT 0 FOR PA 0x%x, size = "
-						"0x%x\n", paddr, ul_num_bytes);
-					bad_page_dump(paddr, pg);
-				} else {
-					set_page_dirty(pg);
-					page_cache_release(pg);
-				}
-				paddr += HW_PAGE_SIZE4KB;
-			}
 			if (hw_mmu_pte_clear(pte_addr_l2, va_curr, pte_size)) {
 				status = -EPERM;
 				goto EXIT_LOOP;
@@ -1488,28 +1365,6 @@ skip_coarse_page:
 			break;
 		}
 
-		if (pte_size == HW_PAGE_SIZE1MB)
-			numof4k_pages = 256;
-		else
-			numof4k_pages = 4096;
-		temp = 0;
-		/* Collect Physical addresses from VA */
-		paddr = (pte_val & ~(pte_size - 1));
-		while (temp++ < numof4k_pages) {
-			if (pfn_valid(__phys_to_pfn(paddr))) {
-				pg = PHYS_TO_PAGE(paddr);
-				if (page_count(pg) < 1) {
-					pr_info("DSPBRIDGE: UNMAP function: "
-						"COUNT 0 FOR PA 0x%x, size = "
-						"0x%x\n", paddr, ul_num_bytes);
-					bad_page_dump(paddr, pg);
-				} else {
-					set_page_dirty(pg);
-					page_cache_release(pg);
-				}
-			}
-			paddr += HW_PAGE_SIZE4KB;
-		}
 		if (!hw_mmu_pte_clear(l1_base_va, va_curr, pte_size)) {
 			status = 0;
 			rem_bytes -= pte_size;
@@ -1519,6 +1374,15 @@ skip_coarse_page:
 			goto EXIT_LOOP;
 		}
 	}
+
+	pages = pfns_vector_pages(pfns);
+	if (pages) {
+		int i;
+
+		for (i = 0; i < pfns_vector_count(pfns); i++)
+			set_page_dirty(pages[i]);
+	}
+	put_vaddr_pfns(pfns);
 	/*
 	 * It is better to flush the TLB here, so that any stale old entries
 	 * get flushed
@@ -1533,41 +1397,6 @@ EXIT_LOOP:
 }
 
 /*
- *  ======== user_va2_pa ========
- *  Purpose:
- *      This function walks through the page tables to convert a userland
- *      virtual address to physical address
- */
-static u32 user_va2_pa(struct mm_struct *mm, u32 address)
-{
-	pgd_t *pgd;
-	pud_t *pud;
-	pmd_t *pmd;
-	pte_t *ptep, pte;
-
-	pgd = pgd_offset(mm, address);
-	if (pgd_none(*pgd) || pgd_bad(*pgd))
-		return 0;
-
-	pud = pud_offset(pgd, address);
-	if (pud_none(*pud) || pud_bad(*pud))
-		return 0;
-
-	pmd = pmd_offset(pud, address);
-	if (pmd_none(*pmd) || pmd_bad(*pmd))
-		return 0;
-
-	ptep = pte_offset_map(pmd, address);
-	if (ptep) {
-		pte = *ptep;
-		if (pte_present(pte))
-			return pte & PAGE_MASK;
-	}
-
-	return 0;
-}
-
-/*
  *  ======== pte_update ========
  *      This function calculates the optimum page-aligned addresses and sizes
  *      Caller must pass page-aligned values
diff --git a/drivers/staging/tidspbridge/include/dspbridge/drv.h b/drivers/staging/tidspbridge/include/dspbridge/drv.h
index b0c7708321b2..e8640e223e9c 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/drv.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/drv.h
@@ -87,8 +87,7 @@ struct dmm_map_object {
 	u32 dsp_addr;
 	u32 mpu_addr;
 	u32 size;
-	u32 num_usr_pgs;
-	struct page **pages;
+	struct pinned_pfns *pfns;
 	struct bridge_dma_map_info dma_info;
 };
 
diff --git a/drivers/staging/tidspbridge/include/dspbridge/dspdefs.h b/drivers/staging/tidspbridge/include/dspbridge/dspdefs.h
index ed32bf383132..f1108413827c 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/dspdefs.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/dspdefs.h
@@ -166,17 +166,16 @@ typedef int(*fxn_brd_memwrite) (struct bridge_dev_context
  *      ul_num_bytes:     Number of bytes to map.
  *      map_attrs:       Mapping attributes (e.g. endianness).
  *  Returns:
- *      0:        Success.
- *      -EPERM:      Other, unspecified error.
+ *      Pointer to pinned pages / NULL:   Success.
+ *      ERR_PTR(-EPERM):                  Other, unspecified error.
  *  Requires:
  *      dev_ctxt != NULL;
  *  Ensures:
  */
-typedef int(*fxn_brd_memmap) (struct bridge_dev_context
+typedef struct pinned_pfns *(*fxn_brd_memmap) (struct bridge_dev_context
 				     * dev_ctxt, u32 ul_mpu_addr,
 				     u32 virt_addr, u32 ul_num_bytes,
-				     u32 map_attr,
-				     struct page **mapped_pages);
+				     u32 map_attr);
 
 /*
  *  ======== bridge_brd_mem_un_map ========
diff --git a/drivers/staging/tidspbridge/rmgr/proc.c b/drivers/staging/tidspbridge/rmgr/proc.c
index cd5235a4f77c..5d4426f75ba8 100644
--- a/drivers/staging/tidspbridge/rmgr/proc.c
+++ b/drivers/staging/tidspbridge/rmgr/proc.c
@@ -124,9 +124,8 @@ static struct dmm_map_object *add_mapping_info(struct process_context *pr_ctxt,
 
 	INIT_LIST_HEAD(&map_obj->link);
 
-	map_obj->pages = kcalloc(num_usr_pgs, sizeof(struct page *),
-				 GFP_KERNEL);
-	if (!map_obj->pages) {
+	map_obj->pfns = pfns_vector_create(num_usr_pgs);
+	if (!map_obj->pfns) {
 		kfree(map_obj);
 		return NULL;
 	}
@@ -134,7 +133,6 @@ static struct dmm_map_object *add_mapping_info(struct process_context *pr_ctxt,
 	map_obj->mpu_addr = mpu_addr;
 	map_obj->dsp_addr = dsp_addr;
 	map_obj->size = size;
-	map_obj->num_usr_pgs = num_usr_pgs;
 
 	spin_lock(&pr_ctxt->dmm_map_lock);
 	list_add(&map_obj->link, &pr_ctxt->dmm_map_list);
@@ -154,10 +152,11 @@ static int match_exact_map_obj(struct dmm_map_object *map_obj,
 		map_obj->size == size;
 }
 
-static void remove_mapping_information(struct process_context *pr_ctxt,
-						u32 dsp_addr, u32 size)
+static struct dmm_map_object *find_mapping_information(
+					struct process_context *pr_ctxt,
+					u32 dsp_addr, u32 size)
 {
-	struct dmm_map_object *map_obj;
+	struct dmm_map_object *map_obj = NULL;
 
 	pr_debug("%s: looking for virt 0x%x size 0x%x\n", __func__,
 							dsp_addr, size);
@@ -171,11 +170,7 @@ static void remove_mapping_information(struct process_context *pr_ctxt,
 							map_obj->size);
 
 		if (match_exact_map_obj(map_obj, dsp_addr, size)) {
-			pr_debug("%s: match, deleting map info\n", __func__);
-			list_del(&map_obj->link);
-			kfree(map_obj->dma_info.sg);
-			kfree(map_obj->pages);
-			kfree(map_obj);
+			pr_debug("%s: match map info\n", __func__);
 			goto out;
 		}
 		pr_debug("%s: candidate didn't match\n", __func__);
@@ -184,6 +179,19 @@ static void remove_mapping_information(struct process_context *pr_ctxt,
 	pr_err("%s: failed to find given map info\n", __func__);
 out:
 	spin_unlock(&pr_ctxt->dmm_map_lock);
+	return map_obj
+}
+
+static void remove_mapping_information(struct process_context *pr_ctxt,
+				       struct dmm_map_object *map_obj)
+{
+	spin_lock(&pr_ctxt->dmm_map_lock);
+	pr_debug("%s: deleting map info\n", __func__);
+	list_del(&map_obj->link);
+	kfree(map_obj->dma_info.sg);
+	pfns_vector_destroy(map_obj->pfns);
+	kfree(map_obj);
+	spin_unlock(&pr_ctxt->dmm_map_lock);
 }
 
 static int match_containing_map_obj(struct dmm_map_object *map_obj,
@@ -231,7 +239,7 @@ static int find_first_page_in_cache(struct dmm_map_object *map_obj,
 	u32 requested_base_page = mpu_addr >> PAGE_SHIFT;
 	int pg_index = requested_base_page - mapped_base_page;
 
-	if (pg_index < 0 || pg_index >= map_obj->num_usr_pgs) {
+	if (pg_index < 0 || pg_index >= pfns_vector_count(map_obj->pfns)) {
 		pr_err("%s: failed (got %d)\n", __func__, pg_index);
 		return -1;
 	}
@@ -243,16 +251,18 @@ static int find_first_page_in_cache(struct dmm_map_object *map_obj,
 static inline struct page *get_mapping_page(struct dmm_map_object *map_obj,
 								int pg_i)
 {
+	int num_usr_pgs = pfns_vector_count(map_obj->pfns);
+
 	pr_debug("%s: looking for pg_i %d, num_usr_pgs: %d\n", __func__,
-					pg_i, map_obj->num_usr_pgs);
+					pg_i, num_usr_pgs);
 
-	if (pg_i < 0 || pg_i >= map_obj->num_usr_pgs) {
+	if (pg_i < 0 || pg_i >= num_usr_pgs) {
 		pr_err("%s: requested pg_i %d is out of mapped range\n",
 				__func__, pg_i);
 		return NULL;
 	}
 
-	return map_obj->pages[pg_i];
+	return pfns_vector_pages(map_obj->pfns)[pg_i];
 }
 
 /*
@@ -1262,7 +1272,7 @@ int proc_map(void *hprocessor, void *pmpu_addr, u32 ul_size,
 	u32 size_align;
 	int status = 0;
 	struct proc_object *p_proc_object = (struct proc_object *)hprocessor;
-	struct dmm_map_object *map_obj;
+	struct dmm_map_object *map_obj = NULL;
 	u32 tmp_addr = 0;
 
 #ifdef CONFIG_TIDSPBRIDGE_CACHE_LINE_CHECK
@@ -1307,13 +1317,14 @@ int proc_map(void *hprocessor, void *pmpu_addr, u32 ul_size,
 		else
 			status = (*p_proc_object->intf_fxns->brd_mem_map)
 			    (p_proc_object->bridge_context, pa_align, va_align,
-			     size_align, ul_map_attr, map_obj->pages);
+			     size_align, ul_map_attr, map_obj->pfns);
 	}
 	if (!status) {
 		/* Mapped address = MSB of VA | LSB of PA */
 		*pp_map_addr = (void *) tmp_addr;
 	} else {
-		remove_mapping_information(pr_ctxt, tmp_addr, size_align);
+		if (map_obj)
+			remove_mapping_information(pr_ctxt, map_obj);
 		dmm_un_map_memory(dmm_mgr, va_align, &size_align);
 	}
 	mutex_unlock(&proc_lock);
@@ -1592,6 +1603,7 @@ int proc_un_map(void *hprocessor, void *map_addr,
 	struct dmm_object *dmm_mgr;
 	u32 va_align;
 	u32 size_align;
+	struct dmm_map_object *map_obj;
 
 	va_align = PG_ALIGN_LOW((u32) map_addr, PG_SIZE4K);
 	if (!p_proc_object) {
@@ -1607,17 +1619,21 @@ int proc_un_map(void *hprocessor, void *map_addr,
 
 	/* Critical section */
 	mutex_lock(&proc_lock);
+
 	/*
 	 * Update DMM structures. Get the size to unmap.
 	 * This function returns error if the VA is not mapped
 	 */
 	status = dmm_un_map_memory(dmm_mgr, (u32) va_align, &size_align);
+	if (status)
+		goto unmap_failed;
+	map_obj = find_mapping_information(pr_ctxt, (u32) map_addr, size_align);
+	if (!map_obj)
+		goto unmap_failed;
 	/* Remove mapping from the page tables. */
-	if (!status) {
-		status = (*p_proc_object->intf_fxns->brd_mem_un_map)
-		    (p_proc_object->bridge_context, va_align, size_align);
-	}
-
+	status = (*p_proc_object->intf_fxns->brd_mem_un_map)
+		    (p_proc_object->bridge_context, va_align, size_align,
+		     map_obj);
 	if (status)
 		goto unmap_failed;
 
@@ -1626,7 +1642,7 @@ int proc_un_map(void *hprocessor, void *map_addr,
 	 * from dmm_map_list, so that mapped memory resource tracking
 	 * remains uptodate
 	 */
-	remove_mapping_information(pr_ctxt, (u32) map_addr, size_align);
+	remove_mapping_information(pr_ctxt, map_obj);
 
 unmap_failed:
 	mutex_unlock(&proc_lock);
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 9/9] staging: tidspbridge: Convert to get_vaddr_pfns()
@ 2014-03-17 19:49   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-17 19:49 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara

Convert the driver to use get_vaddr_pfns() instead of get_user_pages()
or a hand made page table walk. This removes knowledge about vmas
and mmap_sem locking from the driver.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 drivers/staging/tidspbridge/core/tiomap3430.c      | 261 ++++-----------------
 .../staging/tidspbridge/include/dspbridge/drv.h    |   3 +-
 .../tidspbridge/include/dspbridge/dspdefs.h        |   9 +-
 drivers/staging/tidspbridge/rmgr/proc.c            |  66 ++++--
 4 files changed, 91 insertions(+), 248 deletions(-)

diff --git a/drivers/staging/tidspbridge/core/tiomap3430.c b/drivers/staging/tidspbridge/core/tiomap3430.c
index b770b2281ce8..a7cbc2f90b60 100644
--- a/drivers/staging/tidspbridge/core/tiomap3430.c
+++ b/drivers/staging/tidspbridge/core/tiomap3430.c
@@ -100,9 +100,10 @@ static int bridge_brd_mem_write(struct bridge_dev_context *dev_ctxt,
 static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt,
 				  u32 ul_mpu_addr, u32 virt_addr,
 				  u32 ul_num_bytes, u32 ul_map_attr,
-				  struct page **mapped_pages);
+				  struct pinned_pfns *pfns);
 static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt,
-				     u32 virt_addr, u32 ul_num_bytes);
+				     u32 virt_addr, u32 ul_num_bytes,
+				     struct pinned_pfns *pfns);
 static int bridge_dev_create(struct bridge_dev_context
 					**dev_cntxt,
 					struct dev_object *hdev_obj,
@@ -1122,24 +1123,18 @@ static int bridge_brd_mem_write(struct bridge_dev_context *dev_ctxt,
  *  TODO: Disable MMU while updating the page tables (but that'll stall DSP)
  */
 static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt,
-				  u32 ul_mpu_addr, u32 virt_addr,
-				  u32 ul_num_bytes, u32 ul_map_attr,
-				  struct page **mapped_pages)
+			      u32 ul_mpu_addr, u32 virt_addr, u32 ul_num_bytes,
+			      u32 ul_map_attr, struct pinned_pfns *pfns)
 {
 	u32 attrs;
 	int status = 0;
 	struct bridge_dev_context *dev_context = dev_ctxt;
 	struct hw_mmu_map_attrs_t hw_attrs;
-	struct vm_area_struct *vma;
-	struct mm_struct *mm = current->mm;
 	u32 write = 0;
-	u32 num_usr_pgs = 0;
-	struct page *mapped_page, *pg;
-	s32 pg_num;
+	int num_usr_pgs, i;
 	u32 va = virt_addr;
-	struct task_struct *curr_task = current;
-	u32 pg_i = 0;
-	u32 mpu_addr, pa;
+	u32 pa;
+	struct page **pages;
 
 	dev_dbg(bridge,
 		"%s hDevCtxt %p, pa %x, va %x, size %x, ul_map_attr %x\n",
@@ -1201,128 +1196,36 @@ static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt,
 	if ((attrs & DSP_MAPPHYSICALADDR)) {
 		status = pte_update(dev_context, ul_mpu_addr, virt_addr,
 				    ul_num_bytes, &hw_attrs);
-		goto func_cont;
+		goto out;
 	}
 
-	/*
-	 * Important Note: ul_mpu_addr is mapped from user application process
-	 * to current process - it must lie completely within the current
-	 * virtual memory address space in order to be of use to us here!
-	 */
-	down_read(&mm->mmap_sem);
-	vma = find_vma(mm, ul_mpu_addr);
-	if (vma)
-		dev_dbg(bridge,
-			"VMAfor UserBuf: ul_mpu_addr=%x, ul_num_bytes=%x, "
-			"vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", ul_mpu_addr,
-			ul_num_bytes, vma->vm_start, vma->vm_end,
-			vma->vm_flags);
-
-	/*
-	 * It is observed that under some circumstances, the user buffer is
-	 * spread across several VMAs. So loop through and check if the entire
-	 * user buffer is covered
-	 */
-	while ((vma) && (ul_mpu_addr + ul_num_bytes > vma->vm_end)) {
-		/* jump to the next VMA region */
-		vma = find_vma(mm, vma->vm_end + 1);
-		dev_dbg(bridge,
-			"VMA for UserBuf ul_mpu_addr=%x ul_num_bytes=%x, "
-			"vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", ul_mpu_addr,
-			ul_num_bytes, vma->vm_start, vma->vm_end,
-			vma->vm_flags);
-	}
-	if (!vma) {
-		pr_err("%s: Failed to get VMA region for 0x%x (%d)\n",
-		       __func__, ul_mpu_addr, ul_num_bytes);
-		status = -EINVAL;
-		up_read(&mm->mmap_sem);
-		goto func_cont;
+	BUG_ON(!pfns);
+	num_usr_pgs = ul_num_bytes / PG_SIZE4K;
+	status = get_vaddr_pfns(ul_mpu_addr, num_usr_pgs, write, 1, pfns);
+	if (status < 0)
+		goto out;
+	if (status != num_usr_pgs) {
+		status = -EFAULT;
+		goto put_pfns;
 	}
 
-	if (vma->vm_flags & VM_IO) {
-		num_usr_pgs = ul_num_bytes / PG_SIZE4K;
-		mpu_addr = ul_mpu_addr;
-
-		/* Get the physical addresses for user buffer */
-		for (pg_i = 0; pg_i < num_usr_pgs; pg_i++) {
-			pa = user_va2_pa(mm, mpu_addr);
-			if (!pa) {
-				status = -EPERM;
-				pr_err("DSPBRIDGE: VM_IO mapping physical"
-				       "address is invalid\n");
-				break;
-			}
-			if (pfn_valid(__phys_to_pfn(pa))) {
-				pg = PHYS_TO_PAGE(pa);
-				get_page(pg);
-				if (page_count(pg) < 1) {
-					pr_err("Bad page in VM_IO buffer\n");
-					bad_page_dump(pa, pg);
-				}
-			}
-			status = pte_set(dev_context->pt_attrs, pa,
-					 va, HW_PAGE_SIZE4KB, &hw_attrs);
-			if (status)
-				break;
-
-			va += HW_PAGE_SIZE4KB;
-			mpu_addr += HW_PAGE_SIZE4KB;
-			pa += HW_PAGE_SIZE4KB;
-		}
-	} else {
-		num_usr_pgs = ul_num_bytes / PG_SIZE4K;
-		if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE))
-			write = 1;
-
-		for (pg_i = 0; pg_i < num_usr_pgs; pg_i++) {
-			pg_num = get_user_pages(curr_task, mm, ul_mpu_addr, 1,
-						write, 1, &mapped_page, NULL);
-			if (pg_num > 0) {
-				if (page_count(mapped_page) < 1) {
-					pr_err("Bad page count after doing"
-					       "get_user_pages on"
-					       "user buffer\n");
-					bad_page_dump(page_to_phys(mapped_page),
-						      mapped_page);
-				}
-				status = pte_set(dev_context->pt_attrs,
-						 page_to_phys(mapped_page), va,
-						 HW_PAGE_SIZE4KB, &hw_attrs);
-				if (status)
-					break;
-
-				if (mapped_pages)
-					mapped_pages[pg_i] = mapped_page;
-
-				va += HW_PAGE_SIZE4KB;
-				ul_mpu_addr += HW_PAGE_SIZE4KB;
-			} else {
-				pr_err("DSPBRIDGE: get_user_pages FAILED,"
-				       "MPU addr = 0x%x,"
-				       "vma->vm_flags = 0x%lx,"
-				       "get_user_pages Err"
-				       "Value = %d, Buffer"
-				       "size=0x%x\n", ul_mpu_addr,
-				       vma->vm_flags, pg_num, ul_num_bytes);
-				status = -EPERM;
-				break;
-			}
-		}
-	}
-	up_read(&mm->mmap_sem);
-func_cont:
-	if (status) {
-		/*
-		 * Roll out the mapped pages incase it failed in middle of
-		 * mapping
-		 */
-		if (pg_i) {
-			bridge_brd_mem_un_map(dev_context, virt_addr,
-					   (pg_i * PG_SIZE4K));
-		}
-		status = -EPERM;
+	status = pfns_vector_to_pages(pfns);
+	if (status < 0)
+		goto put_pfns;
+
+	pages = pfns_vector_pages(pfns);
+	for (i = 0; i < num_usr_pgs; i++) {
+		status = pte_set(dev_context->pt_attrs, page_to_phys(pages[i]),
+				 va, HW_PAGE_SIZE4KB, &hw_attrs);
+		if (status)
+			goto put_pfns;
+		va += HW_PAGE_SIZE4KB;
 	}
+	status = 0;
+	goto out;
+put_pfns:
+	put_vaddr_pfns(pfns);
+out:
 	/*
 	 * In any case, flush the TLB
 	 * This is called from here instead from pte_update to avoid unnecessary
@@ -1343,7 +1246,8 @@ func_cont:
  *      we clear consecutive PTEs until we unmap all the bytes
  */
 static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt,
-				     u32 virt_addr, u32 ul_num_bytes)
+				 u32 virt_addr, u32 ul_num_bytes,
+				 struct pinned_pfns *pfns)
 {
 	u32 l1_base_va;
 	u32 l2_base_va;
@@ -1357,13 +1261,10 @@ static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt,
 	u32 rem_bytes;
 	u32 rem_bytes_l2;
 	u32 va_curr;
-	struct page *pg = NULL;
 	int status = 0;
 	struct bridge_dev_context *dev_context = dev_ctxt;
 	struct pg_table_attrs *pt = dev_context->pt_attrs;
-	u32 temp;
-	u32 paddr;
-	u32 numof4k_pages = 0;
+	struct page **pages;
 
 	va_curr = virt_addr;
 	rem_bytes = ul_num_bytes;
@@ -1423,30 +1324,6 @@ static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt,
 				break;
 			}
 
-			/* Collect Physical addresses from VA */
-			paddr = (pte_val & ~(pte_size - 1));
-			if (pte_size == HW_PAGE_SIZE64KB)
-				numof4k_pages = 16;
-			else
-				numof4k_pages = 1;
-			temp = 0;
-			while (temp++ < numof4k_pages) {
-				if (!pfn_valid(__phys_to_pfn(paddr))) {
-					paddr += HW_PAGE_SIZE4KB;
-					continue;
-				}
-				pg = PHYS_TO_PAGE(paddr);
-				if (page_count(pg) < 1) {
-					pr_info("DSPBRIDGE: UNMAP function: "
-						"COUNT 0 FOR PA 0x%x, size = "
-						"0x%x\n", paddr, ul_num_bytes);
-					bad_page_dump(paddr, pg);
-				} else {
-					set_page_dirty(pg);
-					page_cache_release(pg);
-				}
-				paddr += HW_PAGE_SIZE4KB;
-			}
 			if (hw_mmu_pte_clear(pte_addr_l2, va_curr, pte_size)) {
 				status = -EPERM;
 				goto EXIT_LOOP;
@@ -1488,28 +1365,6 @@ skip_coarse_page:
 			break;
 		}
 
-		if (pte_size == HW_PAGE_SIZE1MB)
-			numof4k_pages = 256;
-		else
-			numof4k_pages = 4096;
-		temp = 0;
-		/* Collect Physical addresses from VA */
-		paddr = (pte_val & ~(pte_size - 1));
-		while (temp++ < numof4k_pages) {
-			if (pfn_valid(__phys_to_pfn(paddr))) {
-				pg = PHYS_TO_PAGE(paddr);
-				if (page_count(pg) < 1) {
-					pr_info("DSPBRIDGE: UNMAP function: "
-						"COUNT 0 FOR PA 0x%x, size = "
-						"0x%x\n", paddr, ul_num_bytes);
-					bad_page_dump(paddr, pg);
-				} else {
-					set_page_dirty(pg);
-					page_cache_release(pg);
-				}
-			}
-			paddr += HW_PAGE_SIZE4KB;
-		}
 		if (!hw_mmu_pte_clear(l1_base_va, va_curr, pte_size)) {
 			status = 0;
 			rem_bytes -= pte_size;
@@ -1519,6 +1374,15 @@ skip_coarse_page:
 			goto EXIT_LOOP;
 		}
 	}
+
+	pages = pfns_vector_pages(pfns);
+	if (pages) {
+		int i;
+
+		for (i = 0; i < pfns_vector_count(pfns); i++)
+			set_page_dirty(pages[i]);
+	}
+	put_vaddr_pfns(pfns);
 	/*
 	 * It is better to flush the TLB here, so that any stale old entries
 	 * get flushed
@@ -1533,41 +1397,6 @@ EXIT_LOOP:
 }
 
 /*
- *  ======== user_va2_pa ========
- *  Purpose:
- *      This function walks through the page tables to convert a userland
- *      virtual address to physical address
- */
-static u32 user_va2_pa(struct mm_struct *mm, u32 address)
-{
-	pgd_t *pgd;
-	pud_t *pud;
-	pmd_t *pmd;
-	pte_t *ptep, pte;
-
-	pgd = pgd_offset(mm, address);
-	if (pgd_none(*pgd) || pgd_bad(*pgd))
-		return 0;
-
-	pud = pud_offset(pgd, address);
-	if (pud_none(*pud) || pud_bad(*pud))
-		return 0;
-
-	pmd = pmd_offset(pud, address);
-	if (pmd_none(*pmd) || pmd_bad(*pmd))
-		return 0;
-
-	ptep = pte_offset_map(pmd, address);
-	if (ptep) {
-		pte = *ptep;
-		if (pte_present(pte))
-			return pte & PAGE_MASK;
-	}
-
-	return 0;
-}
-
-/*
  *  ======== pte_update ========
  *      This function calculates the optimum page-aligned addresses and sizes
  *      Caller must pass page-aligned values
diff --git a/drivers/staging/tidspbridge/include/dspbridge/drv.h b/drivers/staging/tidspbridge/include/dspbridge/drv.h
index b0c7708321b2..e8640e223e9c 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/drv.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/drv.h
@@ -87,8 +87,7 @@ struct dmm_map_object {
 	u32 dsp_addr;
 	u32 mpu_addr;
 	u32 size;
-	u32 num_usr_pgs;
-	struct page **pages;
+	struct pinned_pfns *pfns;
 	struct bridge_dma_map_info dma_info;
 };
 
diff --git a/drivers/staging/tidspbridge/include/dspbridge/dspdefs.h b/drivers/staging/tidspbridge/include/dspbridge/dspdefs.h
index ed32bf383132..f1108413827c 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/dspdefs.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/dspdefs.h
@@ -166,17 +166,16 @@ typedef int(*fxn_brd_memwrite) (struct bridge_dev_context
  *      ul_num_bytes:     Number of bytes to map.
  *      map_attrs:       Mapping attributes (e.g. endianness).
  *  Returns:
- *      0:        Success.
- *      -EPERM:      Other, unspecified error.
+ *      Pointer to pinned pages / NULL:   Success.
+ *      ERR_PTR(-EPERM):                  Other, unspecified error.
  *  Requires:
  *      dev_ctxt != NULL;
  *  Ensures:
  */
-typedef int(*fxn_brd_memmap) (struct bridge_dev_context
+typedef struct pinned_pfns *(*fxn_brd_memmap) (struct bridge_dev_context
 				     * dev_ctxt, u32 ul_mpu_addr,
 				     u32 virt_addr, u32 ul_num_bytes,
-				     u32 map_attr,
-				     struct page **mapped_pages);
+				     u32 map_attr);
 
 /*
  *  ======== bridge_brd_mem_un_map ========
diff --git a/drivers/staging/tidspbridge/rmgr/proc.c b/drivers/staging/tidspbridge/rmgr/proc.c
index cd5235a4f77c..5d4426f75ba8 100644
--- a/drivers/staging/tidspbridge/rmgr/proc.c
+++ b/drivers/staging/tidspbridge/rmgr/proc.c
@@ -124,9 +124,8 @@ static struct dmm_map_object *add_mapping_info(struct process_context *pr_ctxt,
 
 	INIT_LIST_HEAD(&map_obj->link);
 
-	map_obj->pages = kcalloc(num_usr_pgs, sizeof(struct page *),
-				 GFP_KERNEL);
-	if (!map_obj->pages) {
+	map_obj->pfns = pfns_vector_create(num_usr_pgs);
+	if (!map_obj->pfns) {
 		kfree(map_obj);
 		return NULL;
 	}
@@ -134,7 +133,6 @@ static struct dmm_map_object *add_mapping_info(struct process_context *pr_ctxt,
 	map_obj->mpu_addr = mpu_addr;
 	map_obj->dsp_addr = dsp_addr;
 	map_obj->size = size;
-	map_obj->num_usr_pgs = num_usr_pgs;
 
 	spin_lock(&pr_ctxt->dmm_map_lock);
 	list_add(&map_obj->link, &pr_ctxt->dmm_map_list);
@@ -154,10 +152,11 @@ static int match_exact_map_obj(struct dmm_map_object *map_obj,
 		map_obj->size == size;
 }
 
-static void remove_mapping_information(struct process_context *pr_ctxt,
-						u32 dsp_addr, u32 size)
+static struct dmm_map_object *find_mapping_information(
+					struct process_context *pr_ctxt,
+					u32 dsp_addr, u32 size)
 {
-	struct dmm_map_object *map_obj;
+	struct dmm_map_object *map_obj = NULL;
 
 	pr_debug("%s: looking for virt 0x%x size 0x%x\n", __func__,
 							dsp_addr, size);
@@ -171,11 +170,7 @@ static void remove_mapping_information(struct process_context *pr_ctxt,
 							map_obj->size);
 
 		if (match_exact_map_obj(map_obj, dsp_addr, size)) {
-			pr_debug("%s: match, deleting map info\n", __func__);
-			list_del(&map_obj->link);
-			kfree(map_obj->dma_info.sg);
-			kfree(map_obj->pages);
-			kfree(map_obj);
+			pr_debug("%s: match map info\n", __func__);
 			goto out;
 		}
 		pr_debug("%s: candidate didn't match\n", __func__);
@@ -184,6 +179,19 @@ static void remove_mapping_information(struct process_context *pr_ctxt,
 	pr_err("%s: failed to find given map info\n", __func__);
 out:
 	spin_unlock(&pr_ctxt->dmm_map_lock);
+	return map_obj
+}
+
+static void remove_mapping_information(struct process_context *pr_ctxt,
+				       struct dmm_map_object *map_obj)
+{
+	spin_lock(&pr_ctxt->dmm_map_lock);
+	pr_debug("%s: deleting map info\n", __func__);
+	list_del(&map_obj->link);
+	kfree(map_obj->dma_info.sg);
+	pfns_vector_destroy(map_obj->pfns);
+	kfree(map_obj);
+	spin_unlock(&pr_ctxt->dmm_map_lock);
 }
 
 static int match_containing_map_obj(struct dmm_map_object *map_obj,
@@ -231,7 +239,7 @@ static int find_first_page_in_cache(struct dmm_map_object *map_obj,
 	u32 requested_base_page = mpu_addr >> PAGE_SHIFT;
 	int pg_index = requested_base_page - mapped_base_page;
 
-	if (pg_index < 0 || pg_index >= map_obj->num_usr_pgs) {
+	if (pg_index < 0 || pg_index >= pfns_vector_count(map_obj->pfns)) {
 		pr_err("%s: failed (got %d)\n", __func__, pg_index);
 		return -1;
 	}
@@ -243,16 +251,18 @@ static int find_first_page_in_cache(struct dmm_map_object *map_obj,
 static inline struct page *get_mapping_page(struct dmm_map_object *map_obj,
 								int pg_i)
 {
+	int num_usr_pgs = pfns_vector_count(map_obj->pfns);
+
 	pr_debug("%s: looking for pg_i %d, num_usr_pgs: %d\n", __func__,
-					pg_i, map_obj->num_usr_pgs);
+					pg_i, num_usr_pgs);
 
-	if (pg_i < 0 || pg_i >= map_obj->num_usr_pgs) {
+	if (pg_i < 0 || pg_i >= num_usr_pgs) {
 		pr_err("%s: requested pg_i %d is out of mapped range\n",
 				__func__, pg_i);
 		return NULL;
 	}
 
-	return map_obj->pages[pg_i];
+	return pfns_vector_pages(map_obj->pfns)[pg_i];
 }
 
 /*
@@ -1262,7 +1272,7 @@ int proc_map(void *hprocessor, void *pmpu_addr, u32 ul_size,
 	u32 size_align;
 	int status = 0;
 	struct proc_object *p_proc_object = (struct proc_object *)hprocessor;
-	struct dmm_map_object *map_obj;
+	struct dmm_map_object *map_obj = NULL;
 	u32 tmp_addr = 0;
 
 #ifdef CONFIG_TIDSPBRIDGE_CACHE_LINE_CHECK
@@ -1307,13 +1317,14 @@ int proc_map(void *hprocessor, void *pmpu_addr, u32 ul_size,
 		else
 			status = (*p_proc_object->intf_fxns->brd_mem_map)
 			    (p_proc_object->bridge_context, pa_align, va_align,
-			     size_align, ul_map_attr, map_obj->pages);
+			     size_align, ul_map_attr, map_obj->pfns);
 	}
 	if (!status) {
 		/* Mapped address = MSB of VA | LSB of PA */
 		*pp_map_addr = (void *) tmp_addr;
 	} else {
-		remove_mapping_information(pr_ctxt, tmp_addr, size_align);
+		if (map_obj)
+			remove_mapping_information(pr_ctxt, map_obj);
 		dmm_un_map_memory(dmm_mgr, va_align, &size_align);
 	}
 	mutex_unlock(&proc_lock);
@@ -1592,6 +1603,7 @@ int proc_un_map(void *hprocessor, void *map_addr,
 	struct dmm_object *dmm_mgr;
 	u32 va_align;
 	u32 size_align;
+	struct dmm_map_object *map_obj;
 
 	va_align = PG_ALIGN_LOW((u32) map_addr, PG_SIZE4K);
 	if (!p_proc_object) {
@@ -1607,17 +1619,21 @@ int proc_un_map(void *hprocessor, void *map_addr,
 
 	/* Critical section */
 	mutex_lock(&proc_lock);
+
 	/*
 	 * Update DMM structures. Get the size to unmap.
 	 * This function returns error if the VA is not mapped
 	 */
 	status = dmm_un_map_memory(dmm_mgr, (u32) va_align, &size_align);
+	if (status)
+		goto unmap_failed;
+	map_obj = find_mapping_information(pr_ctxt, (u32) map_addr, size_align);
+	if (!map_obj)
+		goto unmap_failed;
 	/* Remove mapping from the page tables. */
-	if (!status) {
-		status = (*p_proc_object->intf_fxns->brd_mem_un_map)
-		    (p_proc_object->bridge_context, va_align, size_align);
-	}
-
+	status = (*p_proc_object->intf_fxns->brd_mem_un_map)
+		    (p_proc_object->bridge_context, va_align, size_align,
+		     map_obj);
 	if (status)
 		goto unmap_failed;
 
@@ -1626,7 +1642,7 @@ int proc_un_map(void *hprocessor, void *map_addr,
 	 * from dmm_map_list, so that mapped memory resource tracking
 	 * remains uptodate
 	 */
-	remove_mapping_information(pr_ctxt, (u32) map_addr, size_align);
+	remove_mapping_information(pr_ctxt, map_obj);
 
 unmap_failed:
 	mutex_unlock(&proc_lock);
-- 
1.8.1.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
  2014-03-17 19:49   ` Jan Kara
@ 2014-03-17 20:53     ` Dave Hansen
  -1 siblings, 0 replies; 44+ messages in thread
From: Dave Hansen @ 2014-03-17 20:53 UTC (permalink / raw)
  To: Jan Kara, linux-mm; +Cc: linux-media

On 03/17/2014 12:49 PM, Jan Kara wrote:
> +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
> +		   struct pinned_pfns *pfns)
> +{
...
> +	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
> +		pfns->got_ref = 1;
> +		pfns->is_pages = 1;
> +		ret = get_user_pages(current, mm, start, nr_pfns, write, force,
> +				     pfns_vector_pages(pfns), NULL);
> +		goto out;
> +	}

Have you given any thought to how this should deal with VM_MIXEDMAP
vmas?  get_user_pages() will freak when it hits the !vm_normal_page()
test on the pfnmapped ones, and jump out.  Shouldn't get_vaddr_pfns() be
able to handle those too?

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
@ 2014-03-17 20:53     ` Dave Hansen
  0 siblings, 0 replies; 44+ messages in thread
From: Dave Hansen @ 2014-03-17 20:53 UTC (permalink / raw)
  To: Jan Kara, linux-mm; +Cc: linux-media

On 03/17/2014 12:49 PM, Jan Kara wrote:
> +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
> +		   struct pinned_pfns *pfns)
> +{
...
> +	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
> +		pfns->got_ref = 1;
> +		pfns->is_pages = 1;
> +		ret = get_user_pages(current, mm, start, nr_pfns, write, force,
> +				     pfns_vector_pages(pfns), NULL);
> +		goto out;
> +	}

Have you given any thought to how this should deal with VM_MIXEDMAP
vmas?  get_user_pages() will freak when it hits the !vm_normal_page()
test on the pfnmapped ones, and jump out.  Shouldn't get_vaddr_pfns() be
able to handle those too?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
  2014-03-17 20:53     ` Dave Hansen
@ 2014-03-18 10:25       ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-18 10:25 UTC (permalink / raw)
  To: Dave Hansen; +Cc: Jan Kara, linux-mm, linux-media

On Mon 17-03-14 13:53:35, Dave Hansen wrote:
> On 03/17/2014 12:49 PM, Jan Kara wrote:
> > +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
> > +		   struct pinned_pfns *pfns)
> > +{
> ...
> > +	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
> > +		pfns->got_ref = 1;
> > +		pfns->is_pages = 1;
> > +		ret = get_user_pages(current, mm, start, nr_pfns, write, force,
> > +				     pfns_vector_pages(pfns), NULL);
> > +		goto out;
> > +	}
> 
> Have you given any thought to how this should deal with VM_MIXEDMAP
> vmas?  get_user_pages() will freak when it hits the !vm_normal_page()
> test on the pfnmapped ones, and jump out.  Shouldn't get_vaddr_pfns() be
> able to handle those too?
  It could and it doesn't seem as a big complication. Although none of the
converted drivers need this functionality, I guess it makes sense to
implement this to make the API more consistent. So I can have a look at it
for the next iteration.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
@ 2014-03-18 10:25       ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-03-18 10:25 UTC (permalink / raw)
  To: Dave Hansen; +Cc: Jan Kara, linux-mm, linux-media

On Mon 17-03-14 13:53:35, Dave Hansen wrote:
> On 03/17/2014 12:49 PM, Jan Kara wrote:
> > +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
> > +		   struct pinned_pfns *pfns)
> > +{
> ...
> > +	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
> > +		pfns->got_ref = 1;
> > +		pfns->is_pages = 1;
> > +		ret = get_user_pages(current, mm, start, nr_pfns, write, force,
> > +				     pfns_vector_pages(pfns), NULL);
> > +		goto out;
> > +	}
> 
> Have you given any thought to how this should deal with VM_MIXEDMAP
> vmas?  get_user_pages() will freak when it hits the !vm_normal_page()
> test on the pfnmapped ones, and jump out.  Shouldn't get_vaddr_pfns() be
> able to handle those too?
  It could and it doesn't seem as a big complication. Although none of the
converted drivers need this functionality, I guess it makes sense to
implement this to make the API more consistent. So I can have a look at it
for the next iteration.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-03-17 19:49 ` Jan Kara
@ 2014-04-10 10:02   ` Marek Szyprowski
  -1 siblings, 0 replies; 44+ messages in thread
From: Marek Szyprowski @ 2014-04-10 10:02 UTC (permalink / raw)
  To: Jan Kara, linux-mm
  Cc: linux-media, linaro-mm-sig, 'Tomasz Stanislawski',
	Laurent Pinchart

Hello,

On 2014-03-17 20:49, Jan Kara wrote:
>    The following patch series is my first stab at abstracting vma handling
> from the various media drivers. After this patch set drivers have to know
> much less details about vmas, their types, and locking. My motivation for
> the series is that I want to change get_user_pages() locking and I want
> to handle subtle locking details in as few places as possible.
>
> The core of the series is the new helper get_vaddr_pfns() which is given a
> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> normal pages it also grabs references to these pages. The difference from
> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> mappings which is what the media drivers need.
>
> The patches are just compile tested (since I don't have any of the hardware
> I'm afraid I won't be able to do any more testing anyway) so please handle
> with care. I'm grateful for any comments.

Thanks for posting this series! I will check if it works with our 
hardware soon.
This is something I wanted to introduce some time ago to simplify buffer
handling in dma-buf, but I had no time to start working.

However I would like to go even further with integration of your pfn 
vector idea.
This structure looks like a best solution for a compact representation 
of the
memory buffer, which should be considered by the hardware as contiguous 
(either
contiguous in physical memory or mapped contiguously into dma address 
space by
the respective iommu). As you already noticed it is widely used by 
graphics and
video drivers.

I would also like to add support for pfn vector directly to the dma-mapping
subsystem. This can be done quite easily (even with a fallback for 
architectures
which don't provide method for it). I will try to prepare rfc soon. This 
will
finally remove the need for hacks in media/v4l2-core/videobuf2-dma-contig.c

Thanks for motivating me to finally start working on this!

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-10 10:02   ` Marek Szyprowski
  0 siblings, 0 replies; 44+ messages in thread
From: Marek Szyprowski @ 2014-04-10 10:02 UTC (permalink / raw)
  To: Jan Kara, linux-mm
  Cc: linux-media, linaro-mm-sig, 'Tomasz Stanislawski',
	Laurent Pinchart

Hello,

On 2014-03-17 20:49, Jan Kara wrote:
>    The following patch series is my first stab at abstracting vma handling
> from the various media drivers. After this patch set drivers have to know
> much less details about vmas, their types, and locking. My motivation for
> the series is that I want to change get_user_pages() locking and I want
> to handle subtle locking details in as few places as possible.
>
> The core of the series is the new helper get_vaddr_pfns() which is given a
> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> normal pages it also grabs references to these pages. The difference from
> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> mappings which is what the media drivers need.
>
> The patches are just compile tested (since I don't have any of the hardware
> I'm afraid I won't be able to do any more testing anyway) so please handle
> with care. I'm grateful for any comments.

Thanks for posting this series! I will check if it works with our 
hardware soon.
This is something I wanted to introduce some time ago to simplify buffer
handling in dma-buf, but I had no time to start working.

However I would like to go even further with integration of your pfn 
vector idea.
This structure looks like a best solution for a compact representation 
of the
memory buffer, which should be considered by the hardware as contiguous 
(either
contiguous in physical memory or mapped contiguously into dma address 
space by
the respective iommu). As you already noticed it is widely used by 
graphics and
video drivers.

I would also like to add support for pfn vector directly to the dma-mapping
subsystem. This can be done quite easily (even with a fallback for 
architectures
which don't provide method for it). I will try to prepare rfc soon. This 
will
finally remove the need for hacks in media/v4l2-core/videobuf2-dma-contig.c

Thanks for motivating me to finally start working on this!

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-04-10 10:02   ` Marek Szyprowski
@ 2014-04-10 10:32     ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-10 10:32 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: Jan Kara, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

  Hello,

On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> On 2014-03-17 20:49, Jan Kara wrote:
> >   The following patch series is my first stab at abstracting vma handling
> >from the various media drivers. After this patch set drivers have to know
> >much less details about vmas, their types, and locking. My motivation for
> >the series is that I want to change get_user_pages() locking and I want
> >to handle subtle locking details in as few places as possible.
> >
> >The core of the series is the new helper get_vaddr_pfns() which is given a
> >virtual address and it fills in PFNs into provided array. If PFNs correspond to
> >normal pages it also grabs references to these pages. The difference from
> >get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> >mappings which is what the media drivers need.
> >
> >The patches are just compile tested (since I don't have any of the hardware
> >I'm afraid I won't be able to do any more testing anyway) so please handle
> >with care. I'm grateful for any comments.
> 
> Thanks for posting this series! I will check if it works with our
> hardware soon.  This is something I wanted to introduce some time ago to
> simplify buffer handling in dma-buf, but I had no time to start working.
  Thanks for having a look in the series.

> However I would like to go even further with integration of your pfn
> vector idea.  This structure looks like a best solution for a compact
> representation of the memory buffer, which should be considered by the
> hardware as contiguous (either contiguous in physical memory or mapped
> contiguously into dma address space by the respective iommu). As you
> already noticed it is widely used by graphics and video drivers.
> 
> I would also like to add support for pfn vector directly to the
> dma-mapping subsystem. This can be done quite easily (even with a
> fallback for architectures which don't provide method for it). I will try
> to prepare rfc soon.  This will finally remove the need for hacks in
> media/v4l2-core/videobuf2-dma-contig.c
  That would be a worthwhile thing to do. When I was reading the code this
seemed like something which could be done but I delibrately avoided doing
more unification than necessary for my purposes as I don't have any
hardware to test and don't know all the subtleties in the code... BTW, is
there some way to test the drivers without the physical video HW?

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-10 10:32     ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-10 10:32 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: Jan Kara, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

  Hello,

On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> On 2014-03-17 20:49, Jan Kara wrote:
> >   The following patch series is my first stab at abstracting vma handling
> >from the various media drivers. After this patch set drivers have to know
> >much less details about vmas, their types, and locking. My motivation for
> >the series is that I want to change get_user_pages() locking and I want
> >to handle subtle locking details in as few places as possible.
> >
> >The core of the series is the new helper get_vaddr_pfns() which is given a
> >virtual address and it fills in PFNs into provided array. If PFNs correspond to
> >normal pages it also grabs references to these pages. The difference from
> >get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> >mappings which is what the media drivers need.
> >
> >The patches are just compile tested (since I don't have any of the hardware
> >I'm afraid I won't be able to do any more testing anyway) so please handle
> >with care. I'm grateful for any comments.
> 
> Thanks for posting this series! I will check if it works with our
> hardware soon.  This is something I wanted to introduce some time ago to
> simplify buffer handling in dma-buf, but I had no time to start working.
  Thanks for having a look in the series.

> However I would like to go even further with integration of your pfn
> vector idea.  This structure looks like a best solution for a compact
> representation of the memory buffer, which should be considered by the
> hardware as contiguous (either contiguous in physical memory or mapped
> contiguously into dma address space by the respective iommu). As you
> already noticed it is widely used by graphics and video drivers.
> 
> I would also like to add support for pfn vector directly to the
> dma-mapping subsystem. This can be done quite easily (even with a
> fallback for architectures which don't provide method for it). I will try
> to prepare rfc soon.  This will finally remove the need for hacks in
> media/v4l2-core/videobuf2-dma-contig.c
  That would be a worthwhile thing to do. When I was reading the code this
seemed like something which could be done but I delibrately avoided doing
more unification than necessary for my purposes as I don't have any
hardware to test and don't know all the subtleties in the code... BTW, is
there some way to test the drivers without the physical video HW?

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-04-10 10:32     ` Jan Kara
@ 2014-04-10 11:07       ` Hans Verkuil
  -1 siblings, 0 replies; 44+ messages in thread
From: Hans Verkuil @ 2014-04-10 11:07 UTC (permalink / raw)
  To: Jan Kara
  Cc: Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On 04/10/14 12:32, Jan Kara wrote:
>   Hello,
> 
> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
>> On 2014-03-17 20:49, Jan Kara wrote:
>>>   The following patch series is my first stab at abstracting vma handling
>> >from the various media drivers. After this patch set drivers have to know
>>> much less details about vmas, their types, and locking. My motivation for
>>> the series is that I want to change get_user_pages() locking and I want
>>> to handle subtle locking details in as few places as possible.
>>>
>>> The core of the series is the new helper get_vaddr_pfns() which is given a
>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
>>> normal pages it also grabs references to these pages. The difference from
>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
>>> mappings which is what the media drivers need.
>>>
>>> The patches are just compile tested (since I don't have any of the hardware
>>> I'm afraid I won't be able to do any more testing anyway) so please handle
>>> with care. I'm grateful for any comments.
>>
>> Thanks for posting this series! I will check if it works with our
>> hardware soon.  This is something I wanted to introduce some time ago to
>> simplify buffer handling in dma-buf, but I had no time to start working.
>   Thanks for having a look in the series.
> 
>> However I would like to go even further with integration of your pfn
>> vector idea.  This structure looks like a best solution for a compact
>> representation of the memory buffer, which should be considered by the
>> hardware as contiguous (either contiguous in physical memory or mapped
>> contiguously into dma address space by the respective iommu). As you
>> already noticed it is widely used by graphics and video drivers.
>>
>> I would also like to add support for pfn vector directly to the
>> dma-mapping subsystem. This can be done quite easily (even with a
>> fallback for architectures which don't provide method for it). I will try
>> to prepare rfc soon.  This will finally remove the need for hacks in
>> media/v4l2-core/videobuf2-dma-contig.c
>   That would be a worthwhile thing to do. When I was reading the code this
> seemed like something which could be done but I delibrately avoided doing
> more unification than necessary for my purposes as I don't have any
> hardware to test and don't know all the subtleties in the code... BTW, is
> there some way to test the drivers without the physical video HW?

You can use the vivi driver (drivers/media/platform/vivi) for this.
However, while the vivi driver can import dma buffers it cannot export
them. If you want that, then you have to use this tree:

http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4

Regards,

	Hans

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-10 11:07       ` Hans Verkuil
  0 siblings, 0 replies; 44+ messages in thread
From: Hans Verkuil @ 2014-04-10 11:07 UTC (permalink / raw)
  To: Jan Kara
  Cc: Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On 04/10/14 12:32, Jan Kara wrote:
>   Hello,
> 
> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
>> On 2014-03-17 20:49, Jan Kara wrote:
>>>   The following patch series is my first stab at abstracting vma handling
>> >from the various media drivers. After this patch set drivers have to know
>>> much less details about vmas, their types, and locking. My motivation for
>>> the series is that I want to change get_user_pages() locking and I want
>>> to handle subtle locking details in as few places as possible.
>>>
>>> The core of the series is the new helper get_vaddr_pfns() which is given a
>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
>>> normal pages it also grabs references to these pages. The difference from
>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
>>> mappings which is what the media drivers need.
>>>
>>> The patches are just compile tested (since I don't have any of the hardware
>>> I'm afraid I won't be able to do any more testing anyway) so please handle
>>> with care. I'm grateful for any comments.
>>
>> Thanks for posting this series! I will check if it works with our
>> hardware soon.  This is something I wanted to introduce some time ago to
>> simplify buffer handling in dma-buf, but I had no time to start working.
>   Thanks for having a look in the series.
> 
>> However I would like to go even further with integration of your pfn
>> vector idea.  This structure looks like a best solution for a compact
>> representation of the memory buffer, which should be considered by the
>> hardware as contiguous (either contiguous in physical memory or mapped
>> contiguously into dma address space by the respective iommu). As you
>> already noticed it is widely used by graphics and video drivers.
>>
>> I would also like to add support for pfn vector directly to the
>> dma-mapping subsystem. This can be done quite easily (even with a
>> fallback for architectures which don't provide method for it). I will try
>> to prepare rfc soon.  This will finally remove the need for hacks in
>> media/v4l2-core/videobuf2-dma-contig.c
>   That would be a worthwhile thing to do. When I was reading the code this
> seemed like something which could be done but I delibrately avoided doing
> more unification than necessary for my purposes as I don't have any
> hardware to test and don't know all the subtleties in the code... BTW, is
> there some way to test the drivers without the physical video HW?

You can use the vivi driver (drivers/media/platform/vivi) for this.
However, while the vivi driver can import dma buffers it cannot export
them. If you want that, then you have to use this tree:

http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4

Regards,

	Hans

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-04-10 11:07       ` Hans Verkuil
@ 2014-04-10 12:15         ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-10 12:15 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Jan Kara, Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
> On 04/10/14 12:32, Jan Kara wrote:
> >   Hello,
> > 
> > On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> >> On 2014-03-17 20:49, Jan Kara wrote:
> >>>   The following patch series is my first stab at abstracting vma handling
> >> >from the various media drivers. After this patch set drivers have to know
> >>> much less details about vmas, their types, and locking. My motivation for
> >>> the series is that I want to change get_user_pages() locking and I want
> >>> to handle subtle locking details in as few places as possible.
> >>>
> >>> The core of the series is the new helper get_vaddr_pfns() which is given a
> >>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> >>> normal pages it also grabs references to these pages. The difference from
> >>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> >>> mappings which is what the media drivers need.
> >>>
> >>> The patches are just compile tested (since I don't have any of the hardware
> >>> I'm afraid I won't be able to do any more testing anyway) so please handle
> >>> with care. I'm grateful for any comments.
> >>
> >> Thanks for posting this series! I will check if it works with our
> >> hardware soon.  This is something I wanted to introduce some time ago to
> >> simplify buffer handling in dma-buf, but I had no time to start working.
> >   Thanks for having a look in the series.
> > 
> >> However I would like to go even further with integration of your pfn
> >> vector idea.  This structure looks like a best solution for a compact
> >> representation of the memory buffer, which should be considered by the
> >> hardware as contiguous (either contiguous in physical memory or mapped
> >> contiguously into dma address space by the respective iommu). As you
> >> already noticed it is widely used by graphics and video drivers.
> >>
> >> I would also like to add support for pfn vector directly to the
> >> dma-mapping subsystem. This can be done quite easily (even with a
> >> fallback for architectures which don't provide method for it). I will try
> >> to prepare rfc soon.  This will finally remove the need for hacks in
> >> media/v4l2-core/videobuf2-dma-contig.c
> >   That would be a worthwhile thing to do. When I was reading the code this
> > seemed like something which could be done but I delibrately avoided doing
> > more unification than necessary for my purposes as I don't have any
> > hardware to test and don't know all the subtleties in the code... BTW, is
> > there some way to test the drivers without the physical video HW?
> 
> You can use the vivi driver (drivers/media/platform/vivi) for this.
> However, while the vivi driver can import dma buffers it cannot export
> them. If you want that, then you have to use this tree:
> 
> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
  Thanks for the pointer that looks good. I've also found
drivers/media/platform/mem2mem_testdev.c which seems to do even more
testing of the area I made changes to. So now I have to find some userspace
tool which can issue proper ioctls to setup and use the buffers and I can
start testing what I wrote :)

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-10 12:15         ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-10 12:15 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Jan Kara, Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
> On 04/10/14 12:32, Jan Kara wrote:
> >   Hello,
> > 
> > On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> >> On 2014-03-17 20:49, Jan Kara wrote:
> >>>   The following patch series is my first stab at abstracting vma handling
> >> >from the various media drivers. After this patch set drivers have to know
> >>> much less details about vmas, their types, and locking. My motivation for
> >>> the series is that I want to change get_user_pages() locking and I want
> >>> to handle subtle locking details in as few places as possible.
> >>>
> >>> The core of the series is the new helper get_vaddr_pfns() which is given a
> >>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> >>> normal pages it also grabs references to these pages. The difference from
> >>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> >>> mappings which is what the media drivers need.
> >>>
> >>> The patches are just compile tested (since I don't have any of the hardware
> >>> I'm afraid I won't be able to do any more testing anyway) so please handle
> >>> with care. I'm grateful for any comments.
> >>
> >> Thanks for posting this series! I will check if it works with our
> >> hardware soon.  This is something I wanted to introduce some time ago to
> >> simplify buffer handling in dma-buf, but I had no time to start working.
> >   Thanks for having a look in the series.
> > 
> >> However I would like to go even further with integration of your pfn
> >> vector idea.  This structure looks like a best solution for a compact
> >> representation of the memory buffer, which should be considered by the
> >> hardware as contiguous (either contiguous in physical memory or mapped
> >> contiguously into dma address space by the respective iommu). As you
> >> already noticed it is widely used by graphics and video drivers.
> >>
> >> I would also like to add support for pfn vector directly to the
> >> dma-mapping subsystem. This can be done quite easily (even with a
> >> fallback for architectures which don't provide method for it). I will try
> >> to prepare rfc soon.  This will finally remove the need for hacks in
> >> media/v4l2-core/videobuf2-dma-contig.c
> >   That would be a worthwhile thing to do. When I was reading the code this
> > seemed like something which could be done but I delibrately avoided doing
> > more unification than necessary for my purposes as I don't have any
> > hardware to test and don't know all the subtleties in the code... BTW, is
> > there some way to test the drivers without the physical video HW?
> 
> You can use the vivi driver (drivers/media/platform/vivi) for this.
> However, while the vivi driver can import dma buffers it cannot export
> them. If you want that, then you have to use this tree:
> 
> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
  Thanks for the pointer that looks good. I've also found
drivers/media/platform/mem2mem_testdev.c which seems to do even more
testing of the area I made changes to. So now I have to find some userspace
tool which can issue proper ioctls to setup and use the buffers and I can
start testing what I wrote :)

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-04-10 12:15         ` Jan Kara
@ 2014-04-10 12:22           ` Hans Verkuil
  -1 siblings, 0 replies; 44+ messages in thread
From: Hans Verkuil @ 2014-04-10 12:22 UTC (permalink / raw)
  To: Jan Kara
  Cc: Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On 04/10/14 14:15, Jan Kara wrote:
> On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
>> On 04/10/14 12:32, Jan Kara wrote:
>>>   Hello,
>>>
>>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
>>>> On 2014-03-17 20:49, Jan Kara wrote:
>>>>>   The following patch series is my first stab at abstracting vma handling
>>>> >from the various media drivers. After this patch set drivers have to know
>>>>> much less details about vmas, their types, and locking. My motivation for
>>>>> the series is that I want to change get_user_pages() locking and I want
>>>>> to handle subtle locking details in as few places as possible.
>>>>>
>>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
>>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
>>>>> normal pages it also grabs references to these pages. The difference from
>>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
>>>>> mappings which is what the media drivers need.
>>>>>
>>>>> The patches are just compile tested (since I don't have any of the hardware
>>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
>>>>> with care. I'm grateful for any comments.
>>>>
>>>> Thanks for posting this series! I will check if it works with our
>>>> hardware soon.  This is something I wanted to introduce some time ago to
>>>> simplify buffer handling in dma-buf, but I had no time to start working.
>>>   Thanks for having a look in the series.
>>>
>>>> However I would like to go even further with integration of your pfn
>>>> vector idea.  This structure looks like a best solution for a compact
>>>> representation of the memory buffer, which should be considered by the
>>>> hardware as contiguous (either contiguous in physical memory or mapped
>>>> contiguously into dma address space by the respective iommu). As you
>>>> already noticed it is widely used by graphics and video drivers.
>>>>
>>>> I would also like to add support for pfn vector directly to the
>>>> dma-mapping subsystem. This can be done quite easily (even with a
>>>> fallback for architectures which don't provide method for it). I will try
>>>> to prepare rfc soon.  This will finally remove the need for hacks in
>>>> media/v4l2-core/videobuf2-dma-contig.c
>>>   That would be a worthwhile thing to do. When I was reading the code this
>>> seemed like something which could be done but I delibrately avoided doing
>>> more unification than necessary for my purposes as I don't have any
>>> hardware to test and don't know all the subtleties in the code... BTW, is
>>> there some way to test the drivers without the physical video HW?
>>
>> You can use the vivi driver (drivers/media/platform/vivi) for this.
>> However, while the vivi driver can import dma buffers it cannot export
>> them. If you want that, then you have to use this tree:
>>
>> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
>   Thanks for the pointer that looks good. I've also found
> drivers/media/platform/mem2mem_testdev.c which seems to do even more
> testing of the area I made changes to. So now I have to find some userspace
> tool which can issue proper ioctls to setup and use the buffers and I can
> start testing what I wrote :)

Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
You want the v4l2-ctl tool. Don't use the version supplied by your distro,
that's often too old.

'v4l2-ctl --help-streaming' gives the available options for doing streaming.

So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.

If you need help with testing it's easiest to contact me on the #v4l irc
channel.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-10 12:22           ` Hans Verkuil
  0 siblings, 0 replies; 44+ messages in thread
From: Hans Verkuil @ 2014-04-10 12:22 UTC (permalink / raw)
  To: Jan Kara
  Cc: Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On 04/10/14 14:15, Jan Kara wrote:
> On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
>> On 04/10/14 12:32, Jan Kara wrote:
>>>   Hello,
>>>
>>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
>>>> On 2014-03-17 20:49, Jan Kara wrote:
>>>>>   The following patch series is my first stab at abstracting vma handling
>>>> >from the various media drivers. After this patch set drivers have to know
>>>>> much less details about vmas, their types, and locking. My motivation for
>>>>> the series is that I want to change get_user_pages() locking and I want
>>>>> to handle subtle locking details in as few places as possible.
>>>>>
>>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
>>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
>>>>> normal pages it also grabs references to these pages. The difference from
>>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
>>>>> mappings which is what the media drivers need.
>>>>>
>>>>> The patches are just compile tested (since I don't have any of the hardware
>>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
>>>>> with care. I'm grateful for any comments.
>>>>
>>>> Thanks for posting this series! I will check if it works with our
>>>> hardware soon.  This is something I wanted to introduce some time ago to
>>>> simplify buffer handling in dma-buf, but I had no time to start working.
>>>   Thanks for having a look in the series.
>>>
>>>> However I would like to go even further with integration of your pfn
>>>> vector idea.  This structure looks like a best solution for a compact
>>>> representation of the memory buffer, which should be considered by the
>>>> hardware as contiguous (either contiguous in physical memory or mapped
>>>> contiguously into dma address space by the respective iommu). As you
>>>> already noticed it is widely used by graphics and video drivers.
>>>>
>>>> I would also like to add support for pfn vector directly to the
>>>> dma-mapping subsystem. This can be done quite easily (even with a
>>>> fallback for architectures which don't provide method for it). I will try
>>>> to prepare rfc soon.  This will finally remove the need for hacks in
>>>> media/v4l2-core/videobuf2-dma-contig.c
>>>   That would be a worthwhile thing to do. When I was reading the code this
>>> seemed like something which could be done but I delibrately avoided doing
>>> more unification than necessary for my purposes as I don't have any
>>> hardware to test and don't know all the subtleties in the code... BTW, is
>>> there some way to test the drivers without the physical video HW?
>>
>> You can use the vivi driver (drivers/media/platform/vivi) for this.
>> However, while the vivi driver can import dma buffers it cannot export
>> them. If you want that, then you have to use this tree:
>>
>> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
>   Thanks for the pointer that looks good. I've also found
> drivers/media/platform/mem2mem_testdev.c which seems to do even more
> testing of the area I made changes to. So now I have to find some userspace
> tool which can issue proper ioctls to setup and use the buffers and I can
> start testing what I wrote :)

Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
You want the v4l2-ctl tool. Don't use the version supplied by your distro,
that's often too old.

'v4l2-ctl --help-streaming' gives the available options for doing streaming.

So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.

If you need help with testing it's easiest to contact me on the #v4l irc
channel.

Regards,

	Hans

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-04-10 12:22           ` Hans Verkuil
@ 2014-04-10 21:57             ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-10 21:57 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Jan Kara, Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On Thu 10-04-14 14:22:20, Hans Verkuil wrote:
> On 04/10/14 14:15, Jan Kara wrote:
> > On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
> >> On 04/10/14 12:32, Jan Kara wrote:
> >>>   Hello,
> >>>
> >>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> >>>> On 2014-03-17 20:49, Jan Kara wrote:
> >>>>>   The following patch series is my first stab at abstracting vma handling
> >>>> >from the various media drivers. After this patch set drivers have to know
> >>>>> much less details about vmas, their types, and locking. My motivation for
> >>>>> the series is that I want to change get_user_pages() locking and I want
> >>>>> to handle subtle locking details in as few places as possible.
> >>>>>
> >>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
> >>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> >>>>> normal pages it also grabs references to these pages. The difference from
> >>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> >>>>> mappings which is what the media drivers need.
> >>>>>
> >>>>> The patches are just compile tested (since I don't have any of the hardware
> >>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
> >>>>> with care. I'm grateful for any comments.
> >>>>
> >>>> Thanks for posting this series! I will check if it works with our
> >>>> hardware soon.  This is something I wanted to introduce some time ago to
> >>>> simplify buffer handling in dma-buf, but I had no time to start working.
> >>>   Thanks for having a look in the series.
> >>>
> >>>> However I would like to go even further with integration of your pfn
> >>>> vector idea.  This structure looks like a best solution for a compact
> >>>> representation of the memory buffer, which should be considered by the
> >>>> hardware as contiguous (either contiguous in physical memory or mapped
> >>>> contiguously into dma address space by the respective iommu). As you
> >>>> already noticed it is widely used by graphics and video drivers.
> >>>>
> >>>> I would also like to add support for pfn vector directly to the
> >>>> dma-mapping subsystem. This can be done quite easily (even with a
> >>>> fallback for architectures which don't provide method for it). I will try
> >>>> to prepare rfc soon.  This will finally remove the need for hacks in
> >>>> media/v4l2-core/videobuf2-dma-contig.c
> >>>   That would be a worthwhile thing to do. When I was reading the code this
> >>> seemed like something which could be done but I delibrately avoided doing
> >>> more unification than necessary for my purposes as I don't have any
> >>> hardware to test and don't know all the subtleties in the code... BTW, is
> >>> there some way to test the drivers without the physical video HW?
> >>
> >> You can use the vivi driver (drivers/media/platform/vivi) for this.
> >> However, while the vivi driver can import dma buffers it cannot export
> >> them. If you want that, then you have to use this tree:
> >>
> >> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
> >   Thanks for the pointer that looks good. I've also found
> > drivers/media/platform/mem2mem_testdev.c which seems to do even more
> > testing of the area I made changes to. So now I have to find some userspace
> > tool which can issue proper ioctls to setup and use the buffers and I can
> > start testing what I wrote :)
> 
> Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
> You want the v4l2-ctl tool. Don't use the version supplied by your distro,
> that's often too old.
> 
> 'v4l2-ctl --help-streaming' gives the available options for doing streaming.
> 
> So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
> You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.
  Great, it seems to be doing something and it shows there's some bug in my
code. Thanks a lot for help.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-10 21:57             ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-10 21:57 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Jan Kara, Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On Thu 10-04-14 14:22:20, Hans Verkuil wrote:
> On 04/10/14 14:15, Jan Kara wrote:
> > On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
> >> On 04/10/14 12:32, Jan Kara wrote:
> >>>   Hello,
> >>>
> >>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> >>>> On 2014-03-17 20:49, Jan Kara wrote:
> >>>>>   The following patch series is my first stab at abstracting vma handling
> >>>> >from the various media drivers. After this patch set drivers have to know
> >>>>> much less details about vmas, their types, and locking. My motivation for
> >>>>> the series is that I want to change get_user_pages() locking and I want
> >>>>> to handle subtle locking details in as few places as possible.
> >>>>>
> >>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
> >>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> >>>>> normal pages it also grabs references to these pages. The difference from
> >>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> >>>>> mappings which is what the media drivers need.
> >>>>>
> >>>>> The patches are just compile tested (since I don't have any of the hardware
> >>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
> >>>>> with care. I'm grateful for any comments.
> >>>>
> >>>> Thanks for posting this series! I will check if it works with our
> >>>> hardware soon.  This is something I wanted to introduce some time ago to
> >>>> simplify buffer handling in dma-buf, but I had no time to start working.
> >>>   Thanks for having a look in the series.
> >>>
> >>>> However I would like to go even further with integration of your pfn
> >>>> vector idea.  This structure looks like a best solution for a compact
> >>>> representation of the memory buffer, which should be considered by the
> >>>> hardware as contiguous (either contiguous in physical memory or mapped
> >>>> contiguously into dma address space by the respective iommu). As you
> >>>> already noticed it is widely used by graphics and video drivers.
> >>>>
> >>>> I would also like to add support for pfn vector directly to the
> >>>> dma-mapping subsystem. This can be done quite easily (even with a
> >>>> fallback for architectures which don't provide method for it). I will try
> >>>> to prepare rfc soon.  This will finally remove the need for hacks in
> >>>> media/v4l2-core/videobuf2-dma-contig.c
> >>>   That would be a worthwhile thing to do. When I was reading the code this
> >>> seemed like something which could be done but I delibrately avoided doing
> >>> more unification than necessary for my purposes as I don't have any
> >>> hardware to test and don't know all the subtleties in the code... BTW, is
> >>> there some way to test the drivers without the physical video HW?
> >>
> >> You can use the vivi driver (drivers/media/platform/vivi) for this.
> >> However, while the vivi driver can import dma buffers it cannot export
> >> them. If you want that, then you have to use this tree:
> >>
> >> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
> >   Thanks for the pointer that looks good. I've also found
> > drivers/media/platform/mem2mem_testdev.c which seems to do even more
> > testing of the area I made changes to. So now I have to find some userspace
> > tool which can issue proper ioctls to setup and use the buffers and I can
> > start testing what I wrote :)
> 
> Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
> You want the v4l2-ctl tool. Don't use the version supplied by your distro,
> that's often too old.
> 
> 'v4l2-ctl --help-streaming' gives the available options for doing streaming.
> 
> So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
> You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.
  Great, it seems to be doing something and it shows there's some bug in my
code. Thanks a lot for help.

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-04-10 21:57             ` Jan Kara
@ 2014-04-10 22:18               ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-10 22:18 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Jan Kara, Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On Thu 10-04-14 23:57:38, Jan Kara wrote:
> On Thu 10-04-14 14:22:20, Hans Verkuil wrote:
> > On 04/10/14 14:15, Jan Kara wrote:
> > > On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
> > >> On 04/10/14 12:32, Jan Kara wrote:
> > >>>   Hello,
> > >>>
> > >>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> > >>>> On 2014-03-17 20:49, Jan Kara wrote:
> > >>>>>   The following patch series is my first stab at abstracting vma handling
> > >>>> >from the various media drivers. After this patch set drivers have to know
> > >>>>> much less details about vmas, their types, and locking. My motivation for
> > >>>>> the series is that I want to change get_user_pages() locking and I want
> > >>>>> to handle subtle locking details in as few places as possible.
> > >>>>>
> > >>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
> > >>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> > >>>>> normal pages it also grabs references to these pages. The difference from
> > >>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> > >>>>> mappings which is what the media drivers need.
> > >>>>>
> > >>>>> The patches are just compile tested (since I don't have any of the hardware
> > >>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
> > >>>>> with care. I'm grateful for any comments.
> > >>>>
> > >>>> Thanks for posting this series! I will check if it works with our
> > >>>> hardware soon.  This is something I wanted to introduce some time ago to
> > >>>> simplify buffer handling in dma-buf, but I had no time to start working.
> > >>>   Thanks for having a look in the series.
> > >>>
> > >>>> However I would like to go even further with integration of your pfn
> > >>>> vector idea.  This structure looks like a best solution for a compact
> > >>>> representation of the memory buffer, which should be considered by the
> > >>>> hardware as contiguous (either contiguous in physical memory or mapped
> > >>>> contiguously into dma address space by the respective iommu). As you
> > >>>> already noticed it is widely used by graphics and video drivers.
> > >>>>
> > >>>> I would also like to add support for pfn vector directly to the
> > >>>> dma-mapping subsystem. This can be done quite easily (even with a
> > >>>> fallback for architectures which don't provide method for it). I will try
> > >>>> to prepare rfc soon.  This will finally remove the need for hacks in
> > >>>> media/v4l2-core/videobuf2-dma-contig.c
> > >>>   That would be a worthwhile thing to do. When I was reading the code this
> > >>> seemed like something which could be done but I delibrately avoided doing
> > >>> more unification than necessary for my purposes as I don't have any
> > >>> hardware to test and don't know all the subtleties in the code... BTW, is
> > >>> there some way to test the drivers without the physical video HW?
> > >>
> > >> You can use the vivi driver (drivers/media/platform/vivi) for this.
> > >> However, while the vivi driver can import dma buffers it cannot export
> > >> them. If you want that, then you have to use this tree:
> > >>
> > >> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
> > >   Thanks for the pointer that looks good. I've also found
> > > drivers/media/platform/mem2mem_testdev.c which seems to do even more
> > > testing of the area I made changes to. So now I have to find some userspace
> > > tool which can issue proper ioctls to setup and use the buffers and I can
> > > start testing what I wrote :)
> > 
> > Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
> > You want the v4l2-ctl tool. Don't use the version supplied by your distro,
> > that's often too old.
> > 
> > 'v4l2-ctl --help-streaming' gives the available options for doing streaming.
> > 
> > So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
> > You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.
>   Great, it seems to be doing something and it shows there's some bug in my
> code. Thanks a lot for help.
  OK, so after a small fix the basic functionality seems to be working. It
doesn't seem there's a way to test multiplanar buffers with vivi, is there?

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-10 22:18               ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-10 22:18 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Jan Kara, Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On Thu 10-04-14 23:57:38, Jan Kara wrote:
> On Thu 10-04-14 14:22:20, Hans Verkuil wrote:
> > On 04/10/14 14:15, Jan Kara wrote:
> > > On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
> > >> On 04/10/14 12:32, Jan Kara wrote:
> > >>>   Hello,
> > >>>
> > >>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> > >>>> On 2014-03-17 20:49, Jan Kara wrote:
> > >>>>>   The following patch series is my first stab at abstracting vma handling
> > >>>> >from the various media drivers. After this patch set drivers have to know
> > >>>>> much less details about vmas, their types, and locking. My motivation for
> > >>>>> the series is that I want to change get_user_pages() locking and I want
> > >>>>> to handle subtle locking details in as few places as possible.
> > >>>>>
> > >>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
> > >>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> > >>>>> normal pages it also grabs references to these pages. The difference from
> > >>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> > >>>>> mappings which is what the media drivers need.
> > >>>>>
> > >>>>> The patches are just compile tested (since I don't have any of the hardware
> > >>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
> > >>>>> with care. I'm grateful for any comments.
> > >>>>
> > >>>> Thanks for posting this series! I will check if it works with our
> > >>>> hardware soon.  This is something I wanted to introduce some time ago to
> > >>>> simplify buffer handling in dma-buf, but I had no time to start working.
> > >>>   Thanks for having a look in the series.
> > >>>
> > >>>> However I would like to go even further with integration of your pfn
> > >>>> vector idea.  This structure looks like a best solution for a compact
> > >>>> representation of the memory buffer, which should be considered by the
> > >>>> hardware as contiguous (either contiguous in physical memory or mapped
> > >>>> contiguously into dma address space by the respective iommu). As you
> > >>>> already noticed it is widely used by graphics and video drivers.
> > >>>>
> > >>>> I would also like to add support for pfn vector directly to the
> > >>>> dma-mapping subsystem. This can be done quite easily (even with a
> > >>>> fallback for architectures which don't provide method for it). I will try
> > >>>> to prepare rfc soon.  This will finally remove the need for hacks in
> > >>>> media/v4l2-core/videobuf2-dma-contig.c
> > >>>   That would be a worthwhile thing to do. When I was reading the code this
> > >>> seemed like something which could be done but I delibrately avoided doing
> > >>> more unification than necessary for my purposes as I don't have any
> > >>> hardware to test and don't know all the subtleties in the code... BTW, is
> > >>> there some way to test the drivers without the physical video HW?
> > >>
> > >> You can use the vivi driver (drivers/media/platform/vivi) for this.
> > >> However, while the vivi driver can import dma buffers it cannot export
> > >> them. If you want that, then you have to use this tree:
> > >>
> > >> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
> > >   Thanks for the pointer that looks good. I've also found
> > > drivers/media/platform/mem2mem_testdev.c which seems to do even more
> > > testing of the area I made changes to. So now I have to find some userspace
> > > tool which can issue proper ioctls to setup and use the buffers and I can
> > > start testing what I wrote :)
> > 
> > Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
> > You want the v4l2-ctl tool. Don't use the version supplied by your distro,
> > that's often too old.
> > 
> > 'v4l2-ctl --help-streaming' gives the available options for doing streaming.
> > 
> > So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
> > You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.
>   Great, it seems to be doing something and it shows there's some bug in my
> code. Thanks a lot for help.
  OK, so after a small fix the basic functionality seems to be working. It
doesn't seem there's a way to test multiplanar buffers with vivi, is there?

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-04-10 22:18               ` Jan Kara
@ 2014-04-11  6:58                 ` Hans Verkuil
  -1 siblings, 0 replies; 44+ messages in thread
From: Hans Verkuil @ 2014-04-11  6:58 UTC (permalink / raw)
  To: Jan Kara
  Cc: Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On 04/11/2014 12:18 AM, Jan Kara wrote:
> On Thu 10-04-14 23:57:38, Jan Kara wrote:
>> On Thu 10-04-14 14:22:20, Hans Verkuil wrote:
>>> On 04/10/14 14:15, Jan Kara wrote:
>>>> On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
>>>>> On 04/10/14 12:32, Jan Kara wrote:
>>>>>>   Hello,
>>>>>>
>>>>>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
>>>>>>> On 2014-03-17 20:49, Jan Kara wrote:
>>>>>>>>   The following patch series is my first stab at abstracting vma handling
>>>>>>> >from the various media drivers. After this patch set drivers have to know
>>>>>>>> much less details about vmas, their types, and locking. My motivation for
>>>>>>>> the series is that I want to change get_user_pages() locking and I want
>>>>>>>> to handle subtle locking details in as few places as possible.
>>>>>>>>
>>>>>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
>>>>>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
>>>>>>>> normal pages it also grabs references to these pages. The difference from
>>>>>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
>>>>>>>> mappings which is what the media drivers need.
>>>>>>>>
>>>>>>>> The patches are just compile tested (since I don't have any of the hardware
>>>>>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
>>>>>>>> with care. I'm grateful for any comments.
>>>>>>>
>>>>>>> Thanks for posting this series! I will check if it works with our
>>>>>>> hardware soon.  This is something I wanted to introduce some time ago to
>>>>>>> simplify buffer handling in dma-buf, but I had no time to start working.
>>>>>>   Thanks for having a look in the series.
>>>>>>
>>>>>>> However I would like to go even further with integration of your pfn
>>>>>>> vector idea.  This structure looks like a best solution for a compact
>>>>>>> representation of the memory buffer, which should be considered by the
>>>>>>> hardware as contiguous (either contiguous in physical memory or mapped
>>>>>>> contiguously into dma address space by the respective iommu). As you
>>>>>>> already noticed it is widely used by graphics and video drivers.
>>>>>>>
>>>>>>> I would also like to add support for pfn vector directly to the
>>>>>>> dma-mapping subsystem. This can be done quite easily (even with a
>>>>>>> fallback for architectures which don't provide method for it). I will try
>>>>>>> to prepare rfc soon.  This will finally remove the need for hacks in
>>>>>>> media/v4l2-core/videobuf2-dma-contig.c
>>>>>>   That would be a worthwhile thing to do. When I was reading the code this
>>>>>> seemed like something which could be done but I delibrately avoided doing
>>>>>> more unification than necessary for my purposes as I don't have any
>>>>>> hardware to test and don't know all the subtleties in the code... BTW, is
>>>>>> there some way to test the drivers without the physical video HW?
>>>>>
>>>>> You can use the vivi driver (drivers/media/platform/vivi) for this.
>>>>> However, while the vivi driver can import dma buffers it cannot export
>>>>> them. If you want that, then you have to use this tree:
>>>>>
>>>>> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
>>>>   Thanks for the pointer that looks good. I've also found
>>>> drivers/media/platform/mem2mem_testdev.c which seems to do even more
>>>> testing of the area I made changes to. So now I have to find some userspace
>>>> tool which can issue proper ioctls to setup and use the buffers and I can
>>>> start testing what I wrote :)
>>>
>>> Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
>>> You want the v4l2-ctl tool. Don't use the version supplied by your distro,
>>> that's often too old.
>>>
>>> 'v4l2-ctl --help-streaming' gives the available options for doing streaming.
>>>
>>> So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
>>> You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.
>>   Great, it seems to be doing something and it shows there's some bug in my
>> code. Thanks a lot for help.
>   OK, so after a small fix the basic functionality seems to be working. It
> doesn't seem there's a way to test multiplanar buffers with vivi, is there?

For that you need to switch to the vb2-part4 branch as well. That has support
for multiplanar.

Regards,

	Hans


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-11  6:58                 ` Hans Verkuil
  0 siblings, 0 replies; 44+ messages in thread
From: Hans Verkuil @ 2014-04-11  6:58 UTC (permalink / raw)
  To: Jan Kara
  Cc: Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On 04/11/2014 12:18 AM, Jan Kara wrote:
> On Thu 10-04-14 23:57:38, Jan Kara wrote:
>> On Thu 10-04-14 14:22:20, Hans Verkuil wrote:
>>> On 04/10/14 14:15, Jan Kara wrote:
>>>> On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
>>>>> On 04/10/14 12:32, Jan Kara wrote:
>>>>>>   Hello,
>>>>>>
>>>>>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
>>>>>>> On 2014-03-17 20:49, Jan Kara wrote:
>>>>>>>>   The following patch series is my first stab at abstracting vma handling
>>>>>>> >from the various media drivers. After this patch set drivers have to know
>>>>>>>> much less details about vmas, their types, and locking. My motivation for
>>>>>>>> the series is that I want to change get_user_pages() locking and I want
>>>>>>>> to handle subtle locking details in as few places as possible.
>>>>>>>>
>>>>>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
>>>>>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
>>>>>>>> normal pages it also grabs references to these pages. The difference from
>>>>>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
>>>>>>>> mappings which is what the media drivers need.
>>>>>>>>
>>>>>>>> The patches are just compile tested (since I don't have any of the hardware
>>>>>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
>>>>>>>> with care. I'm grateful for any comments.
>>>>>>>
>>>>>>> Thanks for posting this series! I will check if it works with our
>>>>>>> hardware soon.  This is something I wanted to introduce some time ago to
>>>>>>> simplify buffer handling in dma-buf, but I had no time to start working.
>>>>>>   Thanks for having a look in the series.
>>>>>>
>>>>>>> However I would like to go even further with integration of your pfn
>>>>>>> vector idea.  This structure looks like a best solution for a compact
>>>>>>> representation of the memory buffer, which should be considered by the
>>>>>>> hardware as contiguous (either contiguous in physical memory or mapped
>>>>>>> contiguously into dma address space by the respective iommu). As you
>>>>>>> already noticed it is widely used by graphics and video drivers.
>>>>>>>
>>>>>>> I would also like to add support for pfn vector directly to the
>>>>>>> dma-mapping subsystem. This can be done quite easily (even with a
>>>>>>> fallback for architectures which don't provide method for it). I will try
>>>>>>> to prepare rfc soon.  This will finally remove the need for hacks in
>>>>>>> media/v4l2-core/videobuf2-dma-contig.c
>>>>>>   That would be a worthwhile thing to do. When I was reading the code this
>>>>>> seemed like something which could be done but I delibrately avoided doing
>>>>>> more unification than necessary for my purposes as I don't have any
>>>>>> hardware to test and don't know all the subtleties in the code... BTW, is
>>>>>> there some way to test the drivers without the physical video HW?
>>>>>
>>>>> You can use the vivi driver (drivers/media/platform/vivi) for this.
>>>>> However, while the vivi driver can import dma buffers it cannot export
>>>>> them. If you want that, then you have to use this tree:
>>>>>
>>>>> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
>>>>   Thanks for the pointer that looks good. I've also found
>>>> drivers/media/platform/mem2mem_testdev.c which seems to do even more
>>>> testing of the area I made changes to. So now I have to find some userspace
>>>> tool which can issue proper ioctls to setup and use the buffers and I can
>>>> start testing what I wrote :)
>>>
>>> Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
>>> You want the v4l2-ctl tool. Don't use the version supplied by your distro,
>>> that's often too old.
>>>
>>> 'v4l2-ctl --help-streaming' gives the available options for doing streaming.
>>>
>>> So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
>>> You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.
>>   Great, it seems to be doing something and it shows there's some bug in my
>> code. Thanks a lot for help.
>   OK, so after a small fix the basic functionality seems to be working. It
> doesn't seem there's a way to test multiplanar buffers with vivi, is there?

For that you need to switch to the vb2-part4 branch as well. That has support
for multiplanar.

Regards,

	Hans

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
  2014-04-11  6:58                 ` Hans Verkuil
@ 2014-04-14 21:19                   ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-14 21:19 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Jan Kara, Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On Fri 11-04-14 08:58:59, Hans Verkuil wrote:
> On 04/11/2014 12:18 AM, Jan Kara wrote:
> > On Thu 10-04-14 23:57:38, Jan Kara wrote:
> >> On Thu 10-04-14 14:22:20, Hans Verkuil wrote:
> >>> On 04/10/14 14:15, Jan Kara wrote:
> >>>> On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
> >>>>> On 04/10/14 12:32, Jan Kara wrote:
> >>>>>>   Hello,
> >>>>>>
> >>>>>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> >>>>>>> On 2014-03-17 20:49, Jan Kara wrote:
> >>>>>>>>   The following patch series is my first stab at abstracting vma handling
> >>>>>>> >from the various media drivers. After this patch set drivers have to know
> >>>>>>>> much less details about vmas, their types, and locking. My motivation for
> >>>>>>>> the series is that I want to change get_user_pages() locking and I want
> >>>>>>>> to handle subtle locking details in as few places as possible.
> >>>>>>>>
> >>>>>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
> >>>>>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> >>>>>>>> normal pages it also grabs references to these pages. The difference from
> >>>>>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> >>>>>>>> mappings which is what the media drivers need.
> >>>>>>>>
> >>>>>>>> The patches are just compile tested (since I don't have any of the hardware
> >>>>>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
> >>>>>>>> with care. I'm grateful for any comments.
> >>>>>>>
> >>>>>>> Thanks for posting this series! I will check if it works with our
> >>>>>>> hardware soon.  This is something I wanted to introduce some time ago to
> >>>>>>> simplify buffer handling in dma-buf, but I had no time to start working.
> >>>>>>   Thanks for having a look in the series.
> >>>>>>
> >>>>>>> However I would like to go even further with integration of your pfn
> >>>>>>> vector idea.  This structure looks like a best solution for a compact
> >>>>>>> representation of the memory buffer, which should be considered by the
> >>>>>>> hardware as contiguous (either contiguous in physical memory or mapped
> >>>>>>> contiguously into dma address space by the respective iommu). As you
> >>>>>>> already noticed it is widely used by graphics and video drivers.
> >>>>>>>
> >>>>>>> I would also like to add support for pfn vector directly to the
> >>>>>>> dma-mapping subsystem. This can be done quite easily (even with a
> >>>>>>> fallback for architectures which don't provide method for it). I will try
> >>>>>>> to prepare rfc soon.  This will finally remove the need for hacks in
> >>>>>>> media/v4l2-core/videobuf2-dma-contig.c
> >>>>>>   That would be a worthwhile thing to do. When I was reading the code this
> >>>>>> seemed like something which could be done but I delibrately avoided doing
> >>>>>> more unification than necessary for my purposes as I don't have any
> >>>>>> hardware to test and don't know all the subtleties in the code... BTW, is
> >>>>>> there some way to test the drivers without the physical video HW?
> >>>>>
> >>>>> You can use the vivi driver (drivers/media/platform/vivi) for this.
> >>>>> However, while the vivi driver can import dma buffers it cannot export
> >>>>> them. If you want that, then you have to use this tree:
> >>>>>
> >>>>> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
> >>>>   Thanks for the pointer that looks good. I've also found
> >>>> drivers/media/platform/mem2mem_testdev.c which seems to do even more
> >>>> testing of the area I made changes to. So now I have to find some userspace
> >>>> tool which can issue proper ioctls to setup and use the buffers and I can
> >>>> start testing what I wrote :)
> >>>
> >>> Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
> >>> You want the v4l2-ctl tool. Don't use the version supplied by your distro,
> >>> that's often too old.
> >>>
> >>> 'v4l2-ctl --help-streaming' gives the available options for doing streaming.
> >>>
> >>> So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
> >>> You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.
> >>   Great, it seems to be doing something and it shows there's some bug in my
> >> code. Thanks a lot for help.
> >   OK, so after a small fix the basic functionality seems to be working. It
> > doesn't seem there's a way to test multiplanar buffers with vivi, is there?
> 
> For that you need to switch to the vb2-part4 branch as well. That has support
> for multiplanar.
  OK, I've merged that branch to my kernel but I failed to find the setting
for vivi that would create multiplanar buffers and in fact I don't see
multiplanar capabilities among the capabilities reported by the v4l2-ctl
tool. Can you help me please?

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [RFC] Helper to abstract vma handling in media layer
@ 2014-04-14 21:19                   ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2014-04-14 21:19 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Jan Kara, Marek Szyprowski, linux-mm, linux-media, linaro-mm-sig,
	'Tomasz Stanislawski',
	Laurent Pinchart

On Fri 11-04-14 08:58:59, Hans Verkuil wrote:
> On 04/11/2014 12:18 AM, Jan Kara wrote:
> > On Thu 10-04-14 23:57:38, Jan Kara wrote:
> >> On Thu 10-04-14 14:22:20, Hans Verkuil wrote:
> >>> On 04/10/14 14:15, Jan Kara wrote:
> >>>> On Thu 10-04-14 13:07:42, Hans Verkuil wrote:
> >>>>> On 04/10/14 12:32, Jan Kara wrote:
> >>>>>>   Hello,
> >>>>>>
> >>>>>> On Thu 10-04-14 12:02:50, Marek Szyprowski wrote:
> >>>>>>> On 2014-03-17 20:49, Jan Kara wrote:
> >>>>>>>>   The following patch series is my first stab at abstracting vma handling
> >>>>>>> >from the various media drivers. After this patch set drivers have to know
> >>>>>>>> much less details about vmas, their types, and locking. My motivation for
> >>>>>>>> the series is that I want to change get_user_pages() locking and I want
> >>>>>>>> to handle subtle locking details in as few places as possible.
> >>>>>>>>
> >>>>>>>> The core of the series is the new helper get_vaddr_pfns() which is given a
> >>>>>>>> virtual address and it fills in PFNs into provided array. If PFNs correspond to
> >>>>>>>> normal pages it also grabs references to these pages. The difference from
> >>>>>>>> get_user_pages() is that this function can also deal with pfnmap, mixed, and io
> >>>>>>>> mappings which is what the media drivers need.
> >>>>>>>>
> >>>>>>>> The patches are just compile tested (since I don't have any of the hardware
> >>>>>>>> I'm afraid I won't be able to do any more testing anyway) so please handle
> >>>>>>>> with care. I'm grateful for any comments.
> >>>>>>>
> >>>>>>> Thanks for posting this series! I will check if it works with our
> >>>>>>> hardware soon.  This is something I wanted to introduce some time ago to
> >>>>>>> simplify buffer handling in dma-buf, but I had no time to start working.
> >>>>>>   Thanks for having a look in the series.
> >>>>>>
> >>>>>>> However I would like to go even further with integration of your pfn
> >>>>>>> vector idea.  This structure looks like a best solution for a compact
> >>>>>>> representation of the memory buffer, which should be considered by the
> >>>>>>> hardware as contiguous (either contiguous in physical memory or mapped
> >>>>>>> contiguously into dma address space by the respective iommu). As you
> >>>>>>> already noticed it is widely used by graphics and video drivers.
> >>>>>>>
> >>>>>>> I would also like to add support for pfn vector directly to the
> >>>>>>> dma-mapping subsystem. This can be done quite easily (even with a
> >>>>>>> fallback for architectures which don't provide method for it). I will try
> >>>>>>> to prepare rfc soon.  This will finally remove the need for hacks in
> >>>>>>> media/v4l2-core/videobuf2-dma-contig.c
> >>>>>>   That would be a worthwhile thing to do. When I was reading the code this
> >>>>>> seemed like something which could be done but I delibrately avoided doing
> >>>>>> more unification than necessary for my purposes as I don't have any
> >>>>>> hardware to test and don't know all the subtleties in the code... BTW, is
> >>>>>> there some way to test the drivers without the physical video HW?
> >>>>>
> >>>>> You can use the vivi driver (drivers/media/platform/vivi) for this.
> >>>>> However, while the vivi driver can import dma buffers it cannot export
> >>>>> them. If you want that, then you have to use this tree:
> >>>>>
> >>>>> http://git.linuxtv.org/cgit.cgi/hverkuil/media_tree.git/log/?h=vb2-part4
> >>>>   Thanks for the pointer that looks good. I've also found
> >>>> drivers/media/platform/mem2mem_testdev.c which seems to do even more
> >>>> testing of the area I made changes to. So now I have to find some userspace
> >>>> tool which can issue proper ioctls to setup and use the buffers and I can
> >>>> start testing what I wrote :)
> >>>
> >>> Get the v4l-utils.git repository (http://git.linuxtv.org/cgit.cgi/v4l-utils.git/).
> >>> You want the v4l2-ctl tool. Don't use the version supplied by your distro,
> >>> that's often too old.
> >>>
> >>> 'v4l2-ctl --help-streaming' gives the available options for doing streaming.
> >>>
> >>> So simple capturing from vivi is 'v4l2-ctl --stream-mmap' or '--stream-user'.
> >>> You can't test dmabuf unless you switch to the vb2-part4 branch of my tree.
> >>   Great, it seems to be doing something and it shows there's some bug in my
> >> code. Thanks a lot for help.
> >   OK, so after a small fix the basic functionality seems to be working. It
> > doesn't seem there's a way to test multiplanar buffers with vivi, is there?
> 
> For that you need to switch to the vb2-part4 branch as well. That has support
> for multiplanar.
  OK, I've merged that branch to my kernel but I failed to find the setting
for vivi that would create multiplanar buffers and in fact I don't see
multiplanar capabilities among the capabilities reported by the v4l2-ctl
tool. Can you help me please?

								Honza
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
  2014-03-17 19:49   ` Jan Kara
@ 2015-04-24 16:21     ` Jan Kara
  -1 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2015-04-24 16:21 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara, Andrew Morton, aarcange

On Mon 17-03-14 20:49:28, Jan Kara wrote:
> Provide new function get_vaddr_pfns().  This function maps virtual
> addresses from given start and fills given array with page frame numbers of
> the corresponding pages. If given start belongs to a normal vma, the function
> grabs reference to each of the pages to pin them in memory. If start
> belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller
> should make sure pfns aren't reused for anything else while he is using
> them.
> 
> This function is created for various drivers to simplify handling of
> their buffers.
  MM guys, could you have a look at this patch? Linux Media people like the
abstraction of buffer handling and would like to merge the patch set (see
http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/89268).
But for that they need ack from mm people that the interface is ok with
them. So far the only comment I got regarding the interface was from Dave
Hansen that I could also handle VM_MIXEDMAP mappings - I could if people
really want that but so far there are no users for that. Thanks!

								Honza
> 
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  include/linux/mm.h |  46 +++++++++++++++
>  mm/memory.c        | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 211 insertions(+)
> 
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index da8ad480bea7..b3bd29cc40dd 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -18,6 +18,8 @@
>  #include <linux/pfn.h>
>  #include <linux/bit_spinlock.h>
>  #include <linux/shrinker.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
>  
>  struct mempolicy;
>  struct anon_vma;
> @@ -1180,6 +1182,50 @@ get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
>  	return ret;
>  }
>  
> +/* Container for pinned pfns / pages */
> +struct pinned_pfns {
> +	unsigned int nr_allocated_pfns;	/* Number of pfns we have space for */
> +	unsigned int nr_pfns;		/* Number of pfns stored in pfns array */
> +	unsigned int got_ref:1;		/* Did we pin pfns by getting page ref? */
> +	unsigned int is_pages:1;	/* Does array contain pages or pfns? */
> +	void *ptrs[0];			/* Array of pinned pfns / pages.
> +					 * Use pfns_vector_pages() or
> +					 * pfns_vector_pfns() for access */
> +};
> +
> +struct pinned_pfns *pfns_vector_create(int nr_pfns);
> +static inline void pfns_vector_destroy(struct pinned_pfns *pfns)
> +{
> +	if (!is_vmalloc_addr(pfns))
> +		kfree(pfns);
> +	else
> +		vfree(pfns);
> +}
> +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
> +		   struct pinned_pfns *pfns);
> +void put_vaddr_pfns(struct pinned_pfns *pfns);
> +int pfns_vector_to_pages(struct pinned_pfns *pfns);
> +
> +static inline int pfns_vector_count(struct pinned_pfns *pfns)
> +{
> +	return pfns->nr_pfns;
> +}
> +
> +static inline struct page **pfns_vector_pages(struct pinned_pfns *pfns)
> +{
> +	if (!pfns->is_pages)
> +		return NULL;
> +	return (struct page **)(pfns->ptrs);
> +}
> +
> +static inline unsigned long *pfns_vector_pfns(struct pinned_pfns *pfns)
> +{
> +	if (pfns->is_pages)
> +		return NULL;
> +	return (unsigned long *)(pfns->ptrs);
> +}
> +
> +
>  struct kvec;
>  int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
>  			struct page **pages);
> diff --git a/mm/memory.c b/mm/memory.c
> index 22dfa617bddb..87bebcfb8911 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -2024,6 +2024,171 @@ long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
>  EXPORT_SYMBOL(get_user_pages);
>  
>  /**
> + * get_vaddr_pfns() - map virtual addresses to pfns
> + * @start:	starting user address
> + * @nr_pfns:	number of pfns from start to map
> + * @write:	whether pages will be written to by the caller
> + * @force:	whether to force write access even if user mapping is
> + *		readonly. This will result in the page being COWed even
> + *		in MAP_SHARED mappings. You do not want this.
> + * @pfns:	structure which receives pfns of the pages mapped.
> + *		It should have space for at least nr_pfns pfns. 
> + *
> + * This function maps virtual addresses from @start and fills @pfns structure
> + * with page frame numbers of corresponding pages. If @start belongs to a
> + * normal vma, the function grabs reference to each of the pages to pin them in
> + * memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't touch page
> + * structures. Caller should make sure pfns aren't reused for anything else
> + * while he is using them.
> + *
> + * This function takes care of grabbing mmap_sem as necessary.
> + */
> +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
> +		   struct pinned_pfns *pfns)
> +{
> +	struct mm_struct *mm = current->mm;
> +	struct vm_area_struct *vma;
> +	int ret = 0;
> +	int err;
> +
> +	if (nr_pfns <= 0)
> +		return 0;
> +
> +	if (nr_pfns > pfns->nr_allocated_pfns)
> +		nr_pfns = pfns->nr_allocated_pfns;
> +
> +	down_read(&mm->mmap_sem);
> +	vma = find_vma_intersection(mm, start, start + 1);
> +	if (!vma) {
> +		ret = -EFAULT;
> +		goto out;
> +	}
> +	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
> +		pfns->got_ref = 1;
> +		pfns->is_pages = 1;
> +		ret = get_user_pages(current, mm, start, nr_pfns, write, force,
> +				     pfns_vector_pages(pfns), NULL);
> +		goto out;
> +	}
> +
> +	pfns->got_ref = 0;
> +	pfns->is_pages = 0;
> +	do {
> +		unsigned long *nums = pfns_vector_pfns(pfns);
> +
> +		while (ret < nr_pfns && start + PAGE_SIZE <= vma->vm_end) {
> +			err = follow_pfn(vma, start, &nums[ret]);
> +			if (err) {
> +				if (ret == 0)
> +					ret = err;
> +				goto out;
> +			}
> +			start += PAGE_SIZE;
> +			ret++;
> +		}
> +		/*
> +		 * We stop if we have enough pages or if VMA doesn't completely
> +		 * cover the tail page.
> +		 */
> +		if (ret >= nr_pfns || start < vma->vm_end)
> +			break;
> +		vma = find_vma_intersection(mm, start, start + 1);
> +	} while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP));
> +out:
> +	up_read(&mm->mmap_sem);
> +	if (!ret)
> +		ret = -EFAULT;
> +	if (ret > 0)
> +		pfns->nr_pfns = ret;
> +	return ret;
> +}
> +EXPORT_SYMBOL(get_vaddr_pfns);
> +
> +/**
> + * put_vaddr_pfns() - drop references to pages if get_vaddr_pfns() acquired them
> + * @pfns:     structure with pfns we pinned
> + *
> + * Drop references to pages if get_vaddr_pfns() acquired them. We also
> + * invalidate the array of pfns so that it is prepared for the next call into
> + * get_vaddr_pfns().
> + */
> +void put_vaddr_pfns(struct pinned_pfns *pfns)
> +{
> +	int i;
> +
> +	if (!pfns->got_ref)
> +		goto out;
> +	if (pfns->is_pages) {
> +		struct page **pages = pfns_vector_pages(pfns);
> +
> +		for (i = 0; i < pfns->nr_pfns; i++)
> +			put_page(pages[i]);
> +	} else {
> +		unsigned long *nums = pfns_vector_pfns(pfns);
> +
> +		for (i = 0; i < pfns->nr_pfns; i++)
> +			put_page(pfn_to_page(nums[i]));
> +	}
> +	pfns->got_ref = 0;
> +out:
> +	pfns->nr_pfns = 0;
> +}
> +EXPORT_SYMBOL(put_vaddr_pfns);
> +
> +/**
> + * pfns_vector_to_pages - convert vector of pfns to vector of page pointers
> + * @pfns:	structure with pinned pfns
> + *
> + * Convert @pfns to not contain array of pfns but array of page pointers.
> + * If the conversion is successful, return 0. Otherwise return an error.
> + */
> +int pfns_vector_to_pages(struct pinned_pfns *pfns)
> +{
> +	int i;
> +	unsigned long *nums;
> +	struct page **pages;
> +
> +	if (pfns->is_pages)
> +		return 0;
> +	nums = pfns_vector_pfns(pfns);
> +	for (i = 0; i < pfns->nr_pfns; i++)
> +		if (!pfn_valid(nums[i]))
> +			return -EINVAL;
> +	pages = (struct page **)nums;
> +	for (i = 0; i < pfns->nr_pfns; i++)
> +		pages[i] = pfn_to_page(nums[i]);
> +	pfns->is_pages = 1;
> +	return 0;
> +}
> +EXPORT_SYMBOL(pfns_vector_to_pages);
> +
> +/**
> + * pfns_vector_create() - allocate & initialize structure for pinned pfns
> + * @nr_pfns:	number of pfns slots we should reserve
> + *
> + * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
> + * pfns.
> + */
> +struct pinned_pfns *pfns_vector_create(int nr_pfns)
> +{
> +	struct pinned_pfns *pfns;
> +	int size = sizeof(struct pinned_pfns) + sizeof(unsigned long) * nr_pfns;
> +
> +	if (WARN_ON_ONCE(nr_pfns <= 0))
> +		return NULL;
> +	if (size <= PAGE_SIZE)
> +		pfns = kmalloc(size, GFP_KERNEL);
> +	else
> +		pfns = vmalloc(size);
> +	if (!pfns)
> +		return NULL;
> +	pfns->nr_allocated_pfns = nr_pfns;
> +	pfns->nr_pfns = 0;
> +	return 0;
> +}
> +EXPORT_SYMBOL(pfns_vector_create);
> +
> +/**
>   * get_dump_page() - pin user page in memory while writing it to core dump
>   * @addr: user address
>   *
> -- 
> 1.8.1.4
> 
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
@ 2015-04-24 16:21     ` Jan Kara
  0 siblings, 0 replies; 44+ messages in thread
From: Jan Kara @ 2015-04-24 16:21 UTC (permalink / raw)
  To: linux-mm; +Cc: linux-media, Jan Kara, Andrew Morton, aarcange

On Mon 17-03-14 20:49:28, Jan Kara wrote:
> Provide new function get_vaddr_pfns().  This function maps virtual
> addresses from given start and fills given array with page frame numbers of
> the corresponding pages. If given start belongs to a normal vma, the function
> grabs reference to each of the pages to pin them in memory. If start
> belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller
> should make sure pfns aren't reused for anything else while he is using
> them.
> 
> This function is created for various drivers to simplify handling of
> their buffers.
  MM guys, could you have a look at this patch? Linux Media people like the
abstraction of buffer handling and would like to merge the patch set (see
http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/89268).
But for that they need ack from mm people that the interface is ok with
them. So far the only comment I got regarding the interface was from Dave
Hansen that I could also handle VM_MIXEDMAP mappings - I could if people
really want that but so far there are no users for that. Thanks!

								Honza
> 
> Signed-off-by: Jan Kara <jack@suse.cz>
> ---
>  include/linux/mm.h |  46 +++++++++++++++
>  mm/memory.c        | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 211 insertions(+)
> 
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index da8ad480bea7..b3bd29cc40dd 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -18,6 +18,8 @@
>  #include <linux/pfn.h>
>  #include <linux/bit_spinlock.h>
>  #include <linux/shrinker.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
>  
>  struct mempolicy;
>  struct anon_vma;
> @@ -1180,6 +1182,50 @@ get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
>  	return ret;
>  }
>  
> +/* Container for pinned pfns / pages */
> +struct pinned_pfns {
> +	unsigned int nr_allocated_pfns;	/* Number of pfns we have space for */
> +	unsigned int nr_pfns;		/* Number of pfns stored in pfns array */
> +	unsigned int got_ref:1;		/* Did we pin pfns by getting page ref? */
> +	unsigned int is_pages:1;	/* Does array contain pages or pfns? */
> +	void *ptrs[0];			/* Array of pinned pfns / pages.
> +					 * Use pfns_vector_pages() or
> +					 * pfns_vector_pfns() for access */
> +};
> +
> +struct pinned_pfns *pfns_vector_create(int nr_pfns);
> +static inline void pfns_vector_destroy(struct pinned_pfns *pfns)
> +{
> +	if (!is_vmalloc_addr(pfns))
> +		kfree(pfns);
> +	else
> +		vfree(pfns);
> +}
> +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
> +		   struct pinned_pfns *pfns);
> +void put_vaddr_pfns(struct pinned_pfns *pfns);
> +int pfns_vector_to_pages(struct pinned_pfns *pfns);
> +
> +static inline int pfns_vector_count(struct pinned_pfns *pfns)
> +{
> +	return pfns->nr_pfns;
> +}
> +
> +static inline struct page **pfns_vector_pages(struct pinned_pfns *pfns)
> +{
> +	if (!pfns->is_pages)
> +		return NULL;
> +	return (struct page **)(pfns->ptrs);
> +}
> +
> +static inline unsigned long *pfns_vector_pfns(struct pinned_pfns *pfns)
> +{
> +	if (pfns->is_pages)
> +		return NULL;
> +	return (unsigned long *)(pfns->ptrs);
> +}
> +
> +
>  struct kvec;
>  int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
>  			struct page **pages);
> diff --git a/mm/memory.c b/mm/memory.c
> index 22dfa617bddb..87bebcfb8911 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -2024,6 +2024,171 @@ long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
>  EXPORT_SYMBOL(get_user_pages);
>  
>  /**
> + * get_vaddr_pfns() - map virtual addresses to pfns
> + * @start:	starting user address
> + * @nr_pfns:	number of pfns from start to map
> + * @write:	whether pages will be written to by the caller
> + * @force:	whether to force write access even if user mapping is
> + *		readonly. This will result in the page being COWed even
> + *		in MAP_SHARED mappings. You do not want this.
> + * @pfns:	structure which receives pfns of the pages mapped.
> + *		It should have space for at least nr_pfns pfns. 
> + *
> + * This function maps virtual addresses from @start and fills @pfns structure
> + * with page frame numbers of corresponding pages. If @start belongs to a
> + * normal vma, the function grabs reference to each of the pages to pin them in
> + * memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't touch page
> + * structures. Caller should make sure pfns aren't reused for anything else
> + * while he is using them.
> + *
> + * This function takes care of grabbing mmap_sem as necessary.
> + */
> +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force,
> +		   struct pinned_pfns *pfns)
> +{
> +	struct mm_struct *mm = current->mm;
> +	struct vm_area_struct *vma;
> +	int ret = 0;
> +	int err;
> +
> +	if (nr_pfns <= 0)
> +		return 0;
> +
> +	if (nr_pfns > pfns->nr_allocated_pfns)
> +		nr_pfns = pfns->nr_allocated_pfns;
> +
> +	down_read(&mm->mmap_sem);
> +	vma = find_vma_intersection(mm, start, start + 1);
> +	if (!vma) {
> +		ret = -EFAULT;
> +		goto out;
> +	}
> +	if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
> +		pfns->got_ref = 1;
> +		pfns->is_pages = 1;
> +		ret = get_user_pages(current, mm, start, nr_pfns, write, force,
> +				     pfns_vector_pages(pfns), NULL);
> +		goto out;
> +	}
> +
> +	pfns->got_ref = 0;
> +	pfns->is_pages = 0;
> +	do {
> +		unsigned long *nums = pfns_vector_pfns(pfns);
> +
> +		while (ret < nr_pfns && start + PAGE_SIZE <= vma->vm_end) {
> +			err = follow_pfn(vma, start, &nums[ret]);
> +			if (err) {
> +				if (ret == 0)
> +					ret = err;
> +				goto out;
> +			}
> +			start += PAGE_SIZE;
> +			ret++;
> +		}
> +		/*
> +		 * We stop if we have enough pages or if VMA doesn't completely
> +		 * cover the tail page.
> +		 */
> +		if (ret >= nr_pfns || start < vma->vm_end)
> +			break;
> +		vma = find_vma_intersection(mm, start, start + 1);
> +	} while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP));
> +out:
> +	up_read(&mm->mmap_sem);
> +	if (!ret)
> +		ret = -EFAULT;
> +	if (ret > 0)
> +		pfns->nr_pfns = ret;
> +	return ret;
> +}
> +EXPORT_SYMBOL(get_vaddr_pfns);
> +
> +/**
> + * put_vaddr_pfns() - drop references to pages if get_vaddr_pfns() acquired them
> + * @pfns:     structure with pfns we pinned
> + *
> + * Drop references to pages if get_vaddr_pfns() acquired them. We also
> + * invalidate the array of pfns so that it is prepared for the next call into
> + * get_vaddr_pfns().
> + */
> +void put_vaddr_pfns(struct pinned_pfns *pfns)
> +{
> +	int i;
> +
> +	if (!pfns->got_ref)
> +		goto out;
> +	if (pfns->is_pages) {
> +		struct page **pages = pfns_vector_pages(pfns);
> +
> +		for (i = 0; i < pfns->nr_pfns; i++)
> +			put_page(pages[i]);
> +	} else {
> +		unsigned long *nums = pfns_vector_pfns(pfns);
> +
> +		for (i = 0; i < pfns->nr_pfns; i++)
> +			put_page(pfn_to_page(nums[i]));
> +	}
> +	pfns->got_ref = 0;
> +out:
> +	pfns->nr_pfns = 0;
> +}
> +EXPORT_SYMBOL(put_vaddr_pfns);
> +
> +/**
> + * pfns_vector_to_pages - convert vector of pfns to vector of page pointers
> + * @pfns:	structure with pinned pfns
> + *
> + * Convert @pfns to not contain array of pfns but array of page pointers.
> + * If the conversion is successful, return 0. Otherwise return an error.
> + */
> +int pfns_vector_to_pages(struct pinned_pfns *pfns)
> +{
> +	int i;
> +	unsigned long *nums;
> +	struct page **pages;
> +
> +	if (pfns->is_pages)
> +		return 0;
> +	nums = pfns_vector_pfns(pfns);
> +	for (i = 0; i < pfns->nr_pfns; i++)
> +		if (!pfn_valid(nums[i]))
> +			return -EINVAL;
> +	pages = (struct page **)nums;
> +	for (i = 0; i < pfns->nr_pfns; i++)
> +		pages[i] = pfn_to_page(nums[i]);
> +	pfns->is_pages = 1;
> +	return 0;
> +}
> +EXPORT_SYMBOL(pfns_vector_to_pages);
> +
> +/**
> + * pfns_vector_create() - allocate & initialize structure for pinned pfns
> + * @nr_pfns:	number of pfns slots we should reserve
> + *
> + * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
> + * pfns.
> + */
> +struct pinned_pfns *pfns_vector_create(int nr_pfns)
> +{
> +	struct pinned_pfns *pfns;
> +	int size = sizeof(struct pinned_pfns) + sizeof(unsigned long) * nr_pfns;
> +
> +	if (WARN_ON_ONCE(nr_pfns <= 0))
> +		return NULL;
> +	if (size <= PAGE_SIZE)
> +		pfns = kmalloc(size, GFP_KERNEL);
> +	else
> +		pfns = vmalloc(size);
> +	if (!pfns)
> +		return NULL;
> +	pfns->nr_allocated_pfns = nr_pfns;
> +	pfns->nr_pfns = 0;
> +	return 0;
> +}
> +EXPORT_SYMBOL(pfns_vector_create);
> +
> +/**
>   * get_dump_page() - pin user page in memory while writing it to core dump
>   * @addr: user address
>   *
> -- 
> 1.8.1.4
> 
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2015-04-24 16:21 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-17 19:49 [RFC] Helper to abstract vma handling in media layer Jan Kara
2014-03-17 19:49 ` Jan Kara
2014-03-17 19:49 ` [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-03-17 20:53   ` Dave Hansen
2014-03-17 20:53     ` Dave Hansen
2014-03-18 10:25     ` Jan Kara
2014-03-18 10:25       ` Jan Kara
2015-04-24 16:21   ` Jan Kara
2015-04-24 16:21     ` Jan Kara
2014-03-17 19:49 ` [PATCH 2/9] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-03-17 19:49 ` [PATCH 3/9] media: vb2: Teach vb2_queue_or_prepare_buf() to get pfns for user buffers Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-03-17 19:49 ` [PATCH 4/9] media: vb2: Convert vb2_dma_sg_get_userptr() to use pinned pfns Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-03-17 19:49 ` [PATCH 5/9] media: vb2: Convert vb2_vmalloc_get_userptr() to use pfns vector Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-03-17 19:49 ` [PATCH 6/9] media: vb2: Convert vb2_dc_get_userptr() " Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-03-17 19:49 ` [PATCH 7/9] media: vb2: Remove unused functions Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-03-17 19:49 ` [PATCH 8/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_pfn() Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-03-17 19:49 ` [PATCH 9/9] staging: tidspbridge: Convert to get_vaddr_pfns() Jan Kara
2014-03-17 19:49   ` Jan Kara
2014-04-10 10:02 ` [RFC] Helper to abstract vma handling in media layer Marek Szyprowski
2014-04-10 10:02   ` Marek Szyprowski
2014-04-10 10:32   ` Jan Kara
2014-04-10 10:32     ` Jan Kara
2014-04-10 11:07     ` Hans Verkuil
2014-04-10 11:07       ` Hans Verkuil
2014-04-10 12:15       ` Jan Kara
2014-04-10 12:15         ` Jan Kara
2014-04-10 12:22         ` Hans Verkuil
2014-04-10 12:22           ` Hans Verkuil
2014-04-10 21:57           ` Jan Kara
2014-04-10 21:57             ` Jan Kara
2014-04-10 22:18             ` Jan Kara
2014-04-10 22:18               ` Jan Kara
2014-04-11  6:58               ` Hans Verkuil
2014-04-11  6:58                 ` Hans Verkuil
2014-04-14 21:19                 ` Jan Kara
2014-04-14 21:19                   ` Jan Kara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.