2021-04-20 13:17:39

by Mike Rapoport

[permalink] [raw]
Subject: [PATCH v2 0/2] secretmem: optimize page_is_secretmem()

From: Mike Rapoport <[email protected]>

Hi,

This is an updated version of page_is_secretmem() changes.
This is based on v5.12-rc7-mmots-2021-04-15-16-28.

@Andrew, please let me know if you'd like me to rebase it differently or
resend the entire set.

v2:
* move the check for secretmem page in gup_pte_range after we get a
reference to the page, per Matthew.

Mike Rapoport (2):
secretmem/gup: don't check if page is secretmem without reference
secretmem: optimize page_is_secretmem()

include/linux/secretmem.h | 26 +++++++++++++++++++++++++-
mm/gup.c | 6 +++---
mm/secretmem.c | 12 +-----------
3 files changed, 29 insertions(+), 15 deletions(-)

--
2.28.0


2021-04-20 13:17:50

by Mike Rapoport

[permalink] [raw]
Subject: [PATCH v2 1/2] secretmem/gup: don't check if page is secretmem without reference

From: Mike Rapoport <[email protected]>

The check in gup_pte_range() whether a page belongs to a secretmem mapping
is performed before grabbing the page reference.

To avoid potential race move the check after try_grab_compound_head().

Signed-off-by: Mike Rapoport <[email protected]>
---
mm/gup.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/mm/gup.c b/mm/gup.c
index c3a17b189064..4b58c016e949 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -2080,13 +2080,13 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
page = pte_page(pte);

- if (page_is_secretmem(page))
- goto pte_unmap;
-
head = try_grab_compound_head(page, 1, flags);
if (!head)
goto pte_unmap;

