@@ -676,6 +676,7 @@ struct kvm_clock_data {
#define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2)
/* Available with KVM_CAP_PPC_GET_PVINFO */
#define KVM_PPC_GET_PVINFO _IOW(KVMIO, 0xa1, struct kvm_ppc_pvinfo)
+#define KVM_UNPOISON_ADDRESS _IO(KVMIO, 0xa2)
/*
* ioctls for vcpu fds
@@ -1512,6 +1512,14 @@ extern int sysctl_memory_failure_recover
extern void shake_page(struct page *p, int access);
extern atomic_long_t mce_bad_pages;
extern int soft_offline_page(struct page *page, int flags);
+#ifdef CONFIG_MEMORY_FAILURE
+int unpoison_address(unsigned long addr);
+#else
+static inline int unpoison_address(unsigned long addr)
+{
+ return -EINVAL;
+}
+#endif
extern void dump_page(struct page *page);
@@ -1433,3 +1433,42 @@ done:
/* keep elevated page count for bad page */
return ret;
}
+
+int unpoison_address(unsigned long addr)
+{
+ struct mm_struct *mm;
+ pgd_t *pgdp;
+ pud_t pud, *pudp;
+ pmd_t pmd, *pmdp;
+ pte_t pte, *ptep;
+ spinlock_t *ptl;
+ swp_entry_t entry;
+ int rc;
+
+ mm = current->mm;
+ pgdp = pgd_offset(mm, addr);
+ if (!pgd_present(*pgdp))
+ return -EINVAL;
+ pudp = pud_offset(pgdp, addr);
+ pud = *pudp;
+ if (!pud_present(pud) || pud_large(pud))
+ return -EINVAL;
+ pmdp = pmd_offset(pudp, addr);
+ pmd = *pmdp;
+ /* can not unpoison huge page yet */
+ if (!pmd_present(pmd) || pmd_large(pmd))
+ return -EINVAL;
+ ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl);
+ pte = *ptep;
+ rc = -EINVAL;
+ if (!is_swap_pte(pte))
+ goto out;
+ entry = pte_to_swp_entry(pte);
+ if (!is_hwpoison_entry(entry))
+ goto out;
+ pte_clear(mm, addr, ptep);
+out:
+ pte_unmap_unlock(ptep, ptl);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(unpoison_address);
@@ -774,6 +774,17 @@ int kvm_vm_ioctl_set_memory_region(struc
return kvm_set_memory_region(kvm, mem, user_alloc);
}
+static int kvm_unpoison_address(struct kvm *kvm, unsigned long address)
+{
+ int r;
+
+ down_read(¤t->mm->mmap_sem);
+ r = unpoison_address(address);
+ up_read(¤t->mm->mmap_sem);
+
+ return r;
+}
+
int kvm_get_dirty_log(struct kvm *kvm,
struct kvm_dirty_log *log, int *is_dirty)
{
@@ -1728,6 +1739,9 @@ static long kvm_vm_ioctl(struct file *fi
mutex_unlock(&kvm->lock);
break;
#endif
+ case KVM_UNPOISON_ADDRESS:
+ r = kvm_unpoison_address(kvm, arg);
+ break;
default:
r = kvm_arch_vm_ioctl(filp, ioctl, arg);
if (r == -ENOTTY)