int huge_ptep_set_access_flags(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, pte_t pte, int dirty) { int ncontig, i, changed = 0; size_t pgsize = 0; unsigned long pfn = pte_pfn(pte), dpfn; pgprot_t hugeprot; pte_t orig_pte; if (!pte_cont(pte)) return ptep_set_access_flags(vma, addr, ptep, pte, dirty); ncontig = find_num_contig(vma->vm_mm, addr, ptep, &pgsize); dpfn = pgsize >> PAGE_SHIFT; orig_pte = get_clear_flush(vma->vm_mm, addr, ptep, pgsize, ncontig); if (!pte_same(orig_pte, pte)) changed = 1; /* Make sure we don't lose the dirty state */ if (pte_dirty(orig_pte)) pte = pte_mkdirty(pte); hugeprot = pte_pgprot(pte); for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) set_pte_at(vma->vm_mm, addr, ptep, pfn_pte(pfn, hugeprot)); return changed; }
void huge_ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { unsigned long pfn, dpfn; pgprot_t hugeprot; int ncontig, i; size_t pgsize; pte_t pte; if (!pte_cont(READ_ONCE(*ptep))) { ptep_set_wrprotect(mm, addr, ptep); return; } ncontig = find_num_contig(mm, addr, ptep, &pgsize); dpfn = pgsize >> PAGE_SHIFT; pte = get_clear_flush(mm, addr, ptep, pgsize, ncontig); pte = pte_wrprotect(pte); hugeprot = pte_pgprot(pte); pfn = pte_pfn(pte); for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); }
void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte) { size_t pgsize; int i; int ncontig; unsigned long pfn, dpfn; pgprot_t hugeprot; /* * Code needs to be expanded to handle huge swap and migration * entries. Needed for HUGETLB and MEMORY_FAILURE. */ WARN_ON(!pte_present(pte)); if (!pte_cont(pte)) { set_pte_at(mm, addr, ptep, pte); return; } ncontig = find_num_contig(mm, addr, ptep, &pgsize); pfn = pte_pfn(pte); dpfn = pgsize >> PAGE_SHIFT; hugeprot = pte_pgprot(pte); clear_flush(mm, addr, ptep, pgsize, ncontig); for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn) set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot)); }
static int find_num_contig(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte, size_t *pgsize) { pgd_t *pgd = pgd_offset(mm, addr); pud_t *pud; pmd_t *pmd; *pgsize = PAGE_SIZE; if (!pte_cont(pte)) return 1; if (!pgd_present(*pgd)) { VM_BUG_ON(!pgd_present(*pgd)); return 1; } pud = pud_offset(pgd, addr); if (!pud_present(*pud)) { VM_BUG_ON(!pud_present(*pud)); return 1; } pmd = pmd_offset(pud, addr); if (!pmd_present(*pmd)) { VM_BUG_ON(!pmd_present(*pmd)); return 1; } if ((pte_t *)pmd == ptep) { *pgsize = PMD_SIZE; return CONT_PMDS; } return CONT_PTES; }
pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { pte_t pte; if (pte_cont(*ptep)) { int ncontig, i; size_t pgsize; pte_t *cpte; bool is_dirty = false; cpte = huge_pte_offset(mm, addr); ncontig = find_num_contig(mm, addr, cpte, *cpte, &pgsize); /* save the 1st pte to return */ pte = ptep_get_and_clear(mm, addr, cpte); for (i = 1; i < ncontig; ++i) { /* * If HW_AFDBM is enabled, then the HW could * turn on the dirty bit for any of the page * in the set, so check them all. */ ++cpte; if (pte_dirty(ptep_get_and_clear(mm, addr, cpte))) is_dirty = true; } if (is_dirty) return pte_mkdirty(pte); else return pte; } else { return ptep_get_and_clear(mm, addr, ptep); } }
void huge_ptep_clear_flush(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) { size_t pgsize; int ncontig; if (!pte_cont(READ_ONCE(*ptep))) { ptep_clear_flush(vma, addr, ptep); return; } ncontig = find_num_contig(vma->vm_mm, addr, ptep, &pgsize); clear_flush(vma->vm_mm, addr, ptep, pgsize, ncontig); }
pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { int ncontig; size_t pgsize; pte_t orig_pte = huge_ptep_get(ptep); if (!pte_cont(orig_pte)) return ptep_get_and_clear(mm, addr, ptep); ncontig = find_num_contig(mm, addr, ptep, &pgsize); return get_clear_flush(mm, addr, ptep, pgsize, ncontig); }
pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) { pgd_t *pgd; pud_t *pud; pmd_t *pmd = NULL; pte_t *pte = NULL; pgd = pgd_offset(mm, addr); pr_debug("%s: addr:0x%lx pgd:%p\n", __func__, addr, pgd); if (!pgd_present(*pgd)) return NULL; pud = pud_offset(pgd, addr); if (!pud_present(*pud)) return NULL; if (pud_huge(*pud)) return (pte_t *)pud; pmd = pmd_offset(pud, addr); if (!pmd_present(*pmd)) return NULL; if (pte_cont(pmd_pte(*pmd))) { pmd = pmd_offset( pud, (addr & CONT_PMD_MASK)); return (pte_t *)pmd; } if (pmd_huge(*pmd)) return (pte_t *)pmd; pte = pte_offset_kernel(pmd, addr); if (pte_present(*pte) && pte_cont(*pte)) { pte = pte_offset_kernel( pmd, (addr & CONT_PTE_MASK)); return pte; } return NULL; }