#include <linux/kvm_types.h>
#include <linux/kvm_host.h>
#include "gmap.h"
#include "trace.h"
#include "faultin.h"
bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu);
int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fault *f)
{
struct kvm_s390_mmu_cache *local_mc __free(kvm_s390_mmu_cache) = NULL;
struct kvm_s390_mmu_cache *mc = NULL;
struct kvm_memory_slot *slot;
unsigned long inv_seq;
int foll, rc = 0;
foll = f->write_attempt ? FOLL_WRITE : 0;
foll |= f->attempt_pfault ? FOLL_NOWAIT : 0;
if (vcpu) {
kvm = vcpu->kvm;
mc = vcpu->arch.mc;
}
lockdep_assert_held(&kvm->srcu);
scoped_guard(read_lock, &kvm->mmu_lock) {
if (gmap_try_fixup_minor(kvm->arch.gmap, f) == 0)
return 0;
}
while (1) {
f->valid = false;
inv_seq = kvm->mmu_invalidate_seq;
smp_rmb();
if (vcpu)
slot = kvm_vcpu_gfn_to_memslot(vcpu, f->gfn);
else
slot = gfn_to_memslot(kvm, f->gfn);
f->pfn = __kvm_faultin_pfn(slot, f->gfn, foll, &f->writable, &f->page);
if (f->pfn == KVM_PFN_ERR_NEEDS_IO) {
if (unlikely(!f->attempt_pfault))
return -EAGAIN;
if (unlikely(!vcpu))
return -EINVAL;
trace_kvm_s390_major_guest_pfault(vcpu);
if (kvm_arch_setup_async_pf(vcpu))
return 0;
vcpu->stat.pfault_sync++;
foll &= ~FOLL_NOWAIT;
f->pfn = __kvm_faultin_pfn(slot, f->gfn, foll, &f->writable, &f->page);
}
if (is_noslot_pfn(f->pfn))
return PGM_ADDRESSING;
if (f->pfn == KVM_PFN_ERR_SIGPENDING)
return -EAGAIN;
if (f->pfn == KVM_PFN_ERR_RO_FAULT)
return -EOPNOTSUPP;
if (is_error_pfn(f->pfn))
return -EFAULT;
if (!mc) {
local_mc = kvm_s390_new_mmu_cache();
if (!local_mc)
return -ENOMEM;
mc = local_mc;
}
if (mmu_invalidate_retry_gfn_unsafe(kvm, inv_seq, f->gfn)) {
kvm_release_faultin_page(kvm, f->page, true, false);
continue;
}
scoped_guard(read_lock, &kvm->mmu_lock) {
if (!mmu_invalidate_retry_gfn(kvm, inv_seq, f->gfn)) {
f->valid = true;
rc = gmap_link(mc, kvm->arch.gmap, f);
kvm_release_faultin_page(kvm, f->page, !!rc, f->write_attempt);
f->page = NULL;
}
}
kvm_release_faultin_page(kvm, f->page, true, false);
if (rc == -ENOMEM) {
rc = kvm_s390_mmu_cache_topup(mc);
if (rc)
return rc;
} else if (rc != -EAGAIN) {
return rc;
}
}
}
int kvm_s390_get_guest_page(struct kvm *kvm, struct guest_fault *f, gfn_t gfn, bool w)
{
struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
int foll = w ? FOLL_WRITE : 0;
f->write_attempt = w;
f->gfn = gfn;
f->pfn = __kvm_faultin_pfn(slot, gfn, foll, &f->writable, &f->page);
if (is_noslot_pfn(f->pfn))
return PGM_ADDRESSING;
if (is_sigpending_pfn(f->pfn))
return -EINTR;
if (f->pfn == KVM_PFN_ERR_NEEDS_IO)
return -EAGAIN;
if (is_error_pfn(f->pfn))
return -EFAULT;
f->valid = true;
return 0;
}