[PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults

Paolo Bonzini posted 3 patches 2 months ago
drivers/gpu/drm/drm_gem_shmem_helper.c     | 38 +++++------
drivers/gpu/drm/ttm/ttm_bo_vm.c            |  3 +-
drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c | 44 +++++++------
include/linux/mm.h                         |  4 ++
mm/huge_memory.c                           |  2 +-
mm/memory.c                                | 75 ++++++++++++++++------
6 files changed, 107 insertions(+), 59 deletions(-)
[PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults
Posted by Paolo Bonzini 2 months ago
Warning - DRM parts (i.e. most of the patches) untested; I have Cc'd
the reporter to help with testing these patches.

Right now, users of .pfn_mkwrite() have no way to create a PTE
that has gone through maybe_mkwrite().  Because vma_set_page_prot()
will have cleared the writable PTE bit, users of fixup_user_fault()
will see a read-only PTE and have no clue that the page needs
a *second* fault to reach its final status.

Handling this in fixup_user_fault() is problematic: the information
about the presence of *_mkwrite is only recorded in vma->vm_page_prot,
which is an opaque pgprot_t, therefore only follow_pfnmap_start()
knows how to retrieve it.

There are actually some preexisting functions that suggest how
this is supposed to be handled, namely vmf_insert_page_mkwrite() and
vmf_insert_pfn_pmd().  So, this series adjusts mm/memory.c to export
two new functions vmf_insert_pfn_mkwrite() and __vmf_insert_pfn_prot(),
and then teaches drm's two users of .pfn_mkwrite() to call them.  Let
me know if I should use another name like vmf_insert_pfn_prot_mkwrite(),
instead of the "__"-prefixed one.

The drm_gem_shmem_helper case was reported as a KVM regression, while
the vmwgfx one was found by inspection of .pfn_mkwrite() implementors.

Thanks,

Paolo

Paolo Bonzini (3):
  mm: export variants of vmf_insert_pfn* for use with pfn_mkwrite()
  drm/shmem_helper: use vmf_insert_pfn_mkwrite()
  drm/ttm, drm/vmwgfx: directly create writable PTEs when mkwrite is
    in use

 drivers/gpu/drm/drm_gem_shmem_helper.c     | 38 +++++------
 drivers/gpu/drm/ttm/ttm_bo_vm.c            |  3 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c | 44 +++++++------
 include/linux/mm.h                         |  4 ++
 mm/huge_memory.c                           |  2 +-
 mm/memory.c                                | 75 ++++++++++++++++------
 6 files changed, 107 insertions(+), 59 deletions(-)

-- 
2.55.0
Re: [PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults
Posted by David Hildenbrand (Arm) 1 month, 4 weeks ago
On 7/31/26 18:43, Paolo Bonzini wrote:
> Warning - DRM parts (i.e. most of the patches) untested; I have Cc'd
> the reporter to help with testing these patches.
> 
> Right now, users of .pfn_mkwrite() have no way to create a PTE
> that has gone through maybe_mkwrite().  Because vma_set_page_prot()
> will have cleared the writable PTE bit, users of fixup_user_fault()
> will see a read-only PTE and have no clue that the page needs
> a *second* fault to reach its final status.
> 
> Handling this in fixup_user_fault() is problematic: the information
> about the presence of *_mkwrite is only recorded in vma->vm_page_prot,
> which is an opaque pgprot_t, therefore only follow_pfnmap_start()
> knows how to retrieve it.

How is mprotect() supposed to work in that case?

-- 
Cheers,

David
Re: [PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults
Posted by Paolo Bonzini 1 month, 4 weeks ago
On 8/3/26 10:55, David Hildenbrand (Arm) wrote:
> On 7/31/26 18:05, Paolo Bonzini wrote:
>> Reported-by: Sergio Lopez <slp@redhat.com>
> 
> Reported-by: without Fixes: is odd.

Fixes: 6da8e9634bb7 ("mm: new follow_pfnmap API") would also be odd :) 
but I can certainly add it.
>> +	 * @write_fault: if true, fail with -EFAULT unless the mapping is
> 
> Just wondering whether EPERM would be better.

It would be EACCES if anything, not EPERM; but almost all callers 
already pass EFAULT to userspace, and write() to a PROT_READ area 
returns EFAULT, so I don't think EACCES is the right choice.

>> +	 * writable
>>  	 */
>>  	struct vm_area_struct *vma;
>>  	unsigned long address;
>> +	bool write_fault;
> 
> "write_fault" is a rather odd name for this, given that this function will not
> trigger a write fault.
> 
> You want something that matches FOLL_WRITE.
> 
> "write_access" / "check_writable" maybe?
There are no for_write, write_access or check_write in mm/,  but there 
are a handful of each of these

	int write = (gup_flags & FOLL_WRITE);
	bool write = vmf->flags & FAULT_FLAG_WRITE;

so I'll go for just "write".

Thanks,

Paolo
Re: [PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults
Posted by Paolo Bonzini 1 month, 4 weeks ago
On 8/3/26 13:54, David Hildenbrand (Arm) wrote:
> On 7/31/26 18:43, Paolo Bonzini wrote:
>> Warning - DRM parts (i.e. most of the patches) untested; I have Cc'd
>> the reporter to help with testing these patches.
>>
>> Right now, users of .pfn_mkwrite() have no way to create a PTE
>> that has gone through maybe_mkwrite().  Because vma_set_page_prot()
>> will have cleared the writable PTE bit, users of fixup_user_fault()
>> will see a read-only PTE and have no clue that the page needs
>> a *second* fault to reach its final status.
>>
>> Handling this in fixup_user_fault() is problematic: the information
>> about the presence of *_mkwrite is only recorded in vma->vm_page_prot,
>> which is an opaque pgprot_t, therefore only follow_pfnmap_start()
>> knows how to retrieve it.
> 
> How is mprotect() supposed to work in that case?

Hi David,

not sure what you are worried about specifically, but fixup_user_fault() 
catches !VM_WRITE VMAs and returns early (see vma_permits_fault()).

Also, do_wp_page() has the comment:

         /*
          * Shared mapping: we are guaranteed to have VM_WRITE and
          * FAULT_FLAG_WRITE set at this point.
          */

before the call to wp_pfn_shared() which is where .pfn_mkwrite() is called.

Let me know if this was not what you were asking.

Thanks for the review of patch 1---I mentioned here in the cover letter 
that the name was temporary and I'll take your suggestion.  I can either 
use EXPORT_SYMBOL_GPL or switch to inlines, but not both because the 
existing functions like vmf_insert_pfn_prot() need to stay non-GPL-only.

Anyhow, now that the series has a Tested-by I'll clean up everything, 
and repost later this week.

Paolo
Re: [PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults
Posted by David Hildenbrand (Arm) 1 month, 4 weeks ago
On 8/3/26 16:19, Paolo Bonzini wrote:
> On 8/3/26 13:54, David Hildenbrand (Arm) wrote:
>> On 7/31/26 18:43, Paolo Bonzini wrote:
>>> Warning - DRM parts (i.e. most of the patches) untested; I have Cc'd
>>> the reporter to help with testing these patches.
>>>
>>> Right now, users of .pfn_mkwrite() have no way to create a PTE
>>> that has gone through maybe_mkwrite().  Because vma_set_page_prot()
>>> will have cleared the writable PTE bit, users of fixup_user_fault()
>>> will see a read-only PTE and have no clue that the page needs
>>> a *second* fault to reach its final status.
>>>
>>> Handling this in fixup_user_fault() is problematic: the information
>>> about the presence of *_mkwrite is only recorded in vma->vm_page_prot,
>>> which is an opaque pgprot_t, therefore only follow_pfnmap_start()
>>> knows how to retrieve it.
>>
>> How is mprotect() supposed to work in that case?
> 
> Hi David,

Hi!

> 
> not sure what you are worried about specifically, but fixup_user_fault() catches !VM_WRITE VMAs and returns early (see vma_permits_fault()).

That part is clear, I was wondering about the following:

mprotect(PROT_READ)

followed by

mprotect(PROT_READ | PROT_WRITE)

You'd similarly end up without the writable bit in the PTE, and apparently there is not really a way
to recover from this.

Maybe that's just ok (just sounded odd :) ).

> 
> Also, do_wp_page() has the comment:
> 
>         /*
>          * Shared mapping: we are guaranteed to have VM_WRITE and
>          * FAULT_FLAG_WRITE set at this point.
>          */
> 
> before the call to wp_pfn_shared() which is where .pfn_mkwrite() is called.
> 
> Let me know if this was not what you were asking.
> 
> Thanks for the review of patch 1---I mentioned here in the cover letter that the name was temporary and I'll take your suggestion.  I can either use EXPORT_SYMBOL_GPL or switch to inlines, but not
> both because the existing functions like vmf_insert_pfn_prot() need to stay non-GPL-only.
> 
> Anyhow, now that the series has a Tested-by I'll clean up everything, and repost later this week.

Thanks!

-- 
Cheers,

David
Re: [PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults
Posted by Paolo Bonzini 1 month, 4 weeks ago
On Mon, Aug 3, 2026 at 5:18 PM David Hildenbrand (Arm) <david@kernel.org> wrote:
> >>> Handling this in fixup_user_fault() is problematic: the information
> >>> about the presence of *_mkwrite is only recorded in vma->vm_page_prot,
> >>> which is an opaque pgprot_t, therefore only follow_pfnmap_start()
> >>> knows how to retrieve it.
> >>
> >> How is mprotect() supposed to work in that case?
> >
> I was wondering about the following:
>
> mprotect(PROT_READ)
>
> followed by
>
> mprotect(PROT_READ | PROT_WRITE)
>
> You'd similarly end up without the writable bit in the PTE, and apparently there is not really a way
> to recover from this.

Why not? mprotect_fixup() calls vma_set_page_prot(), the PTE as you
say lacks the writable bit (unless pte_dirty(pte)), and then the next
fault calls .pfn_mkwrite().

Paolo
Re: [PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults
Posted by David Hildenbrand (Arm) 1 month, 4 weeks ago
On 8/4/26 09:50, Paolo Bonzini wrote:
> On Mon, Aug 3, 2026 at 5:18 PM David Hildenbrand (Arm) <david@kernel.org> wrote:
>>>
>> I was wondering about the following:
>>
>> mprotect(PROT_READ)
>>
>> followed by
>>
>> mprotect(PROT_READ | PROT_WRITE)
>>
>> You'd similarly end up without the writable bit in the PTE, and apparently there is not really a way
>> to recover from this.
> 
> Why not? mprotect_fixup() calls vma_set_page_prot(), the PTE as you
> say lacks the writable bit (unless pte_dirty(pte)), and then the next
> fault calls .pfn_mkwrite().

Ah, if this works, great. I guess I was confused about your explanation about
fixup_user_fault().

So this really only about avoiding the second fault, makes sense thanks!

-- 
Cheers,

David
Re: [PATCH RFT 0/3] mm, drm: ensure .fault() does not have to be followed by .pfn_mkwrite() for write faults
Posted by Sergio Lopez Pascual 2 months ago
Paolo Bonzini <pbonzini@redhat.com> writes:

> Warning - DRM parts (i.e. most of the patches) untested; I have Cc'd
> the reporter to help with testing these patches.

Tested alongside with the mm fix [1], and it fixes the virtio-gpu
mapping issue [2] for both the absent PTE case and the pre-faulted
read-only PTE case.

Tested-by: Sergio Lopez <slp@redhat.com>

Thanks, Paolo.

[1] https://lore.kernel.org/kvm/CABgObfbkqYNsPQnKxK1_4adXF_tSdtScPU5-Xrg-sXyeWfMVfQ@mail.gmail.com/T/#t
[2] https://lore.kernel.org/kvm/299bddc0-fafc-48b3-a9c6-ec171136a46a@redhat.com/T/#t