From: Mike Rapoport rppt@linux.ibm.com
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(-)
From: Mike Rapoport rppt@linux.ibm.com
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 rppt@linux.ibm.com --- 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;
On 20.04.21 15:16, Mike Rapoport wrote:
From: Mike Rapoport rppt@linux.ibm.com
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 rppt@linux.ibm.com
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;
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 rppt@linux.ibm.com
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 rppt@linux.ibm.com
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
From: Mike Rapoport rppt@linux.ibm.com
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 oliver.sang@intel.com Signed-off-by: Mike Rapoport rppt@linux.ibm.com --- 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)
From: Mike Rapoport rppt@linux.ibm.com
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 rppt@linux.ibm.com --- 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;
From: Mike Rapoport rppt@linux.ibm.com
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 oliver.sang@intel.com Signed-off-by: Mike Rapoport rppt@linux.ibm.com --- 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)
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 rppt@linux.ibm.com
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
linux-kselftest-mirror@lists.linaro.org