Commit 96c78807 authored by Wang Yuanheng's avatar Wang Yuanheng Committed by guzitao
Browse files

sw64: mm: fix PFN of PMDs for 512M hugepage

Sunway inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IB73UR



--------------------------------

A 512M hugepage is composed of 64 contiguous 8M hugepages. When
setting up the page table entries, it assigned the PFN of first
8M hugepage to all the 64 contiguous PMDs and set the HPFN bit.
This caused incorrect page in gup_huge_pmd() because of PMD_MASK,
and resulted in incorrect address access in some scenarios when
512M hugepages were used.

This patch fixes the page table setup to ensure that each PMD's
PFN corresponds to an individual 8M hugepage.

For kvm, although the original code works fine, this patch makes
similar modifications to Additional Page Table(APT) to maintain
consistency in the 512M hugepage setup method.

Signed-off-by: default avatarWang Yuanheng <wangyuanheng@wxiat.com>
Reviewed-by: default avatarHe Sheng <hesheng@wxiat.com>
Signed-off-by: default avatarGu Zitao <guzitao@wxiat.com>
parent 3d116b4f
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -956,10 +956,11 @@ static int apt_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,


static int apt_set_pmd_huge(struct kvm *kvm, struct kvm_mmu_memory_cache
		*cache, phys_addr_t addr, const pmd_t *new_pmd, unsigned long sz)
		*cache, phys_addr_t addr, pmd_t *new_pmd, unsigned long sz)
{
	pmd_t *pmd, old_pmd, *ori_pmd;
	int i;
	unsigned long dpfn;
retry:
	pmd = apt_get_pmd(kvm, cache, addr, sz);
	VM_BUG_ON(!pmd);
@@ -1022,8 +1023,11 @@ static int apt_set_pmd_huge(struct kvm *kvm, struct kvm_mmu_memory_cache

	/* Do we need WRITE_ONCE(pmd, new_pmd)? */
	if (sz == CONT_PMD_SIZE) {
		for (i = 0; i < CONT_PMDS; i++, ori_pmd++)
		dpfn = 1UL << (_PFN_SHIFT + PMD_SHIFT - PAGE_SHIFT);
		for (i = 0; i < CONT_PMDS; i++, ori_pmd++) {
			set_pmd(ori_pmd, *new_pmd);
			new_pmd->pmd += dpfn;
		}
	} else
		set_pmd(pmd, *new_pmd);
	return 0;
+9 −6
Original line number Diff line number Diff line
@@ -215,7 +215,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
	size_t pgsize;
	int i;
	int ncontig;
	unsigned long pfn;
	unsigned long pfn, dpfn;
	pgprot_t hugeprot;

	/*
@@ -231,11 +231,12 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,

	ncontig = num_contig_ptes(sz, &pgsize);
	pfn = pte_pfn(pte);
	dpfn = PMD_SIZE >> PAGE_SHIFT;
	hugeprot = pte_pgprot(pte);

	clear_flush(mm, addr, ptep, pgsize, ncontig);

	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize)
	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
		set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
}

@@ -254,7 +255,7 @@ void set_huge_swap_pte_at(struct mm_struct *mm, unsigned long addr,
void huge_ptep_set_wrprotect(struct mm_struct *mm,
		unsigned long addr, pte_t *ptep)
{
	unsigned long pfn;
	unsigned long pfn, dpfn;
	pgprot_t hugeprot;
	int ncontig, i;
	size_t pgsize;
@@ -266,6 +267,7 @@ void huge_ptep_set_wrprotect(struct mm_struct *mm,
	}

	ncontig = CONT_PMDS;
	dpfn = PMD_SIZE >> PAGE_SHIFT;

	pte = get_and_clear(mm, addr, ptep, pgsize, ncontig);
	pte = pte_wrprotect(pte);
@@ -273,7 +275,7 @@ void huge_ptep_set_wrprotect(struct mm_struct *mm,
	hugeprot = pte_pgprot(pte);
	pfn = pte_pfn(pte);

	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize)
	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
		set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
}

@@ -332,7 +334,7 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma,
{
	int ncontig, i;
	size_t pgsize = 0;
	unsigned long pfn = pte_pfn(pte);
	unsigned long pfn = pte_pfn(pte), dpfn;
	pgprot_t hugeprot;
	pte_t orig_pte;

@@ -340,6 +342,7 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma,
		return ptep_set_access_flags(vma, addr, ptep, pte, dirty);

	ncontig = CONT_PMDS;
	dpfn = PMD_SIZE >> PAGE_SHIFT;

	if (!__cont_access_flags_changed(ptep, pte, ncontig))
		return 0;
@@ -355,7 +358,7 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma,
		pte = pte_mkyoung(pte);

	hugeprot = pte_pgprot(pte);
	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize)
	for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
		set_pte_at(vma->vm_mm, addr, ptep, pfn_pte(pfn, hugeprot));

	return 1;