Skip to content

Commit

Permalink
KVM: Inject asynchronous page fault into a PV guest if page is swappe…
Browse files Browse the repository at this point in the history
…d out.

Send async page fault to a PV guest if it accesses swapped out memory.
Guest will choose another task to run upon receiving the fault.

Allow async page fault injection only when guest is in user mode since
otherwise guest may be in non-sleepable context and will not be able
to reschedule.

Vcpu will be halted if guest will fault on the same page again or if
vcpu executes kernel code.

Acked-by: Rik van Riel <[email protected]>
Signed-off-by: Gleb Natapov <[email protected]>
Signed-off-by: Marcelo Tosatti <[email protected]>
  • Loading branch information
Gleb Natapov authored and avikivity committed Jan 12, 2011
1 parent 631bc48 commit 7c90705
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 12 deletions.
3 changes: 3 additions & 0 deletions arch/x86/include/asm/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ struct kvm_vcpu_arch {
gfn_t gfns[roundup_pow_of_two(ASYNC_PF_PER_VCPU)];
struct gfn_to_hva_cache data;
u64 msr_val;
u32 id;
} apf;
};

Expand Down Expand Up @@ -596,6 +597,7 @@ struct kvm_x86_ops {
};

struct kvm_arch_async_pf {
u32 token;
gfn_t gfn;
};

Expand Down Expand Up @@ -819,6 +821,7 @@ void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
struct kvm_async_pf *work);
void kvm_arch_async_page_ready(struct kvm_vcpu *vcpu,
struct kvm_async_pf *work);
bool kvm_arch_can_inject_async_page_present(struct kvm_vcpu *vcpu);
extern bool kvm_find_async_pf_gfn(struct kvm_vcpu *vcpu, gfn_t gfn);

#endif /* _ASM_X86_KVM_HOST_H */
1 change: 1 addition & 0 deletions arch/x86/kvm/mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -2592,6 +2592,7 @@ static int nonpaging_page_fault(struct kvm_vcpu *vcpu, gva_t gva,
int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn)
{
struct kvm_arch_async_pf arch;
arch.token = (vcpu->arch.apf.id++ << 12) | vcpu->vcpu_id;
arch.gfn = gfn;

return kvm_setup_async_pf(vcpu, gva, gfn, &arch);
Expand Down
43 changes: 38 additions & 5 deletions arch/x86/kvm/x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -6248,20 +6248,53 @@ static void kvm_del_async_pf_gfn(struct kvm_vcpu *vcpu, gfn_t gfn)
}
}

static int apf_put_user(struct kvm_vcpu *vcpu, u32 val)
{

return kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.apf.data, &val,
sizeof(val));
}

void kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
struct kvm_async_pf *work)
{
trace_kvm_async_pf_not_present(work->gva);

kvm_make_request(KVM_REQ_APF_HALT, vcpu);
trace_kvm_async_pf_not_present(work->arch.token, work->gva);
kvm_add_async_pf_gfn(vcpu, work->arch.gfn);

if (!(vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED) ||
kvm_x86_ops->get_cpl(vcpu) == 0)
kvm_make_request(KVM_REQ_APF_HALT, vcpu);
else if (!apf_put_user(vcpu, KVM_PV_REASON_PAGE_NOT_PRESENT)) {
vcpu->arch.fault.error_code = 0;
vcpu->arch.fault.address = work->arch.token;
kvm_inject_page_fault(vcpu);
}
}

void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
struct kvm_async_pf *work)
{
trace_kvm_async_pf_ready(work->gva);
kvm_del_async_pf_gfn(vcpu, work->arch.gfn);
trace_kvm_async_pf_ready(work->arch.token, work->gva);
if (is_error_page(work->page))
work->arch.token = ~0; /* broadcast wakeup */
else
kvm_del_async_pf_gfn(vcpu, work->arch.gfn);

if ((vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED) &&
!apf_put_user(vcpu, KVM_PV_REASON_PAGE_READY)) {
vcpu->arch.fault.error_code = 0;
vcpu->arch.fault.address = work->arch.token;
kvm_inject_page_fault(vcpu);
}
}

bool kvm_arch_can_inject_async_page_present(struct kvm_vcpu *vcpu)
{
if (!(vcpu->arch.apf.msr_val & KVM_ASYNC_PF_ENABLED))
return true;
else
return !kvm_event_needs_reinjection(vcpu) &&
kvm_x86_ops->interrupt_allowed(vcpu);
}

EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit);
Expand Down
17 changes: 11 additions & 6 deletions include/trace/events/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,34 +204,39 @@ TRACE_EVENT(

TRACE_EVENT(
kvm_async_pf_not_present,
TP_PROTO(u64 gva),
TP_ARGS(gva),
TP_PROTO(u64 token, u64 gva),
TP_ARGS(token, gva),

TP_STRUCT__entry(
__field(__u64, token)
__field(__u64, gva)
),

TP_fast_assign(
__entry->token = token;
__entry->gva = gva;
),

TP_printk("gva %#llx not present", __entry->gva)
TP_printk("token %#llx gva %#llx not present", __entry->token,
__entry->gva)
);

TRACE_EVENT(
kvm_async_pf_ready,
TP_PROTO(u64 gva),
TP_ARGS(gva),
TP_PROTO(u64 token, u64 gva),
TP_ARGS(token, gva),

TP_STRUCT__entry(
__field(__u64, token)
__field(__u64, gva)
),

TP_fast_assign(
__entry->token = token;
__entry->gva = gva;
),

TP_printk("gva %#llx ready", __entry->gva)
TP_printk("token %#llx gva %#llx ready", __entry->token, __entry->gva)
);

TRACE_EVENT(
Expand Down
3 changes: 2 additions & 1 deletion virt/kvm/async_pf.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu)
{
struct kvm_async_pf *work;

if (list_empty_careful(&vcpu->async_pf.done))
if (list_empty_careful(&vcpu->async_pf.done) ||
!kvm_arch_can_inject_async_page_present(vcpu))
return;

spin_lock(&vcpu->async_pf.lock);
Expand Down

0 comments on commit 7c90705

Please sign in to comment.