diff mbox series

[2/2] kvm: Use huge pages for DAX-backed files

Message ID 20181109203921.178363-3-brho@google.com (mailing list archive)
State Superseded
Headers show
Series kvm: Use huge pages for DAX-backed files | expand

Commit Message

Barret Rhoden Nov. 9, 2018, 8:39 p.m. UTC
This change allows KVM to map DAX-backed files made of huge pages with
huge mappings in the EPT/TDP.

DAX pages are not PageTransCompound.  The existing check is trying to
determine if the mapping for the pfn is a huge mapping or not.  For
non-DAX maps, e.g. hugetlbfs, that means checking PageTransCompound.
For DAX, we can check the page table itself.

Note that KVM already faulted in the page (or huge page) in the host's
page table, and we hold the KVM mmu spinlock (grabbed before checking
the mmu seq).

Signed-off-by: Barret Rhoden <brho@google.com>
---
 arch/x86/kvm/mmu.c | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

Comments

Paolo Bonzini Nov. 12, 2018, 7:31 p.m. UTC | #1
On 09/11/2018 21:39, Barret Rhoden wrote:
> This change allows KVM to map DAX-backed files made of huge pages with
> huge mappings in the EPT/TDP.
> 
> DAX pages are not PageTransCompound.  The existing check is trying to
> determine if the mapping for the pfn is a huge mapping or not.  For
> non-DAX maps, e.g. hugetlbfs, that means checking PageTransCompound.
> For DAX, we can check the page table itself.
> 
> Note that KVM already faulted in the page (or huge page) in the host's
> page table, and we hold the KVM mmu spinlock (grabbed before checking
> the mmu seq).
> 
> Signed-off-by: Barret Rhoden <brho@google.com>
> ---
>  arch/x86/kvm/mmu.c | 34 ++++++++++++++++++++++++++++++++--
>  1 file changed, 32 insertions(+), 2 deletions(-)

Looks good.  What's the plan for removing PageReserved from DAX pages?

Thanks,

Paolo

> diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
> index cf5f572f2305..2df8c459dc6a 100644
> --- a/arch/x86/kvm/mmu.c
> +++ b/arch/x86/kvm/mmu.c
> @@ -3152,6 +3152,36 @@ static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, kvm_pfn_t pfn)
>  	return -EFAULT;
>  }
>  
> +static bool pfn_is_huge_mapped(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn)
> +{
> +	struct page *page = pfn_to_page(pfn);
> +	unsigned long hva, map_shift;
> +
> +	if (!is_zone_device_page(page))
> +		return PageTransCompoundMap(page);
> +
> +	/*
> +	 * DAX pages do not use compound pages.  The page should have already
> +	 * been mapped into the host-side page table during try_async_pf(), so
> +	 * we can check the page tables directly.
> +	 */
> +	hva = gfn_to_hva(kvm, gfn);
> +	if (kvm_is_error_hva(hva))
> +		return false;
> +
> +	/*
> +	 * Our caller grabbed the KVM mmu_lock with a successful
> +	 * mmu_notifier_retry, so we're safe to walk the page table.
> +	 */
> +	map_shift = dev_pagemap_mapping_shift(hva, current->mm);
> +	switch (map_shift) {
> +	case PMD_SHIFT:
> +	case PUD_SIZE:
> +		return true;
> +	}
> +	return false;
> +}
> +
>  static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
>  					gfn_t *gfnp, kvm_pfn_t *pfnp,
>  					int *levelp)
> @@ -3168,7 +3198,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
>  	 */
>  	if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
>  	    level == PT_PAGE_TABLE_LEVEL &&
> -	    PageTransCompoundMap(pfn_to_page(pfn)) &&
> +	    pfn_is_huge_mapped(vcpu->kvm, gfn, pfn) &&
>  	    !mmu_gfn_lpage_is_disallowed(vcpu, gfn, PT_DIRECTORY_LEVEL)) {
>  		unsigned long mask;
>  		/*
> @@ -5678,7 +5708,7 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
>  		 */
>  		if (sp->role.direct &&
>  			!kvm_is_reserved_pfn(pfn) &&
> -			PageTransCompoundMap(pfn_to_page(pfn))) {
> +			pfn_is_huge_mapped(kvm, sp->gfn, pfn)) {
>  			pte_list_remove(rmap_head, sptep);
>  			need_tlb_flush = 1;
>  			goto restart;
>
David Hildenbrand Nov. 13, 2018, 9:36 a.m. UTC | #2
On 09.11.18 21:39, Barret Rhoden wrote:
> This change allows KVM to map DAX-backed files made of huge pages with
> huge mappings in the EPT/TDP.
> 
> DAX pages are not PageTransCompound.  The existing check is trying to
> determine if the mapping for the pfn is a huge mapping or not.  For
> non-DAX maps, e.g. hugetlbfs, that means checking PageTransCompound.
> For DAX, we can check the page table itself.
> 
> Note that KVM already faulted in the page (or huge page) in the host's
> page table, and we hold the KVM mmu spinlock (grabbed before checking
> the mmu seq).

I wonder if the KVM mmu spinlock is enough for walking (not KVM
exclusive) host page tables. Can you elaborate?

> 
> Signed-off-by: Barret Rhoden <brho@google.com>
> ---
>  arch/x86/kvm/mmu.c | 34 ++++++++++++++++++++++++++++++++--
>  1 file changed, 32 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
> index cf5f572f2305..2df8c459dc6a 100644
> --- a/arch/x86/kvm/mmu.c
> +++ b/arch/x86/kvm/mmu.c
> @@ -3152,6 +3152,36 @@ static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, kvm_pfn_t pfn)
>  	return -EFAULT;
>  }
>  
> +static bool pfn_is_huge_mapped(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn)
> +{
> +	struct page *page = pfn_to_page(pfn);
> +	unsigned long hva, map_shift;
> +
> +	if (!is_zone_device_page(page))
> +		return PageTransCompoundMap(page);
> +
> +	/*
> +	 * DAX pages do not use compound pages.  The page should have already
> +	 * been mapped into the host-side page table during try_async_pf(), so
> +	 * we can check the page tables directly.
> +	 */
> +	hva = gfn_to_hva(kvm, gfn);
> +	if (kvm_is_error_hva(hva))
> +		return false;
> +
> +	/*
> +	 * Our caller grabbed the KVM mmu_lock with a successful
> +	 * mmu_notifier_retry, so we're safe to walk the page table.
> +	 */
> +	map_shift = dev_pagemap_mapping_shift(hva, current->mm);

You could get rid of that local variable map_shift.

> +	switch (map_shift) {
> +	case PMD_SHIFT:
> +	case PUD_SIZE:
> +		return true;
> +	}
> +	return false;
> +}
> +
>  static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
>  					gfn_t *gfnp, kvm_pfn_t *pfnp,
>  					int *levelp)
> @@ -3168,7 +3198,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
>  	 */
>  	if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
>  	    level == PT_PAGE_TABLE_LEVEL &&
> -	    PageTransCompoundMap(pfn_to_page(pfn)) &&
> +	    pfn_is_huge_mapped(vcpu->kvm, gfn, pfn) &&
>  	    !mmu_gfn_lpage_is_disallowed(vcpu, gfn, PT_DIRECTORY_LEVEL)) {
>  		unsigned long mask;
>  		/*
> @@ -5678,7 +5708,7 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
>  		 */
>  		if (sp->role.direct &&
>  			!kvm_is_reserved_pfn(pfn) &&
> -			PageTransCompoundMap(pfn_to_page(pfn))) {
> +			pfn_is_huge_mapped(kvm, sp->gfn, pfn)) {
>  			pte_list_remove(rmap_head, sptep);
>  			need_tlb_flush = 1;
>  			goto restart;
> 

This looks surprisingly simple to me :)
Pankaj Gupta Nov. 13, 2018, 10:02 a.m. UTC | #3
> 
> On 09.11.18 21:39, Barret Rhoden wrote:
> > This change allows KVM to map DAX-backed files made of huge pages with
> > huge mappings in the EPT/TDP.
> > 
> > DAX pages are not PageTransCompound.  The existing check is trying to
> > determine if the mapping for the pfn is a huge mapping or not.  For
> > non-DAX maps, e.g. hugetlbfs, that means checking PageTransCompound.
> > For DAX, we can check the page table itself.
> > 
> > Note that KVM already faulted in the page (or huge page) in the host's
> > page table, and we hold the KVM mmu spinlock (grabbed before checking
> > the mmu seq).
> 
> I wonder if the KVM mmu spinlock is enough for walking (not KVM
> exclusive) host page tables. Can you elaborate?

As this patch is dependent on PageReserved patch(which is in progress), just 
wondering if we are able to test the code path for hugepage with DAX.

Thanks,
Pankaj 
 
> 
> > 
> > Signed-off-by: Barret Rhoden <brho@google.com>
> > ---
> >  arch/x86/kvm/mmu.c | 34 ++++++++++++++++++++++++++++++++--
> >  1 file changed, 32 insertions(+), 2 deletions(-)
> > 
> > diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
> > index cf5f572f2305..2df8c459dc6a 100644
> > --- a/arch/x86/kvm/mmu.c
> > +++ b/arch/x86/kvm/mmu.c
> > @@ -3152,6 +3152,36 @@ static int kvm_handle_bad_page(struct kvm_vcpu
> > *vcpu, gfn_t gfn, kvm_pfn_t pfn)
> >  	return -EFAULT;
> >  }
> >  
> > +static bool pfn_is_huge_mapped(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn)
> > +{
> > +	struct page *page = pfn_to_page(pfn);
> > +	unsigned long hva, map_shift;
> > +
> > +	if (!is_zone_device_page(page))
> > +		return PageTransCompoundMap(page);
> > +
> > +	/*
> > +	 * DAX pages do not use compound pages.  The page should have already
> > +	 * been mapped into the host-side page table during try_async_pf(), so
> > +	 * we can check the page tables directly.
> > +	 */
> > +	hva = gfn_to_hva(kvm, gfn);
> > +	if (kvm_is_error_hva(hva))
> > +		return false;
> > +
> > +	/*
> > +	 * Our caller grabbed the KVM mmu_lock with a successful
> > +	 * mmu_notifier_retry, so we're safe to walk the page table.
> > +	 */
> > +	map_shift = dev_pagemap_mapping_shift(hva, current->mm);
> 
> You could get rid of that local variable map_shift.
> 
> > +	switch (map_shift) {
> > +	case PMD_SHIFT:
> > +	case PUD_SIZE:
> > +		return true;
> > +	}
> > +	return false;
> > +}
> > +
> >  static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
> >  					gfn_t *gfnp, kvm_pfn_t *pfnp,
> >  					int *levelp)
> > @@ -3168,7 +3198,7 @@ static void transparent_hugepage_adjust(struct
> > kvm_vcpu *vcpu,
> >  	 */
> >  	if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
> >  	    level == PT_PAGE_TABLE_LEVEL &&
> > -	    PageTransCompoundMap(pfn_to_page(pfn)) &&
> > +	    pfn_is_huge_mapped(vcpu->kvm, gfn, pfn) &&
> >  	    !mmu_gfn_lpage_is_disallowed(vcpu, gfn, PT_DIRECTORY_LEVEL)) {
> >  		unsigned long mask;
> >  		/*
> > @@ -5678,7 +5708,7 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm
> > *kvm,
> >  		 */
> >  		if (sp->role.direct &&
> >  			!kvm_is_reserved_pfn(pfn) &&
> > -			PageTransCompoundMap(pfn_to_page(pfn))) {
> > +			pfn_is_huge_mapped(kvm, sp->gfn, pfn)) {
> >  			pte_list_remove(rmap_head, sptep);
> >  			need_tlb_flush = 1;
> >  			goto restart;
> > 
> 
> This looks surprisingly simple to me :)
> 
> --
> 
> Thanks,
> 
> David / dhildenb
>
Paolo Bonzini Nov. 13, 2018, 12:41 p.m. UTC | #4
On 13/11/2018 11:02, Pankaj Gupta wrote:
> 
>>
>> On 09.11.18 21:39, Barret Rhoden wrote:
>>> This change allows KVM to map DAX-backed files made of huge pages with
>>> huge mappings in the EPT/TDP.
>>>
>>> DAX pages are not PageTransCompound.  The existing check is trying to
>>> determine if the mapping for the pfn is a huge mapping or not.  For
>>> non-DAX maps, e.g. hugetlbfs, that means checking PageTransCompound.
>>> For DAX, we can check the page table itself.
>>>
>>> Note that KVM already faulted in the page (or huge page) in the host's
>>> page table, and we hold the KVM mmu spinlock (grabbed before checking
>>> the mmu seq).
>>
>> I wonder if the KVM mmu spinlock is enough for walking (not KVM
>> exclusive) host page tables. Can you elaborate?
> 
> As this patch is dependent on PageReserved patch(which is in progress), just 
> wondering if we are able to test the code path for hugepage with DAX.

The MMU spinlock is taken in kvm_mmu_notifier_invalidate_range_end, so
it should be enough.

Paolo

> 
> Thanks,
> Pankaj 
>  
>>
>>>
>>> Signed-off-by: Barret Rhoden <brho@google.com>
>>> ---
>>>  arch/x86/kvm/mmu.c | 34 ++++++++++++++++++++++++++++++++--
>>>  1 file changed, 32 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
>>> index cf5f572f2305..2df8c459dc6a 100644
>>> --- a/arch/x86/kvm/mmu.c
>>> +++ b/arch/x86/kvm/mmu.c
>>> @@ -3152,6 +3152,36 @@ static int kvm_handle_bad_page(struct kvm_vcpu
>>> *vcpu, gfn_t gfn, kvm_pfn_t pfn)
>>>  	return -EFAULT;
>>>  }
>>>  
>>> +static bool pfn_is_huge_mapped(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn)
>>> +{
>>> +	struct page *page = pfn_to_page(pfn);
>>> +	unsigned long hva, map_shift;
>>> +
>>> +	if (!is_zone_device_page(page))
>>> +		return PageTransCompoundMap(page);
>>> +
>>> +	/*
>>> +	 * DAX pages do not use compound pages.  The page should have already
>>> +	 * been mapped into the host-side page table during try_async_pf(), so
>>> +	 * we can check the page tables directly.
>>> +	 */
>>> +	hva = gfn_to_hva(kvm, gfn);
>>> +	if (kvm_is_error_hva(hva))
>>> +		return false;
>>> +
>>> +	/*
>>> +	 * Our caller grabbed the KVM mmu_lock with a successful
>>> +	 * mmu_notifier_retry, so we're safe to walk the page table.
>>> +	 */
>>> +	map_shift = dev_pagemap_mapping_shift(hva, current->mm);
>>
>> You could get rid of that local variable map_shift.
>>
>>> +	switch (map_shift) {
>>> +	case PMD_SHIFT:
>>> +	case PUD_SIZE:
>>> +		return true;
>>> +	}
>>> +	return false;
>>> +}
>>> +
>>>  static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
>>>  					gfn_t *gfnp, kvm_pfn_t *pfnp,
>>>  					int *levelp)
>>> @@ -3168,7 +3198,7 @@ static void transparent_hugepage_adjust(struct
>>> kvm_vcpu *vcpu,
>>>  	 */
>>>  	if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
>>>  	    level == PT_PAGE_TABLE_LEVEL &&
>>> -	    PageTransCompoundMap(pfn_to_page(pfn)) &&
>>> +	    pfn_is_huge_mapped(vcpu->kvm, gfn, pfn) &&
>>>  	    !mmu_gfn_lpage_is_disallowed(vcpu, gfn, PT_DIRECTORY_LEVEL)) {
>>>  		unsigned long mask;
>>>  		/*
>>> @@ -5678,7 +5708,7 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm
>>> *kvm,
>>>  		 */
>>>  		if (sp->role.direct &&
>>>  			!kvm_is_reserved_pfn(pfn) &&
>>> -			PageTransCompoundMap(pfn_to_page(pfn))) {
>>> +			pfn_is_huge_mapped(kvm, sp->gfn, pfn)) {
>>>  			pte_list_remove(rmap_head, sptep);
>>>  			need_tlb_flush = 1;
>>>  			goto restart;
>>>
>>
>> This looks surprisingly simple to me :)
>>
>> --
>>
>> Thanks,
>>
>> David / dhildenb
>>
Barret Rhoden Nov. 13, 2018, 3:50 p.m. UTC | #5
On 2018-11-13 at 10:36 David Hildenbrand <david@redhat.com> wrote:
> > Note that KVM already faulted in the page (or huge page) in the host's
> > page table, and we hold the KVM mmu spinlock (grabbed before checking
> > the mmu seq).  
> 
> I wonder if the KVM mmu spinlock is enough for walking (not KVM
> exclusive) host page tables. Can you elaborate?

I'll update the commit message with the info from Paolo's email (about
kvm_mmu_notifier_invalidate_range_end()).

Thanks,

Barret
Barret Rhoden Nov. 13, 2018, 3:56 p.m. UTC | #6
On 2018-11-13 at 05:02 Pankaj Gupta <pagupta@redhat.com> wrote:
> As this patch is dependent on PageReserved patch(which is in progress), just 
> wondering if we are able to test the code path for hugepage with DAX.

For testing, I used the following patch.  It's not 100%, since it
intercepts at kvm_is_reserved_pfn(), and not PageReserved() directly.
The only difference is with kvm_set_pfn_dirty() I think.

I also have a nasty module that would dump the EPT's and the host page
table's mappings so I could confirm that the huge pages are being mapped
correctly.

-----------------------
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 2679e476b6c3..1b394a0752a0 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -148,6 +148,10 @@ __weak int kvm_arch_mmu_notifier_invalidate_range(struct kvm *kvm,
 
 bool kvm_is_reserved_pfn(kvm_pfn_t pfn)
 {
+	// XXX hack
+	if (is_zone_device_page(pfn_to_page(pfn)))
+		return false;
+
 	if (pfn_valid(pfn))
 		return PageReserved(pfn_to_page(pfn));
 
-----------------------

Thanks,

Barret
Barret Rhoden Nov. 13, 2018, 4:21 p.m. UTC | #7
On 2018-11-12 at 20:31 Paolo Bonzini <pbonzini@redhat.com> wrote:
> Looks good.  What's the plan for removing PageReserved from DAX pages?

I hear that's going on in this thread:

https://lore.kernel.org/lkml/154145268025.30046.11742652345962594283.stgit@ahduyck-desk1.jf.intel.com/

Though it looks like it's speeding up page initialization, and not
explicitly making the PageReserved change, yet.

Alternatively, I could change kvm_is_reserved_pfn() to single out
zone_device pages if we don't want to wait or if there is a concern
that it won't happen.

On a related note, there are two places in KVM where we check
PageReserved outside of kvm_is_reserved_pfn().

For reference:

bool kvm_is_reserved_pfn(kvm_pfn_t pfn)
{
        if (pfn_valid(pfn))
                return PageReserved(pfn_to_page(pfn));

        return true;
}

One caller of PageReserved():

void kvm_set_pfn_dirty(kvm_pfn_t pfn)
{
        if (!kvm_is_reserved_pfn(pfn)) {
                struct page *page = pfn_to_page(pfn);

                if (!PageReserved(page))
                        SetPageDirty(page);
        }
}

In that one, the PageReserved() check looks redundant, since if the
page was PageReserved, then it would have been kvm_is_reserved.

The other is:

static bool kvm_is_mmio_pfn(kvm_pfn_t pfn)
{
        if (pfn_valid(pfn))
                return !is_zero_pfn(pfn) && PageReserved(pfn_to_page(pfn)) &&
                        /*
                         * Some reserved pages, such as those from NVDIMM
                         * DAX devices, are not for MMIO, and can be mapped
                         * with cached memory type for better performance.
                         * However, the above check misconceives those pages
                         * as MMIO, and results in KVM mapping them with UC
                         * memory type, which would hurt the performance.
                         * Therefore, we check the host memory type in addition
                         * and only treat UC/UC-/WC pages as MMIO.
                         */
                        (!pat_enabled() || pat_pfn_immune_to_uc_mtrr(pfn));

        return true;
}

Where the PAT stuff was motivated by DAX.  The PageReserved check here
looks like a broken-out version of kvm_is_reserved_pfn(), so that we can
make some extra checks around it.

Anyway, I can get rid of those two PageReserved checks and/or have
kvm_is_reserved_pfn() just check DAX pages, if everyone is OK with that.

Thanks,

Barret
Paolo Bonzini Nov. 13, 2018, 6:22 p.m. UTC | #8
On 13/11/2018 17:21, Barret Rhoden wrote:
> On 2018-11-12 at 20:31 Paolo Bonzini <pbonzini@redhat.com> wrote:
>> Looks good.  What's the plan for removing PageReserved from DAX pages?
> 
> I hear that's going on in this thread:
> 
> https://lore.kernel.org/lkml/154145268025.30046.11742652345962594283.stgit@ahduyck-desk1.jf.intel.com/
> 
> Though it looks like it's speeding up page initialization, and not
> explicitly making the PageReserved change, yet.
> 
> Alternatively, I could change kvm_is_reserved_pfn() to single out
> zone_device pages if we don't want to wait or if there is a concern
> that it won't happen.
> 
> On a related note, there are two places in KVM where we check
> PageReserved outside of kvm_is_reserved_pfn().
> 
> For reference:
> 
> bool kvm_is_reserved_pfn(kvm_pfn_t pfn)
> {
>         if (pfn_valid(pfn))
>                 return PageReserved(pfn_to_page(pfn));
> 
>         return true;
> }
> 
> One caller of PageReserved():
> 
> void kvm_set_pfn_dirty(kvm_pfn_t pfn)
> {
>         if (!kvm_is_reserved_pfn(pfn)) {
>                 struct page *page = pfn_to_page(pfn);
> 
>                 if (!PageReserved(page))
>                         SetPageDirty(page);
>         }
> }
> 
> In that one, the PageReserved() check looks redundant, since if the
> page was PageReserved, then it would have been kvm_is_reserved.

Make sense, and a patch to fix this is welcome.

> 
> The other is:
> 
> static bool kvm_is_mmio_pfn(kvm_pfn_t pfn)
> {
>         if (pfn_valid(pfn))
>                 return !is_zero_pfn(pfn) && PageReserved(pfn_to_page(pfn)) &&
>                         /*
>                          * Some reserved pages, such as those from NVDIMM
>                          * DAX devices, are not for MMIO, and can be mapped
>                          * with cached memory type for better performance.
>                          * However, the above check misconceives those pages
>                          * as MMIO, and results in KVM mapping them with UC
>                          * memory type, which would hurt the performance.
>                          * Therefore, we check the host memory type in addition
>                          * and only treat UC/UC-/WC pages as MMIO.
>                          */
>                         (!pat_enabled() || pat_pfn_immune_to_uc_mtrr(pfn));
> 
>         return true;
> }
> 
> Where the PAT stuff was motivated by DAX.  The PageReserved check here
> looks like a broken-out version of kvm_is_reserved_pfn(), so that we can
> make some extra checks around it.

Since this one is indeed motivated by DAX, it can be left in for now and
it will DTRT.  But when DAX is not PageReserved anymore, then this
second part of the condition can be reverted.

Paolo
Pankaj Gupta Nov. 14, 2018, 9:09 a.m. UTC | #9
> > As this patch is dependent on PageReserved patch(which is in progress),
> > just
> > wondering if we are able to test the code path for hugepage with DAX.
> 
> For testing, I used the following patch.  It's not 100%, since it
> intercepts at kvm_is_reserved_pfn(), and not PageReserved() directly.
> The only difference is with kvm_set_pfn_dirty() I think.
> 

Yes, this should be ok.

Thanks,
Pankaj 

> I also have a nasty module that would dump the EPT's and the host page
> table's mappings so I could confirm that the huge pages are being mapped
> correctly.
> 
> -----------------------
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index 2679e476b6c3..1b394a0752a0 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -148,6 +148,10 @@ __weak int kvm_arch_mmu_notifier_invalidate_range(struct
> kvm *kvm,
>  
>  bool kvm_is_reserved_pfn(kvm_pfn_t pfn)
>  {
> +	// XXX hack
> +	if (is_zone_device_page(pfn_to_page(pfn)))
> +		return false;
> +
>  	if (pfn_valid(pfn))
>  		return PageReserved(pfn_to_page(pfn));
>  
> -----------------------
> 
> Thanks,
> 
> Barret
> 
>
diff mbox series

Patch

diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index cf5f572f2305..2df8c459dc6a 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -3152,6 +3152,36 @@  static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, kvm_pfn_t pfn)
 	return -EFAULT;
 }
 
+static bool pfn_is_huge_mapped(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn)
+{
+	struct page *page = pfn_to_page(pfn);
+	unsigned long hva, map_shift;
+
+	if (!is_zone_device_page(page))
+		return PageTransCompoundMap(page);
+
+	/*
+	 * DAX pages do not use compound pages.  The page should have already
+	 * been mapped into the host-side page table during try_async_pf(), so
+	 * we can check the page tables directly.
+	 */
+	hva = gfn_to_hva(kvm, gfn);
+	if (kvm_is_error_hva(hva))
+		return false;
+
+	/*
+	 * Our caller grabbed the KVM mmu_lock with a successful
+	 * mmu_notifier_retry, so we're safe to walk the page table.
+	 */
+	map_shift = dev_pagemap_mapping_shift(hva, current->mm);
+	switch (map_shift) {
+	case PMD_SHIFT:
+	case PUD_SIZE:
+		return true;
+	}
+	return false;
+}
+
 static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
 					gfn_t *gfnp, kvm_pfn_t *pfnp,
 					int *levelp)
@@ -3168,7 +3198,7 @@  static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
 	 */
 	if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
 	    level == PT_PAGE_TABLE_LEVEL &&
-	    PageTransCompoundMap(pfn_to_page(pfn)) &&
+	    pfn_is_huge_mapped(vcpu->kvm, gfn, pfn) &&
 	    !mmu_gfn_lpage_is_disallowed(vcpu, gfn, PT_DIRECTORY_LEVEL)) {
 		unsigned long mask;
 		/*
@@ -5678,7 +5708,7 @@  static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
 		 */
 		if (sp->role.direct &&
 			!kvm_is_reserved_pfn(pfn) &&
-			PageTransCompoundMap(pfn_to_page(pfn))) {
+			pfn_is_huge_mapped(kvm, sp->gfn, pfn)) {
 			pte_list_remove(rmap_head, sptep);
 			need_tlb_flush = 1;
 			goto restart;