Re: cr3 OOS optimisation breaks 32-bit GNU/kFreeBSD guest

Marcelo Tosatti
2009-04-04T17:02:23Z

On Sat, Apr 04, 2009 at 01:37:39PM +0300, Avi Kivity wrote:
>> diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
>> index 09782a9..728be72 100644
>> --- a/arch/x86/kvm/paging_tmpl.h
>> +++ b/arch/x86/kvm/paging_tmpl.h
>> @@ -308,8 +308,14 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
>> break;
>> }
>> - if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep))
>> + if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep)) {
>> + if (level-1 == PT_PAGE_TABLE_LEVEL) {
>> + shadow_page = page_header(__pa(sptep));
>> + if (shadow_page->unsync && shadow_page->global)
>> + kvm_sync_page(vcpu, shadow_page);
>> + }
>> continue;
>> + }
>> if (is_large_pte(*sptep)) {
>> rmap_remove(vcpu->kvm, sptep);
>>
>
> But here the shadow page is already linked? Isn't the root cause that
> an invlpg was called when the page wasn't linked, so it wasn't seen by
> invlpg?
>
> So I thought the best place would be in fetch(), after
> kvm_mmu_get_page(). If we're linking a page which contains global ptes,
> they might be unsynced due to invlpgs that we've missed.
>
> Or am I missing something about the root cause?

The problem is when the page is unreachable due to a higher level path
being unlinked. Say:

level 4 -> level 3 . level 2 -> level 1 (global unsync)

The dot there means level 3 is not linked to level 2, so invlpg can't
reach the global unsync at level 1.

kvm_mmu_get_page does sync pages when it finds them, so the code is
already safe for the "linking a page which contains global ptes" case
you mention above.