vaddr_pin_user_pages_remote() is the "vaddr_pin_pages" corresponding
variant to get_user_pages_remote(), except that:
   a) it sets FOLL_PIN, and
   b) it can handle FOLL_LONGTERM (and the associated vaddr_pin arg).

Change process_vm_rw_single_vec() to invoke the new function.

Signed-off-by: John Hubbard <jhubb...@nvidia.com>
Cc: Ira Weiny <ira.we...@intel.com>
---
 include/linux/mm.h     |  5 +++++
 mm/gup.c               | 34 ++++++++++++++++++++++++++++++++++
 mm/process_vm_access.c | 23 +++++++++++++----------
 3 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 6e7de424bf5e..849b509e9f89 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1606,6 +1606,11 @@ int __account_locked_vm(struct mm_struct *mm, unsigned 
long pages, bool inc,
 long vaddr_pin_pages(unsigned long addr, unsigned long nr_pages,
                     unsigned int gup_flags, struct page **pages,
                     struct vaddr_pin *vaddr_pin);
+long vaddr_pin_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
+                                unsigned long start, unsigned long nr_pages,
+                                unsigned int gup_flags, struct page **pages,
+                                struct vm_area_struct **vmas, int *locked,
+                                struct vaddr_pin *vaddr_pin);
 void vaddr_unpin_pages(struct page **pages, unsigned long nr_pages,
                       struct vaddr_pin *vaddr_pin, bool make_dirty);
 bool mapping_inode_has_layout(struct vaddr_pin *vaddr_pin, struct page *page);
diff --git a/mm/gup.c b/mm/gup.c
index ba316d960d7a..d713ed9d4b9a 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -2522,3 +2522,37 @@ void vaddr_unpin_pages(struct page **pages, unsigned 
long nr_pages,
        __put_user_pages_dirty_lock(pages, nr_pages, make_dirty, vaddr_pin);
 }
 EXPORT_SYMBOL(vaddr_unpin_pages);
+
+/**
+ * vaddr_pin_user_pages_remote() - pin pages by virtual address and return the
+ * pages to the user.
+ *
+ * @tsk:       the task_struct to use for page fault accounting, or
+ *             NULL if faults are not to be recorded.
+ * @mm:                mm_struct of target mm
+ * @addr:      start address
+ * @nr_pages:  number of pages to pin
+ * @gup_flags: flags to use for the pin. Please see FOLL_* documentation in
+ *             mm.h.
+ * @pages:     array of pages returned
+ * @vaddr_pin:  If FOLL_LONGTERM is set, then vaddr_pin should point to an
+ * initialized struct that contains the owning mm and file. Otherwise, 
vaddr_pin
+ * should be set to NULL.
+ *
+ * This is the "vaddr_pin_pages" corresponding variant to
+ * get_user_pages_remote(), except that:
+ *    a) it sets FOLL_PIN, and
+ *    b) it can handle FOLL_LONGTERM (and the associated vaddr_pin arg).
+ */
+long vaddr_pin_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
+                                unsigned long start, unsigned long nr_pages,
+                                unsigned int gup_flags, struct page **pages,
+                                struct vm_area_struct **vmas, int *locked,
+                                struct vaddr_pin *vaddr_pin)
+{
+       gup_flags |= FOLL_TOUCH | FOLL_REMOTE | FOLL_PIN;
+
+       return __get_user_pages_locked(tsk, mm, start, nr_pages, pages, vmas,
+                                      locked, gup_flags, vaddr_pin);
+}
+EXPORT_SYMBOL(vaddr_pin_user_pages_remote);
diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c
index 357aa7bef6c0..28e0a17b6080 100644
--- a/mm/process_vm_access.c
+++ b/mm/process_vm_access.c
@@ -44,7 +44,6 @@ static int process_vm_rw_pages(struct page **pages,
 
                if (vm_write) {
                        copied = copy_page_from_iter(page, offset, copy, iter);
-                       set_page_dirty_lock(page);
                } else {
                        copied = copy_page_to_iter(page, offset, copy, iter);
                }
@@ -96,7 +95,7 @@ static int process_vm_rw_single_vec(unsigned long addr,
                flags |= FOLL_WRITE;
 
        while (!rc && nr_pages && iov_iter_count(iter)) {
-               int pages = min(nr_pages, max_pages_per_loop);
+               int pinned_pages = min(nr_pages, max_pages_per_loop);
                int locked = 1;
                size_t bytes;
 
@@ -106,14 +105,17 @@ static int process_vm_rw_single_vec(unsigned long addr,
                 * current/current->mm
                 */
                down_read(&mm->mmap_sem);
-               pages = get_user_pages_remote(task, mm, pa, pages, flags,
-                                             process_pages, NULL, &locked);
+
+               pinned_pages = vaddr_pin_user_pages_remote(task, mm, pa,
+                                                          pinned_pages, flags,
+                                                          process_pages, NULL,
+                                                          &locked, NULL);
                if (locked)
                        up_read(&mm->mmap_sem);
-               if (pages <= 0)
+               if (pinned_pages <= 0)
                        return -EFAULT;
 
-               bytes = pages * PAGE_SIZE - start_offset;
+               bytes = pinned_pages * PAGE_SIZE - start_offset;
                if (bytes > len)
                        bytes = len;
 
@@ -122,10 +124,11 @@ static int process_vm_rw_single_vec(unsigned long addr,
                                         vm_write);
                len -= bytes;
                start_offset = 0;
-               nr_pages -= pages;
-               pa += pages * PAGE_SIZE;
-               while (pages)
-                       put_page(process_pages[--pages]);
+               nr_pages -= pinned_pages;
+               pa += pinned_pages * PAGE_SIZE;
+
+               /* If vm_write is set, the pages need to be made dirty: */
+               vaddr_unpin_pages(process_pages, pinned_pages, NULL, vm_write);
        }
 
        return rc;
-- 
2.22.1

Reply via email to