mm: page_check_address bug fix and make it validate subpages in huge pages
Subsystems (4)
PATCH 1/2 mm: page_check_address bug fix and make it validate subpages in huge pages
There is a bug in __page_check_address() that may trigger a rare case of schedule in atomic on huge pages if CONFIG_HIGHPTE is enabled:
if a huge page is validated by this function, the returned pte_t * is actually a pmd_t* which is not mapped by kmap_atomic(), but will later be kunmap_atomic(). This may result in a false preempt count. This patch adds another parameter named "need_pte_unmap" to let it tell outside if this is a good huge page and should not be pte_unmap(). All callsites have been modified to use another new uniformed call:page_check_address_unmap_unlock(ptl, pte, need_pte_unmap), to finalize the page_check_address().
Another tiny bug in huge_pte_offset() is that when it was called in __page_check_address(), there is no good-reasoned guarantee that the "address" passed in is really mapped to a huge page even if PageHuge(page) is true. So it's too early to return a pmd without checking its _PAGE_PSE.
This patch also makes the page_check_address() can validate if a subpage is in its place in a huge page pointed by the address. This can be useful when ksm does not split huge pages when comparing the subpages one by one.
---
arch/x86/mm/hugetlbpage.c +2 -0
@@ -164,6 +164,8 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)if (pud_large (*pud))return (pte_t *)pud; pmd =pmd_offset (pud, addr); +if (!pmd_huge (*pmd)) + pmd =NULL ; } }return (pte_t *) pmd;
include/linux/rmap.h +21 -5
@@ -9,6 +9,7 @@#include <linux/mm.h> #include <linux/spinlock.h> #include <linux/memcontrol.h> +#include <linux/highmem.h> /* * The anon_vma heads a list of private "related" vmas, to scan if
@@ -208,20 +209,35 @@ int try_to_unmap_one(struct page *, struct vm_area_struct *,* Called from mm/filemap_xip.c to unmap empty zero page */ pte_t *__page_check_address (struct page *,struct mm_struct *, -unsigned long ,spinlock_t **,int ); +unsigned long ,spinlock_t **,int ,int *); -static inline pte_t *page_check_address (struct page *page,struct mm_struct *mm, -unsigned long address, -spinlock_t **ptlp,int sync) +static inline +pte_t *page_check_address (struct page *page,struct mm_struct *mm, +unsigned long address,spinlock_t **ptlp, +int sync,int *need_pte_unmap) {pte_t *ptep;__cond_lock (*ptlp, ptep =__page_check_address (page, mm, address, - ptlp, sync)); + ptlp, sync, + need_pte_unmap));return ptep; }/* +* After a successful page_check_address() call this is the way to finalize +*/ +static inline +void page_check_address_unmap_unlock (spinlock_t *ptl,pte_t *pte, +int need_pte_unmap) +{ +if (need_pte_unmap) +pte_unmap (pte); + +spin_unlock (ptl); +} + +/* * Used by swapoff to help locate where page is expected in vma. */ unsigned long page_address_in_vma (struct page *,struct vm_area_struct *);
mm/filemap_xip.c +4 -2
@@ -175,6 +175,7 @@ __xip_unmap (struct address_space * mapping,struct page *page;unsigned count;int locked =0 ; +int need_unmap; count =read_seqcount_begin (&xip_sparse_seq);
@@ -189,7 +190,8 @@ retry: address = vma->vm_start + ((pgoff - vma->vm_pgoff) <<PAGE_SHIFT );BUG_ON (address < vma->vm_start || address >= vma->vm_end); - pte =page_check_address (page, mm, address, &ptl,1 ); + pte =page_check_address (page, mm, address, &ptl,1 , + &need_pte_unmap);if (pte) {/* Nuke the page table entry. */ flush_cache_page (vma, address,pte_pfn (*pte));
@@ -197,7 +199,7 @@ retry:page_remove_rmap (page);dec_mm_counter (mm,MM_FILEPAGES );BUG_ON (pte_dirty (pteval)); -pte_unmap_unlock (pte, ptl); +page_check_address_unmap_unlock (ptl, pte, need_unmap);page_cache_release (page); } }
mm/rmap.c +45 -16
158 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/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>