From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ADF7A35F5E6; Thu, 2 Apr 2026 04:12:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103133; cv=none; b=DHvAv2YfJkg+ZPniorPzkYE6hbAV7w5gPwkFCHPsrJavUHyi0aocX+S6myDeIGj9HBe1XMJdUYSMeM7X5tz3WO2YbrYXCL5KyMMmlhC4SKBOxLvJzWL6GhsQYaZh5IKDewS5wM0IGFwcZ0inkyExiQLdIsje6IxQdKes5lMUlUo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103133; c=relaxed/simple; bh=ILAepUGKrqJkLKLRQyhDJaBdtTZkSW4gONujo05yQQw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KgFJW6YkqicGHHuzOIx5Hx7XFEKY3Ye0DoFYTMAxr6YxMyh+hqMNIKwUcM9RKU8lxCM6OhErXhnXDAorf+34dYdM3OXWJy8kkpQlpKRewSHei5ZZr1GgT1eHjxXuEWGEH3TC/H4seUK+awXWLgft+PrgM9XsNd8m2hhKcDTrBfM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OW4DnM9v; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OW4DnM9v" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D1472C19424; Thu, 2 Apr 2026 04:12:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103133; bh=ILAepUGKrqJkLKLRQyhDJaBdtTZkSW4gONujo05yQQw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OW4DnM9vSohN14okui8L6bWH/FbKehx9LYxnEhEvlgJYRmyYfwrbc3+OazmjCUduN q5iaHI6A9ccmJhR1FhbZCdGG+JjPBLnqfihHFmJ6cZmt3a0p012TIIRGR5reSsL1uV +KASP1vM3g1ZLLmdmWuRPfKa2El8WEFMAM6M/Iy683foM+qc7p3hFJNJixI7rqnn+B LeZ5y9uDip13tqI/I0EsNBBV+zqbl/Yl0yV0i+UnBZx29Kf8i2Fz8TEUjWlcwJ87ao nwTWmAC7t6+3EbkEauFaT8goH84O96EtEGMcb+kEu+IhdIBJthKeje94AH1dLDgriH vLLQ40zXYEqrQ== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, "Harry Yoo (Oracle)" Subject: [PATCH v4 01/15] userfaultfd: introduce mfill_copy_folio_locked() helper Date: Thu, 2 Apr 2026 07:11:42 +0300 Message-ID: <20260402041156.1377214-2-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" Split copying of data when locks held from mfill_atomic_pte_copy() into a helper function mfill_copy_folio_locked(). This makes improves code readability and makes complex mfill_atomic_pte_copy() function easier to comprehend. No functional change. Signed-off-by: Mike Rapoport (Microsoft) Acked-by: Peter Xu Reviewed-by: David Hildenbrand (Arm) Reviewed-by: Harry Yoo (Oracle) --- mm/userfaultfd.c | 59 ++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 927086bb4a3c..32637d557c95 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -238,6 +238,40 @@ int mfill_atomic_install_pte(pmd_t *dst_pmd, return ret; } =20 +static int mfill_copy_folio_locked(struct folio *folio, unsigned long src_= addr) +{ + void *kaddr; + int ret; + + kaddr =3D kmap_local_folio(folio, 0); + /* + * The read mmap_lock is held here. Despite the + * mmap_lock being read recursive a deadlock is still + * possible if a writer has taken a lock. For example: + * + * process A thread 1 takes read lock on own mmap_lock + * process A thread 2 calls mmap, blocks taking write lock + * process B thread 1 takes page fault, read lock on own mmap lock + * process B thread 2 calls mmap, blocks taking write lock + * process A thread 1 blocks taking read lock on process B + * process B thread 1 blocks taking read lock on process A + * + * Disable page faults to prevent potential deadlock + * and retry the copy outside the mmap_lock. + */ + pagefault_disable(); + ret =3D copy_from_user(kaddr, (const void __user *) src_addr, + PAGE_SIZE); + pagefault_enable(); + kunmap_local(kaddr); + + if (ret) + return -EFAULT; + + flush_dcache_folio(folio); + return ret; +} + static int mfill_atomic_pte_copy(pmd_t *dst_pmd, struct vm_area_struct *dst_vma, unsigned long dst_addr, @@ -245,7 +279,6 @@ static int mfill_atomic_pte_copy(pmd_t *dst_pmd, uffd_flags_t flags, struct folio **foliop) { - void *kaddr; int ret; struct folio *folio; =20 @@ -256,27 +289,7 @@ static int mfill_atomic_pte_copy(pmd_t *dst_pmd, if (!folio) goto out; =20 - kaddr =3D kmap_local_folio(folio, 0); - /* - * The read mmap_lock is held here. Despite the - * mmap_lock being read recursive a deadlock is still - * possible if a writer has taken a lock. For example: - * - * process A thread 1 takes read lock on own mmap_lock - * process A thread 2 calls mmap, blocks taking write lock - * process B thread 1 takes page fault, read lock on own mmap lock - * process B thread 2 calls mmap, blocks taking write lock - * process A thread 1 blocks taking read lock on process B - * process B thread 1 blocks taking read lock on process A - * - * Disable page faults to prevent potential deadlock - * and retry the copy outside the mmap_lock. - */ - pagefault_disable(); - ret =3D copy_from_user(kaddr, (const void __user *) src_addr, - PAGE_SIZE); - pagefault_enable(); - kunmap_local(kaddr); + ret =3D mfill_copy_folio_locked(folio, src_addr); =20 /* fallback to copy_from_user outside mmap_lock */ if (unlikely(ret)) { @@ -285,8 +298,6 @@ static int mfill_atomic_pte_copy(pmd_t *dst_pmd, /* don't free the page */ goto out; } - - flush_dcache_folio(folio); } else { folio =3D *foliop; *foliop =3D NULL; --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 964B43603D7; Thu, 2 Apr 2026 04:12:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103140; cv=none; b=dMhsfQ+vovgmYtpPRVUpUpZHLNW/k/JG54E9+aFKC0Cty0cFbGEshH5nyNNhQuJXJKAqHPp0r+olvXFErnDSNxnGt1h8lLQWbXx8AixIWU+aOvxgG0SF3N29ks8UYhVJ1LjUovoSS4KqKa5fbg3c6ExbHA+kRE9uZO7vsykCecQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103140; c=relaxed/simple; bh=Ibzvh4mmDhdNLy62raQyz44ZZGrqsaxE++tKr6wD2MA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tN6G8yHJGnfgp2UAcGg3Hzrkc1b3EUrgCI5pmgBQFzQHMh4wdXCYpsSNAEKKePFlbWlXcbgtCR3FrFdxnTa3Bdkw6hUGXIN4jEPe+DlGvDo6pccVpq/kM8O7R/Ik9RoSt8QUZKK40W59jKTELnS+j71+kGwTz1UOrOqmq+CXj3Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nuySZNSS; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nuySZNSS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D003AC19423; Thu, 2 Apr 2026 04:12:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103140; bh=Ibzvh4mmDhdNLy62raQyz44ZZGrqsaxE++tKr6wD2MA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nuySZNSSQCsKxusyO9lwYQOvSUv/YBp5honCfrQAdNa7teZJm+oUOW+VAYc1Czpty pvd0i7tYH+/5odbiwyv6sxaDboHJ4siGnADYhy8INw+rVDqxIuz82PlDjCB92gKvJe ikm4HOZFqcR9VcvOR8EWdQV0r0TziEeB72SpeHbdrf3aU5emH/5p8vvm80BPvNhy7O rKLxwqQ/kmMZ0DH6S18ZiXD3YXjXrFHFvK9bBsXZq2zrs4oUuwpauLeYxjJuYPmJJ+ 6lQMUkNtpvWQqguXbq/jhOn+wpmEDoDB/r++Cr6vMda7Trj7aoTcE1XXJNV5BLLg3Y QVGmVUSRRWVRA== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 02/15] userfaultfd: introduce struct mfill_state Date: Thu, 2 Apr 2026 07:11:43 +0300 Message-ID: <20260402041156.1377214-3-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" mfill_atomic() passes a lot of parameters down to its callees. Aggregate them all into mfill_state structure and pass this structure to functions that implement various UFFDIO_ commands. Tracking the state in a structure will allow moving the code that retries copying of data for UFFDIO_COPY into mfill_atomic_pte_copy() and make the loop in mfill_atomic() identical for all UFFDIO operations on PTE-mapped memory. The mfill_state definition is deliberately local to mm/userfaultfd.c, hence shmem_mfill_atomic_pte() is not updated. [harry.yoo@oracle.com: properly initialize mfill_state.len to fix folio_add_new_anon_rmap() WARN] Link: https://lkml.kernel.org/r/abehBY7QakYF9bK4@hyeyoo Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Harry Yoo Acked-by: David Hildenbrand (Arm) Reviewed-by: Harry Yoo (Oracle) --- mm/userfaultfd.c | 147 ++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 66 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 32637d557c95..c4074b6f4aca 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -20,6 +20,20 @@ #include "internal.h" #include "swap.h" =20 +struct mfill_state { + struct userfaultfd_ctx *ctx; + unsigned long src_start; + unsigned long dst_start; + unsigned long len; + uffd_flags_t flags; + + struct vm_area_struct *vma; + unsigned long src_addr; + unsigned long dst_addr; + struct folio *folio; + pmd_t *pmd; +}; + static __always_inline bool validate_dst_vma(struct vm_area_struct *dst_vma, unsigned long dst_en= d) { @@ -272,17 +286,17 @@ static int mfill_copy_folio_locked(struct folio *foli= o, unsigned long src_addr) return ret; } =20 -static int mfill_atomic_pte_copy(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - unsigned long src_addr, - uffd_flags_t flags, - struct folio **foliop) +static int mfill_atomic_pte_copy(struct mfill_state *state) { - int ret; + struct vm_area_struct *dst_vma =3D state->vma; + unsigned long dst_addr =3D state->dst_addr; + unsigned long src_addr =3D state->src_addr; + uffd_flags_t flags =3D state->flags; + pmd_t *dst_pmd =3D state->pmd; struct folio *folio; + int ret; =20 - if (!*foliop) { + if (!state->folio) { ret =3D -ENOMEM; folio =3D vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0, dst_vma, dst_addr); @@ -294,13 +308,13 @@ static int mfill_atomic_pte_copy(pmd_t *dst_pmd, /* fallback to copy_from_user outside mmap_lock */ if (unlikely(ret)) { ret =3D -ENOENT; - *foliop =3D folio; + state->folio =3D folio; /* don't free the page */ goto out; } } else { - folio =3D *foliop; - *foliop =3D NULL; + folio =3D state->folio; + state->folio =3D NULL; } =20 /* @@ -357,10 +371,11 @@ static int mfill_atomic_pte_zeroed_folio(pmd_t *dst_p= md, return ret; } =20 -static int mfill_atomic_pte_zeropage(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr) +static int mfill_atomic_pte_zeropage(struct mfill_state *state) { + struct vm_area_struct *dst_vma =3D state->vma; + unsigned long dst_addr =3D state->dst_addr; + pmd_t *dst_pmd =3D state->pmd; pte_t _dst_pte, *dst_pte; spinlock_t *ptl; int ret; @@ -392,13 +407,14 @@ static int mfill_atomic_pte_zeropage(pmd_t *dst_pmd, } =20 /* Handles UFFDIO_CONTINUE for all shmem VMAs (shared or private). */ -static int mfill_atomic_pte_continue(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - uffd_flags_t flags) +static int mfill_atomic_pte_continue(struct mfill_state *state) { - struct inode *inode =3D file_inode(dst_vma->vm_file); + struct vm_area_struct *dst_vma =3D state->vma; + unsigned long dst_addr =3D state->dst_addr; pgoff_t pgoff =3D linear_page_index(dst_vma, dst_addr); + struct inode *inode =3D file_inode(dst_vma->vm_file); + uffd_flags_t flags =3D state->flags; + pmd_t *dst_pmd =3D state->pmd; struct folio *folio; struct page *page; int ret; @@ -436,15 +452,15 @@ static int mfill_atomic_pte_continue(pmd_t *dst_pmd, } =20 /* Handles UFFDIO_POISON for all non-hugetlb VMAs. */ -static int mfill_atomic_pte_poison(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - uffd_flags_t flags) +static int mfill_atomic_pte_poison(struct mfill_state *state) { - int ret; + struct vm_area_struct *dst_vma =3D state->vma; struct mm_struct *dst_mm =3D dst_vma->vm_mm; + unsigned long dst_addr =3D state->dst_addr; + pmd_t *dst_pmd =3D state->pmd; pte_t _dst_pte, *dst_pte; spinlock_t *ptl; + int ret; =20 _dst_pte =3D make_pte_marker(PTE_MARKER_POISONED); ret =3D -EAGAIN; @@ -668,22 +684,20 @@ extern ssize_t mfill_atomic_hugetlb(struct userfaultf= d_ctx *ctx, uffd_flags_t flags); #endif /* CONFIG_HUGETLB_PAGE */ =20 -static __always_inline ssize_t mfill_atomic_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - unsigned long src_addr, - uffd_flags_t flags, - struct folio **foliop) +static __always_inline ssize_t mfill_atomic_pte(struct mfill_state *state) { + struct vm_area_struct *dst_vma =3D state->vma; + unsigned long src_addr =3D state->src_addr; + unsigned long dst_addr =3D state->dst_addr; + struct folio **foliop =3D &state->folio; + uffd_flags_t flags =3D state->flags; + pmd_t *dst_pmd =3D state->pmd; ssize_t err; =20 - if (uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) { - return mfill_atomic_pte_continue(dst_pmd, dst_vma, - dst_addr, flags); - } else if (uffd_flags_mode_is(flags, MFILL_ATOMIC_POISON)) { - return mfill_atomic_pte_poison(dst_pmd, dst_vma, - dst_addr, flags); - } + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) + return mfill_atomic_pte_continue(state); + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_POISON)) + return mfill_atomic_pte_poison(state); =20 /* * The normal page fault path for a shmem will invoke the @@ -697,12 +711,9 @@ static __always_inline ssize_t mfill_atomic_pte(pmd_t = *dst_pmd, */ if (!(dst_vma->vm_flags & VM_SHARED)) { if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) - err =3D mfill_atomic_pte_copy(dst_pmd, dst_vma, - dst_addr, src_addr, - flags, foliop); + err =3D mfill_atomic_pte_copy(state); else - err =3D mfill_atomic_pte_zeropage(dst_pmd, - dst_vma, dst_addr); + err =3D mfill_atomic_pte_zeropage(state); } else { err =3D shmem_mfill_atomic_pte(dst_pmd, dst_vma, dst_addr, src_addr, @@ -718,13 +729,20 @@ static __always_inline ssize_t mfill_atomic(struct us= erfaultfd_ctx *ctx, unsigned long len, uffd_flags_t flags) { + struct mfill_state state =3D (struct mfill_state){ + .ctx =3D ctx, + .dst_start =3D dst_start, + .src_start =3D src_start, + .flags =3D flags, + .len =3D len, + .src_addr =3D src_start, + .dst_addr =3D dst_start, + }; struct mm_struct *dst_mm =3D ctx->mm; struct vm_area_struct *dst_vma; + long copied =3D 0; ssize_t err; pmd_t *dst_pmd; - unsigned long src_addr, dst_addr; - long copied; - struct folio *folio; =20 /* * Sanitize the command parameters: @@ -736,10 +754,6 @@ static __always_inline ssize_t mfill_atomic(struct use= rfaultfd_ctx *ctx, VM_WARN_ON_ONCE(src_start + len <=3D src_start); VM_WARN_ON_ONCE(dst_start + len <=3D dst_start); =20 - src_addr =3D src_start; - dst_addr =3D dst_start; - copied =3D 0; - folio =3D NULL; retry: /* * Make sure the vma is not shared, that the dst range is @@ -750,6 +764,7 @@ static __always_inline ssize_t mfill_atomic(struct user= faultfd_ctx *ctx, err =3D PTR_ERR(dst_vma); goto out; } + state.vma =3D dst_vma; =20 /* * If memory mappings are changing because of non-cooperative @@ -790,12 +805,12 @@ static __always_inline ssize_t mfill_atomic(struct us= erfaultfd_ctx *ctx, uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) goto out_unlock; =20 - while (src_addr < src_start + len) { - pmd_t dst_pmdval; + while (state.src_addr < src_start + len) { + VM_WARN_ON_ONCE(state.dst_addr >=3D dst_start + len); =20 - VM_WARN_ON_ONCE(dst_addr >=3D dst_start + len); + pmd_t dst_pmdval; =20 - dst_pmd =3D mm_alloc_pmd(dst_mm, dst_addr); + dst_pmd =3D mm_alloc_pmd(dst_mm, state.dst_addr); if (unlikely(!dst_pmd)) { err =3D -ENOMEM; break; @@ -827,34 +842,34 @@ static __always_inline ssize_t mfill_atomic(struct us= erfaultfd_ctx *ctx, * tables under us; pte_offset_map_lock() will deal with that. */ =20 - err =3D mfill_atomic_pte(dst_pmd, dst_vma, dst_addr, - src_addr, flags, &folio); + state.pmd =3D dst_pmd; + err =3D mfill_atomic_pte(&state); cond_resched(); =20 if (unlikely(err =3D=3D -ENOENT)) { void *kaddr; =20 up_read(&ctx->map_changing_lock); - uffd_mfill_unlock(dst_vma); - VM_WARN_ON_ONCE(!folio); + uffd_mfill_unlock(state.vma); + VM_WARN_ON_ONCE(!state.folio); =20 - kaddr =3D kmap_local_folio(folio, 0); + kaddr =3D kmap_local_folio(state.folio, 0); err =3D copy_from_user(kaddr, - (const void __user *) src_addr, + (const void __user *)state.src_addr, PAGE_SIZE); kunmap_local(kaddr); if (unlikely(err)) { err =3D -EFAULT; goto out; } - flush_dcache_folio(folio); + flush_dcache_folio(state.folio); goto retry; } else - VM_WARN_ON_ONCE(folio); + VM_WARN_ON_ONCE(state.folio); =20 if (!err) { - dst_addr +=3D PAGE_SIZE; - src_addr +=3D PAGE_SIZE; + state.dst_addr +=3D PAGE_SIZE; + state.src_addr +=3D PAGE_SIZE; copied +=3D PAGE_SIZE; =20 if (fatal_signal_pending(current)) @@ -866,10 +881,10 @@ static __always_inline ssize_t mfill_atomic(struct us= erfaultfd_ctx *ctx, =20 out_unlock: up_read(&ctx->map_changing_lock); - uffd_mfill_unlock(dst_vma); + uffd_mfill_unlock(state.vma); out: - if (folio) - folio_put(folio); + if (state.folio) + folio_put(state.folio); VM_WARN_ON_ONCE(copied < 0); VM_WARN_ON_ONCE(err > 0); VM_WARN_ON_ONCE(!copied && !err); --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6684E35FF5B; Thu, 2 Apr 2026 04:12:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103147; cv=none; b=sDt2tGHryhw4M1mfvG2vQ6mQBmpkk0cHaZgBibsnpNPEGFEbR1BVgSMBJyeW7DXXAL91JZvHDqvooa+II49dqI0mF+76EGEAed/dorOigeV7ON0vuTw1pT4255Qe/AO1e+EfgVWTMIX4lFnUF0HCl4kGxL3BpWxb76wWbyTqv84= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103147; c=relaxed/simple; bh=r29RRcoEme4UqN+o6MMTGc0o3ga+E0dZNYRM9ni1tig=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Z7kU/4Ah1FSO7FhDCNQZCgbmV+wlsiXlbWrW4wM35iJBkp89zl/ngUeU3fgV6lGBwuEu9ogb0lfMK9jdB0m6JeXW3Z1K73xK/Ylzo69WpT8qBVp3VA4T7xIG7w2oxs727yxGjwvLSl+yKXXiUSteyoBqIh51ITEcHqdbv43zmmQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=W0wnJHed; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="W0wnJHed" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9AFF1C19424; Thu, 2 Apr 2026 04:12:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103147; bh=r29RRcoEme4UqN+o6MMTGc0o3ga+E0dZNYRM9ni1tig=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=W0wnJHed8Py1/QjFAsaFbm+ktVcRQhSvRRsO7j8/qGRWjH3U5vvwyCxf0bJ4KQPQO 1arzTZm+aJtyigQO50fSmS7u19Y3yQ7VgvFbfsSKyUPAzBRScBsxpV8JXnBFzQz4uQ +u96RdoVp3uEPPycvXHZVuNBdAM6s7wH6UZWZK81+Rm0ymUd4vNR1HgHwsCJMRjUPD ALs4fw0e4BioIrIr/C9ko0uPbkL6pcA/DVZmdXmesGz6I1dPNfCIoThVdQDdRV/MUz Gqv16BEn6zNP5UFoikllMyeLKP7F4NViLB0Glc+YIZNbWco/xNS0ucpctdpW0P/PJe +f25Wy/Ruj5qQ== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, "Harry Yoo (Oracle)" Subject: [PATCH v4 03/15] userfaultfd: introduce mfill_establish_pmd() helper Date: Thu, 2 Apr 2026 07:11:44 +0300 Message-ID: <20260402041156.1377214-4-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" 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(). Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Harry Yoo (Oracle) --- mm/userfaultfd.c | 102 ++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index c4074b6f4aca..f653fb3f74df 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -157,6 +157,56 @@ static void uffd_mfill_unlock(struct vm_area_struct *v= ma) } #endif =20 +static pmd_t *mm_alloc_pmd(struct mm_struct *mm, unsigned long address) +{ + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + + pgd =3D pgd_offset(mm, address); + p4d =3D p4d_alloc(mm, pgd, address); + if (!p4d) + return NULL; + pud =3D pud_alloc(mm, p4d, address); + if (!pud) + return NULL; + /* + * Note that we didn't run this because the pmd was + * missing, the *pmd may be already established and in + * turn it may also be a trans_huge_pmd. + */ + return pmd_alloc(mm, pud, address); +} + +static int mfill_establish_pmd(struct mfill_state *state) +{ + struct mm_struct *dst_mm =3D state->ctx->mm; + pmd_t *dst_pmd, dst_pmdval; + + dst_pmd =3D mm_alloc_pmd(dst_mm, state->dst_addr); + if (unlikely(!dst_pmd)) + return -ENOMEM; + + dst_pmdval =3D pmdp_get_lockless(dst_pmd); + if (unlikely(pmd_none(dst_pmdval)) && + unlikely(__pte_alloc(dst_mm, dst_pmd))) + return -ENOMEM; + + dst_pmdval =3D pmdp_get_lockless(dst_pmd); + /* + * If the dst_pmd is THP don't override it and just be strict. + * (This includes the case where the PMD used to be THP and + * changed back to none after __pte_alloc().) + */ + if (unlikely(!pmd_present(dst_pmdval) || pmd_leaf(dst_pmdval))) + return -EEXIST; + if (unlikely(pmd_bad(dst_pmdval))) + return -EFAULT; + + state->pmd =3D dst_pmd; + return 0; +} + /* Check if dst_addr is outside of file's size. Must be called with ptl he= ld. */ static bool mfill_file_over_size(struct vm_area_struct *dst_vma, unsigned long dst_addr) @@ -489,27 +539,6 @@ static int mfill_atomic_pte_poison(struct mfill_state = *state) return ret; } =20 -static pmd_t *mm_alloc_pmd(struct mm_struct *mm, unsigned long address) -{ - pgd_t *pgd; - p4d_t *p4d; - pud_t *pud; - - pgd =3D pgd_offset(mm, address); - p4d =3D p4d_alloc(mm, pgd, address); - if (!p4d) - return NULL; - pud =3D pud_alloc(mm, p4d, address); - if (!pud) - return NULL; - /* - * Note that we didn't run this because the pmd was - * missing, the *pmd may be already established and in - * turn it may also be a trans_huge_pmd. - */ - return pmd_alloc(mm, pud, address); -} - #ifdef CONFIG_HUGETLB_PAGE /* * mfill_atomic processing for HUGETLB vmas. Note that this routine is @@ -742,7 +771,6 @@ static __always_inline ssize_t mfill_atomic(struct user= faultfd_ctx *ctx, struct vm_area_struct *dst_vma; long copied =3D 0; ssize_t err; - pmd_t *dst_pmd; =20 /* * Sanitize the command parameters: @@ -808,41 +836,15 @@ static __always_inline ssize_t mfill_atomic(struct us= erfaultfd_ctx *ctx, while (state.src_addr < src_start + len) { VM_WARN_ON_ONCE(state.dst_addr >=3D dst_start + len); =20 - pmd_t dst_pmdval; - - dst_pmd =3D mm_alloc_pmd(dst_mm, state.dst_addr); - if (unlikely(!dst_pmd)) { - err =3D -ENOMEM; + err =3D mfill_establish_pmd(&state); + if (err) break; - } =20 - dst_pmdval =3D pmdp_get_lockless(dst_pmd); - if (unlikely(pmd_none(dst_pmdval)) && - unlikely(__pte_alloc(dst_mm, dst_pmd))) { - err =3D -ENOMEM; - break; - } - dst_pmdval =3D pmdp_get_lockless(dst_pmd); - /* - * If the dst_pmd is THP don't override it and just be strict. - * (This includes the case where the PMD used to be THP and - * changed back to none after __pte_alloc().) - */ - if (unlikely(!pmd_present(dst_pmdval) || - pmd_trans_huge(dst_pmdval))) { - err =3D -EEXIST; - break; - } - if (unlikely(pmd_bad(dst_pmdval))) { - err =3D -EFAULT; - break; - } /* * For shmem mappings, khugepaged is allowed to remove page * tables under us; pte_offset_map_lock() will deal with that. */ =20 - state.pmd =3D dst_pmd; err =3D mfill_atomic_pte(&state); cond_resched(); =20 --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6144A35BDC2; Thu, 2 Apr 2026 04:12:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103154; cv=none; b=bawP4C/O894nuCJu01IckFFYpmXPe1vDbyfwrhQqlh2t+8m0Kv/dlU9+tzaSqKMTXqChkbTo3x4Goxigh/0BkRwW7bT647J0nNF8/cEBvZMNHfpjn3tXCEWfez57fV+Av8Zsx+rKj0+QINgzb0MiWiMjaV5W7BV1BXOGh6vi8ns= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103154; c=relaxed/simple; bh=ioV0AIuZ8YXMYtun9GJuzmLOOslMKT49lU11YBPNu3A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=I6l0a34HAsa3KvlNRJhhGVJ/3I0xoFgWsKdfhov+VjyVc/DfdClUoy65KVyGYCxOxkwzfSwdWytJ+mIXU+FhihpFl4Ge7x2iNIgl0jsMSXURk6vzbOA5/OFU3XrPPlHcWAfW+z/mqNeq7abMXJHjoj2wCXhwpZdaVlXnP+tKWUs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=O6LpCCvm; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="O6LpCCvm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 990E0C19423; Thu, 2 Apr 2026 04:12:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103153; bh=ioV0AIuZ8YXMYtun9GJuzmLOOslMKT49lU11YBPNu3A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=O6LpCCvmyoASnA3Ul3G2H5VZP5ja5GFeNlfotTaUKFNEDJPBWQasBquvOFTxmwGkX 2Upft2ZfOHlPacRUK7hKJ4YWFsDP8lD9x7bySPvv4lBy5/UG0AiFm4Ol2PM5i2/iJ1 Yv61x1HnIEf/9L68PpHQkRLw0bsKdaXhzqGCBJCoSsfAuex6zu7ZBwJ3lQDiqjWFRE NhIoFNOU1Pv0K5Z6sMOtdiK4AwCqaW+XDKlgPwPV7nI03LG5YADmDduRjqjE3bcAiu Ygw1t0jmuQ3T5xJM1ZQvljWHrzU/5MAjRUmPVAF7L3I/U+C7JjqPjlu88cFRvLFqcv vtCJiSnyz6AJQ== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 04/15] userfaultfd: introduce mfill_get_vma() and mfill_put_vma() Date: Thu, 2 Apr 2026 07:11:45 +0300 Message-ID: <20260402041156.1377214-5-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" Split the code that finds, locks and verifies VMA from mfill_atomic() into a helper function. This function will be used later during refactoring of mfill_atomic_pte_copy(). Add a counterpart mfill_put_vma() helper that unlocks the VMA and releases map_changing_lock. [avagin@google.com: fix lock leak in mfill_get_vma()] Link: https://lkml.kernel.org/r/20260316173829.1126728-1-avagin@google.com Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Andrei Vagin Reviewed-by: Harry Yoo (Oracle) --- mm/userfaultfd.c | 125 ++++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index f653fb3f74df..c6a38db45343 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -157,6 +157,75 @@ static void uffd_mfill_unlock(struct vm_area_struct *v= ma) } #endif =20 +static void mfill_put_vma(struct mfill_state *state) +{ + if (!state->vma) + return; + + up_read(&state->ctx->map_changing_lock); + uffd_mfill_unlock(state->vma); + state->vma =3D NULL; +} + +static int mfill_get_vma(struct mfill_state *state) +{ + struct userfaultfd_ctx *ctx =3D state->ctx; + uffd_flags_t flags =3D state->flags; + struct vm_area_struct *dst_vma; + int err; + + /* + * Make sure the vma is not shared, that the dst range is + * both valid and fully within a single existing vma. + */ + dst_vma =3D uffd_mfill_lock(ctx->mm, state->dst_start, state->len); + if (IS_ERR(dst_vma)) + return PTR_ERR(dst_vma); + + /* + * If memory mappings are changing because of non-cooperative + * operation (e.g. mremap) running in parallel, bail out and + * request the user to retry later + */ + down_read(&ctx->map_changing_lock); + state->vma =3D dst_vma; + err =3D -EAGAIN; + if (atomic_read(&ctx->mmap_changing)) + goto out_unlock; + + err =3D -EINVAL; + + /* + * shmem_zero_setup is invoked in mmap for MAP_ANONYMOUS|MAP_SHARED but + * it will overwrite vm_ops, so vma_is_anonymous must return false. + */ + if (WARN_ON_ONCE(vma_is_anonymous(dst_vma) && + dst_vma->vm_flags & VM_SHARED)) + goto out_unlock; + + /* + * validate 'mode' now that we know the dst_vma: don't allow + * a wrprotect copy if the userfaultfd didn't register as WP. + */ + if ((flags & MFILL_ATOMIC_WP) && !(dst_vma->vm_flags & VM_UFFD_WP)) + goto out_unlock; + + if (is_vm_hugetlb_page(dst_vma)) + return 0; + + if (!vma_is_anonymous(dst_vma) && !vma_is_shmem(dst_vma)) + goto out_unlock; + if (!vma_is_shmem(dst_vma) && + uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) + goto out_unlock; + + return 0; + +out_unlock: + mfill_put_vma(state); + return err; +} + static pmd_t *mm_alloc_pmd(struct mm_struct *mm, unsigned long address) { pgd_t *pgd; @@ -767,8 +836,6 @@ static __always_inline ssize_t mfill_atomic(struct user= faultfd_ctx *ctx, .src_addr =3D src_start, .dst_addr =3D dst_start, }; - struct mm_struct *dst_mm =3D ctx->mm; - struct vm_area_struct *dst_vma; long copied =3D 0; ssize_t err; =20 @@ -783,56 +850,17 @@ static __always_inline ssize_t mfill_atomic(struct us= erfaultfd_ctx *ctx, VM_WARN_ON_ONCE(dst_start + len <=3D dst_start); =20 retry: - /* - * Make sure the vma is not shared, that the dst range is - * both valid and fully within a single existing vma. - */ - dst_vma =3D uffd_mfill_lock(dst_mm, dst_start, len); - if (IS_ERR(dst_vma)) { - err =3D PTR_ERR(dst_vma); + err =3D mfill_get_vma(&state); + if (err) goto out; - } - state.vma =3D dst_vma; - - /* - * If memory mappings are changing because of non-cooperative - * operation (e.g. mremap) running in parallel, bail out and - * request the user to retry later - */ - down_read(&ctx->map_changing_lock); - err =3D -EAGAIN; - if (atomic_read(&ctx->mmap_changing)) - goto out_unlock; - - err =3D -EINVAL; - /* - * shmem_zero_setup is invoked in mmap for MAP_ANONYMOUS|MAP_SHARED but - * it will overwrite vm_ops, so vma_is_anonymous must return false. - */ - if (WARN_ON_ONCE(vma_is_anonymous(dst_vma) && - dst_vma->vm_flags & VM_SHARED)) - goto out_unlock; - - /* - * validate 'mode' now that we know the dst_vma: don't allow - * a wrprotect copy if the userfaultfd didn't register as WP. - */ - if ((flags & MFILL_ATOMIC_WP) && !(dst_vma->vm_flags & VM_UFFD_WP)) - goto out_unlock; =20 /* * If this is a HUGETLB vma, pass off to appropriate routine */ - if (is_vm_hugetlb_page(dst_vma)) - return mfill_atomic_hugetlb(ctx, dst_vma, dst_start, + if (is_vm_hugetlb_page(state.vma)) + return mfill_atomic_hugetlb(ctx, state.vma, dst_start, src_start, len, flags); =20 - if (!vma_is_anonymous(dst_vma) && !vma_is_shmem(dst_vma)) - goto out_unlock; - if (!vma_is_shmem(dst_vma) && - uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) - goto out_unlock; - while (state.src_addr < src_start + len) { VM_WARN_ON_ONCE(state.dst_addr >=3D dst_start + len); =20 @@ -851,8 +879,7 @@ static __always_inline ssize_t mfill_atomic(struct user= faultfd_ctx *ctx, if (unlikely(err =3D=3D -ENOENT)) { void *kaddr; =20 - up_read(&ctx->map_changing_lock); - uffd_mfill_unlock(state.vma); + mfill_put_vma(&state); VM_WARN_ON_ONCE(!state.folio); =20 kaddr =3D kmap_local_folio(state.folio, 0); @@ -881,9 +908,7 @@ static __always_inline ssize_t mfill_atomic(struct user= faultfd_ctx *ctx, break; } =20 -out_unlock: - up_read(&ctx->map_changing_lock); - uffd_mfill_unlock(state.vma); + mfill_put_vma(&state); out: if (state.folio) folio_put(state.folio); --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F0D1C35E92D; Thu, 2 Apr 2026 04:12:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103161; cv=none; b=gOFJjTZM87zMR0Jc6wD5L4C9QziwiT/Hkv5M6LXPtsvzD32c3Q3VcZM5EsyzSndmkbVPGv7PYH9VoOmq3ixS+pCr25qh1D+ZCUwj0Fx9iuXvSu37EBGfZz1ZkuKRu3MdfpjvGUJysOWxf/tACyUUlxdrGfHT5UIKyX22pLrOoyc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103161; c=relaxed/simple; bh=5SzGyiFXZk7l7ucReQ40iRkaPGcy2CcX6r/21eMkk2I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ju9ejd7nMf2gtQWF2iJWtmtou3wvxUMbR5LEiBpUf9dsFrq4PcgXjOIBLHJmLuz/aSXAtrtmOsqgY01WzV4MOMaqUA6UQMWuUX5SlXibnmAtf1DSX7ktBpF9fY8W65j2fHvE0fr20LgaI5dTsbkTc+ZCD1CC2Gb1fBM7nqRzyAk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nb8I1ZCl; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nb8I1ZCl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 63406C19424; Thu, 2 Apr 2026 04:12:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103160; bh=5SzGyiFXZk7l7ucReQ40iRkaPGcy2CcX6r/21eMkk2I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nb8I1ZClTDvPDGKxLBHAn3NCmEgGMU/AnhVCCtaCop4gpA26w+lGeVCMGevXI03Z7 ouBoPoNz1kFRSoUMyRWbj1Uwc6X15ppsOD1eoF7JwetBZZkgI1ZHeU62VnAhCzCpdV CgElsa9j93OX2uIsN8NVJVA1uAtcfmhXIYHd7hzfv5sMbFTLGYOor6KuIM1dcmms/p 2a0yzV0XuFg42PyKC66b+CK3kiM9NjbZh8qz+GI0RQX8A0MyIF++VDQ24nPRn6dKU8 da5kdRFrmmwwVlmhYmd9FFBvegTJuX2GBYV+d7U1E8pq8yGjXiCQIJ/Y82soByxhIj z1dktaok5sFiA== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 05/15] userfaultfd: retry copying with locks dropped in mfill_atomic_pte_copy() Date: Thu, 2 Apr 2026 07:11:46 +0300 Message-ID: <20260402041156.1377214-6-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" Implementation of UFFDIO_COPY for anonymous memory might fail to copy data from userspace buffer when the destination VMA is locked (either with mm_lock or with per-VMA lock). In that case, mfill_atomic() releases the locks, retries copying the data with locks dropped and then re-locks the destination VMA and re-establishes PMD. Since this retry-reget dance is only relevant for UFFDIO_COPY and it never happens for other UFFDIO_ operations, make it a part of mfill_atomic_pte_copy() that actually implements UFFDIO_COPY for anonymous memory. As a temporal safety measure to avoid breaking biscection mfill_atomic_pte_copy() makes sure to never return -ENOENT so that the loop in mfill_atomic() won't retry copiyng outside of mmap_lock. This is removed later when shmem implementation will be updated later and the loop in mfill_atomic() will be adjusted. [akpm@linux-foundation.org: update mfill_copy_folio_retry()] Link: https://lkml.kernel.org/r/20260316173829.1126728-1-avagin@google.com Link: https://lkml.kernel.org/r/20260306171815.3160826-6-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Andrew Morton Reviewed-by: Harry Yoo (Oracle) --- mm/userfaultfd.c | 75 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index c6a38db45343..82e1a3255e1e 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -405,35 +405,63 @@ static int mfill_copy_folio_locked(struct folio *foli= o, unsigned long src_addr) return ret; } =20 +static int mfill_copy_folio_retry(struct mfill_state *state, struct folio = *folio) +{ + unsigned long src_addr =3D state->src_addr; + void *kaddr; + int err; + + /* retry copying with mm_lock dropped */ + mfill_put_vma(state); + + kaddr =3D kmap_local_folio(folio, 0); + err =3D copy_from_user(kaddr, (const void __user *) src_addr, PAGE_SIZE); + kunmap_local(kaddr); + if (unlikely(err)) + return -EFAULT; + + flush_dcache_folio(folio); + + /* reget VMA and PMD, they could change underneath us */ + err =3D mfill_get_vma(state); + if (err) + return err; + + err =3D mfill_establish_pmd(state); + if (err) + return err; + + return 0; +} + static int mfill_atomic_pte_copy(struct mfill_state *state) { - struct vm_area_struct *dst_vma =3D state->vma; unsigned long dst_addr =3D state->dst_addr; unsigned long src_addr =3D state->src_addr; uffd_flags_t flags =3D state->flags; - pmd_t *dst_pmd =3D state->pmd; struct folio *folio; int ret; =20 - if (!state->folio) { - ret =3D -ENOMEM; - folio =3D vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0, dst_vma, - dst_addr); - if (!folio) - goto out; + folio =3D vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0, state->vma, dst_addr); + if (!folio) + return -ENOMEM; =20 - ret =3D mfill_copy_folio_locked(folio, src_addr); + ret =3D -ENOMEM; + if (mem_cgroup_charge(folio, state->vma->vm_mm, GFP_KERNEL)) + goto out_release; =20 - /* fallback to copy_from_user outside mmap_lock */ - if (unlikely(ret)) { - ret =3D -ENOENT; - state->folio =3D folio; - /* don't free the page */ - goto out; - } - } else { - folio =3D state->folio; - state->folio =3D NULL; + ret =3D mfill_copy_folio_locked(folio, src_addr); + if (unlikely(ret)) { + /* + * Fallback to copy_from_user outside mmap_lock. + * If retry is successful, mfill_copy_folio_locked() returns + * with locks retaken by mfill_get_vma(). + * If there was an error, we must mfill_put_vma() anyway and it + * will take care of unlocking if needed. + */ + ret =3D mfill_copy_folio_retry(state, folio); + if (ret) + goto out_release; } =20 /* @@ -443,17 +471,16 @@ static int mfill_atomic_pte_copy(struct mfill_state *= state) */ __folio_mark_uptodate(folio); =20 - ret =3D -ENOMEM; - if (mem_cgroup_charge(folio, dst_vma->vm_mm, GFP_KERNEL)) - goto out_release; - - ret =3D mfill_atomic_install_pte(dst_pmd, dst_vma, dst_addr, + ret =3D mfill_atomic_install_pte(state->pmd, state->vma, dst_addr, &folio->page, true, flags); if (ret) goto out_release; out: return ret; out_release: + /* Don't return -ENOENT so that our caller won't retry */ + if (ret =3D=3D -ENOENT) + ret =3D -EFAULT; folio_put(folio); goto out; } --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 065432D0C9D; Thu, 2 Apr 2026 04:12:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103168; cv=none; b=o3Zkoz9He+NHr4vh5CrfC4OTNY++5evM7FLuwOppmxinHgLeOaKdfjH6Ys8G246W+AnJTY1flUnFcslnGnRC5odoXzh/tfwtkbb+0Frlw/6+M7HWqk9wtXg80sv1CaVcsSfR0/urumWXXxH8hPNaEY6WnAPXeO/WEOqpUSi2NTQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103168; c=relaxed/simple; bh=KXI5bMAAl/JthbpgvCWeg45Zai/zTi+2zYeJ12CGtXQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=L1vtgkuD369gOdtxmethMLKVr2gi/aw8mUGyoMN8dqHI4vmrapuJTANS91kLaRDY12piEcI/YH5CKSbAIiZwEWNAwUC8ZIJ59M1rLmrf/KGxj9hC5Uv021CVMvW6ooyg1ti1sO60B5cMHtAB44cAW1hMClAf+LwpNvOUm499GUc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nzAN/Pwg; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nzAN/Pwg" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2D665C19423; Thu, 2 Apr 2026 04:12:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103167; bh=KXI5bMAAl/JthbpgvCWeg45Zai/zTi+2zYeJ12CGtXQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nzAN/PwgIhRrXh6o5q6czyx+j2qGkpgSR9hhBZHWcg1db6kDw2la96KkXrHtUesC7 Tm7y161q2JIRVC7hoUR4rlJg1P0VFJLO62HPhdF8MTfxciMMdcPzRap/2LALcbFHel I5BGurbQjYKjEoWYpj3dNsbCAKQdt1tyXNFiCThckleb1wb8x9yzNbbTLR6iKhtLWj kbsEU1yZZ2vGqgkUXPBv316tlVxnf5mjXM855VBXtE1izHv19rF85ftU59w14hLwMq gxl+yuXyzZDcQZm04VdpvGHeTFZZrGq6r/daHFZ8gLwpnZAtIN3cdYt9i6XZHA5Uie C/GlvqKPQVhCQ== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 06/15] userfaultfd: move vma_can_userfault out of line Date: Thu, 2 Apr 2026 07:11:47 +0300 Message-ID: <20260402041156.1377214-7-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" vma_can_userfault() has grown pretty big and it's not called on performance critical path. Move it out of line. No functional changes. Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: David Hildenbrand (Red Hat) Reviewed-by: Liam R. Howlett --- include/linux/userfaultfd_k.h | 35 ++--------------------------------- mm/userfaultfd.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index fd5f42765497..a49cf750e803 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -208,39 +208,8 @@ static inline bool userfaultfd_armed(struct vm_area_st= ruct *vma) return vma->vm_flags & __VM_UFFD_FLAGS; } =20 -static inline bool vma_can_userfault(struct vm_area_struct *vma, - vm_flags_t vm_flags, - bool wp_async) -{ - vm_flags &=3D __VM_UFFD_FLAGS; - - if (vma->vm_flags & VM_DROPPABLE) - return false; - - if ((vm_flags & VM_UFFD_MINOR) && - (!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma))) - return false; - - /* - * If wp async enabled, and WP is the only mode enabled, allow any - * memory type. - */ - if (wp_async && (vm_flags =3D=3D VM_UFFD_WP)) - return true; - - /* - * If user requested uffd-wp but not enabled pte markers for - * uffd-wp, then shmem & hugetlbfs are not supported but only - * anonymous. - */ - if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) && - !vma_is_anonymous(vma)) - return false; - - /* By default, allow any of anon|shmem|hugetlb */ - return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) || - vma_is_shmem(vma); -} +bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, + bool wp_async); =20 static inline bool vma_has_uffd_without_event_remap(struct vm_area_struct = *vma) { diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 82e1a3255e1e..d51e080e9216 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -2018,6 +2018,39 @@ ssize_t move_pages(struct userfaultfd_ctx *ctx, unsi= gned long dst_start, return moved ? moved : err; } =20 +bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, + bool wp_async) +{ + vm_flags &=3D __VM_UFFD_FLAGS; + + if (vma->vm_flags & VM_DROPPABLE) + return false; + + if ((vm_flags & VM_UFFD_MINOR) && + (!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma))) + return false; + + /* + * If wp async enabled, and WP is the only mode enabled, allow any + * memory type. + */ + if (wp_async && (vm_flags =3D=3D VM_UFFD_WP)) + return true; + + /* + * If user requested uffd-wp but not enabled pte markers for + * uffd-wp, then shmem & hugetlbfs are not supported but only + * anonymous. + */ + if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) && + !vma_is_anonymous(vma)) + return false; + + /* By default, allow any of anon|shmem|hugetlb */ + return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) || + vma_is_shmem(vma); +} + static void userfaultfd_set_vm_flags(struct vm_area_struct *vma, vm_flags_t vm_flags) { --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 89AF535F193; Thu, 2 Apr 2026 04:12:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103174; cv=none; b=Lrd0bzCls7bG9Uo2SkKSEy58yd9kof2BhvSQh+JDZY8k+zN0VddIFN8pBZMCFXW3ZUmmuq4ZsT+zY6/dBEq5MFLWzuHVtmvXy0ugUUX+fPMAAt0OxryL0GAXk148FKvvgxOmjvEFqc64ww/cm/+or0dTMSmrIojH4eoIru7TQ04= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103174; c=relaxed/simple; bh=u+ZOOG8J/28GYuaj/GsT8WwOacoYSYHh4DClfNVux6U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lDOQJ+QLEEi/EuW+tKI4pXkG6/D17MmYgvYApeS0veZf2hbZPFETgTDyYFREqJwTT/UCgKBth5hiiUTravpLXtIDxl74kYQrRM2glnV4MjVIKY34VRWML2aj6jTTOfySS6FiGaEDCijF3itdVWa2oeHAGb1Odtj5nXp0vj8qigA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TdSj8YsX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="TdSj8YsX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id ECC46C19424; Thu, 2 Apr 2026 04:12:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103174; bh=u+ZOOG8J/28GYuaj/GsT8WwOacoYSYHh4DClfNVux6U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TdSj8YsXBms6JMAXx3iKSbSOLf55gYfGk7tRtNBmt2PPcCj1b72nOaSt/Gln8t2zP XjsJg346ucFFyRRuzQXzhxyks7T2lEdZ7ogc9gnOrdgaQ5QjP8V0VvOE9a5FBTY1Cw ORQqpoFHeKJ23FSOWzoATxDQaoQVb96FHm3eknirxT8Fkankpbs2lQ3wLr09EAukiX WXPsaCuhxUxonyH+iV5G/7Yob6GwVhzWX+FTATjT4GetWJyJYxwYXy5IDebYViHGgV Frk9QL5Uw5aOkd8BWjWdkNmuioCwS42Tt98HOL740axM7BFjNYNo/28E+iFJoKYRCz ppKu1Rx0OwaRQ== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 07/15] userfaultfd: introduce vm_uffd_ops Date: Thu, 2 Apr 2026 07:11:48 +0300 Message-ID: <20260402041156.1377214-8-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" Current userfaultfd implementation works only with memory managed by core MM: anonymous, shmem and hugetlb. First, there is no fundamental reason to limit userfaultfd support only to the core memory types and userfaults can be handled similarly to regular page faults provided a VMA owner implements appropriate callbacks. Second, historically various code paths were conditioned on vma_is_anonymous(), vma_is_shmem() and is_vm_hugetlb_page() and some of these conditions can be expressed as operations implemented by a particular memory type. Introduce vm_uffd_ops extension to vm_operations_struct that will delegate memory type specific operations to a VMA owner. Operations for anonymous memory are handled internally in userfaultfd using anon_uffd_ops that implicitly assigned to anonymous VMAs. Start with a single operation, ->can_userfault() that will verify that a VMA meets requirements for userfaultfd support at registration time. Implement that method for anonymous, shmem and hugetlb and move relevant parts of vma_can_userfault() into the new callbacks. Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/mm.h | 5 ++++ include/linux/userfaultfd_k.h | 6 +++++ mm/hugetlb.c | 15 ++++++++++++ mm/shmem.c | 15 ++++++++++++ mm/userfaultfd.c | 44 ++++++++++++++++++++++++----------- 5 files changed, 72 insertions(+), 13 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index abb4963c1f06..0fbeb8012983 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -741,6 +741,8 @@ struct vm_fault { */ }; =20 +struct vm_uffd_ops; + /* * These are the virtual MM functions - opening of an area, closing and * unmapping it (needed to keep files on disk up-to-date etc), pointer @@ -826,6 +828,9 @@ struct vm_operations_struct { struct page *(*find_normal_page)(struct vm_area_struct *vma, unsigned long addr); #endif /* CONFIG_FIND_NORMAL_PAGE */ +#ifdef CONFIG_USERFAULTFD + const struct vm_uffd_ops *uffd_ops; +#endif }; =20 #ifdef CONFIG_NUMA_BALANCING diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index a49cf750e803..56e85ab166c7 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -80,6 +80,12 @@ struct userfaultfd_ctx { =20 extern vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long rea= son); =20 +/* VMA userfaultfd operations */ +struct vm_uffd_ops { + /* Checks if a VMA can support userfaultfd */ + bool (*can_userfault)(struct vm_area_struct *vma, vm_flags_t vm_flags); +}; + /* A combined operation mode + behavior flags. */ typedef unsigned int __bitwise uffd_flags_t; =20 diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 327eaa4074d3..530bc4337be6 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -4818,6 +4818,18 @@ static vm_fault_t hugetlb_vm_op_fault(struct vm_faul= t *vmf) return 0; } =20 +#ifdef CONFIG_USERFAULTFD +static bool hugetlb_can_userfault(struct vm_area_struct *vma, + vm_flags_t vm_flags) +{ + return true; +} + +static const struct vm_uffd_ops hugetlb_uffd_ops =3D { + .can_userfault =3D hugetlb_can_userfault, +}; +#endif + /* * When a new function is introduced to vm_operations_struct and added * to hugetlb_vm_ops, please consider adding the function to shm_vm_ops. @@ -4831,6 +4843,9 @@ const struct vm_operations_struct hugetlb_vm_ops =3D { .close =3D hugetlb_vm_op_close, .may_split =3D hugetlb_vm_op_split, .pagesize =3D hugetlb_vm_op_pagesize, +#ifdef CONFIG_USERFAULTFD + .uffd_ops =3D &hugetlb_uffd_ops, +#endif }; =20 static pte_t make_huge_pte(struct vm_area_struct *vma, struct folio *folio, diff --git a/mm/shmem.c b/mm/shmem.c index b40f3cd48961..f2a25805b9bf 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3294,6 +3294,15 @@ int shmem_mfill_atomic_pte(pmd_t *dst_pmd, shmem_inode_unacct_blocks(inode, 1); return ret; } + +static bool shmem_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_= flags) +{ + return true; +} + +static const struct vm_uffd_ops shmem_uffd_ops =3D { + .can_userfault =3D shmem_can_userfault, +}; #endif /* CONFIG_USERFAULTFD */ =20 #ifdef CONFIG_TMPFS @@ -5313,6 +5322,9 @@ static const struct vm_operations_struct shmem_vm_ops= =3D { .set_policy =3D shmem_set_policy, .get_policy =3D shmem_get_policy, #endif +#ifdef CONFIG_USERFAULTFD + .uffd_ops =3D &shmem_uffd_ops, +#endif }; =20 static const struct vm_operations_struct shmem_anon_vm_ops =3D { @@ -5322,6 +5334,9 @@ static const struct vm_operations_struct shmem_anon_v= m_ops =3D { .set_policy =3D shmem_set_policy, .get_policy =3D shmem_get_policy, #endif +#ifdef CONFIG_USERFAULTFD + .uffd_ops =3D &shmem_uffd_ops, +#endif }; =20 int shmem_init_fs_context(struct fs_context *fc) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index d51e080e9216..e3024a39c19d 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -34,6 +34,25 @@ struct mfill_state { pmd_t *pmd; }; =20 +static bool anon_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_f= lags) +{ + /* anonymous memory does not support MINOR mode */ + if (vm_flags & VM_UFFD_MINOR) + return false; + return true; +} + +static const struct vm_uffd_ops anon_uffd_ops =3D { + .can_userfault =3D anon_can_userfault, +}; + +static const struct vm_uffd_ops *vma_uffd_ops(struct vm_area_struct *vma) +{ + if (vma_is_anonymous(vma)) + return &anon_uffd_ops; + return vma->vm_ops ? vma->vm_ops->uffd_ops : NULL; +} + static __always_inline bool validate_dst_vma(struct vm_area_struct *dst_vma, unsigned long dst_en= d) { @@ -2021,34 +2040,33 @@ ssize_t move_pages(struct userfaultfd_ctx *ctx, uns= igned long dst_start, bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags, bool wp_async) { - vm_flags &=3D __VM_UFFD_FLAGS; + const struct vm_uffd_ops *ops =3D vma_uffd_ops(vma); =20 - if (vma->vm_flags & VM_DROPPABLE) - return false; - - if ((vm_flags & VM_UFFD_MINOR) && - (!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma))) - return false; + vm_flags &=3D __VM_UFFD_FLAGS; =20 /* - * If wp async enabled, and WP is the only mode enabled, allow any + * If WP is the only mode enabled and context is wp async, allow any * memory type. */ if (wp_async && (vm_flags =3D=3D VM_UFFD_WP)) return true; =20 + /* For any other mode reject VMAs that don't implement vm_uffd_ops */ + if (!ops) + return false; + + if (vma->vm_flags & VM_DROPPABLE) + return false; + /* * If user requested uffd-wp but not enabled pte markers for - * uffd-wp, then shmem & hugetlbfs are not supported but only - * anonymous. + * uffd-wp, then only anonymous memory is supported */ if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) && !vma_is_anonymous(vma)) return false; =20 - /* By default, allow any of anon|shmem|hugetlb */ - return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) || - vma_is_shmem(vma); + return ops->can_userfault(vma, vm_flags); } =20 static void userfaultfd_set_vm_flags(struct vm_area_struct *vma, --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2A2F02D0C9D; Thu, 2 Apr 2026 04:13:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103181; cv=none; b=GoVCUBD/11yyqdW6cB/DmamyxP1Ptb+ttP9ge32INePIQlw1H1fEg+VRQc0k6f4A1rSdsZeE18gUmDkHbUXp0cUxampLu8B9d0AgLS6XqUqO0mzcqClcpgp5P8DEYKU7q4SH0RUl4eDMc1WNkrI5puM6Frd52QvHb5GN3SaJkTk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103181; c=relaxed/simple; bh=lxXvlQh8xQ0EBf3to4Jw9wikE7NXPrSIjYbQ7Uoyt4w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WDR3j4Na73+e2ilaSZnv1fvIrHkZ3KMuMGkU0d2N048owQSH1rbDn6qFvf9t2jHw+3OK0eSf7yI+1Vej/f7kasnkvSSQClVqsROCAyBGq02eCJM+l/fOrnFsfsr+pYyXDJoUm3JNXScVhRsfiNaWhQHlkMD8IremOfLuL0o1Yt0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NIZNqIdp; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NIZNqIdp" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B74D5C19423; Thu, 2 Apr 2026 04:12:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103181; bh=lxXvlQh8xQ0EBf3to4Jw9wikE7NXPrSIjYbQ7Uoyt4w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NIZNqIdpzm6+W6HPk4rUKciovxte+Ir8R8/epNSPXK4ip3mfFRx//HB3VsV4IcxxY grhk26iS3k+sEk5UN3RQPGXSmhhMYLSeaXHgHVluotjZ6QucMngudS0xvMQNTYyXnd fPX67hTxkQB0+QkM0QhDxLlU5z8oq1o45ObGQwWRFVtkvVRHiPuW5c45f/knY49Lxo bz+Svo6RhYvPkYQaPWYDCZy5cAXqxiuCP3BS1sV6yFr0lF3y+Fv8D/EX7kM03kDhdP d6LWFm8CkHHdTroVD7ewqX5/jlazhuinnayqlFjpEcdv3LiuGb8upVQNvMeIAdvlqF nsIyaZ2SbjyKg== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 08/15] shmem, userfaultfd: use a VMA callback to handle UFFDIO_CONTINUE Date: Thu, 2 Apr 2026 07:11:49 +0300 Message-ID: <20260402041156.1377214-9-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" When userspace resolves a page fault in a shmem VMA with UFFDIO_CONTINUE it needs to get a folio that already exists in the pagecache backing that VMA. Instead of using shmem_get_folio() for that, add a get_folio_noalloc() method to 'struct vm_uffd_ops' that will return a folio if it exists in the VMA's pagecache at given pgoff. Implement get_folio_noalloc() method for shmem and slightly refactor userfaultfd's mfill_get_vma() and mfill_atomic_pte_continue() to support this new API. Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: James Houghton --- include/linux/userfaultfd_k.h | 7 +++++++ mm/shmem.c | 15 ++++++++++++++- mm/userfaultfd.c | 34 ++++++++++++++++++---------------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index 56e85ab166c7..66dfc3c164e6 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -84,6 +84,13 @@ extern vm_fault_t handle_userfault(struct vm_fault *vmf,= unsigned long reason); struct vm_uffd_ops { /* Checks if a VMA can support userfaultfd */ bool (*can_userfault)(struct vm_area_struct *vma, vm_flags_t vm_flags); + /* + * Called to resolve UFFDIO_CONTINUE request. + * Should return the folio found at pgoff in the VMA's pagecache if it + * exists or ERR_PTR otherwise. + * The returned folio is locked and with reference held. + */ + struct folio *(*get_folio_noalloc)(struct inode *inode, pgoff_t pgoff); }; =20 /* A combined operation mode + behavior flags. */ diff --git a/mm/shmem.c b/mm/shmem.c index f2a25805b9bf..7bd887b64f62 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3295,13 +3295,26 @@ int shmem_mfill_atomic_pte(pmd_t *dst_pmd, return ret; } =20 +static struct folio *shmem_get_folio_noalloc(struct inode *inode, pgoff_t = pgoff) +{ + struct folio *folio; + int err; + + err =3D shmem_get_folio(inode, pgoff, 0, &folio, SGP_NOALLOC); + if (err) + return ERR_PTR(err); + + return folio; +} + static bool shmem_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_= flags) { return true; } =20 static const struct vm_uffd_ops shmem_uffd_ops =3D { - .can_userfault =3D shmem_can_userfault, + .can_userfault =3D shmem_can_userfault, + .get_folio_noalloc =3D shmem_get_folio_noalloc, }; #endif /* CONFIG_USERFAULTFD */ =20 diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index e3024a39c19d..832dbdde5868 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -191,6 +191,7 @@ static int mfill_get_vma(struct mfill_state *state) struct userfaultfd_ctx *ctx =3D state->ctx; uffd_flags_t flags =3D state->flags; struct vm_area_struct *dst_vma; + const struct vm_uffd_ops *ops; int err; =20 /* @@ -232,10 +233,12 @@ static int mfill_get_vma(struct mfill_state *state) if (is_vm_hugetlb_page(dst_vma)) return 0; =20 - if (!vma_is_anonymous(dst_vma) && !vma_is_shmem(dst_vma)) + ops =3D vma_uffd_ops(dst_vma); + if (!ops) goto out_unlock; - if (!vma_is_shmem(dst_vma) && - uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) + + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE) && + !ops->get_folio_noalloc) goto out_unlock; =20 return 0; @@ -575,6 +578,7 @@ static int mfill_atomic_pte_zeropage(struct mfill_state= *state) static int mfill_atomic_pte_continue(struct mfill_state *state) { struct vm_area_struct *dst_vma =3D state->vma; + const struct vm_uffd_ops *ops =3D vma_uffd_ops(dst_vma); unsigned long dst_addr =3D state->dst_addr; pgoff_t pgoff =3D linear_page_index(dst_vma, dst_addr); struct inode *inode =3D file_inode(dst_vma->vm_file); @@ -584,17 +588,16 @@ static int mfill_atomic_pte_continue(struct mfill_sta= te *state) struct page *page; int ret; =20 - ret =3D shmem_get_folio(inode, pgoff, 0, &folio, SGP_NOALLOC); - /* Our caller expects us to return -EFAULT if we failed to find folio */ - if (ret =3D=3D -ENOENT) - ret =3D -EFAULT; - if (ret) - goto out; - if (!folio) { - ret =3D -EFAULT; - goto out; + if (!ops) { + VM_WARN_ONCE(1, "UFFDIO_CONTINUE for unsupported VMA"); + return -EOPNOTSUPP; } =20 + folio =3D ops->get_folio_noalloc(inode, pgoff); + /* Our caller expects us to return -EFAULT if we failed to find folio */ + if (IS_ERR_OR_NULL(folio)) + return -EFAULT; + page =3D folio_file_page(folio, pgoff); if (PageHWPoison(page)) { ret =3D -EIO; @@ -607,13 +610,12 @@ static int mfill_atomic_pte_continue(struct mfill_sta= te *state) goto out_release; =20 folio_unlock(folio); - ret =3D 0; -out: - return ret; + return 0; + out_release: folio_unlock(folio); folio_put(folio); - goto out; + return ret; } =20 /* Handles UFFDIO_POISON for all non-hugetlb VMAs. */ --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3CAD235FF75; Thu, 2 Apr 2026 04:13:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103188; cv=none; b=VNsGzMIIXmoqFbKGmxi1zqd/sM87b3Qbzw/tKFduQBOBw1KCA+NAsE/WY1b0Gp0gBy+AiAnzDIj9AHaJ9t77YTFhGXggkYzhnMdTJ3ZqICWvpDcabTnWRLRmcrQWUngTEqtgNgCk+1YbkbYM8uEaavvreAwSlabpqhdLE4N8aaI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103188; c=relaxed/simple; bh=Ye6Z7TrGAOPgV52cgsTw8VTKwOK2VRWtWU0AJXFZvYo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fr68rASG4FA8dOs4+xGd4fmBl5V5DyjwZcpNqhDyb9RhNsX96nVU9UrzVJR/qwznSsG7jQ2tWFWmW4N1s/eLJhc8hCXmBKePLxU2+f4kWPhP/DsRf+bqLhtPV0JkzVFsfyzF4hi+Ob565rnD8RqVLxXC3MJcl3B0aHT9/kjVM9E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jRmA6e+4; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jRmA6e+4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 829EFC19423; Thu, 2 Apr 2026 04:13:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103187; bh=Ye6Z7TrGAOPgV52cgsTw8VTKwOK2VRWtWU0AJXFZvYo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jRmA6e+4sI6CWSR9vvx2sEwae/Tyg7OdWgDWdkkAJFLADbySZ3w+Kc8qIHWijyYyt td9MCBLaKdb3hyaXy02gu20EvmzZBDuuI0ToIaNoDa+tsc0I9v+0G5ugQIk24cNKD+ AJF3CpeE0Pa5rXKocaVkb7XlBwvPjSJso9uiDvDfRIAuidLrSvXdo3p4FoVSHkr/Yt akIdcT4DA/H5AlfRVGz0Vt7EXbuoIIz4Z81yyg0DtMVnL+sNHatuHUsw4idz+ja8Nu P205Jo18lZ8hRXOvp/l/MPYLlQlGN1Q3CFHpgbgac7eCwkc9AUEYgtNg4dlcYwLa81 DzwMaDHl0HrlA== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 09/15] userfaultfd: introduce vm_uffd_ops->alloc_folio() Date: Thu, 2 Apr 2026 07:11:50 +0300 Message-ID: <20260402041156.1377214-10-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" and use it to refactor mfill_atomic_pte_zeroed_folio() and mfill_atomic_pte_copy(). mfill_atomic_pte_zeroed_folio() and mfill_atomic_pte_copy() perform almost identical actions: * allocate a folio * update folio contents (either copy from userspace of fill with zeros) * update page tables with the new folio Split a __mfill_atomic_pte() helper that handles both cases and uses newly introduced vm_uffd_ops->alloc_folio() to allocate the folio. Pass the ops structure from the callers to __mfill_atomic_pte() to later allow using anon_uffd_ops for MAP_PRIVATE mappings of file-backed VMAs. Note, that the new ops method is called alloc_folio() rather than folio_alloc() to avoid clash with alloc_tag macro folio_alloc(). Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: James Houghton --- include/linux/userfaultfd_k.h | 6 +++ mm/userfaultfd.c | 92 ++++++++++++++++++----------------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index 66dfc3c164e6..55a67421de0a 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -91,6 +91,12 @@ struct vm_uffd_ops { * The returned folio is locked and with reference held. */ struct folio *(*get_folio_noalloc)(struct inode *inode, pgoff_t pgoff); + /* + * Called during resolution of UFFDIO_COPY request. + * Should allocate and return a folio or NULL if allocation fails. + */ + struct folio *(*alloc_folio)(struct vm_area_struct *vma, + unsigned long addr); }; =20 /* A combined operation mode + behavior flags. */ diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 832dbdde5868..771a1e607c4c 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -42,8 +42,26 @@ static bool anon_can_userfault(struct vm_area_struct *vm= a, vm_flags_t vm_flags) return true; } =20 +static struct folio *anon_alloc_folio(struct vm_area_struct *vma, + unsigned long addr) +{ + struct folio *folio =3D vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0, vma, + addr); + + if (!folio) + return NULL; + + if (mem_cgroup_charge(folio, vma->vm_mm, GFP_KERNEL)) { + folio_put(folio); + return NULL; + } + + return folio; +} + static const struct vm_uffd_ops anon_uffd_ops =3D { .can_userfault =3D anon_can_userfault, + .alloc_folio =3D anon_alloc_folio, }; =20 static const struct vm_uffd_ops *vma_uffd_ops(struct vm_area_struct *vma) @@ -456,7 +474,8 @@ static int mfill_copy_folio_retry(struct mfill_state *s= tate, struct folio *folio return 0; } =20 -static int mfill_atomic_pte_copy(struct mfill_state *state) +static int __mfill_atomic_pte(struct mfill_state *state, + const struct vm_uffd_ops *ops) { unsigned long dst_addr =3D state->dst_addr; unsigned long src_addr =3D state->src_addr; @@ -464,16 +483,12 @@ static int mfill_atomic_pte_copy(struct mfill_state *= state) struct folio *folio; int ret; =20 - folio =3D vma_alloc_folio(GFP_HIGHUSER_MOVABLE, 0, state->vma, dst_addr); + folio =3D ops->alloc_folio(state->vma, state->dst_addr); if (!folio) return -ENOMEM; =20 - ret =3D -ENOMEM; - if (mem_cgroup_charge(folio, state->vma->vm_mm, GFP_KERNEL)) - goto out_release; - - ret =3D mfill_copy_folio_locked(folio, src_addr); - if (unlikely(ret)) { + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) { + ret =3D mfill_copy_folio_locked(folio, src_addr); /* * Fallback to copy_from_user outside mmap_lock. * If retry is successful, mfill_copy_folio_locked() returns @@ -481,9 +496,15 @@ static int mfill_atomic_pte_copy(struct mfill_state *s= tate) * If there was an error, we must mfill_put_vma() anyway and it * will take care of unlocking if needed. */ - ret =3D mfill_copy_folio_retry(state, folio); - if (ret) - goto out_release; + if (unlikely(ret)) { + ret =3D mfill_copy_folio_retry(state, folio); + if (ret) + goto err_folio_put; + } + } else if (uffd_flags_mode_is(flags, MFILL_ATOMIC_ZEROPAGE)) { + clear_user_highpage(&folio->page, state->dst_addr); + } else { + VM_WARN_ONCE(1, "Unknown UFFDIO operation, flags: %x", flags); } =20 /* @@ -496,47 +517,30 @@ static int mfill_atomic_pte_copy(struct mfill_state *= state) ret =3D mfill_atomic_install_pte(state->pmd, state->vma, dst_addr, &folio->page, true, flags); if (ret) - goto out_release; -out: - return ret; -out_release: + goto err_folio_put; + + return 0; + +err_folio_put: + folio_put(folio); /* Don't return -ENOENT so that our caller won't retry */ if (ret =3D=3D -ENOENT) ret =3D -EFAULT; - folio_put(folio); - goto out; + return ret; } =20 -static int mfill_atomic_pte_zeroed_folio(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr) +static int mfill_atomic_pte_copy(struct mfill_state *state) { - struct folio *folio; - int ret =3D -ENOMEM; - - folio =3D vma_alloc_zeroed_movable_folio(dst_vma, dst_addr); - if (!folio) - return ret; - - if (mem_cgroup_charge(folio, dst_vma->vm_mm, GFP_KERNEL)) - goto out_put; + const struct vm_uffd_ops *ops =3D vma_uffd_ops(state->vma); =20 - /* - * The memory barrier inside __folio_mark_uptodate makes sure that - * zeroing out the folio become visible before mapping the page - * using set_pte_at(). See do_anonymous_page(). - */ - __folio_mark_uptodate(folio); + return __mfill_atomic_pte(state, ops); +} =20 - ret =3D mfill_atomic_install_pte(dst_pmd, dst_vma, dst_addr, - &folio->page, true, 0); - if (ret) - goto out_put; +static int mfill_atomic_pte_zeroed_folio(struct mfill_state *state) +{ + const struct vm_uffd_ops *ops =3D vma_uffd_ops(state->vma); =20 - return 0; -out_put: - folio_put(folio); - return ret; + return __mfill_atomic_pte(state, ops); } =20 static int mfill_atomic_pte_zeropage(struct mfill_state *state) @@ -549,7 +553,7 @@ static int mfill_atomic_pte_zeropage(struct mfill_state= *state) int ret; =20 if (mm_forbids_zeropage(dst_vma->vm_mm)) - return mfill_atomic_pte_zeroed_folio(dst_pmd, dst_vma, dst_addr); + return mfill_atomic_pte_zeroed_folio(state); =20 _dst_pte =3D pte_mkspecial(pfn_pte(my_zero_pfn(dst_addr), dst_vma->vm_page_prot)); --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C4CB13603DF; Thu, 2 Apr 2026 04:13:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103194; cv=none; b=oY31zxFMSO9nOGQ8eApz216y8WynVArepqyLd74i+rPOE6sWlXbDMll19ug5vv5u3sqSNIPnqCyiRkP+9/qNjmOzxg8IC2VHjHE4JBi+FhYY5aLM4aPSRp4eN1Tp3CsD+2gHue5jQ1asZ62qmZr/Y3cUeEqm3ONyTjOFujvOpxc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103194; c=relaxed/simple; bh=nhN0tYK94rMrBIfGb6UL4j3n8or99nhslnrvimSOhI4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eVct+DzWbStLCKtkQTyVeuikqtkyRfqM2VgdAYImrn95t5ky5+qZHi8HYlaga+aMGTu0cy7A2GluTaIzjrHyd2TZtrE4z5plamjlvKM4s7OEa6jWokGGkORdKMSl+I00UkocBV0KG+srZrnoGjOETiaUxzxR7eAJVoyR63OYeqM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HFbQSQpU; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HFbQSQpU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4DD73C19424; Thu, 2 Apr 2026 04:13:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103194; bh=nhN0tYK94rMrBIfGb6UL4j3n8or99nhslnrvimSOhI4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HFbQSQpUZ/1CDj1jYFYxPHMMn9IDjTz054n4kAWAWikhHLYlqgPkF1uZSIKZCgauN 779x6z5H2yAqnbiPWeSb96Ov22D2M/WMle20KxizL1nuNqf6bD+ys5sB7weYMyPgAH yNWsF/QBURbf1bTBHk9IUwRJw3/DcdXloQjjulRIa7Vi20ZXBmtJpSF1Oy3M3Sc7Hc iM3/kurQWSqF1aSqcs/XaJeyonCeA59pf5AmB/H9oP+6LtfK3BlIvUJle/uEsT7kdb oB8IsR9RURfoRTlpQnikds/gf9LyAEeEVfcwsMoW2t0noqPbyBZwZUuprDp0oxSTjF KksEjgkd+A47A== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 10/15] shmem, userfaultfd: implement shmem uffd operations using vm_uffd_ops Date: Thu, 2 Apr 2026 07:11:51 +0300 Message-ID: <20260402041156.1377214-11-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" Add filemap_add() and filemap_remove() methods to vm_uffd_ops and use them in __mfill_atomic_pte() to add shmem folios to page cache and remove them in case of error. Implement these methods in shmem along with vm_uffd_ops->alloc_folio() and drop shmem_mfill_atomic_pte(). Since userfaultfd now does not reference any functions from shmem, drop include if linux/shmem_fs.h from mm/userfaultfd.c mfill_atomic_install_pte() is not used anywhere outside of mm/userfaultfd, make it static. Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: James Houghton --- include/linux/shmem_fs.h | 14 ---- include/linux/userfaultfd_k.h | 19 +++-- mm/shmem.c | 148 ++++++++++++---------------------- mm/userfaultfd.c | 80 +++++++++--------- 4 files changed, 106 insertions(+), 155 deletions(-) diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index a8273b32e041..1a345142af7d 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -221,20 +221,6 @@ static inline pgoff_t shmem_fallocend(struct inode *in= ode, pgoff_t eof) =20 extern bool shmem_charge(struct inode *inode, long pages); =20 -#ifdef CONFIG_USERFAULTFD -#ifdef CONFIG_SHMEM -extern int shmem_mfill_atomic_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - unsigned long src_addr, - uffd_flags_t flags, - struct folio **foliop); -#else /* !CONFIG_SHMEM */ -#define shmem_mfill_atomic_pte(dst_pmd, dst_vma, dst_addr, \ - src_addr, flags, foliop) ({ BUG(); 0; }) -#endif /* CONFIG_SHMEM */ -#endif /* CONFIG_USERFAULTFD */ - /* * Used space is stored as unsigned 64-bit value in bytes but * quota core supports only signed 64-bit values so use that diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index 55a67421de0a..6f33307c2780 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -97,6 +97,20 @@ struct vm_uffd_ops { */ struct folio *(*alloc_folio)(struct vm_area_struct *vma, unsigned long addr); + /* + * Called during resolution of UFFDIO_COPY request. + * Should only be called with a folio returned by alloc_folio() above. + * The folio will be set to locked. + * Returns 0 on success, error code on failure. + */ + int (*filemap_add)(struct folio *folio, struct vm_area_struct *vma, + unsigned long addr); + /* + * Called during resolution of UFFDIO_COPY request on the error + * handling path. + * Should revert the operation of ->filemap_add(). + */ + void (*filemap_remove)(struct folio *folio, struct vm_area_struct *vma); }; =20 /* A combined operation mode + behavior flags. */ @@ -130,11 +144,6 @@ static inline uffd_flags_t uffd_flags_set_mode(uffd_fl= ags_t flags, enum mfill_at /* Flags controlling behavior. These behavior changes are mode-independent= . */ #define MFILL_ATOMIC_WP MFILL_ATOMIC_FLAG(0) =20 -extern int mfill_atomic_install_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, struct page *page, - bool newly_allocated, uffd_flags_t flags); - extern ssize_t mfill_atomic_copy(struct userfaultfd_ctx *ctx, unsigned lon= g dst_start, unsigned long src_start, unsigned long len, uffd_flags_t flags); diff --git a/mm/shmem.c b/mm/shmem.c index 7bd887b64f62..68620caaf75f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3181,118 +3181,73 @@ static struct inode *shmem_get_inode(struct mnt_id= map *idmap, #endif /* CONFIG_TMPFS_QUOTA */ =20 #ifdef CONFIG_USERFAULTFD -int shmem_mfill_atomic_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, - unsigned long src_addr, - uffd_flags_t flags, - struct folio **foliop) -{ - struct inode *inode =3D file_inode(dst_vma->vm_file); - struct shmem_inode_info *info =3D SHMEM_I(inode); +static struct folio *shmem_mfill_folio_alloc(struct vm_area_struct *vma, + unsigned long addr) +{ + struct inode *inode =3D file_inode(vma->vm_file); struct address_space *mapping =3D inode->i_mapping; + struct shmem_inode_info *info =3D SHMEM_I(inode); + pgoff_t pgoff =3D linear_page_index(vma, addr); gfp_t gfp =3D mapping_gfp_mask(mapping); - pgoff_t pgoff =3D linear_page_index(dst_vma, dst_addr); - void *page_kaddr; struct folio *folio; - int ret; - pgoff_t max_off; - - if (shmem_inode_acct_blocks(inode, 1)) { - /* - * We may have got a page, returned -ENOENT triggering a retry, - * and now we find ourselves with -ENOMEM. Release the page, to - * avoid a BUG_ON in our caller. - */ - if (unlikely(*foliop)) { - folio_put(*foliop); - *foliop =3D NULL; - } - return -ENOMEM; - } =20 - if (!*foliop) { - ret =3D -ENOMEM; - folio =3D shmem_alloc_folio(gfp, 0, info, pgoff); - if (!folio) - goto out_unacct_blocks; + if (unlikely(pgoff >=3D DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE))) + return NULL; =20 - if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) { - page_kaddr =3D kmap_local_folio(folio, 0); - /* - * The read mmap_lock is held here. Despite the - * mmap_lock being read recursive a deadlock is still - * possible if a writer has taken a lock. For example: - * - * process A thread 1 takes read lock on own mmap_lock - * process A thread 2 calls mmap, blocks taking write lock - * process B thread 1 takes page fault, read lock on own mmap lock - * process B thread 2 calls mmap, blocks taking write lock - * process A thread 1 blocks taking read lock on process B - * process B thread 1 blocks taking read lock on process A - * - * Disable page faults to prevent potential deadlock - * and retry the copy outside the mmap_lock. - */ - pagefault_disable(); - ret =3D copy_from_user(page_kaddr, - (const void __user *)src_addr, - PAGE_SIZE); - pagefault_enable(); - kunmap_local(page_kaddr); - - /* fallback to copy_from_user outside mmap_lock */ - if (unlikely(ret)) { - *foliop =3D folio; - ret =3D -ENOENT; - /* don't free the page */ - goto out_unacct_blocks; - } + folio =3D shmem_alloc_folio(gfp, 0, info, pgoff); + if (!folio) + return NULL; =20 - flush_dcache_folio(folio); - } else { /* ZEROPAGE */ - clear_user_highpage(&folio->page, dst_addr); - } - } else { - folio =3D *foliop; - VM_BUG_ON_FOLIO(folio_test_large(folio), folio); - *foliop =3D NULL; + if (mem_cgroup_charge(folio, vma->vm_mm, GFP_KERNEL)) { + folio_put(folio); + return NULL; } =20 - VM_BUG_ON(folio_test_locked(folio)); - VM_BUG_ON(folio_test_swapbacked(folio)); + return folio; +} + +static int shmem_mfill_filemap_add(struct folio *folio, + struct vm_area_struct *vma, + unsigned long addr) +{ + struct inode *inode =3D file_inode(vma->vm_file); + struct address_space *mapping =3D inode->i_mapping; + pgoff_t pgoff =3D linear_page_index(vma, addr); + gfp_t gfp =3D mapping_gfp_mask(mapping); + int err; + __folio_set_locked(folio); __folio_set_swapbacked(folio); - __folio_mark_uptodate(folio); - - ret =3D -EFAULT; - max_off =3D DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); - if (unlikely(pgoff >=3D max_off)) - goto out_release; =20 - ret =3D mem_cgroup_charge(folio, dst_vma->vm_mm, gfp); - if (ret) - goto out_release; - ret =3D shmem_add_to_page_cache(folio, mapping, pgoff, NULL, gfp); - if (ret) - goto out_release; + err =3D shmem_add_to_page_cache(folio, mapping, pgoff, NULL, gfp); + if (err) + goto err_unlock; =20 - ret =3D mfill_atomic_install_pte(dst_pmd, dst_vma, dst_addr, - &folio->page, true, flags); - if (ret) - goto out_delete_from_cache; + if (shmem_inode_acct_blocks(inode, 1)) { + err =3D -ENOMEM; + goto err_delete_from_cache; + } =20 + folio_add_lru(folio); shmem_recalc_inode(inode, 1, 0); - folio_unlock(folio); + return 0; -out_delete_from_cache: + +err_delete_from_cache: filemap_remove_folio(folio); -out_release: +err_unlock: + folio_unlock(folio); + return err; +} + +static void shmem_mfill_filemap_remove(struct folio *folio, + struct vm_area_struct *vma) +{ + struct inode *inode =3D file_inode(vma->vm_file); + + filemap_remove_folio(folio); + shmem_recalc_inode(inode, 0, 0); folio_unlock(folio); - folio_put(folio); -out_unacct_blocks: - shmem_inode_unacct_blocks(inode, 1); - return ret; } =20 static struct folio *shmem_get_folio_noalloc(struct inode *inode, pgoff_t = pgoff) @@ -3315,6 +3270,9 @@ static bool shmem_can_userfault(struct vm_area_struct= *vma, vm_flags_t vm_flags) static const struct vm_uffd_ops shmem_uffd_ops =3D { .can_userfault =3D shmem_can_userfault, .get_folio_noalloc =3D shmem_get_folio_noalloc, + .alloc_folio =3D shmem_mfill_folio_alloc, + .filemap_add =3D shmem_mfill_filemap_add, + .filemap_remove =3D shmem_mfill_filemap_remove, }; #endif /* CONFIG_USERFAULTFD */ =20 diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 771a1e607c4c..e672a9e45d0c 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include "internal.h" @@ -338,10 +337,10 @@ static bool mfill_file_over_size(struct vm_area_struc= t *dst_vma, * This function handles both MCOPY_ATOMIC_NORMAL and _CONTINUE for both s= hmem * and anon, and for both shared and private VMAs. */ -int mfill_atomic_install_pte(pmd_t *dst_pmd, - struct vm_area_struct *dst_vma, - unsigned long dst_addr, struct page *page, - bool newly_allocated, uffd_flags_t flags) +static int mfill_atomic_install_pte(pmd_t *dst_pmd, + struct vm_area_struct *dst_vma, + unsigned long dst_addr, struct page *page, + uffd_flags_t flags) { int ret; struct mm_struct *dst_mm =3D dst_vma->vm_mm; @@ -385,9 +384,6 @@ int mfill_atomic_install_pte(pmd_t *dst_pmd, goto out_unlock; =20 if (page_in_cache) { - /* Usually, cache pages are already added to LRU */ - if (newly_allocated) - folio_add_lru(folio); folio_add_file_rmap_pte(folio, page, dst_vma); } else { folio_add_new_anon_rmap(folio, dst_vma, dst_addr, RMAP_EXCLUSIVE); @@ -402,6 +398,9 @@ int mfill_atomic_install_pte(pmd_t *dst_pmd, =20 set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); =20 + if (page_in_cache) + folio_unlock(folio); + /* No need to invalidate - it was non-present before */ update_mmu_cache(dst_vma, dst_addr, dst_pte); ret =3D 0; @@ -514,13 +513,22 @@ static int __mfill_atomic_pte(struct mfill_state *sta= te, */ __folio_mark_uptodate(folio); =20 + if (ops->filemap_add) { + ret =3D ops->filemap_add(folio, state->vma, state->dst_addr); + if (ret) + goto err_folio_put; + } + ret =3D mfill_atomic_install_pte(state->pmd, state->vma, dst_addr, - &folio->page, true, flags); + &folio->page, flags); if (ret) - goto err_folio_put; + goto err_filemap_remove; =20 return 0; =20 +err_filemap_remove: + if (ops->filemap_remove) + ops->filemap_remove(folio, state->vma); err_folio_put: folio_put(folio); /* Don't return -ENOENT so that our caller won't retry */ @@ -533,6 +541,18 @@ static int mfill_atomic_pte_copy(struct mfill_state *s= tate) { const struct vm_uffd_ops *ops =3D vma_uffd_ops(state->vma); =20 + /* + * The normal page fault path for a MAP_PRIVATE mapping in a + * file-backed VMA will invoke the fault, fill the hole in the file and + * COW it right away. The result generates plain anonymous memory. + * So when we are asked to fill a hole in a MAP_PRIVATE mapping, we'll + * generate anonymous memory directly without actually filling the + * hole. For the MAP_PRIVATE case the robustness check only happens in + * the pagetable (to verify it's still none) and not in the page cache. + */ + if (!(state->vma->vm_flags & VM_SHARED)) + ops =3D &anon_uffd_ops; + return __mfill_atomic_pte(state, ops); } =20 @@ -552,7 +572,8 @@ static int mfill_atomic_pte_zeropage(struct mfill_state= *state) spinlock_t *ptl; int ret; =20 - if (mm_forbids_zeropage(dst_vma->vm_mm)) + if (mm_forbids_zeropage(dst_vma->vm_mm) || + (dst_vma->vm_flags & VM_SHARED)) return mfill_atomic_pte_zeroed_folio(state); =20 _dst_pte =3D pte_mkspecial(pfn_pte(my_zero_pfn(dst_addr), @@ -609,11 +630,10 @@ static int mfill_atomic_pte_continue(struct mfill_sta= te *state) } =20 ret =3D mfill_atomic_install_pte(dst_pmd, dst_vma, dst_addr, - page, false, flags); + page, flags); if (ret) goto out_release; =20 - folio_unlock(folio); return 0; =20 out_release: @@ -836,41 +856,19 @@ extern ssize_t mfill_atomic_hugetlb(struct userfaultf= d_ctx *ctx, =20 static __always_inline ssize_t mfill_atomic_pte(struct mfill_state *state) { - struct vm_area_struct *dst_vma =3D state->vma; - unsigned long src_addr =3D state->src_addr; - unsigned long dst_addr =3D state->dst_addr; - struct folio **foliop =3D &state->folio; uffd_flags_t flags =3D state->flags; - pmd_t *dst_pmd =3D state->pmd; - ssize_t err; =20 if (uffd_flags_mode_is(flags, MFILL_ATOMIC_CONTINUE)) return mfill_atomic_pte_continue(state); if (uffd_flags_mode_is(flags, MFILL_ATOMIC_POISON)) return mfill_atomic_pte_poison(state); + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) + return mfill_atomic_pte_copy(state); + if (uffd_flags_mode_is(flags, MFILL_ATOMIC_ZEROPAGE)) + return mfill_atomic_pte_zeropage(state); =20 - /* - * The normal page fault path for a shmem will invoke the - * fault, fill the hole in the file and COW it right away. The - * result generates plain anonymous memory. So when we are - * asked to fill an hole in a MAP_PRIVATE shmem mapping, we'll - * generate anonymous memory directly without actually filling - * the hole. For the MAP_PRIVATE case the robustness check - * only happens in the pagetable (to verify it's still none) - * and not in the radix tree. - */ - if (!(dst_vma->vm_flags & VM_SHARED)) { - if (uffd_flags_mode_is(flags, MFILL_ATOMIC_COPY)) - err =3D mfill_atomic_pte_copy(state); - else - err =3D mfill_atomic_pte_zeropage(state); - } else { - err =3D shmem_mfill_atomic_pte(dst_pmd, dst_vma, - dst_addr, src_addr, - flags, foliop); - } - - return err; + VM_WARN_ONCE(1, "Unknown UFFDIO operation, flags: %x", flags); + return -EOPNOTSUPP; } =20 static __always_inline ssize_t mfill_atomic(struct userfaultfd_ctx *ctx, --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B2BA83612F6; Thu, 2 Apr 2026 04:13:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103201; cv=none; b=BkEnkDAkno3yFqBQnRrEMOp/6U/XQsrrqiX/5CtUOCUkAN78fRKADPjjgTha9bDlJihyhwtr1FPvUzWt8WNXc0NX4Urhv1U0JtLCSk3PqrKOJvHHsazsS6jWUtxWIlbrQ/VIpjByahrmDtfW6mx2cza58k+UIt9jyEPar2waVQQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103201; c=relaxed/simple; bh=5vaQGQBc3neyldkli11Rg6Y+SDtyLj7BMfixF4/GPSE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ur1nDUPvqUv75LG6V9yXl+Uavl2PspcXJg3zOKXFNJdqqs9xYMgcsSnM30uJi4JwX8eq/v6VfTIxjyT7twiVDZTdPq5jCSr4bLZbgY+iZiqkKQfXh4vz1Oa+MOu76GxYTnmLC8ixP+ky/4i1SNGg9Wv9gt8G1lyyF2OUm83Bz3A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Bt28/m2f; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Bt28/m2f" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 178EFC19423; Thu, 2 Apr 2026 04:13:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103201; bh=5vaQGQBc3neyldkli11Rg6Y+SDtyLj7BMfixF4/GPSE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Bt28/m2fkum5A4fexrkHIDiwLmCZisqPq7kYstTpZa4gEYge6jHYkI04dMh8zD8uw Z2KjPZ2GxXl1j5Y/ahMEY7rHgurRo+c/83O8XQj65p1XtNHEOfoXNVei3oY41GnY9m oe9q7EwPbyUpcMLozGy5HkrGB9QGlPUdhRlmbNNV0HKXR21y6jtVRSwKLdU6N7+JVA MPKbfI253YnvrF296FFA/9AFxYH9o3fuqQ5JBlzyuGE9+4s4UaZjnp8X40iLlR/eEz jCsqbSNE7alCszlRzkgCMtGJHHUovkALONmzswZrWCLGdDC1uQLrSJMrXY2RCvwpCh a/Lfg2MwHGhPQ== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 11/15] userfaultfd: mfill_atomic(): remove retry logic Date: Thu, 2 Apr 2026 07:11:52 +0300 Message-ID: <20260402041156.1377214-12-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Mike Rapoport (Microsoft)" Since __mfill_atomic_pte() handles the retry for both anonymous and shmem, there is no need to retry copying the date from the userspace in the loop in mfill_atomic(). Drop the retry logic from mfill_atomic(). Signed-off-by: Mike Rapoport (Microsoft) --- mm/userfaultfd.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index e672a9e45d0c..935a3f6ebeed 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -29,7 +29,6 @@ struct mfill_state { struct vm_area_struct *vma; unsigned long src_addr; unsigned long dst_addr; - struct folio *folio; pmd_t *pmd; }; =20 @@ -899,7 +898,6 @@ static __always_inline ssize_t mfill_atomic(struct user= faultfd_ctx *ctx, VM_WARN_ON_ONCE(src_start + len <=3D src_start); VM_WARN_ON_ONCE(dst_start + len <=3D dst_start); =20 -retry: err =3D mfill_get_vma(&state); if (err) goto out; @@ -926,26 +924,6 @@ static __always_inline ssize_t mfill_atomic(struct use= rfaultfd_ctx *ctx, err =3D mfill_atomic_pte(&state); cond_resched(); =20 - if (unlikely(err =3D=3D -ENOENT)) { - void *kaddr; - - mfill_put_vma(&state); - VM_WARN_ON_ONCE(!state.folio); - - kaddr =3D kmap_local_folio(state.folio, 0); - err =3D copy_from_user(kaddr, - (const void __user *)state.src_addr, - PAGE_SIZE); - kunmap_local(kaddr); - if (unlikely(err)) { - err =3D -EFAULT; - goto out; - } - flush_dcache_folio(state.folio); - goto retry; - } else - VM_WARN_ON_ONCE(state.folio); - if (!err) { state.dst_addr +=3D PAGE_SIZE; state.src_addr +=3D PAGE_SIZE; @@ -960,8 +938,6 @@ static __always_inline ssize_t mfill_atomic(struct user= faultfd_ctx *ctx, =20 mfill_put_vma(&state); out: - if (state.folio) - folio_put(state.folio); VM_WARN_ON_ONCE(copied < 0); VM_WARN_ON_ONCE(err > 0); VM_WARN_ON_ONCE(!copied && !err); --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 75D9135F61A; Thu, 2 Apr 2026 04:13:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103208; cv=none; b=DA3D4GiE+Aw59xU9pMYHPPjilLw0wG2Fs042EWZsTQ3v390rXujg+SRb2WftfE2hhHRRVVBW1Qi0Vx8VA1NxPqw0XZKUB2xevQw+Gl3O5bEMtaDT5ouVjEdhQs1ebncPFg4oQ1JanyjiRNtYGD0w8JorpqLsr0GGO70W0N2Qx14= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103208; c=relaxed/simple; bh=OBbhkJ8YK27hN9vtg3QQXqgXyuXTuw2IdpJE89KAMWE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ELoYoV6VnnZzmSMpTxLJV8au5POKBj7W+ahzWXAv2vHHM/9U0LWuPCQLRazWmAH7QMZiHYRU7EGpN/BR/PXYREkhcqU468F+kWZdBnsdXU/vDz0bUcu9bOqfbLzXWrVDmf0wZr3w/2FCLTs3jeIA3CIi9xgRZo/xVwK/KIerzkg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ge0jvZgB; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ge0jvZgB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D6102C19423; Thu, 2 Apr 2026 04:13:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103208; bh=OBbhkJ8YK27hN9vtg3QQXqgXyuXTuw2IdpJE89KAMWE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ge0jvZgBcHS7QaHra70M9H/IHpYJ7VUA9VKvtPTYNwTO/xeQXcKnk3PAeYNSBHr2z medpk7MIbsJYBNEskrIJ4bLwOMUpfLK3BTdB2vfr0iFRoyui9pgQEyMHQqfDg4cxtF jRxm6GbtgmRifrhluyNeOpy91Kkpj94G2MdbcryqrzzWNfAFbaUbE5w6rXQANE86wM 6RbTCyAXJyF5kh1EFw9ZYwUegCDNKfg5/s4jBRwriyC03hVnT4NLg7G9Kby5PiDL/z un6LWEMqYAE/T7GnGXKRT6B3WUAcI7kPvpwE+6rpUKMvRngKZ3lpyQlpmRaGnDnr2B Y1C5iy4iwJA4w== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 12/15] mm: generalize handling of userfaults in __do_fault() Date: Thu, 2 Apr 2026 07:11:53 +0300 Message-ID: <20260402041156.1377214-13-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Peter Xu When a VMA is registered with userfaulfd, its ->fault() method should check if a folio exists in the page cache and call handle_userfault() with appropriate mode: - VM_UFFD_MINOR if VMA is registered in minor mode and the folio exists - VM_UFFD_MISSING if VMA is registered in missing mode and the folio does not exist Instead of calling handle_userfault() directly from a specific ->fault() handler, call __do_userfault() helper from the generic __do_fault(). For VMAs registered with userfaultfd the new __do_userfault() helper will check if the folio is found in the page cache using vm_uffd_ops->get_folio_noalloc() and call handle_userfault() with the appropriate mode. Make vm_uffd_ops->get_folio_noalloc() required method for non-anonymous VMAs mapped at PTE level. Signed-off-by: Peter Xu Co-developed-by: Mike Rapoport (Microsoft) Signed-off-by: Mike Rapoport (Microsoft) --- mm/memory.c | 43 +++++++++++++++++++++++++++++++++++++++++++ mm/shmem.c | 12 ------------ mm/userfaultfd.c | 9 +++++++++ 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 2f815a34d924..79c5328b26e3 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5329,6 +5329,41 @@ static vm_fault_t do_anonymous_page(struct vm_fault = *vmf) return VM_FAULT_OOM; } =20 +#ifdef CONFIG_USERFAULTFD +static vm_fault_t __do_userfault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma =3D vmf->vma; + struct inode *inode; + struct folio *folio; + + if (!(userfaultfd_missing(vma) || userfaultfd_minor(vma))) + return 0; + + inode =3D file_inode(vma->vm_file); + folio =3D vma->vm_ops->uffd_ops->get_folio_noalloc(inode, vmf->pgoff); + if (!IS_ERR_OR_NULL(folio)) { + /* + * TODO: provide a flag for get_folio_noalloc() to avoid + * locking (or even the extra reference?) + */ + folio_unlock(folio); + folio_put(folio); + if (userfaultfd_minor(vma)) + return handle_userfault(vmf, VM_UFFD_MINOR); + } else { + if (userfaultfd_missing(vma)) + return handle_userfault(vmf, VM_UFFD_MISSING); + } + + return 0; +} +#else +static inline vm_fault_t __do_userfault(struct vm_fault *vmf) +{ + return 0; +} +#endif + /* * The mmap_lock must have been held on entry, and may have been * released depending on flags and vma->vm_ops->fault() return value. @@ -5361,6 +5396,14 @@ static vm_fault_t __do_fault(struct vm_fault *vmf) return VM_FAULT_OOM; } =20 + /* + * If this is a userfault trap, process it in advance before + * triggering the genuine fault handler. + */ + ret =3D __do_userfault(vmf); + if (ret) + return ret; + ret =3D vma->vm_ops->fault(vmf); if (unlikely(ret & (VM_FAULT_ERROR | VM_FAULT_NOPAGE | VM_FAULT_RETRY | VM_FAULT_DONE_COW))) diff --git a/mm/shmem.c b/mm/shmem.c index 68620caaf75f..239545352cd2 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2489,13 +2489,6 @@ static int shmem_get_folio_gfp(struct inode *inode, = pgoff_t index, fault_mm =3D vma ? vma->vm_mm : NULL; =20 folio =3D filemap_get_entry(inode->i_mapping, index); - if (folio && vma && userfaultfd_minor(vma)) { - if (!xa_is_value(folio)) - folio_put(folio); - *fault_type =3D handle_userfault(vmf, VM_UFFD_MINOR); - return 0; - } - if (xa_is_value(folio)) { error =3D shmem_swapin_folio(inode, index, &folio, sgp, gfp, vma, fault_type); @@ -2540,11 +2533,6 @@ static int shmem_get_folio_gfp(struct inode *inode, = pgoff_t index, * Fast cache lookup and swap lookup did not find it: allocate. */ =20 - if (vma && userfaultfd_missing(vma)) { - *fault_type =3D handle_userfault(vmf, VM_UFFD_MISSING); - return 0; - } - /* Find hugepage orders that are allowed for anonymous shmem and tmpfs. */ orders =3D shmem_allowable_huge_orders(inode, vma, index, write_end, fals= e); if (orders > 0) { diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 935a3f6ebeed..9ba6ec8c0781 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -2046,6 +2046,15 @@ bool vma_can_userfault(struct vm_area_struct *vma, v= m_flags_t vm_flags, !vma_is_anonymous(vma)) return false; =20 + /* + * File backed VMAs (except HugeTLB) must implement + * ops->get_folio_noalloc() because it's required by __do_userfault() + * in page fault handling. + */ + if (!vma_is_anonymous(vma) && !is_vm_hugetlb_page(vma) && + !ops->get_folio_noalloc) + return false; + return ops->can_userfault(vma, vm_flags); } =20 --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 47B79361640; Thu, 2 Apr 2026 04:13:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103215; cv=none; b=mZP6ghD2IILH532DAsPl2sl+fpT/nVSav4eH0aLM8j886pdv+QSHNPrTmE/0gLSVN76KbRvxaOvVYwFt5AZV6BQyW3uoy2bdBL5Z3Qo3d7m0lR8Lamlekr3yLRL5bxpNJ0HEuhGHcYoO6YeLY7hqs4ZpnQv3lm2Vbtukqhq6mSk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103215; c=relaxed/simple; bh=sgqKlJf5BnGeophpkDo1XgppS/PlXJsOousdP1fvs+M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cISTpfwCkArYOcRt4swoQWjcWXc2qvGYMaUj7EkQ0eR1/gkYUsOlqYfIZYb40eX9jK4tH8adMfuDaSMnHqTUE6OaENlqzQHMI3sOb86zi/Rpvvh95q2co3fKXFQu4snShxEzRj/muft0ymfriI4td0QYQ7yf0DrlXg3xhmrz7Ro= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=pvr2rNwH; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="pvr2rNwH" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A146DC19423; Thu, 2 Apr 2026 04:13:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103214; bh=sgqKlJf5BnGeophpkDo1XgppS/PlXJsOousdP1fvs+M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pvr2rNwH7jcgFyeELsMcovv2IN7DOgkkZipn+nmUsdhDY8PGn9xg1V65Cd3eDNjZC yOfZo/FK5gNWjQOK3u/HyexXHHp+mGdIFZWjBYTU9DXbCPtneQww1q9LO2qSKMONAc HKUotWOVqa3AjwFO9AHUHNX/Bhqo9JEmAz3n62YCJhKY/kf6nbNnCwyLVIE6y4DNzb HQJK1LPHwqAN24jZAvyWUAZ8uftcU284unGBT8/FPvV0yKxMy2YxeE9/lf2hX0K61y A2GiIRSfzoqJ2ODQtJr4M4/XVfD1KOD7mNJd1dtsWRQRW0UzuhdBcmuNMcXOvOVcUx NR/HodG4P84ng== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 13/15] KVM: guest_memfd: implement userfaultfd operations Date: Thu, 2 Apr 2026 07:11:54 +0300 Message-ID: <20260402041156.1377214-14-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Nikita Kalyazin userfaultfd notifications about page faults used for live migration and snapshotting of VMs. MISSING mode allows post-copy live migration and MINOR mode allows optimization for post-copy live migration for VMs backed with shared hugetlbfs or tmpfs mappings as described in detail in commit 7677f7fd8be7 ("userfaultfd: add minor fault registration mode"). To use the same mechanisms for VMs that use guest_memfd to map their memory, guest_memfd should support userfaultfd operations. Add implementation of vm_uffd_ops to guest_memfd. Signed-off-by: Nikita Kalyazin Co-developed-by: Mike Rapoport (Microsoft) Signed-off-by: Mike Rapoport (Microsoft) --- mm/filemap.c | 1 + virt/kvm/guest_memfd.c | 84 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index 406cef06b684..a91582293118 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -262,6 +262,7 @@ void filemap_remove_folio(struct folio *folio) =20 filemap_free_folio(mapping, folio); } +EXPORT_SYMBOL_FOR_MODULES(filemap_remove_folio, "kvm"); =20 /* * page_cache_delete_batch - delete several folios from page cache diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 017d84a7adf3..46582feeed75 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -7,6 +7,7 @@ #include #include #include +#include =20 #include "kvm_mm.h" =20 @@ -107,6 +108,12 @@ static int kvm_gmem_prepare_folio(struct kvm *kvm, str= uct kvm_memory_slot *slot, return __kvm_gmem_prepare_folio(kvm, slot, index, folio); } =20 +static struct folio *kvm_gmem_get_folio_noalloc(struct inode *inode, pgoff= _t pgoff) +{ + return __filemap_get_folio(inode->i_mapping, pgoff, + FGP_LOCK | FGP_ACCESSED, 0); +} + /* * Returns a locked folio on success. The caller is responsible for * setting the up-to-date flag before the memory is mapped into the guest. @@ -126,8 +133,7 @@ static struct folio *kvm_gmem_get_folio(struct inode *i= node, pgoff_t index) * Fast-path: See if folio is already present in mapping to avoid * policy_lookup. */ - folio =3D __filemap_get_folio(inode->i_mapping, index, - FGP_LOCK | FGP_ACCESSED, 0); + folio =3D kvm_gmem_get_folio_noalloc(inode, index); if (!IS_ERR(folio)) return folio; =20 @@ -457,12 +463,86 @@ static struct mempolicy *kvm_gmem_get_policy(struct v= m_area_struct *vma, } #endif /* CONFIG_NUMA */ =20 +#ifdef CONFIG_USERFAULTFD +static bool kvm_gmem_can_userfault(struct vm_area_struct *vma, vm_flags_t = vm_flags) +{ + struct inode *inode =3D file_inode(vma->vm_file); + + /* + * Only support userfaultfd for guest_memfd with INIT_SHARED flag. + * This ensures the memory can be mapped to userspace. + */ + if (!(GMEM_I(inode)->flags & GUEST_MEMFD_FLAG_INIT_SHARED)) + return false; + + return true; +} + +static struct folio *kvm_gmem_folio_alloc(struct vm_area_struct *vma, + unsigned long addr) +{ + struct inode *inode =3D file_inode(vma->vm_file); + pgoff_t pgoff =3D linear_page_index(vma, addr); + struct mempolicy *mpol; + struct folio *folio; + gfp_t gfp; + + if (unlikely(pgoff >=3D (i_size_read(inode) >> PAGE_SHIFT))) + return NULL; + + gfp =3D mapping_gfp_mask(inode->i_mapping); + mpol =3D mpol_shared_policy_lookup(&GMEM_I(inode)->policy, pgoff); + mpol =3D mpol ?: get_task_policy(current); + folio =3D filemap_alloc_folio(gfp, 0, mpol); + mpol_cond_put(mpol); + + return folio; +} + +static int kvm_gmem_filemap_add(struct folio *folio, + struct vm_area_struct *vma, + unsigned long addr) +{ + struct inode *inode =3D file_inode(vma->vm_file); + struct address_space *mapping =3D inode->i_mapping; + pgoff_t pgoff =3D linear_page_index(vma, addr); + int err; + + __folio_set_locked(folio); + err =3D filemap_add_folio(mapping, folio, pgoff, GFP_KERNEL); + if (err) { + folio_unlock(folio); + return err; + } + + return 0; +} + +static void kvm_gmem_filemap_remove(struct folio *folio, + struct vm_area_struct *vma) +{ + filemap_remove_folio(folio); + folio_unlock(folio); +} + +static const struct vm_uffd_ops kvm_gmem_uffd_ops =3D { + .can_userfault =3D kvm_gmem_can_userfault, + .get_folio_noalloc =3D kvm_gmem_get_folio_noalloc, + .alloc_folio =3D kvm_gmem_folio_alloc, + .filemap_add =3D kvm_gmem_filemap_add, + .filemap_remove =3D kvm_gmem_filemap_remove, +}; +#endif /* CONFIG_USERFAULTFD */ + static const struct vm_operations_struct kvm_gmem_vm_ops =3D { .fault =3D kvm_gmem_fault_user_mapping, #ifdef CONFIG_NUMA .get_policy =3D kvm_gmem_get_policy, .set_policy =3D kvm_gmem_set_policy, #endif +#ifdef CONFIG_USERFAULTFD + .uffd_ops =3D &kvm_gmem_uffd_ops, +#endif }; =20 static int kvm_gmem_mmap(struct file *file, struct vm_area_struct *vma) --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0FF813603F6; Thu, 2 Apr 2026 04:13:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103222; cv=none; b=DHjgnNHZ74j2A6lYh5mJYmaoexdhHrxaZePgB/IZhJm0eCk7VwbqCS3I+BxFWVnLcqTVTCmcLfTWYYYEZlcpDLhNehtT/pcbkrbOE7vzB2qSaM8oG7cgmZ86V4pDRRL3vwCN3QBBDGX/d8AIxiD8w5O/VnRrRYrfDddZB0ZFR7A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103222; c=relaxed/simple; bh=vrai7iUOkplMitPBGsDeCd9GnVnwQZI906Ju/ms0Utg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EoIWnB8Qt4XW6JiogAM97okAT3LAqxi94zbbuTLdbyVPv8ilgdxt4RtzzpmDkY+rpUPrM7IvU165/GRcakq5hCGFT+t3ZBruXn6gwaLchkM1LNKO553j19VJ43QAgidlb1cPWhzMPG/+78C6afC9xISmXvoBybkqRzjL+T325Jw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=j3JlmQ5U; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="j3JlmQ5U" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6CC8EC19423; Thu, 2 Apr 2026 04:13:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103221; bh=vrai7iUOkplMitPBGsDeCd9GnVnwQZI906Ju/ms0Utg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=j3JlmQ5UKZxGjgiCtzC0W6uqZcmLn2kyTfoJnQiKz2i/pv/SwtJGFtCAOUeOROnDk /vKD7hQ8UjLfaJDLtV80QgDLvF9ZgGOtuAnQ8cmLEagcaBhFg21F2yBipSLqHYRpoc Djwy3xo5w6pyA7bJM/+AQAG45GKkRXg+ylI4dkiiAsY51DvQXT+c9DMZ/EyT3iqIgh 20pYyx0b9tLquYgScpjI4KvD6qufTJygxWNYGwZCEYITj0sawuNF1ny3VzCrKyt298 Iwu4iHRrlOtzjKFPQbtoJh8L8JfKfG7Mus4CG4OFJ2/hSKNa1HxVWRMWtjbTEfGX3p QyQs0CoaHZ52Q== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 14/15] KVM: selftests: test userfaultfd minor for guest_memfd Date: Thu, 2 Apr 2026 07:11:55 +0300 Message-ID: <20260402041156.1377214-15-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Nikita Kalyazin The test demonstrates that a minor userfaultfd event in guest_memfd can be resolved via a memcpy followed by a UFFDIO_CONTINUE ioctl. Signed-off-by: Nikita Kalyazin Signed-off-by: Mike Rapoport (Microsoft) --- .../testing/selftests/kvm/guest_memfd_test.c | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing= /selftests/kvm/guest_memfd_test.c index cc329b57ce2e..29f8d686c09f 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -10,13 +10,17 @@ #include #include #include +#include =20 #include #include #include +#include #include #include #include +#include +#include =20 #include "kvm_util.h" #include "numaif.h" @@ -329,6 +333,112 @@ static void test_create_guest_memfd_multiple(struct k= vm_vm *vm) close(fd1); } =20 +struct fault_args { + char *addr; + char value; +}; + +static void *fault_thread_fn(void *arg) +{ + struct fault_args *args =3D arg; + + /* Trigger page fault */ + args->value =3D *args->addr; + return NULL; +} + +static void test_uffd_minor(int fd, size_t total_size) +{ + struct uffdio_register uffd_reg; + struct uffdio_continue uffd_cont; + struct uffd_msg msg; + struct fault_args args; + pthread_t fault_thread; + void *mem, *mem_nofault, *buf =3D NULL; + int uffd, ret; + off_t offset =3D page_size; + void *fault_addr; + const char test_val =3D 0xcd; + + ret =3D posix_memalign(&buf, page_size, total_size); + TEST_ASSERT_EQ(ret, 0); + memset(buf, test_val, total_size); + + uffd =3D syscall(__NR_userfaultfd, O_CLOEXEC); + TEST_ASSERT(uffd !=3D -1, "userfaultfd creation should succeed"); + + struct uffdio_api uffdio_api =3D { + .api =3D UFFD_API, + .features =3D 0, + }; + ret =3D ioctl(uffd, UFFDIO_API, &uffdio_api); + TEST_ASSERT(ret !=3D -1, "ioctl(UFFDIO_API) should succeed"); + + /* Map the guest_memfd twice: once with UFFD registered, once without */ + mem =3D mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + TEST_ASSERT(mem !=3D MAP_FAILED, "mmap should succeed"); + + mem_nofault =3D mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED= , fd, 0); + TEST_ASSERT(mem_nofault !=3D MAP_FAILED, "mmap should succeed"); + + /* Register UFFD_MINOR on the first mapping */ + uffd_reg.range.start =3D (unsigned long)mem; + uffd_reg.range.len =3D total_size; + uffd_reg.mode =3D UFFDIO_REGISTER_MODE_MINOR; + ret =3D ioctl(uffd, UFFDIO_REGISTER, &uffd_reg); + TEST_ASSERT(ret !=3D -1, "ioctl(UFFDIO_REGISTER) should succeed"); + + /* + * Populate the page in the page cache first via mem_nofault. + * This is required for UFFD_MINOR - the page must exist in the cache. + * Write test data to the page. + */ + memcpy(mem_nofault + offset, buf + offset, page_size); + + /* + * Now access the same page via mem (which has UFFD_MINOR registered). + * Since the page exists in the cache, this should trigger UFFD_MINOR. + */ + fault_addr =3D mem + offset; + args.addr =3D fault_addr; + + ret =3D pthread_create(&fault_thread, NULL, fault_thread_fn, &args); + TEST_ASSERT(ret =3D=3D 0, "pthread_create should succeed"); + + ret =3D read(uffd, &msg, sizeof(msg)); + TEST_ASSERT(ret !=3D -1, "read from userfaultfd should succeed"); + TEST_ASSERT(msg.event =3D=3D UFFD_EVENT_PAGEFAULT, "event type should be = pagefault"); + TEST_ASSERT((void *)(msg.arg.pagefault.address & ~(page_size - 1)) =3D=3D= fault_addr, + "pagefault should occur at expected address"); + TEST_ASSERT(msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_MINOR, + "pagefault should be minor fault"); + + /* Resolve the minor fault with UFFDIO_CONTINUE */ + uffd_cont.range.start =3D (unsigned long)fault_addr; + uffd_cont.range.len =3D page_size; + uffd_cont.mode =3D 0; + ret =3D ioctl(uffd, UFFDIO_CONTINUE, &uffd_cont); + TEST_ASSERT(ret !=3D -1, "ioctl(UFFDIO_CONTINUE) should succeed"); + + /* Wait for the faulting thread to complete */ + ret =3D pthread_join(fault_thread, NULL); + TEST_ASSERT(ret =3D=3D 0, "pthread_join should succeed"); + + /* Verify the thread read the correct value */ + TEST_ASSERT(args.value =3D=3D test_val, + "memory should contain the value that was written"); + TEST_ASSERT(*(char *)(mem + offset) =3D=3D test_val, + "no further fault is expected"); + + ret =3D munmap(mem_nofault, total_size); + TEST_ASSERT(!ret, "munmap should succeed"); + + ret =3D munmap(mem, total_size); + TEST_ASSERT(!ret, "munmap should succeed"); + free(buf); + close(uffd); +} + static void test_guest_memfd_flags(struct kvm_vm *vm) { uint64_t valid_flags =3D vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS); @@ -383,6 +493,9 @@ static void __test_guest_memfd(struct kvm_vm *vm, uint6= 4_t flags) gmem_test(file_size, vm, flags); gmem_test(fallocate, vm, flags); gmem_test(invalid_punch_hole, vm, flags); + + if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) + gmem_test(uffd_minor, vm, flags); } =20 static void test_guest_memfd(unsigned long vm_type) --=20 2.53.0 From nobody Mon Apr 6 09:09:55 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D1BC435F61F; Thu, 2 Apr 2026 04:13:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103228; cv=none; b=UxKiQJhtuqWrS6dJLAXvxsEwHd6ViUITtRuNThq0CV5C5lXZ29DUvja4y5J4pnQV0n4ey2uR7r26r50cvvu7nsOV64wCuBfJNngT/zoj1yaorFauhwCnaaVf5wypj+T0qVm9muywmAMiQVFausOiwF8quI1xvtj7xQqXDRk08ak= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775103228; c=relaxed/simple; bh=z+HQ68nm/PN2NlXH0rkdfjkI55JODdIviAntTrnHsgk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eThKNAOboaiuumSK/kWfIjVF8cmZqY7wAjeB8vwxae8hoXovrAGakCwjssrmgqpzqfgZaU/IpBCpl8YI2rIsTO7m27ZmonpFEEQGFfOUgqINPMj9Kaq3LkDURuibCgrj+22H0NVmRq/lORzEgNESKDn/YUzdGTb8fW+ikRcNSi8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=J3swZE2Y; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="J3swZE2Y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 37256C19423; Thu, 2 Apr 2026 04:13:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775103228; bh=z+HQ68nm/PN2NlXH0rkdfjkI55JODdIviAntTrnHsgk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=J3swZE2YyH+2pVU1N5DjDD0TDSZ1KNCz6a4Nu7f6LjmQzi0SwxCUvgzT5USIMVc9X BgSZs0ognCJH2fM7GMkGPxOQ6ozAUdeZZOM4Im2vQOWQp5udB+LyEss9fBrJeZ2+N+ kvzlxOjYOJh/UAmNuJMoF02Gu4ey/B6DEVvfzWqpsdPBjIRBEIu09fEE7Zt7xmtiEA olaAmOrM1jgq5/OIe3GLLQ6FIS4cqOgT+KTV8MOy09Wi0YfV6WVZvX52QkSC6tNn3+ PEObeLaOxTOGniECbLzDzzPSmCul9xI4ymX3Aacbun+ZL5oyTFkO+T9imVEdL+WUZh BToHP/KPtvuyQ== From: Mike Rapoport To: Andrew Morton Cc: Andrea Arcangeli , Andrei Vagin , Axel Rasmussen , Baolin Wang , David Hildenbrand , Harry Yoo , Hugh Dickins , James Houghton , "Liam R. Howlett" , "Lorenzo Stoakes (Oracle)" , "Matthew Wilcox (Oracle)" , Michal Hocko , Mike Rapoport , Muchun Song , Nikita Kalyazin , Oscar Salvador , Paolo Bonzini , Peter Xu , Sean Christopherson , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 15/15] KVM: selftests: test userfaultfd missing for guest_memfd Date: Thu, 2 Apr 2026 07:11:56 +0300 Message-ID: <20260402041156.1377214-16-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260402041156.1377214-1-rppt@kernel.org> References: <20260402041156.1377214-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Nikita Kalyazin The test demonstrates that a missing userfaultfd event in guest_memfd can be resolved via a UFFDIO_COPY ioctl. Signed-off-by: Nikita Kalyazin Signed-off-by: Mike Rapoport (Microsoft) --- .../testing/selftests/kvm/guest_memfd_test.c | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing= /selftests/kvm/guest_memfd_test.c index 29f8d686c09f..eb29ee5d2991 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -439,6 +439,82 @@ static void test_uffd_minor(int fd, size_t total_size) close(uffd); } =20 +static void test_uffd_missing(int fd, size_t total_size) +{ + struct uffdio_register uffd_reg; + struct uffdio_copy uffd_copy; + struct uffd_msg msg; + struct fault_args args; + pthread_t fault_thread; + void *mem, *buf =3D NULL; + int uffd, ret; + off_t offset =3D page_size; + void *fault_addr; + const char test_val =3D 0xab; + + ret =3D posix_memalign(&buf, page_size, total_size); + TEST_ASSERT_EQ(ret, 0); + memset(buf, test_val, total_size); + + uffd =3D syscall(__NR_userfaultfd, O_CLOEXEC); + TEST_ASSERT(uffd !=3D -1, "userfaultfd creation should succeed"); + + struct uffdio_api uffdio_api =3D { + .api =3D UFFD_API, + .features =3D 0, + }; + ret =3D ioctl(uffd, UFFDIO_API, &uffdio_api); + TEST_ASSERT(ret !=3D -1, "ioctl(UFFDIO_API) should succeed"); + + mem =3D mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + TEST_ASSERT(mem !=3D MAP_FAILED, "mmap should succeed"); + + uffd_reg.range.start =3D (unsigned long)mem; + uffd_reg.range.len =3D total_size; + uffd_reg.mode =3D UFFDIO_REGISTER_MODE_MISSING; + ret =3D ioctl(uffd, UFFDIO_REGISTER, &uffd_reg); + TEST_ASSERT(ret !=3D -1, "ioctl(UFFDIO_REGISTER) should succeed"); + + fault_addr =3D mem + offset; + args.addr =3D fault_addr; + + ret =3D pthread_create(&fault_thread, NULL, fault_thread_fn, &args); + TEST_ASSERT(ret =3D=3D 0, "pthread_create should succeed"); + + ret =3D read(uffd, &msg, sizeof(msg)); + TEST_ASSERT(ret !=3D -1, "read from userfaultfd should succeed"); + TEST_ASSERT(msg.event =3D=3D UFFD_EVENT_PAGEFAULT, "event type should be = pagefault"); + TEST_ASSERT((void *)(msg.arg.pagefault.address & ~(page_size - 1)) =3D=3D= fault_addr, + "pagefault should occur at expected address"); + TEST_ASSERT(!(msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP), + "pagefault should not be write-protect"); + + uffd_copy.dst =3D (unsigned long)fault_addr; + uffd_copy.src =3D (unsigned long)(buf + offset); + uffd_copy.len =3D page_size; + uffd_copy.mode =3D 0; + ret =3D ioctl(uffd, UFFDIO_COPY, &uffd_copy); + TEST_ASSERT(ret !=3D -1, "ioctl(UFFDIO_COPY) should succeed"); + + /* Wait for the faulting thread to complete - this provides the memory ba= rrier */ + ret =3D pthread_join(fault_thread, NULL); + TEST_ASSERT(ret =3D=3D 0, "pthread_join should succeed"); + + /* + * Now it's safe to check args.value - the thread has completed + * and memory is synchronized + */ + TEST_ASSERT(args.value =3D=3D test_val, + "memory should contain the value that was copied"); + TEST_ASSERT(*(char *)(mem + offset) =3D=3D test_val, + "no further fault is expected"); + + ret =3D munmap(mem, total_size); + TEST_ASSERT(!ret, "munmap should succeed"); + free(buf); + close(uffd); +} + static void test_guest_memfd_flags(struct kvm_vm *vm) { uint64_t valid_flags =3D vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS); @@ -494,8 +570,10 @@ static void __test_guest_memfd(struct kvm_vm *vm, uint= 64_t flags) gmem_test(fallocate, vm, flags); gmem_test(invalid_punch_hole, vm, flags); =20 - if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) + if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) { gmem_test(uffd_minor, vm, flags); + gmem_test(uffd_missing, vm, flags); + } } =20 static void test_guest_memfd(unsigned long vm_type) --=20 2.53.0