From: Isaku Yamahata <isaku.yamahata@xxxxxxxxx> To test error_remove_page() method of KVM gmem, add a new ioctl to inject memory failure based on offset of guest memfd. Signed-off-by: Isaku Yamahata <isaku.yamahata@xxxxxxxxx> --- include/uapi/linux/kvm.h | 6 ++++ virt/kvm/guest_mem.c | 68 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 65fc983af840..4160614bcc0f 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -2323,4 +2323,10 @@ struct kvm_create_guest_memfd { __u64 reserved[6]; }; +#define KVM_GUEST_MEMORY_FAILURE _IOWR(KVMIO, 0xd5, struct kvm_guest_memory_failure) + +struct kvm_guest_memory_failure { + __u64 offset; +}; + #endif /* __LINUX_KVM_H */ diff --git a/virt/kvm/guest_mem.c b/virt/kvm/guest_mem.c index 01fb4ca861d0..bc9dae50004b 100644 --- a/virt/kvm/guest_mem.c +++ b/virt/kvm/guest_mem.c @@ -291,10 +291,78 @@ static struct file *kvm_gmem_get_file(struct kvm_memory_slot *slot) return file; } +static int kvm_gmem_inject_failure(struct file *file, + struct kvm_guest_memory_failure *mf) +{ + struct inode *inode = file_inode(file); + struct address_space *mapping = inode->i_mapping; + pgoff_t index = mf->offset >> PAGE_SHIFT; + struct folio *folio; + unsigned long pfn; + int err = 0; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + filemap_invalidate_lock_shared(mapping); + + /* Don't allocate page. */ + folio = filemap_get_folio(mapping, index); + if (!folio) { + err = -ENOENT; + goto out; + } + if (IS_ERR(folio)) { + err = PTR_ERR(folio); + goto out; + } + + pfn = folio_pfn(folio) + (index - folio_index(folio)); + folio_put(folio); + +out: + filemap_invalidate_unlock_shared(mapping); + if (err) + return err; + + /* + * Race with pfn: memory_failure() and unpoison_memory() gain invalidate + * lock as the error recovery logic tries to remove pages from + * mapping. + */ + if (!pfn_valid(pfn)) + return -ENXIO; + return memory_failure(pfn, MF_SW_SIMULATED); +} + +static long kvm_gmem_ioctl(struct file *file, unsigned int ioctl, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int r = -EINVAL; + + switch (ioctl) { + case KVM_GUEST_MEMORY_FAILURE: { + struct kvm_guest_memory_failure mf; + + r = -EFAULT; + if (copy_from_user(&mf, argp, sizeof(mf))) + break; + r = kvm_gmem_inject_failure(file, &mf); + break; + } + default: + break; + } + + return r; +} + static const struct file_operations kvm_gmem_fops = { .open = generic_file_open, .release = kvm_gmem_release, .fallocate = kvm_gmem_fallocate, + .unlocked_ioctl = kvm_gmem_ioctl, }; static int kvm_gmem_migrate_folio(struct address_space *mapping, -- 2.25.1