[RFC PATCH 2/6] KVM: guestmem_fd: Make error_remove_page callback to unmap guest memory

From: isaku . yamahata
Date: Wed Sep 13 2023 - 06:49:20 EST


From: Isaku Yamahata <isaku.yamahata@xxxxxxxxx>

Implement error_remove_page inode method for KVM gmem. Update struct
kvm_gfn_range to indicate unmapping gufs because page is poisoned.

Signed-off-by: Isaku Yamahata <isaku.yamahata@xxxxxxxxx>
---
include/linux/kvm_host.h | 2 ++
virt/kvm/guest_mem.c | 47 +++++++++++++++++++++++++++-------------
2 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 091bc89ae805..e81a7123c84f 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -266,8 +266,10 @@ struct kvm_gfn_range {
pte_t pte;
unsigned long attributes;
u64 raw;
+ struct page *page;
} arg;
bool may_block;
+ bool memory_error;
};
bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range);
bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range);
diff --git a/virt/kvm/guest_mem.c b/virt/kvm/guest_mem.c
index 35d8f03e7937..746e683df589 100644
--- a/virt/kvm/guest_mem.c
+++ b/virt/kvm/guest_mem.c
@@ -305,7 +305,7 @@ static int kvm_gmem_error_page(struct address_space *mapping, struct page *page)
struct kvm_gmem *gmem;
unsigned long index;
pgoff_t start, end;
- gfn_t gfn;
+ bool flush;

if (!IS_ENABLED(CONFIG_HAVE_GENERIC_PRIVATE_MEM_HANDLE_ERROR))
return MF_IGNORED;
@@ -316,26 +316,43 @@ static int kvm_gmem_error_page(struct address_space *mapping, struct page *page)
end = start + thp_nr_pages(page);

list_for_each_entry(gmem, gmem_list, entry) {
+ struct kvm *kvm = gmem->kvm;
+
+ KVM_MMU_LOCK(kvm);
+ kvm_mmu_invalidate_begin(kvm);
+ KVM_MMU_UNLOCK(kvm);
+
+ flush = false;
xa_for_each_range(&gmem->bindings, index, slot, start, end - 1) {
- for (gfn = start; gfn < end; gfn++) {
- if (WARN_ON_ONCE(gfn < slot->base_gfn ||
- gfn >= slot->base_gfn + slot->npages))
- continue;
-
- /*
- * FIXME: Tell userspace that the *private*
- * memory encountered an error.
- */
- send_sig_mceerr(BUS_MCEERR_AR,
- (void __user *)gfn_to_hva_memslot(slot, gfn),
- PAGE_SHIFT, current);
- }
+ pgoff_t pgoff;
+
+ if (WARN_ON_ONCE(end < slot->base_gfn ||
+ start >= slot->base_gfn + slot->npages))
+ continue;
+
+ pgoff = slot->gmem.pgoff;
+ struct kvm_gfn_range gfn_range = {
+ .slot = slot,
+ .start = slot->base_gfn + max(pgoff, start) - pgoff,
+ .end = slot->base_gfn + min(pgoff + slot->npages, end) - pgoff,
+ .arg.page = page,
+ .may_block = true,
+ .memory_error = true,
+ };
+
+ flush |= kvm_mmu_unmap_gfn_range(kvm, &gfn_range);
}
+ if (flush)
+ kvm_flush_remote_tlbs(kvm);
+
+ KVM_MMU_LOCK(kvm);
+ kvm_mmu_invalidate_end(kvm);
+ KVM_MMU_UNLOCK(kvm);
}

filemap_invalidate_unlock_shared(mapping);

- return 0;
+ return MF_DELAYED;
}

static const struct address_space_operations kvm_gmem_aops = {
--
2.25.1