pass mm into pagewalkers
Subsystems (4)
RFC PATCH v2 1/2 pass mm into pagewalkers
We need this at least for huge page detection for now.
It might also come in handy for some of the other
users.
---
linux-2.6.git-dave/fs/proc/task_mmu.c | 44 +++++++++++++++++++--------------- linux-2.6.git-dave/include/linux/mm.h | 17 ++++++------- linux-2.6.git-dave/mm/pagewalk.c | 42 +++++++++++++++++--------------- 3 files changed, 56 insertions(+), 47 deletions(-)
mm/pagewalk.c renamed from mm/pagewalk.c~pass-mm-into-pagewalkers +22 -20
@@ -3,14 +3,14 @@#include <linux/sched.h> static int walk_pte_range (pmd_t *pmd,unsigned long addr,unsigned long end, -const struct mm_walk *walk,void *private) +struct mm_walk *walk) {pte_t *pte;int err =0 ; pte =pte_offset_map (pmd, addr);for (;;) { - err = walk->pte_entry (pte, addr, addr +PAGE_SIZE , private); + err = walk->pte_entry (pte, addr, addr +PAGE_SIZE , walk);if (err)break ; addr +=PAGE_SIZE ;
@@ -24,7 +24,7 @@ static int walk_pte_range(pmd_t *pmd, un }static int walk_pmd_range (pud_t *pud,unsigned long addr,unsigned long end, -const struct mm_walk *walk,void *private) +struct mm_walk *walk) {pmd_t *pmd;unsigned long next;
@@ -35,15 +35,15 @@ static int walk_pmd_range(pud_t *pud, un next =pmd_addr_end (addr, end);if (pmd_none_or_clear_bad (pmd)) {if (walk->pte_hole) - err = walk->pte_hole (addr, next, private); + err = walk->pte_hole (addr, next, walk);if (err)break ;continue ; }if (walk->pmd_entry) - err = walk->pmd_entry (pmd, addr, next, private); + err = walk->pmd_entry (pmd, addr, next, walk);if (!err && walk->pte_entry) - err =walk_pte_range (pmd, addr, next, walk, private); + err =walk_pte_range (pmd, addr, next, walk);if (err)break ; }while (pmd++, addr = next, addr != end);
@@ -52,7 +52,7 @@ static int walk_pmd_range(pud_t *pud, un }static int walk_pud_range (pgd_t *pgd,unsigned long addr,unsigned long end, -const struct mm_walk *walk,void *private) +struct mm_walk *walk) {pud_t *pud;unsigned long next;
@@ -63,15 +63,15 @@ static int walk_pud_range(pgd_t *pgd, un next =pud_addr_end (addr, end);if (pud_none_or_clear_bad (pud)) {if (walk->pte_hole) - err = walk->pte_hole (addr, next, private); + err = walk->pte_hole (addr, next, walk);if (err)break ;continue ; }if (walk->pud_entry) - err = walk->pud_entry (pud, addr, next, private); + err = walk->pud_entry (pud, addr, next, walk);if (!err && (walk->pmd_entry || walk->pte_entry)) - err =walk_pmd_range (pud, addr, next, walk, private); + err =walk_pmd_range (pud, addr, next, walk);if (err)break ; }while (pud++, addr = next, addr != end);
@@ -85,15 +85,15 @@ static int walk_pud_range(pgd_t *pgd, un* @addr: starting address * @end: ending address * @walk: set of callbacks to invoke for each level of the tree -* @private: private data passed to the callback function * * Recursively walk the page table for the memory area in a VMA, * calling supplied callbacks. Callbacks are called in-order (first * PGD, first PUD, first PMD, first PTE, second PTE... second PMD, * etc.). If lower-level callbacks are omitted, walking depth is reduced. * -* Each callback receives an entry pointer, the start and end of the -* associated range, and a caller-supplied private data pointer. +* Each callback receives an entry pointer and the start and end of the +* associated range, and a copy of the original mm_walk for access to +* the ->private or ->mm fields. * * No locks are taken, but the bottom level iterator will map PTE * directories from highmem if necessary.
@@ -101,9 +101,8 @@ static int walk_pud_range(pgd_t *pgd, un* If any callback returns a non-zero value, the walk is aborted and * the return value is propagated back to the caller. Otherwise 0 is returned. */ -int walk_page_range (const struct mm_struct *mm, -unsigned long addr,unsigned long end, -const struct mm_walk *walk,void *private) +int walk_page_range (unsigned long addr,unsigned long end, +struct mm_walk *walk) {pgd_t *pgd;unsigned long next;
@@ -112,21 +111,24 @@ int walk_page_range(const struct mm_struif (addr >= end)return err; - pgd =pgd_offset (mm, addr); +if (!walk->mm) +return -EINVAL ; + + pgd =pgd_offset (walk->mm, addr);do { next =pgd_addr_end (addr, end);if (pgd_none_or_clear_bad (pgd)) {if (walk->pte_hole) - err = walk->pte_hole (addr, next, private); + err = walk->pte_hole (addr, next, walk);if (err)break ;continue ; }if (walk->pgd_entry) - err = walk->pgd_entry (pgd, addr, next, private); + err = walk->pgd_entry (pgd, addr, next, walk);if (!err && (walk->pud_entry || walk->pmd_entry || walk->pte_entry)) - err =walk_pud_range (pgd, addr, next, walk, private); + err =walk_pud_range (pgd, addr, next, walk);if (err)break ; }while (pgd++, addr = next, addr != end);
include/linux/mm.h renamed from include/linux/mm.h~pass-mm-into-pagewalkers +9 -8
@@ -760,16 +760,17 @@ unsigned long unmap_vmas(struct mmu_gath* (see walk_page_range for more details) */ struct mm_walk { -int (*pgd_entry)(pgd_t *,unsigned long ,unsigned long ,void *); -int (*pud_entry)(pud_t *,unsigned long ,unsigned long ,void *); -int (*pmd_entry)(pmd_t *,unsigned long ,unsigned long ,void *); -int (*pte_entry)(pte_t *,unsigned long ,unsigned long ,void *); -int (*pte_hole)(unsigned long ,unsigned long ,void *); +int (*pgd_entry)(pgd_t *,unsigned long ,unsigned long ,struct mm_walk *); +int (*pud_entry)(pud_t *,unsigned long ,unsigned long ,struct mm_walk *); +int (*pmd_entry)(pmd_t *,unsigned long ,unsigned long ,struct mm_walk *); +int (*pte_entry)(pte_t *,unsigned long ,unsigned long ,struct mm_walk *); +int (*pte_hole)(unsigned long ,unsigned long ,struct mm_walk *); +struct mm_struct *mm; +void *private; }; -int walk_page_range (const struct mm_struct *,unsigned long addr, -unsigned long end,const struct mm_walk *walk, -void *private); +int walk_page_range (unsigned long addr,unsigned long end, +struct mm_walk *walk);void free_pgd_range (struct mmu_gather **tlb,unsigned long addr,unsigned long end,unsigned long floor,unsigned long ceiling); void free_pgtables(struct mmu_gather **tlb, struct vm_area_struct *start_vma,
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~pass-mm-into-pagewalkers +25 -19
107 lines not on this page · raw message
_
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
RFC PATCH v2 2/2 fix large pages in pagemap
We were walking right into huge page areas in the pagemap walker, and calling the pmds pmd_bad() and clearing them.
That leaked huge pages. Bad.
This patch at least works around that for now. It ignores huge pages in the pagemap walker for the time being, and won't leak those pages.
---
linux-2.6.git-dave/fs/proc/task_mmu.c | 43 ++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-)
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +34 -9
@@ -563,24 +563,49 @@ static u64 swap_pte_to_pagemap_entry(ptereturn swp_type (e) | (swp_offset (e) <<MAX_SWAPFILES_SHIFT ); } +static unsigned long pte_to_pagemap_entry (pte_t pte) +{ +unsigned long pme =0 ; +if (is_swap_pte (pte)) + pme =PM_PFRAME (swap_pte_to_pagemap_entry (pte)) + |PM_PSHIFT (PAGE_SHIFT ) |PM_SWAP ; +else if (pte_present (pte)) + pme =PM_PFRAME (pte_pfn (pte)) + |PM_PSHIFT (PAGE_SHIFT ) |PM_PRESENT ; +return pme; +} +static int pagemap_pte_range (pmd_t *pmd,unsigned long addr,unsigned long end,struct mm_walk *walk) { +struct vm_area_struct *vma =find_vma (walk->mm, addr);struct pagemapread *pm = walk->private;pte_t *pte;int err =0 ;for (; addr != end; addr +=PAGE_SIZE ) {u64 pfn =PM_NOT_PRESENT ; - pte =pte_offset_map (pmd, addr); -if (is_swap_pte (*pte)) - pfn =PM_PFRAME (swap_pte_to_pagemap_entry (*pte)) - |PM_PSHIFT (PAGE_SHIFT ) |PM_SWAP ; -else if (pte_present (*pte)) - pfn =PM_PFRAME (pte_pfn (*pte)) - |PM_PSHIFT (PAGE_SHIFT ) |PM_PRESENT ; -/* unmap so we're not in atomic when we copy to userspace */ -pte_unmap (pte); + +/* +* Remember that find_vma() returns the +* first vma with a vm_end > addr, but +* has no guarantee about addr and +* vm_start. That means we'll always +* find a vma here, unless we're at +* an addr higher than the highest vma. +*/ +if (vma && (addr >= vma->vm_end)) + vma =find_vma (walk->mm, addr); +if (vma && (vma->vm_start <= addr) && + !is_vm_hugetlb_page (vma)) { + pte =pte_offset_map (pmd, addr); + pfn =pte_to_pagemap_entry (*pte); +/* +* unmap so we're not in atomic +* when we copy to userspace +*/ +pte_unmap (pte); + } err =add_to_pagemap (addr, pfn, pm);if (err)return err;
diff -puN mm/pagewalk.c~fix-large-pages-in-pagemap mm/pagewalk.c _
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [RFC v2][PATCH 2/2] fix large pages in pagemap
Quoted 52 lines · Dave Hansen
We were walking right into huge page areas in the pagemap walker, and calling the pmds
pmd_bad()and clearing them.That leaked huge pages. Bad.
This patch at least works around that for now. It ignores huge pages in the pagemap walker for the time being, and won't leak those pages.
Signed-off-by Dave Hansen <[email protected]>---
linux-2.6.git-dave/fs/proc/task_mmu.c | 43 ++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-)diff -puN
fs/proc/task_mmu.c~fix-large-pages-in-pagemapfs/proc/task_mmu.cfs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +13
-8@@ -563,24 +563,49 @@ static u64 swap_pte_to_pagemap_entry(ptereturn swp_type (e) | (swp_offset (e) <<MAX_SWAPFILES_SHIFT ); } +static unsigned long pte_to_pagemap_entry (pte_t pte) +{ +unsigned long pme =0 ; +if (is_swap_pte (pte)) + pme =PM_PFRAME (swap_pte_to_pagemap_entry (pte)) + |PM_PSHIFT (PAGE_SHIFT ) |PM_SWAP ; +else if (pte_present (pte)) + pme =PM_PFRAME (pte_pfn (pte)) + |PM_PSHIFT (PAGE_SHIFT ) |PM_PRESENT ; +return pme; +} +static int pagemap_pte_range (pmd_t *pmd,unsigned long addr,unsigned long end,struct mm_walk *walk) { +struct vm_area_struct *vma =find_vma (walk->mm, addr);struct pagemapread *pm = walk->private;pte_t *pte;int err =0 ;for (; addr != end; addr +=PAGE_SIZE ) {u64 pfn =PM_NOT_PRESENT ; - pte =pte_offset_map (pmd, addr); -if (is_swap_pte (*pte)) - pfn =PM_PFRAME (swap_pte_to_pagemap_entry (*pte)) - |PM_PSHIFT (PAGE_SHIFT ) |PM_SWAP ; -else if (pte_present (*pte)) - pfn =PM_PFRAME (pte_pfn (*pte)) - |PM_PSHIFT (PAGE_SHIFT ) |PM_PRESENT ; -/* unmap so we're not in atomic when we copy to userspace */
fs/proc/task_mmu.c static u64 swap_pte_to_pagemap_entry(pte · line 596
Quoted 10 lines
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +9
-1-pte_unmap (pte); + +/* +* Remember that find_vma() returns the +* first vma with a vm_end > addr, but +* has no guarantee about addr and +* vm_start. That means we'll always +* find a vma here, unless we're at +* an addr higher than the highest vma. +*/
I don't like this comment much - I had to read it several times to convince myself the code was correct. I think it should instead be three pieces and perhaps a new variable name, like this:
i>>? /* find the first VMA at or after our current address */
fs/proc/task_mmu.c static u64 swap_pte_to_pagemap_entry(pte · line 597
Quoted 1 line
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +1
-0+struct vm_area_struct *targetvma =find_vma (walk->mm, addr);
/* find next target VMA if we leave current one */
fs/proc/task_mmu.c static u64 swap_pte_to_pagemap_entry(pte · line 599
Quoted 2 lines
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +2
-0+if (targetvma && (addr >= targetvma->vm_end)) + targetvma =find_vma (walk->mm, addr);
/* if inside non-huge target VMA, map it */
fs/proc/task_mmu.c static u64 swap_pte_to_pagemap_entry(pte · line 607
Quoted 10 lines
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +9
-0+if (targetvma && (targetvma->vm_start <= addr) && + !is_vm_hugetlb_page (targetvma)) { + pte =pte_offset_map (pmd, addr); + pfn =pte_to_pagemap_entry (*pte); +/* +* unmap so we're not in atomic +* when we copy to userspace +*/ +pte_unmap (pte);
Also, might as well move the map/unmap inside the utility function if we're going to have one, no?
Otherwise, I'm liking this.
Signature · 7 lines
Mathematics is the supreme nostalgia of our time. -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
RFC PATCH v3 2/2 fix large pages in pagemap
Quoted 4 lines · Matt Mackall
Quoted 4 lines
(quoted diff) +4
-0+ /* + * Remember that find_vma() returns the + * first vma with a vm_end > addr, but + * has no guarantee about addr and
Quoted 8 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ * vm_start. That means we'll always + * find a vma here, unless we're at + * an addr higher than the highest vma. + */I don't like this comment much - I had to read it several times to convince myself the code was correct. I think it should instead be three pieces and perhaps a new variable name, like this:
I'll agree with you on that. :)
I think I'll keep the variable name the same, though. Adding 'target' doesn't really tell us anything. I can see if we had a couple of different vmas in there, but since there's only one, I think I'll just keep a simple name.
Quoted 10 lines
i>>? /* find the first VMA at or after our current address */
Review
Quoted 1 line
(quoted diff) +1
-0+ struct vm_area_struct *targetvma = find_vma(walk->mm, addr);/* find next target VMA if we leave current one */Review
Quoted 2 lines
(quoted diff) +2
-0+ if (targetvma && (addr >= targetvma->vm_end)) + targetvma = find_vma(walk->mm, addr);/* if inside non-huge target VMA, map it */Review
Quoted 2 lines
(quoted diff) +2
-0+ if (targetvma && (targetvma->vm_start <= addr) && + !is_vm_hugetlb_page(targetvma)) {
I've tried to incorporate the spirit of these comments. Let me know what you think.
Quoted 2 lines
Also, might as well move the map/unmap inside the utility function if we're going to have one, no?
Yeah, but then we have to pass 'pmd' and 'addr' in there, too. I guess I could do that, but this looked OK as is.
How does this look?
--
We were walking right into huge page areas in the pagemap walker, and calling the pmds pmd_bad() and clearing them.
That leaked huge pages. Bad.
This patch at least works around that for now. It ignores huge pages in the pagemap walker for the time being, and won't leak those pages.
---
linux-2.6.git-dave/fs/proc/task_mmu.c | 42 ++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-)
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +33 -9
@@ -563,24 +563,48 @@ static u64 swap_pte_to_pagemap_entry(ptereturn swp_type (e) | (swp_offset (e) <<MAX_SWAPFILES_SHIFT ); } +static unsigned long pte_to_pagemap_entry (pte_t pte) +{ +unsigned long pme =0 ; +if (is_swap_pte (pte)) + pme =PM_PFRAME (swap_pte_to_pagemap_entry (pte)) + |PM_PSHIFT (PAGE_SHIFT ) |PM_SWAP ; +else if (pte_present (pte)) + pme =PM_PFRAME (pte_pfn (pte)) + |PM_PSHIFT (PAGE_SHIFT ) |PM_PRESENT ; +return pme; +} +static int pagemap_pte_range (pmd_t *pmd,unsigned long addr,unsigned long end,struct mm_walk *walk) { +struct vm_area_struct *vma;struct pagemapread *pm = walk->private;pte_t *pte;int err =0 ; +/* find the first VMA at or above 'addr' */ + vma =find_vma (walk->mm, addr);for (; addr != end; addr +=PAGE_SIZE ) {u64 pfn =PM_NOT_PRESENT ; - pte =pte_offset_map (pmd, addr); -if (is_swap_pte (*pte)) - pfn =PM_PFRAME (swap_pte_to_pagemap_entry (*pte)) - |PM_PSHIFT (PAGE_SHIFT ) |PM_SWAP ; -else if (pte_present (*pte)) - pfn =PM_PFRAME (pte_pfn (*pte)) - |PM_PSHIFT (PAGE_SHIFT ) |PM_PRESENT ; -/* unmap so we're not in atomic when we copy to userspace */ -pte_unmap (pte); + +/* check to see if we've left 'vma' behind +* and need a new, higher one */ +if (vma && (addr >= vma->vm_end)) + vma =find_vma (walk->mm, addr); + +/* check that 'vma' actually covers this address, +* and that it isn't a huge page vma */ +if (vma && (vma->vm_start <= addr) && + !is_vm_hugetlb_page (vma)) { + pte =pte_offset_map (pmd, addr); + pfn =pte_to_pagemap_entry (*pte); +/* +* unmap so we're not in atomic +* when we copy to userspace +*/ +pte_unmap (pte); + } err =add_to_pagemap (addr, pfn, pm);if (err)return err;
_
-- Dave
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [RFC v3][PATCH 2/2] fix large pages in pagemap
Quoted 13 lines · Dave Hansen
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +12
-0+static unsigned long pte_to_pagemap_entry (pte_t pte) +{ +unsigned long pme =0 ; +if (is_swap_pte (pte)) + pme =PM_PFRAME (swap_pte_to_pagemap_entry (pte)) + |PM_PSHIFT (PAGE_SHIFT ) |PM_SWAP ; +else if (pte_present (pte)) + pme =PM_PFRAME (pte_pfn (pte)) + |PM_PSHIFT (PAGE_SHIFT ) |PM_PRESENT ; +return pme; +} +
fs/proc/task_mmu.c static u64 swap_pte_to_pagemap_entry(pte · line 587
Quoted 10 lines
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +3
-0static int pagemap_pte_range (pmd_t *pmd,unsigned long addr,unsigned long end,struct mm_walk *walk) { +struct vm_area_struct *vma;struct pagemapread *pm = walk->private;pte_t *pte;int err =0 ; +/* find the first VMA at or above 'addr' */ + vma =find_vma (walk->mm, addr);
Tab weirdness.
Quoted 18 lines
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +7
-9for (; addr != end; addr +=PAGE_SIZE ) {u64 pfn =PM_NOT_PRESENT ; - pte =pte_offset_map (pmd, addr); -if (is_swap_pte (*pte)) - pfn =PM_PFRAME (swap_pte_to_pagemap_entry (*pte)) - |PM_PSHIFT (PAGE_SHIFT ) |PM_SWAP ; -else if (pte_present (*pte)) - pfn =PM_PFRAME (pte_pfn (*pte)) - |PM_PSHIFT (PAGE_SHIFT ) |PM_PRESENT ; -/* unmap so we're not in atomic when we copy to userspace */ -pte_unmap (pte); + +/* check to see if we've left 'vma' behind +* and need a new, higher one */ +if (vma && (addr >= vma->vm_end)) + vma =find_vma (walk->mm, addr); + +/* check that 'vma' actually covers this address,
fs/proc/task_mmu.c static u64 swap_pte_to_pagemap_entry(pte · line 606
Quoted 10 lines
fs/proc/task_mmu.c renamed from fs/proc/task_mmu.c~fix-large-pages-in-pagemap +10
-0+* and that it isn't a huge page vma */ +if (vma && (vma->vm_start <= addr) && + !is_vm_hugetlb_page (vma)) { + pte =pte_offset_map (pmd, addr); + pfn =pte_to_pagemap_entry (*pte); +/* +* unmap so we're not in atomic +* when we copy to userspace +*/ +pte_unmap (pte);
This barely warranted a one line comment but now its four. But anyway, this and 1/2:
Thanks for getting back to this.
Signature · 7 lines
Mathematics is the supreme nostalgia of our time. -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>