Skip to content

Commit cd7548a

Browse files
hnaztorvalds
authored andcommitted
thp: mprotect: transparent huge page support
Natively handle huge pmds when changing page tables on behalf of mprotect(). I left out update_mmu_cache() because we do not need it on x86 anyway but more importantly the interface works on ptes, not pmds. Signed-off-by: Johannes Weiner <[email protected]> Signed-off-by: Andrea Arcangeli <[email protected]> Reviewed-by: Rik van Riel <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent b36f5b0 commit cd7548a

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

include/linux/huge_mm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ extern int zap_huge_pmd(struct mmu_gather *tlb,
2222
extern int mincore_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
2323
unsigned long addr, unsigned long end,
2424
unsigned char *vec);
25+
extern int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
26+
unsigned long addr, pgprot_t newprot);
2527

2628
enum transparent_hugepage_flag {
2729
TRANSPARENT_HUGEPAGE_FLAG,

mm/huge_memory.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,33 @@ int mincore_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
948948
return ret;
949949
}
950950

951+
int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
952+
unsigned long addr, pgprot_t newprot)
953+
{
954+
struct mm_struct *mm = vma->vm_mm;
955+
int ret = 0;
956+
957+
spin_lock(&mm->page_table_lock);
958+
if (likely(pmd_trans_huge(*pmd))) {
959+
if (unlikely(pmd_trans_splitting(*pmd))) {
960+
spin_unlock(&mm->page_table_lock);
961+
wait_split_huge_page(vma->anon_vma, pmd);
962+
} else {
963+
pmd_t entry;
964+
965+
entry = pmdp_get_and_clear(mm, addr, pmd);
966+
entry = pmd_modify(entry, newprot);
967+
set_pmd_at(mm, addr, pmd, entry);
968+
spin_unlock(&vma->vm_mm->page_table_lock);
969+
flush_tlb_range(vma, addr, addr + HPAGE_PMD_SIZE);
970+
ret = 1;
971+
}
972+
} else
973+
spin_unlock(&vma->vm_mm->page_table_lock);
974+
975+
return ret;
976+
}
977+
951978
pmd_t *page_check_address_pmd(struct page *page,
952979
struct mm_struct *mm,
953980
unsigned long address,

mm/mprotect.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,13 @@ static inline void change_pmd_range(struct vm_area_struct *vma, pud_t *pud,
8888
pmd = pmd_offset(pud, addr);
8989
do {
9090
next = pmd_addr_end(addr, end);
91-
split_huge_page_pmd(vma->vm_mm, pmd);
91+
if (pmd_trans_huge(*pmd)) {
92+
if (next - addr != HPAGE_PMD_SIZE)
93+
split_huge_page_pmd(vma->vm_mm, pmd);
94+
else if (change_huge_pmd(vma, pmd, addr, newprot))
95+
continue;
96+
/* fall through */
97+
}
9298
if (pmd_none_or_clear_bad(pmd))
9399
continue;
94100
change_pte_range(vma->vm_mm, pmd, addr, next, newprot,

0 commit comments

Comments
 (0)