Small hugetlb arch updates for i386 and x86_64
Subsystems (2)
Hi Andrew. The following 3 patches update the i386 and x86_64 hugetlb arch code to bring it closer to the other architectures. The first patch adds a pte_huge() macro. The second patch moves the "stale pte" check into huge_pte_alloc() which seems more appropriate to me. The third patch checks for p?d_present() for each step in huge_pte_offset().
Barring any new objections, could we take these for a spin in -mm?
(Actual patches to follow)
Re: [Hugetlb x86] 1/3 Add pte_huge() macro
Fixed whitespace issue in asm-x86_64/pgtable.h
Initial Post (Wed, 17 Aug 2005)
This patch adds a macro pte_huge(pte) for i386/x86_64 which is needed by a patch later in the series. Instead of repeating (_PAGE_PRESENT | _PAGE_PSE), I've added __LARGE_PTE to i386 to match x86_64.
Diffed against 2.6.13-rc6-git7
--- asm-i386/pgtable.h | 4 +++- asm-x86_64/pgtable.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-)
include/asm-i386/pgtable.h +3 -1
@@ -215,11 +215,13 @@ extern unsigned long pg0[];* The following only work if pte_present() is true. * Undefined behaviour if not.. */ +#define __LARGE_PTE (_PAGE_PSE | _PAGE_PRESENT)static inline int pte_user (pte_t pte) {return (pte).pte_low & _PAGE_USER; }static inline int pte_read (pte_t pte) {return (pte).pte_low & _PAGE_USER; }static inline int pte_dirty (pte_t pte) {return (pte).pte_low & _PAGE_DIRTY; }static inline int pte_young (pte_t pte) {return (pte).pte_low & _PAGE_ACCESSED; }static inline int pte_write (pte_t pte) {return (pte).pte_low & _PAGE_RW; } +static inline int pte_huge (pte_t pte) {return ((pte).pte_low & __LARGE_PTE) == __LARGE_PTE; }/* * The following only works if pte_present() is not true.
@@ -236,7 +238,7 @@ static inline pte_t pte_mkexec(pte_t ptestatic inline pte_t pte_mkdirty (pte_t pte) { (pte).pte_low |= _PAGE_DIRTY;return pte; }static inline pte_t pte_mkyoung (pte_t pte) { (pte).pte_low |= _PAGE_ACCESSED;return pte; }static inline pte_t pte_mkwrite (pte_t pte) { (pte).pte_low |= _PAGE_RW;return pte; } -static inline pte_t pte_mkhuge (pte_t pte) { (pte).pte_low |= _PAGE_PRESENT | _PAGE_PSE;return pte; } +static inline pte_t pte_mkhuge (pte_t pte) { (pte).pte_low |= __LARGE_PTE;return pte; }#ifdef CONFIG_X86_PAE # include <asm/pgtable-3level.h>
include/asm-x86_64/pgtable.h +2 -1
@@ -247,6 +247,7 @@ static inline pte_t pfn_pte(unsigned lon* The following only work if pte_present() is true. * Undefined behaviour if not.. */ +#define __LARGE_PTE (_PAGE_PSE|_PAGE_PRESENT)static inline int pte_user (pte_t pte) {return pte_val (pte) & _PAGE_USER; }extern inline int pte_read (pte_t pte) {return pte_val (pte) & _PAGE_USER; }extern inline int pte_exec (pte_t pte) {return pte_val (pte) & _PAGE_USER; }
@@ -254,8 +255,8 @@ extern inline int pte_dirty(pte_t pte)extern inline int pte_young (pte_t pte) {return pte_val (pte) & _PAGE_ACCESSED; }extern inline int pte_write (pte_t pte) {return pte_val (pte) & _PAGE_RW; }static inline int pte_file (pte_t pte) {return pte_val (pte) & _PAGE_FILE; } +static inline int pte_huge (pte_t pte) {return (pte_val (pte) & __LARGE_PTE) == __LARGE_PTE; } -#define __LARGE_PTE (_PAGE_PSE|_PAGE_PRESENT)extern inline pte_t pte_rdprotect (pte_t pte) {set_pte (&pte,__pte (pte_val (pte) & ~_PAGE_USER));return pte; }extern inline pte_t pte_exprotect (pte_t pte) {set_pte (&pte,__pte (pte_val (pte) & ~_PAGE_USER));return pte; }extern inline pte_t pte_mkclean (pte_t pte) {set_pte (&pte,__pte (pte_val (pte) & ~_PAGE_DIRTY));return pte; }
Re: [Hugetlb x86] 2/3 Move stale pte check into huge_pte_alloc()
In reply to Adam LitkeInitial Post (Wed, 17 Aug 2005)
This patch moves the
if (! pte_none(*pte)) hugetlb_clean_stale_pgtable(pte);
logic into huge_pte_alloc() so all of its callers can be immune to the bug described by Kenneth Chen at http://lkml.org/lkml/2004/6/16/246
Quoted 6 lines
It turns out there is a bug in
hugetlb_prefault(): with 3 level page table,huge_pte_alloc()might return a pmd that points to a PTE page. It happens if the virtual address for hugetlb mmap is recycled from previously used normal page mmap.free_pgtables()might not scrub the pmd entry on munmap andhugetlb_prefaultskips on any pmd presence regardless what type it is.
Unless I am missing something, it seems more correct to place the check inside huge_pte_alloc() to prevent a the same bug wherever a huge pte is allocated. It also allows checking for this condition when lazily faulting huge pages later in the series.
Diffed against 2.6.13-rc6
--- arch/i386/mm/hugetlbpage.c | 13 +++++++++++-- mm/hugetlb.c | 2 -- 2 files changed, 11 insertions(+), 4 deletions(-)
arch/i386/mm/hugetlbpage.c +11 -2
@@ -22,12 +22,21 @@ pte_t *huge_pte_alloc(struct mm_struct * {pgd_t *pgd;pud_t *pud; -pmd_t *pmd =NULL ; +pmd_t *pmd; +pte_t *pte =NULL ; pgd =pgd_offset (mm, addr); pud =pud_alloc (mm, pgd, addr); pmd =pmd_alloc (mm, pud, addr); -return (pte_t *) pmd; + +if (!pmd) + goto out; + + pte = (pte_t *) pmd; +if (!pte_none (*pte) && !pte_huge (*pte)) +hugetlb_clean_stale_pgtable (pte); +out: +return pte; }pte_t *huge_pte_offset (struct mm_struct *mm,unsigned long addr)
mm/hugetlb.c +0 -2
@@ -360,8 +360,6 @@ int hugetlb_prefault(struct address_spac ret = -ENOMEM ; goto out; } -if (!pte_none (*pte)) -hugetlb_clean_stale_pgtable (pte); idx = ((addr - vma->vm_start) >>HPAGE_SHIFT ) + (vma->vm_pgoff >> (HPAGE_SHIFT -PAGE_SHIFT ));
Re: [Hugetlb x86] 3/3 Check p?d_present in huge_pte_offset()
In reply to Adam LitkeInitial Post (Wed, 17 Aug 2005)
For demand faulting, we cannot assume that the page tables will be populated. Do what the rest of the architectures do and test p?d_present() while walking down the page table.
Diffed against 2.6.13-rc6
--- hugetlbpage.c | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-)
arch/i386/mm/hugetlbpage.c +5 -2
@@ -46,8 +46,11 @@ pte_t *huge_pte_offset(struct mm_structpmd_t *pmd =NULL ; pgd =pgd_offset (mm, addr); - pud =pud_offset (pgd, addr); - pmd =pmd_offset (pud, addr); +if (pgd_present (*pgd)) { + pud =pud_offset (pgd, addr); +if (pud_present (*pud)) + pmd =pmd_offset (pud, addr); + }return (pte_t *) pmd; }
PATCH RESEND 1/1 1/3 Add pte_huge() macro
In reply to Adam LitkeFixed whitespace issue in asm-x86_64/pgtable.h
Initial Post (Wed, 17 Aug 2005)
This patch adds a macro pte_huge(pte) for i386/x86_64 which is needed by a patch later in the series. Instead of repeating (_PAGE_PRESENT | _PAGE_PSE), I've added __LARGE_PTE to i386 to match x86_64.
Diffed against 2.6.13-rc6-git7
--- asm-i386/pgtable.h | 4 +++- asm-x86_64/pgtable.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-)
include/asm-i386/pgtable.h +3 -1
@@ -215,11 +215,13 @@ extern unsigned long pg0[];* The following only work if pte_present() is true. * Undefined behaviour if not.. */ +#define __LARGE_PTE (_PAGE_PSE | _PAGE_PRESENT)static inline int pte_user (pte_t pte) {return (pte).pte_low & _PAGE_USER; }static inline int pte_read (pte_t pte) {return (pte).pte_low & _PAGE_USER; }static inline int pte_dirty (pte_t pte) {return (pte).pte_low & _PAGE_DIRTY; }static inline int pte_young (pte_t pte) {return (pte).pte_low & _PAGE_ACCESSED; }static inline int pte_write (pte_t pte) {return (pte).pte_low & _PAGE_RW; } +static inline int pte_huge (pte_t pte) {return ((pte).pte_low & __LARGE_PTE) == __LARGE_PTE; }/* * The following only works if pte_present() is not true.
@@ -236,7 +238,7 @@ static inline pte_t pte_mkexec(pte_t ptestatic inline pte_t pte_mkdirty (pte_t pte) { (pte).pte_low |= _PAGE_DIRTY;return pte; }static inline pte_t pte_mkyoung (pte_t pte) { (pte).pte_low |= _PAGE_ACCESSED;return pte; }static inline pte_t pte_mkwrite (pte_t pte) { (pte).pte_low |= _PAGE_RW;return pte; } -static inline pte_t pte_mkhuge (pte_t pte) { (pte).pte_low |= _PAGE_PRESENT | _PAGE_PSE;return pte; } +static inline pte_t pte_mkhuge (pte_t pte) { (pte).pte_low |= __LARGE_PTE;return pte; }#ifdef CONFIG_X86_PAE # include <asm/pgtable-3level.h>
include/asm-x86_64/pgtable.h +2 -1
@@ -247,6 +247,7 @@ static inline pte_t pfn_pte(unsigned lon* The following only work if pte_present() is true. * Undefined behaviour if not.. */ +#define __LARGE_PTE (_PAGE_PSE|_PAGE_PRESENT)static inline int pte_user (pte_t pte) {return pte_val (pte) & _PAGE_USER; }extern inline int pte_read (pte_t pte) {return pte_val (pte) & _PAGE_USER; }extern inline int pte_exec (pte_t pte) {return pte_val (pte) & _PAGE_USER; }
@@ -254,8 +255,8 @@ extern inline int pte_dirty(pte_t pte)extern inline int pte_young (pte_t pte) {return pte_val (pte) & _PAGE_ACCESSED; }extern inline int pte_write (pte_t pte) {return pte_val (pte) & _PAGE_RW; }static inline int pte_file (pte_t pte) {return pte_val (pte) & _PAGE_FILE; } +static inline int pte_huge (pte_t pte) {return (pte_val (pte) & __LARGE_PTE) == __LARGE_PTE; } -#define __LARGE_PTE (_PAGE_PSE|_PAGE_PRESENT)extern inline pte_t pte_rdprotect (pte_t pte) {set_pte (&pte,__pte (pte_val (pte) & ~_PAGE_USER));return pte; }extern inline pte_t pte_exprotect (pte_t pte) {set_pte (&pte,__pte (pte_val (pte) & ~_PAGE_USER));return pte; }extern inline pte_t pte_mkclean (pte_t pte) {set_pte (&pte,__pte (pte_val (pte) & ~_PAGE_DIRTY));return pte; }
PATCH RESEND 1/1 2/3 Move stale pte check into huge_pte_alloc()
In reply to Adam LitkeInitial Post (Wed, 17 Aug 2005)
This patch moves the
if (! pte_none(*pte)) hugetlb_clean_stale_pgtable(pte);
logic into huge_pte_alloc() so all of its callers can be immune to the bug described by Kenneth Chen at http://lkml.org/lkml/2004/6/16/246
Quoted 6 lines
It turns out there is a bug in
hugetlb_prefault(): with 3 level page table,huge_pte_alloc()might return a pmd that points to a PTE page. It happens if the virtual address for hugetlb mmap is recycled from previously used normal page mmap.free_pgtables()might not scrub the pmd entry on munmap andhugetlb_prefaultskips on any pmd presence regardless what type it is.
Unless I am missing something, it seems more correct to place the check inside huge_pte_alloc() to prevent a the same bug wherever a huge pte is allocated. It also allows checking for this condition when lazily faulting huge pages later in the series.
Diffed against 2.6.13-rc6
--- arch/i386/mm/hugetlbpage.c | 13 +++++++++++-- mm/hugetlb.c | 2 -- 2 files changed, 11 insertions(+), 4 deletions(-)
arch/i386/mm/hugetlbpage.c +11 -2
@@ -22,12 +22,21 @@ pte_t *huge_pte_alloc(struct mm_struct * {pgd_t *pgd;pud_t *pud; -pmd_t *pmd =NULL ; +pmd_t *pmd; +pte_t *pte =NULL ; pgd =pgd_offset (mm, addr); pud =pud_alloc (mm, pgd, addr); pmd =pmd_alloc (mm, pud, addr); -return (pte_t *) pmd; + +if (!pmd) + goto out; + + pte = (pte_t *) pmd; +if (!pte_none (*pte) && !pte_huge (*pte)) +hugetlb_clean_stale_pgtable (pte); +out: +return pte; }pte_t *huge_pte_offset (struct mm_struct *mm,unsigned long addr)
mm/hugetlb.c +0 -2
@@ -360,8 +360,6 @@ int hugetlb_prefault(struct address_spac ret = -ENOMEM ; goto out; } -if (!pte_none (*pte)) -hugetlb_clean_stale_pgtable (pte); idx = ((addr - vma->vm_start) >>HPAGE_SHIFT ) + (vma->vm_pgoff >> (HPAGE_SHIFT -PAGE_SHIFT ));
PATCH RESEND 1/1 3/3 Check p?d_present in huge_pte_offset()
In reply to Adam LitkeInitial Post (Wed, 17 Aug 2005)
For demand faulting, we cannot assume that the page tables will be populated. Do what the rest of the architectures do and test p?d_present() while walking down the page table.
Diffed against 2.6.13-rc6
--- hugetlbpage.c | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-)
arch/i386/mm/hugetlbpage.c +5 -2
@@ -46,8 +46,11 @@ pte_t *huge_pte_offset(struct mm_structpmd_t *pmd =NULL ; pgd =pgd_offset (mm, addr); - pud =pud_offset (pgd, addr); - pmd =pmd_offset (pud, addr); +if (pgd_present (*pgd)) { + pud =pud_offset (pgd, addr); +if (pud_present (*pud)) + pmd =pmd_offset (pud, addr); + }return (pte_t *) pmd; }