Correct hash flushing from huge_ptep_set_wrprotect()

As Andy Whitcroft recently pointed out, the current powerpc version of
huge_ptep_set_wrprotect() has a bug.  It just calls ptep_set_wrprotect()
which in turn calls pte_update() then hpte_need_flush() with the 'huge'
argument set to 0.  This will cause hpte_need_flush() to flush the wrong
hash entries (of any).  Andy's fix for this is already in the powerpc
tree as commit 016b33c495.

I have confirmed this is a real bug, not masked by some other
synchronization, with a new testcase for libhugetlbfs.  A process write
a (MAP_PRIVATE) hugepage mapping, fork(), then alter the mapping and
have the child incorrectly see the second write.

Therefore, this should be fixed for 2.6.26, and for the stable tree.
Here is a suitable patch for 2.6.26, which I think will also be suitable
for the stable tree (neither of the headers in question has been changed
much recently).

It is cut down slighlty from Andy's original version, in that it does
not include a 32-bit version of huge_ptep_set_wrprotect().  Currently,
hugepages are not supported on any 32-bit powerpc platform.  When they
are, a suitable 32-bit version can be added - the only 32-bit hardware
which supports hugepages does not use the conventional hashtable MMU and
so will have different needs anyway.

Signed-off-by: Andy Whitcroft <apw@shadowen.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
David Gibson 2008-07-08 15:58:16 +10:00 коммит произвёл Linus Torvalds
Родитель 7b51ba38d9
Коммит 86df864249
2 изменённых файлов: 10 добавлений и 6 удалений

Просмотреть файл

@ -49,12 +49,6 @@ static inline pte_t huge_pte_wrprotect(pte_t pte)
return pte_wrprotect(pte); return pte_wrprotect(pte);
} }
static inline void huge_ptep_set_wrprotect(struct mm_struct *mm,
unsigned long addr, pte_t *ptep)
{
ptep_set_wrprotect(mm, addr, ptep);
}
static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep, unsigned long addr, pte_t *ptep,
pte_t pte, int dirty) pte_t pte, int dirty)

Просмотреть файл

@ -314,6 +314,16 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr,
old = pte_update(mm, addr, ptep, _PAGE_RW, 0); old = pte_update(mm, addr, ptep, _PAGE_RW, 0);
} }
static inline void huge_ptep_set_wrprotect(struct mm_struct *mm,
unsigned long addr, pte_t *ptep)
{
unsigned long old;
if ((pte_val(*ptep) & _PAGE_RW) == 0)
return;
old = pte_update(mm, addr, ptep, _PAGE_RW, 1);
}
/* /*
* We currently remove entries from the hashtable regardless of whether * We currently remove entries from the hashtable regardless of whether
* the entry was young or dirty. The generic routines only flush if the * the entry was young or dirty. The generic routines only flush if the