diff mbox series

[v2,3/4] mm: Introduce page_needs_cow_for_dma() for deciding whether cow

Message ID 20210204145033.136755-4-peterx@redhat.com (mailing list archive)
State New, archived
Headers show
Series mm/hugetlb: Early cow on fork, and a few cleanups | expand

Commit Message

Peter Xu Feb. 4, 2021, 2:50 p.m. UTC
We've got quite a few places (pte, pmd, pud) that explicitly checked against
whether we should break the cow right now during fork().  It's easier to
provide a helper, especially before we work the same thing on hugetlbfs.

Since we'll reference is_cow_mapping() in mm.h, move it there too.  Actually it
suites mm.h more since internal.h is mm/ only, but mm.h is exported to the
whole kernel.  With that we should expect another patch to use is_cow_mapping()
whenever we can across the kernel since we do use it quite a lot but it's
always done with raw code against VM_* flags.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 include/linux/mm.h | 21 +++++++++++++++++++++
 mm/huge_memory.c   |  8 ++------
 mm/internal.h      |  5 -----
 mm/memory.c        |  7 +------
 4 files changed, 24 insertions(+), 17 deletions(-)

Comments

Linus Torvalds Feb. 4, 2021, 5:54 p.m. UTC | #1
On Thu, Feb 4, 2021 at 6:50 AM Peter Xu <peterx@redhat.com> wrote:
>
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -1291,6 +1291,27 @@ static inline bool page_maybe_dma_pinned(struct page *page)
>                 GUP_PIN_COUNTING_BIAS;
>  }
>
> +static inline bool is_cow_mapping(vm_flags_t flags)
> +{
> +       return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
> +}

Oh, and I just realized: moving this to <linux/mm.h> means that this
patch could/should also get rid of

 - manual copy of this in mm/hugetlb.c:

        cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;

 - private #define for the same thing in fs/proc/task_mmu.c

    #define is_cow_mapping(flags) (((flags) & (VM_SHARED |
