Skip to content

Commit e2e0b82

Browse files
rpptakpm00
authored andcommitted
userfaultfd: introduce mfill_establish_pmd() helper
There is a lengthy code chunk in mfill_atomic() that establishes the PMD for UFFDIO operations. This code may be called twice: first time when the copy is performed with VMA/mm locks held and the other time after the copy is retried with locks dropped. Move the code that establishes a PMD into a helper function so it can be reused later during refactoring of mfill_atomic_pte_copy(). Link: https://lore.kernel.org/20260402041156.1377214-4-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org> Reviewed-by: Harry Yoo (Oracle) <harry@kernel.org> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Andrei Vagin <avagin@google.com> Cc: Axel Rasmussen <axelrasmussen@google.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: David Hildenbrand (Arm) <david@kernel.org> Cc: Harry Yoo <harry.yoo@oracle.com> Cc: Hugh Dickins <hughd@google.com> Cc: James Houghton <jthoughton@google.com> Cc: Liam Howlett <liam.howlett@oracle.com> Cc: Lorenzo Stoakes (Oracle) <ljs@kernel.org> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Muchun Song <muchun.song@linux.dev> Cc: Nikita Kalyazin <kalyazin@amazon.com> Cc: Oscar Salvador <osalvador@suse.de> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Peter Xu <peterx@redhat.com> Cc: Sean Christopherson <seanjc@google.com> Cc: Shuah Khan <shuah@kernel.org> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: David Carlier <devnexen@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent db0062d commit e2e0b82

1 file changed

Lines changed: 52 additions & 50 deletions

File tree

mm/userfaultfd.c

Lines changed: 52 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,56 @@ static void uffd_mfill_unlock(struct vm_area_struct *vma)
157157
}
158158
#endif
159159

160+
static pmd_t *mm_alloc_pmd(struct mm_struct *mm, unsigned long address)
161+
{
162+
pgd_t *pgd;
163+
p4d_t *p4d;
164+
pud_t *pud;
165+
166+
pgd = pgd_offset(mm, address);
167+
p4d = p4d_alloc(mm, pgd, address);
168+
if (!p4d)
169+
return NULL;
170+
pud = pud_alloc(mm, p4d, address);
171+
if (!pud)
172+
return NULL;
173+
/*
174+
* Note that we didn't run this because the pmd was
175+
* missing, the *pmd may be already established and in
176+
* turn it may also be a trans_huge_pmd.
177+
*/
178+
return pmd_alloc(mm, pud, address);
179+
}
180+
181+
static int mfill_establish_pmd(struct mfill_state *state)
182+
{
183+
struct mm_struct *dst_mm = state->ctx->mm;
184+
pmd_t *dst_pmd, dst_pmdval;
185+
186+
dst_pmd = mm_alloc_pmd(dst_mm, state->dst_addr);
187+
if (unlikely(!dst_pmd))
188+
return -ENOMEM;
189+
190+
dst_pmdval = pmdp_get_lockless(dst_pmd);
191+
if (unlikely(pmd_none(dst_pmdval)) &&
192+
unlikely(__pte_alloc(dst_mm, dst_pmd)))
193+
return -ENOMEM;
194+
195+
dst_pmdval = pmdp_get_lockless(dst_pmd);
196+
/*
197+
* If the dst_pmd is THP don't override it and just be strict.
198+
* (This includes the case where the PMD used to be THP and
199+
* changed back to none after __pte_alloc().)
200+
*/
201+
if (unlikely(!pmd_present(dst_pmdval) || pmd_leaf(dst_pmdval)))
202+
return -EEXIST;
203+
if (unlikely(pmd_bad(dst_pmdval)))
204+
return -EFAULT;
205+
206+
state->pmd = dst_pmd;
207+
return 0;
208+
}
209+
160210
/* Check if dst_addr is outside of file's size. Must be called with ptl held. */
161211
static bool mfill_file_over_size(struct vm_area_struct *dst_vma,
162212
unsigned long dst_addr)
@@ -489,27 +539,6 @@ static int mfill_atomic_pte_poison(struct mfill_state *state)
489539
return ret;
490540
}
491541

492-
static pmd_t *mm_alloc_pmd(struct mm_struct *mm, unsigned long address)
493-
{
494-
pgd_t *pgd;
495-
p4d_t *p4d;
496-
pud_t *pud;
497-
498-
pgd = pgd_offset(mm, address);
499-
p4d = p4d_alloc(mm, pgd, address);
500-
if (!p4d)
501-
return NULL;
502-
pud = pud_alloc(mm, p4d, address);
503-
if (!pud)
504-
return NULL;
505-
/*
506-
* Note that we didn't run this because the pmd was
507-
* missing, the *pmd may be already established and in
508-
* turn it may also be a trans_huge_pmd.
509-
*/
510-
return pmd_alloc(mm, pud, address);
511-
}
512-
513542
#ifdef CONFIG_HUGETLB_PAGE
514543
/*
515544
* mfill_atomic processing for HUGETLB vmas. Note that this routine is
@@ -742,7 +771,6 @@ static __always_inline ssize_t mfill_atomic(struct userfaultfd_ctx *ctx,
742771
struct vm_area_struct *dst_vma;
743772
long copied = 0;
744773
ssize_t err;
745-
pmd_t *dst_pmd;
746774

747775
/*
748776
* Sanitize the command parameters:
@@ -808,41 +836,15 @@ static __always_inline ssize_t mfill_atomic(struct userfaultfd_ctx *ctx,
808836
while (state.src_addr < src_start + len) {
809837
VM_WARN_ON_ONCE(state.dst_addr >= dst_start + len);
810838

811-
pmd_t dst_pmdval;
812-
813-
dst_pmd = mm_alloc_pmd(dst_mm, state.dst_addr);
814-
if (unlikely(!dst_pmd)) {
815-
err = -ENOMEM;
839+
err = mfill_establish_pmd(&state);
840+
if (err)
816841
break;
817-
}
818842

819-
dst_pmdval = pmdp_get_lockless(dst_pmd);
820-
if (unlikely(pmd_none(dst_pmdval)) &&
821-
unlikely(__pte_alloc(dst_mm, dst_pmd))) {
822-
err = -ENOMEM;
823-
break;
824-
}
825-
dst_pmdval = pmdp_get_lockless(dst_pmd);
826-
/*
827-
* If the dst_pmd is THP don't override it and just be strict.
828-
* (This includes the case where the PMD used to be THP and
829-
* changed back to none after __pte_alloc().)
830-
*/
831-
if (unlikely(!pmd_present(dst_pmdval) ||
832-
pmd_trans_huge(dst_pmdval))) {
833-
err = -EEXIST;
834-
break;
835-
}
836-
if (unlikely(pmd_bad(dst_pmdval))) {
837-
err = -EFAULT;
838-
break;
839-
}
840843
/*
841844
* For shmem mappings, khugepaged is allowed to remove page
842845
* tables under us; pte_offset_map_lock() will deal with that.
843846
*/
844847

845-
state.pmd = dst_pmd;
846848
err = mfill_atomic_pte(&state);
847849
cond_resched();
848850

0 commit comments

Comments
 (0)