+ if (page_is_secretmem(page))
+ goto pte_unmap;
+
if (unlikely(pte_val(pte) != pte_val(*ptep))) {
put_compound_head(head, 1, flags);
goto pte_unmap;
--
2.28.0

2021-04-20 13:18:27

by Mike Rapoport

[permalink] [raw]
Subject: [PATCH v2 2/2] secretmem/gup: don't check if page is secretmem without reference

From: Mike Rapoport <[email protected]>

The check in gup_pte_range() whether a page belongs to a secretmem mapping
is performed before grabbing the page reference.

To avoid potential race move the check after try_grab_compound_head().

Signed-off-by: Mike Rapoport <[email protected]>
---
mm/gup.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/mm/gup.c b/mm/gup.c
index c3a17b189064..4b58c016e949 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -2080,13 +2080,13 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
page = pte_page(pte);

- if (page_is_secretmem(page))
- goto pte_unmap;
-
head = try_grab_compound_head(page, 1, flags);
if (!head)
goto pte_unmap;

+ if (page_is_secretmem(page))
+ goto pte_unmap;
+
if (unlikely(pte_val(pte) != pte_val(*ptep))) {
put_compound_head(head, 1, flags);
goto pte_unmap;
--
2.28.0

2021-04-20 13:19:10

by Mike Rapoport

[permalink] [raw]
Subject: [PATCH v2 1/2] secretmem: optimize page_is_secretmem()

From: Mike Rapoport <[email protected]>

Kernel test robot reported -4.2% regression of will-it-scale.per_thread_ops
due to commit "mm: introduce memfd_secret system call to create "secret"
memory areas".

The perf profile of the test indicated that the regression is caused by
page_is_secretmem() called from gup_pte_range() (inlined by gup_pgd_range):

27.76 +2.5 30.23 perf-profile.children.cycles-pp.gup_pgd_range
0.00 +3.2 3.19 ± 2% perf-profile.children.cycles-pp.page_mapping
0.00 +3.7 3.66 ± 2% perf-profile.children.cycles-pp.page_is_secretmem

Further analysis showed that the slow down happens because neither
page_is_secretmem() nor page_mapping() are not inline and moreover,
multiple page flags checks in page_mapping() involve calling
compound_head() several times for the same page.

Make page_is_secretmem() inline and replace page_mapping() with page flag
checks that do not imply page-to-head conversion.

Reported-by: kernel test robot <[email protected]>
Signed-off-by: Mike Rapoport <[email protected]>
---
include/linux/secretmem.h | 26 +++++++++++++++++++++++++-
mm/secretmem.c | 12 +-----------
2 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/include/linux/secretmem.h b/include/linux/secretmem.h
index 907a6734059c..b842b38cbeb1 100644
--- a/include/linux/secretmem.h
+++ b/include/linux/secretmem.h
@@ -4,8 +4,32 @@

#ifdef CONFIG_SECRETMEM

+extern const struct address_space_operations secretmem_aops;
+
+static inline bool page_is_secretmem(struct page *page)
+{
+ struct address_space *mapping;
+
+ /*
+ * Using page_mapping() is quite slow because of the actual call
+ * instruction and repeated compound_head(page) inside the
+ * page_mapping() function.
+ * We know that secretmem pages are not compound and LRU so we can
+ * save a couple of cycles here.
+ */
+ if (PageCompound(page) || !PageLRU(page))
+ return false;
+
+ mapping = (struct address_space *)
+ ((unsigned long)page->mapping & ~PAGE_MAPPING_FLAGS);
+
+ if (mapping != page->mapping)
+ return false;
+
+ return page->mapping->a_ops == &secretmem_aops;
+}
+
bool vma_is_secretmem(struct vm_area_struct *vma);
-bool page_is_secretmem(struct page *page);
bool secretmem_active(void);

#else
diff --git a/mm/secretmem.c b/mm/secretmem.c
index 3b1ba3991964..0bcd15e1b549 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -151,22 +151,12 @@ static void secretmem_freepage(struct page *page)
clear_highpage(page);
}

-static const struct address_space_operations secretmem_aops = {
+const struct address_space_operations secretmem_aops = {
.freepage = secretmem_freepage,
.migratepage = secretmem_migratepage,
.isolate_page = secretmem_isolate_page,
};

-bool page_is_secretmem(struct page *page)
-{
- struct address_space *mapping = page_mapping(page);
-
- if (!mapping)
- return false;
-
- return mapping->a_ops == &secretmem_aops;
-}
-
static struct vfsmount *secretmem_mnt;

static struct file *secretmem_file_create(unsigned long flags)
--
2.28.0

2021-04-20 13:19:12

by Mike Rapoport

[permalink] [raw]
Subject: [PATCH v2 2/2] secretmem: optimize page_is_secretmem()

From: Mike Rapoport <[email protected]>

Kernel test robot reported -4.2% regression of will-it-scale.per_thread_ops
due to commit "mm: introduce memfd_secret system call to create "secret"
memory areas".

The perf profile of the test indicated that the regression is caused by
page_is_secretmem() called from gup_pte_range() (inlined by gup_pgd_range):

27.76 +2.5 30.23 perf-profile.children.cycles-pp.gup_pgd_range
0.00 +3.2 3.19 ± 2% perf-profile.children.cycles-pp.page_mapping
0.00 +3.7 3.66 ± 2% perf-profile.children.cycles-pp.page_is_secretmem

Further analysis showed that the slow down happens because neither
page_is_secretmem() nor page_mapping() are not inline and moreover,
multiple page flags checks in page_mapping() involve calling
compound_head() several times for the same page.

Make page_is_secretmem() inline and replace page_mapping() with page flag
checks that do not imply page-to-head conversion.

Reported-by: kernel test robot <[email protected]>
Signed-off-by: Mike Rapoport <[email protected]>
---
include/linux/secretmem.h | 26 +++++++++++++++++++++++++-
mm/secretmem.c | 12 +-----------
2 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/include/linux/secretmem.h b/include/linux/secretmem.h
index 907a6734059c..b842b38cbeb1 100644
--- a/include/linux/secretmem.h
+++ b/include/linux/secretmem.h
@@ -4,8 +4,32 @@

#ifdef CONFIG_SECRETMEM

+extern const struct address_space_operations secretmem_aops;
+
+static inline bool page_is_secretmem(struct page *page)
+{
+ struct address_space *mapping;
+
+ /*
+ * Using page_mapping() is quite slow because of the actual call
+ * instruction and repeated compound_head(page) inside the
+ * page_mapping() function.
+ * We know that secretmem pages are not compound and LRU so we can
+ * save a couple of cycles here.
+ */
+ if (PageCompound(page) || !PageLRU(page))
+ return false;
+
+ mapping = (struct address_space *)
+ ((unsigned long)page->mapping & ~PAGE_MAPPING_FLAGS);
+
+ if (mapping != page->mapping)
+ return false;
+
+ return page->mapping->a_ops == &secretmem_aops;
+}
+
bool vma_is_secretmem(struct vm_area_struct *vma);
-bool page_is_secretmem(struct page *page);
bool secretmem_active(void);

#else
diff --git a/mm/secretmem.c b/mm/secretmem.c
index 3b1ba3991964..0bcd15e1b549 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -151,22 +151,12 @@ static void secretmem_freepage(struct page *page)
clear_highpage(page);
}

-static const struct address_space_operations secretmem_aops = {
+const struct address_space_operations secretmem_aops = {
.freepage = secretmem_freepage,
.migratepage = secretmem_migratepage,
.isolate_page = secretmem_isolate_page,
};

-bool page_is_secretmem(struct page *page)
-{
- struct address_space *mapping = page_mapping(page);
-
- if (!mapping)
- return false;
-
- return mapping->a_ops == &secretmem_aops;
-}
-
static struct vfsmount *secretmem_mnt;

static struct file *secretmem_file_create(unsigned long flags)
--
2.28.0

2021-04-20 13:22:31

by David Hildenbrand

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] secretmem/gup: don't check if page is secretmem without reference

On 20.04.21 15:16, Mike Rapoport wrote:
> From: Mike Rapoport <[email protected]>
>
> The check in gup_pte_range() whether a page belongs to a secretmem mapping
> is performed before grabbing the page reference.
>
> To avoid potential race move the check after try_grab_compound_head().
>
> Signed-off-by: Mike Rapoport <[email protected]>
> ---
> mm/gup.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/mm/gup.c b/mm/gup.c
> index c3a17b189064..4b58c016e949 100644
> --- a/mm/gup.c
> +++ b/mm/gup.c
> @@ -2080,13 +2080,13 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
> VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
> page = pte_page(pte);
>
> - if (page_is_secretmem(page))
> - goto pte_unmap;
> -
> head = try_grab_compound_head(page, 1, flags);
> if (!head)
> goto pte_unmap;
>
> + if (page_is_secretmem(page))
> + goto pte_unmap;
> +

Looking at the hunk below, I wonder if you're missing a put_compound_head().

(also, I'd do if unlikely(page_is_secretmem()) but that's a different
discussion)

> if (unlikely(pte_val(pte) != pte_val(*ptep))) {
> put_compound_head(head, 1, flags);
> goto pte_unmap;
>


--
Thanks,

David / dhildenb

2021-04-20 13:31:05

by Mike Rapoport

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] secretmem/gup: don't check if page is secretmem without reference

On Tue, Apr 20, 2021 at 03:19:56PM +0200, David Hildenbrand wrote:
> On 20.04.21 15:16, Mike Rapoport wrote:
> > From: Mike Rapoport <[email protected]>
> >
> > The check in gup_pte_range() whether a page belongs to a secretmem mapping
> > is performed before grabbing the page reference.
> >
> > To avoid potential race move the check after try_grab_compound_head().
> >
> > Signed-off-by: Mike Rapoport <[email protected]>
> > ---
> > mm/gup.c | 6 +++---
> > 1 file changed, 3 insertions(+), 3 deletions(-)
> >
> > diff --git a/mm/gup.c b/mm/gup.c
> > index c3a17b189064..4b58c016e949 100644
> > --- a/mm/gup.c
> > +++ b/mm/gup.c
> > @@ -2080,13 +2080,13 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
> > VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
> > page = pte_page(pte);
> > - if (page_is_secretmem(page))
> > - goto pte_unmap;
> > -
> > head = try_grab_compound_head(page, 1, flags);
> > if (!head)
> > goto pte_unmap;
> > + if (page_is_secretmem(page))
> > + goto pte_unmap;
> > +
>
> Looking at the hunk below, I wonder if you're missing a put_compound_head().

Hmm, yes.

> (also, I'd do if unlikely(page_is_secretmem()) but that's a different
> discussion)

I don't mind, actually. I don't think there would be massive secretmem
usage soon.

> > if (unlikely(pte_val(pte) != pte_val(*ptep))) {
> > put_compound_head(head, 1, flags);
> > goto pte_unmap;
> >
>
>
> --
> Thanks,
>
> David / dhildenb
>

--
Sincerely yours,
Mike.

2021-04-20 14:41:00

by Mike Rapoport

[permalink] [raw]
Subject: Re: [PATCH v2 0/2] secretmem: optimize page_is_secretmem()

It seems I've messed this posting entirely :(

Please ignore it, sorry for the noise.

On Tue, Apr 20, 2021 at 04:16:07PM +0300, Mike Rapoport wrote:
> From: Mike Rapoport <[email protected]>
>
> Hi,
>
> This is an updated version of page_is_secretmem() changes.
> This is based on v5.12-rc7-mmots-2021-04-15-16-28.
>
> @Andrew, please let me know if you'd like me to rebase it differently or
> resend the entire set.
>
> v2:
> * move the check for secretmem page in gup_pte_range after we get a
> reference to the page, per Matthew.
>
> Mike Rapoport (2):
> secretmem/gup: don't check if page is secretmem without reference
> secretmem: optimize page_is_secretmem()
>
> include/linux/secretmem.h | 26 +++++++++++++++++++++++++-
> mm/gup.c | 6 +++---
> mm/secretmem.c | 12 +-----------
> 3 files changed, 29 insertions(+), 15 deletions(-)
>
> --
> 2.28.0
>

--
Sincerely yours,
Mike.