VM_MAYWRITE)) == VM_MAYWRITE)

 - manual copy in drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c:

        bool is_cow_mapping =
                (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;

I guess it could be a later cleanup patch too, but maybe best done in
this series just to not forget about it.

               Linus
Peter Xu Feb. 4, 2021, 7:25 p.m. UTC | #2
On Thu, Feb 04, 2021 at 09:54:37AM -0800, Linus Torvalds wrote:
> On Thu, Feb 4, 2021 at 6:50 AM Peter Xu <peterx@redhat.com> wrote:
> >
> > --- a/include/linux/mm.h
> > +++ b/include/linux/mm.h
> > @@ -1291,6 +1291,27 @@ static inline bool page_maybe_dma_pinned(struct page *page)
> >                 GUP_PIN_COUNTING_BIAS;
> >  }
> >
> > +static inline bool is_cow_mapping(vm_flags_t flags)
> > +{
> > +       return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
> > +}
> 
> Oh, and I just realized: moving this to <linux/mm.h> means that this
> patch could/should also get rid of
> 
>  - manual copy of this in mm/hugetlb.c:
> 
>         cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
> 
>  - private #define for the same thing in fs/proc/task_mmu.c
> 
>     #define is_cow_mapping(flags) (((flags) & (VM_SHARED |
> VM_MAYWRITE)) == VM_MAYWRITE)
> 
>  - manual copy in drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c:
> 
>         bool is_cow_mapping =
>                 (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
> 
> I guess it could be a later cleanup patch too, but maybe best done in
> this series just to not forget about it.

Will do in v3 if there is.  Or I'll post a standalone patch to not disturb Gal
just in case he's kicked a test.  Thanks,
Jason Gunthorpe Feb. 4, 2021, 11:20 p.m. UTC | #3
On Thu, Feb 04, 2021 at 09:50:32AM -0500, Peter Xu wrote:
> We've got quite a few places (pte, pmd, pud) that explicitly checked against
> whether we should break the cow right now during fork().  It's easier to
> provide a helper, especially before we work the same thing on hugetlbfs.
> 
> Since we'll reference is_cow_mapping() in mm.h, move it there too.  Actually it
> suites mm.h more since internal.h is mm/ only, but mm.h is exported to the
> whole kernel.  With that we should expect another patch to use is_cow_mapping()
> whenever we can across the kernel since we do use it quite a lot but it's
> always done with raw code against VM_* flags.
> 
> Signed-off-by: Peter Xu <peterx@redhat.com>
>  include/linux/mm.h | 21 +++++++++++++++++++++
>  mm/huge_memory.c   |  8 ++------
>  mm/internal.h      |  5 -----
>  mm/memory.c        |  7 +------
>  4 files changed, 24 insertions(+), 17 deletions(-)
> 
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index ecdf8a8cd6ae..6ea20721d349 100644
> +++ b/include/linux/mm.h
> @@ -1291,6 +1291,27 @@ static inline bool page_maybe_dma_pinned(struct page *page)
>  		GUP_PIN_COUNTING_BIAS;
>  }
>  
> +static inline bool is_cow_mapping(vm_flags_t flags)
> +{

It feels a bit more logical to pass in a struct vm_area_struct *' here?

Jason
Peter Xu Feb. 5, 2021, 12:50 a.m. UTC | #4
On Thu, Feb 04, 2021 at 07:20:00PM -0400, Jason Gunthorpe wrote:
> > +static inline bool is_cow_mapping(vm_flags_t flags)
> > +{
> 
> It feels a bit more logical to pass in a struct vm_area_struct *' here?

Agree, but only if I'm adding this as a new function.  Though it's a code
movement from internal.h to mm.h, so I avoided touching all the users of this
function that existed.

Thanks,
diff mbox series

Patch

diff --git a/include/linux/mm.h b/include/linux/mm.h
index ecdf8a8cd6ae..6ea20721d349 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1291,6 +1291,27 @@  static inline bool page_maybe_dma_pinned(struct page *page)
 		GUP_PIN_COUNTING_BIAS;
 }
 
+static inline bool is_cow_mapping(vm_flags_t flags)
+{
+	return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
+}
+
+/*
+ * This should most likely only be called during fork() to see whether we
+ * should break the cow immediately for a page on the src mm.
+ */
+static inline bool page_needs_cow_for_dma(struct vm_area_struct *vma,
+					  struct page *page)
+{
+	if (!is_cow_mapping(vma->vm_flags))
+		return false;
+
+	if (!atomic_read(&vma->vm_mm->has_pinned))
+		return false;
+
+	return page_maybe_dma_pinned(page);
+}
+
 #if defined(CONFIG_SPARSEMEM) && !defined(CONFIG_SPARSEMEM_VMEMMAP)
 #define SECTION_IN_PAGE_FLAGS
 #endif
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 9237976abe72..dbff6c7eda67 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1095,9 +1095,7 @@  int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
 	 * best effort that the pinned pages won't be replaced by another
 	 * random page during the coming copy-on-write.
 	 */
-	if (unlikely(is_cow_mapping(vma->vm_flags) &&
-		     atomic_read(&src_mm->has_pinned) &&
-		     page_maybe_dma_pinned(src_page))) {
+	if (unlikely(page_needs_cow_for_dma(vma, src_page))) {
 		pte_free(dst_mm, pgtable);
 		spin_unlock(src_ptl);
 		spin_unlock(dst_ptl);
@@ -1209,9 +1207,7 @@  int copy_huge_pud(struct mm_struct *dst_mm, struct mm_struct *src_mm,
 	}
 
 	/* Please refer to comments in copy_huge_pmd() */
-	if (unlikely(is_cow_mapping(vma->vm_flags) &&
-		     atomic_read(&src_mm->has_pinned) &&
-		     page_maybe_dma_pinned(pud_page(pud)))) {
+	if (unlikely(page_needs_cow_for_dma(vma, pud_page(pud)))) {
 		spin_unlock(src_ptl);
 		spin_unlock(dst_ptl);
 		__split_huge_pud(vma, src_pud, addr);
diff --git a/mm/internal.h b/mm/internal.h
index 25d2b2439f19..24eec93d0dac 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -296,11 +296,6 @@  static inline unsigned int buddy_order(struct page *page)
  */
 #define buddy_order_unsafe(page)	READ_ONCE(page_private(page))
 
-static inline bool is_cow_mapping(vm_flags_t flags)
-{
-	return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
-}
-
 /*
  * These three helpers classifies VMAs for virtual memory accounting.
  */
diff --git a/mm/memory.c b/mm/memory.c
index feff48e1465a..b2849e1d4aab 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -800,9 +800,6 @@  copy_present_page(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma
 	struct mm_struct *src_mm = src_vma->vm_mm;
 	struct page *new_page;
 
-	if (!is_cow_mapping(src_vma->vm_flags))
-		return 1;
-
 	/*
 	 * What we want to do is to check whether this page may
 	 * have been pinned by the parent process.  If so,
@@ -816,9 +813,7 @@  copy_present_page(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma
 	 * the page count. That might give false positives for
 	 * for pinning, but it will work correctly.
 	 */
-	if (likely(!atomic_read(&src_mm->has_pinned)))
-		return 1;
-	if (likely(!page_maybe_dma_pinned(page)))
+	if (likely(!page_needs_cow_for_dma(src_vma, page)))
 		return 1;
 
 	new_page = *prealloc;