Commit 531f5200 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

Merge branch 'mm-delete-change-gpte' into HEAD



The .change_pte() MMU notifier callback was intended as an optimization
and for this reason it was initially called without a surrounding
mmu_notifier_invalidate_range_{start,end}() pair.  It was only ever
implemented by KVM (which was also the original user of MMU notifiers)
and the rules on when to call set_pte_at_notify() rather than set_pte_at()
have always been pretty obscure.

It may seem a miracle that it has never caused any hard to trigger
bugs, but there's a good reason for that: KVM's implementation has
been nonfunctional for a good part of its existence.  Already in
2012, commit 6bdb913f ("mm: wrap calls to set_pte_at_notify with
invalidate_range_start and invalidate_range_end", 2012-10-09) changed the
.change_pte() callback to occur within an invalidate_range_start/end()
pair; and because KVM unmaps the sPTEs during .invalidate_range_start(),
.change_pte() has no hope of finding a sPTE to change.

Therefore, all the code for .change_pte() can be removed from both KVM
and mm/, and set_pte_at_notify() can be replaced with just set_pte_at().

Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parents 9bc60f73 f7842747
Loading
Loading
Loading
Loading
+0 −34
Original line number Diff line number Diff line
@@ -1768,40 +1768,6 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
	return false;
}

bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
{
	kvm_pfn_t pfn = pte_pfn(range->arg.pte);

	if (!kvm->arch.mmu.pgt)
		return false;

	WARN_ON(range->end - range->start != 1);

	/*
	 * If the page isn't tagged, defer to user_mem_abort() for sanitising
	 * the MTE tags. The S2 pte should have been unmapped by
	 * mmu_notifier_invalidate_range_end().
	 */
	if (kvm_has_mte(kvm) && !page_mte_tagged(pfn_to_page(pfn)))
		return false;

	/*
	 * We've moved a page around, probably through CoW, so let's treat
	 * it just like a translation fault and the map handler will clean
	 * the cache to the PoC.
	 *
	 * The MMU notifiers will have unmapped a huge PMD before calling
	 * ->change_pte() (which in turn calls kvm_set_spte_gfn()) and
	 * therefore we never need to clear out a huge PMD through this
	 * calling path and a memcache is not required.
	 */
	kvm_pgtable_stage2_map(kvm->arch.mmu.pgt, range->start << PAGE_SHIFT,
			       PAGE_SIZE, __pfn_to_phys(pfn),
			       KVM_PGTABLE_PROT_R, NULL, 0);

	return false;
}

bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
{
	u64 size = (range->end - range->start) << PAGE_SHIFT;
+0 −1
Original line number Diff line number Diff line
@@ -203,7 +203,6 @@ void kvm_flush_tlb_all(void);
void kvm_flush_tlb_gpa(struct kvm_vcpu *vcpu, unsigned long gpa);
int kvm_handle_mm_fault(struct kvm_vcpu *vcpu, unsigned long badv, bool write);

void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte);
int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end, bool blockable);
int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end);
int kvm_test_age_hva(struct kvm *kvm, unsigned long hva);
+0 −32
Original line number Diff line number Diff line
@@ -494,38 +494,6 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
			range->end << PAGE_SHIFT, &ctx);
}

bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
{
	unsigned long prot_bits;
	kvm_pte_t *ptep;
	kvm_pfn_t pfn = pte_pfn(range->arg.pte);
	gpa_t gpa = range->start << PAGE_SHIFT;

	ptep = kvm_populate_gpa(kvm, NULL, gpa, 0);
	if (!ptep)
		return false;

	/* Replacing an absent or old page doesn't need flushes */
	if (!kvm_pte_present(NULL, ptep) || !kvm_pte_young(*ptep)) {
		kvm_set_pte(ptep, 0);
		return false;
	}

	/* Fill new pte if write protected or page migrated */
	prot_bits = _PAGE_PRESENT | __READABLE;
	prot_bits |= _CACHE_MASK & pte_val(range->arg.pte);

	/*
	 * Set _PAGE_WRITE or _PAGE_DIRTY iff old and new pte both support
	 * _PAGE_WRITE for map_page_fast if next page write fault
	 * _PAGE_DIRTY since gpa has already recorded as dirty page
	 */
	prot_bits |= __WRITEABLE & *ptep & pte_val(range->arg.pte);
	kvm_set_pte(ptep, kvm_pfn_pte(pfn, __pgprot(prot_bits)));

	return true;
}

bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
{
	kvm_ptw_ctx ctx;
+0 −30
Original line number Diff line number Diff line
@@ -444,36 +444,6 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
	return true;
}

bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
{
	gpa_t gpa = range->start << PAGE_SHIFT;
	pte_t hva_pte = range->arg.pte;
	pte_t *gpa_pte = kvm_mips_pte_for_gpa(kvm, NULL, gpa);
	pte_t old_pte;

	if (!gpa_pte)
		return false;

	/* Mapping may need adjusting depending on memslot flags */
	old_pte = *gpa_pte;
	if (range->slot->flags & KVM_MEM_LOG_DIRTY_PAGES && !pte_dirty(old_pte))
		hva_pte = pte_mkclean(hva_pte);
	else if (range->slot->flags & KVM_MEM_READONLY)
		hva_pte = pte_wrprotect(hva_pte);

	set_pte(gpa_pte, hva_pte);

	/* Replacing an absent or old page doesn't need flushes */
	if (!pte_present(old_pte) || !pte_young(old_pte))
		return false;

	/* Pages swapped, aged, moved, or cleaned require flushes */
	return !pte_present(hva_pte) ||
	       !pte_young(hva_pte) ||
	       pte_pfn(old_pte) != pte_pfn(hva_pte) ||
	       (pte_dirty(old_pte) && !pte_dirty(hva_pte));
}

bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
{
	return kvm_mips_mkold_gpa_pt(kvm, range->start, range->end);
+0 −1
Original line number Diff line number Diff line
@@ -287,7 +287,6 @@ struct kvmppc_ops {
	bool (*unmap_gfn_range)(struct kvm *kvm, struct kvm_gfn_range *range);
	bool (*age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
	bool (*test_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
	bool (*set_spte_gfn)(struct kvm *kvm, struct kvm_gfn_range *range);
	void (*free_memslot)(struct kvm_memory_slot *slot);
	int (*init_vm)(struct kvm *kvm);
	void (*destroy_vm)(struct kvm *kvm);
Loading