From nobody Mon Apr 6 10:31:30 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 CAD5E3BA238; Mon, 30 Mar 2026 10:11:33 +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=1774865493; cv=none; b=aD+2muWFLAqqIwNtHihKsJ2UroHDqEBe0R+CaZs9SCyBWvA5f2btGIU8lXbswP0lF8AIMVWop170l23Aooh9Fnl1SW01nQDQstEsRqa5IWjGNfK1Dtcce36TuUtWiMDXPyVRWUbeAPaK8GPaQxAhcusVeRUAAD0XipwYqv23sG4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865493; c=relaxed/simple; bh=QyXpu9HXbfbZXv1V/Q6cFWUb/O2hxT2SCUEnl/mL9Tg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nKG9ZcY8q8VC8GpOV6PnsnSMblZTXD89RCN+5uwioRhsJgkm6EjNuroGkfEDJ6P8+pjEemCAhgsvdktfWq+GwKEknDFLuYk4cdF4mPK4epjKPpscA3l+wpQaDmCPuUqeOcx2UOJL459xZeYUQhKZkJfLHD1rsaNq93R7jio3Sls= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=vNyD+lRi; 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="vNyD+lRi" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 62E06C4AF09; Mon, 30 Mar 2026 10:11:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865493; bh=QyXpu9HXbfbZXv1V/Q6cFWUb/O2hxT2SCUEnl/mL9Tg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vNyD+lRitJL6FQ08aJ2Ko9YEkn9XcqCTCToMIMy6KXBGYHirerrqpnx9wQJ/lbQUf jrjAH/939HGIiB+8//xJ6BkneVvgQgOOgrdOykR4LH2DxFcm84JgXOdgO6R6qN3eI1 fvzfe5DRsDPoqMPWtX4PgjizstOvrWWqRw4kYAJ+vnLsPYo02rCuCY0ghBouNvClfw nbZvA/MDezrrO0jmOynPpaB4RfA6Zm3JCOPztbjhB2LpcahiUwF/LJkbKbNDyoO0zo S7kU7amZbd7ez6V+LWcgIvyX25AoJjwsorPuTIcrD46phatb3RO/99PEaJpHoDmI9f AG1AA5cDImkQA== 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 v3 01/15] userfaultfd: introduce mfill_copy_folio_locked() helper Date: Mon, 30 Mar 2026 13:11:02 +0300 Message-ID: <20260330101116.1117699-2-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 720CD3BB9EE; Mon, 30 Mar 2026 10:11: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=1774865500; cv=none; b=Cxz2ylP9OYkNkHuWjZjGdvxMYzYvDJvkWsUwGyHRzqBCHtPN1FTR4KFehtzetNuDrMS+G41r3UJk9rLqy0UT1wSbZi7qFte3OfED11IJs55rHrR3+vo2FG3sGDr7Z/XmjV+sAACCXYVy9ZCrFQaoQCYjpsn8/UZ+tiDaFyGPIAg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865500; c=relaxed/simple; bh=VgyPdwYS7n+S8efxkXU85aJ9D+6gFYcP8W3h39W+MKU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IMgWQ9gOHzSXq1vB4RpTYnvziQ8Ucel33q+m1lFvmpYl4g+E6NzuGVnzTcYwuAIA9whUIp6zfgABJtH9WQ+v/2iG6TOu0vIfYu+YRkDnOaKdORMkdfcv1Aca30Wxjj7n38aw0+kJiRRXXHLTGmlGo/0ZKGVF/qssxKC00eRn35M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jcCVqb+T; 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="jcCVqb+T" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 21855C2BC9E; Mon, 30 Mar 2026 10:11:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865500; bh=VgyPdwYS7n+S8efxkXU85aJ9D+6gFYcP8W3h39W+MKU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jcCVqb+Tsz0hlTedYH/gRbksM5Nnphpr0EBdRysH97H/K1awvTX42WX08fkB/ZdZO Fl0oogUIPE/hdfNGf90ubnBkxN9D6x/Js+vYqmKcd7mHRyROgzTrCkldyaJ8je/xI+ JotXbysOX7USgkkYYzRKgtF2PssPsnlN3x/qihhjReNcJwv5HiEG8zh9xYXEnOQSCw jdu85w2T5ulnlhIS8yksjVGSRpE6X/q3CZiSiAtYR4vaxgXK4ye27RcnyTMQK2X3dQ Y+XeHPqiYJCSwDl3L6CKxEU4AF7ssC2Cps7ADIUxZdtvTgHphc0t0epHlHUByKePe4 yWiQWxBFFlZUw== 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 v3 02/15] userfaultfd: introduce struct mfill_state Date: Mon, 30 Mar 2026 13:11:03 +0300 Message-ID: <20260330101116.1117699-3-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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) --- mm/userfaultfd.c | 148 ++++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 66 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 32637d557c95..fa9622ec7279 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 @@ -790,12 +804,14 @@ 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; + state.vma =3D dst_vma; =20 - VM_WARN_ON_ONCE(dst_addr >=3D dst_start + len); + while (state.src_addr < src_start + len) { + VM_WARN_ON_ONCE(state.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 +843,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 +882,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 10:31:30 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 3E5AE38F9C; Mon, 30 Mar 2026 10:11: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=1774865507; cv=none; b=QzwXovsy40TPEjv6dzNxTnvP5crqI8CkZ7T5/w5YUHsI0EdDgNFnWDc+bzkDjkotWSsCwB8kBRZZhiT636LaS4tEQszD9BJ1MoWZRUKmkB1C1Beby2KKoKLr6z26kZd7epXGQGvWSza3MXyPaeuGW6HLolGUezVaD9lRs2YI8Y8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865507; c=relaxed/simple; bh=1DrXuSwiAhOzaulO5G9xZyzpNsLyxsrB7l2LjMZyGp0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OYP4x5hm0gKOEKFfGtM0E+jVJ6ntJIImbzZdI0+4HGAmqFXl0cOsxmwkqxLE+hSW1l9UTtDVaeSLWYL3T5N0ZnVF7W9Ux0jFeo9S9G3dQfDFZbbowgm9JaMyJF7rSERNUFg32Ufo2sGTRA7atZ7vhDaePLdfngVm19PCvntjjF4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PkFVBcKE; 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="PkFVBcKE" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D40C8C2BC9E; Mon, 30 Mar 2026 10:11:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865507; bh=1DrXuSwiAhOzaulO5G9xZyzpNsLyxsrB7l2LjMZyGp0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PkFVBcKEy+nKe+MP3hw+nxp/uTTTK4Qt4cC8Bp1NGkORfgNDuWEFsMdvuEca3uIAO 7dmS1MwmwLrXJeSYaKHyDqI+hElmO5KM67rKEKP9DPwf1eYV0It5rn2IUMPVjXkBB3 DU4nzNtK+zF1YZwuOFrQHXDwbISsHEPADqec87BJiw7aBw9tCeB6LiKlD8QyEZezE7 BRmRNjLofhHABT3Lf0uBQvTeT4Gxz20Xj9nSclmPBkEqQphgeaY1cOnjKgR3PWMHdl oFTDblZOKQHdtqcmqPlvaB5b0Xzwzj3p4Q0cEswOxmJ3zV8dTl1qejLsSeKyEFda55 VHDlFfXmWMFVg== 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 v3 03/15] userfaultfd: introduce mfill_establish_pmd() helper Date: Mon, 30 Mar 2026 13:11:04 +0300 Message-ID: <20260330101116.1117699-4-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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) Acked-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 fa9622ec7279..291e5cfed431 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: @@ -809,41 +837,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 10:31:30 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 03DE93BA257; Mon, 30 Mar 2026 10:11:53 +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=1774865514; cv=none; b=AHbggwK/TXNitVplLvxIWfnMb/H75GR8Rkgzu7RxDg6zA54He4NfTFSC1axhRvr+PKbN4hP/4XaevgSD3VFGdfSBnhnsKZL+F4NlquvwH19962PPxHRa7Xa0iGmlfEGBAw/xi7STVn0GrUB0uk2wuGWehrn3vVneVKWhz9amJEw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865514; c=relaxed/simple; bh=ynjTmCe+1IWEv5uzQ9YDrK1M2CISmsrzcaIqVGmX5v0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cD8cRdoDkqMyVTXjnBmq+ceBZm93e1eH6Q/grDNfdh99sldkTFLjFgzeXKCWyIvKlyRq0ptwngL75ZKGyjW6uD4PKZPNiY6R9Df7FIWKIOlc6UwaxJY8gU+2pDA0RxCdey2BpbJnUZm4vA6Jws6t8yOKytqv8JLYdyGMYBowd8Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=J9PBouR4; 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="J9PBouR4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9232FC2BCB5; Mon, 30 Mar 2026 10:11:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865513; bh=ynjTmCe+1IWEv5uzQ9YDrK1M2CISmsrzcaIqVGmX5v0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=J9PBouR46yBSGMp/xczSDwNBSz01ILMMSUj54LcWOXbIqNAWCWDiQOgfDL2mOpJeX jWmG3aMQSTYTPDkRqV982sQciqG5TEdyiaeAEcRpBwctsK1vNMR8kLvjmKH+OTaMh8 ZXPPiEZPMEfVIIMFDVu/qYHj/2MLKn7dwA3DEdchKRyf2i77SsBedtIGw2xcPwO4fw YgMb4eBrNILU5qOsa8Au+P2E4ooFL2zy8mIGhp+S7Anm6rAiRfKpcXiCw8L/gYdBge Ata/+5uKD1wBFajLa+AtZtMH42pL7iE6FbyBqGi+1qfJDSK3Z0NwCVZnovq4SmH0T7 KWc50FhDtW59A== 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 v3 04/15] userfaultfd: introduce mfill_get_vma() and mfill_put_vma() Date: Mon, 30 Mar 2026 13:11:05 +0300 Message-ID: <20260330101116.1117699-5-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 --- mm/userfaultfd.c | 126 ++++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 51 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 291e5cfed431..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,57 +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; - } - - /* - * 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; - - state.vma =3D dst_vma; - while (state.src_addr < src_start + len) { VM_WARN_ON_ONCE(state.dst_addr >=3D dst_start + len); =20 @@ -852,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); @@ -882,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 10:31:30 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 485ED3BD23B; Mon, 30 Mar 2026 10:12: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=1774865521; cv=none; b=rngStRcHg60awidOZSj4nvQhbLE61Mzb7xduZjBVsj8kehpOxvBn2wH7ilcczyUzuh8xCQl02A1KGLjxi4x0ZFcOw6FsEtN02y/mV0MnKtftTPSKYblEVoYf6s9r0gfRlij82HHd6CEcqJw0vVzXOtX3s8KpKArfPhnbTPus44E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865521; c=relaxed/simple; bh=5SzGyiFXZk7l7ucReQ40iRkaPGcy2CcX6r/21eMkk2I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K3p5tkE9Q7W0VNqE7u1b9RKBMWnasRmRVZ5YXLamRSBlgAbWjsFSeKOoGzDu7inY/DYLw0FnF6yKCKauZe8Ksu5nQf6yHbKB7lH729pcVjrZwBRjskvdwFNLIbJV3ToslLQH7/smtAbNGkRxeNalqtyDeiUuHxM4egcmKM0uI/s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=rnZmUZP7; 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="rnZmUZP7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 500A1C4AF09; Mon, 30 Mar 2026 10:11:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865520; bh=5SzGyiFXZk7l7ucReQ40iRkaPGcy2CcX6r/21eMkk2I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rnZmUZP7gvczpmYCEel4ZXmQ2PPmuQj3/XLGNLve0YawXSP2n2h581cordUCP7K2b MmmnEb7e8DcyW1uRo2AewcgnTOx9blnu4afb+Q6gn6po2hgdRBuRrJswZumlsmYQNN WYGrErDR5bviDJ6dVcSCtl71iYEOnC/KGr//7ne2uRD2lGWNH5EdqFy8ypdTN6NjbK wMigsbkomvod7dn/ko2q+w52YIV1vn+g4FPibveRYXwvjuSLuiL4fl4nvG3oPg8ZBe Vh0YR62YlJQBCLgTH39pGzGix1bhYmhld6ri6Jv3CHDGtgiWE7cMHSiyIKoqN90k+r gOXP70eeEkHwg== 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 v3 05/15] userfaultfd: retry copying with locks dropped in mfill_atomic_pte_copy() Date: Mon, 30 Mar 2026 13:11:06 +0300 Message-ID: <20260330101116.1117699-6-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 --- 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 10:31:30 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 957323BADA5; Mon, 30 Mar 2026 10:12: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=1774865527; cv=none; b=XnmO4p6F2X+flw+Zt5v+VKjyR2p5pW5+Qd0orOE6WWP7WRhmkTyCdUf2bezZZQOx/kyVB49C9lBwwIi12eUhp0loQquSDtH0YroPMBrD1VM6CcZDcGQfMPzJo90h35AU7/Jq05xR89cidXvGkiSQRuBe/HAx5o9e29NITbCdzD4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865527; c=relaxed/simple; bh=KXI5bMAAl/JthbpgvCWeg45Zai/zTi+2zYeJ12CGtXQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IMXcVhiuPsjPdrbqOyxCC6ceQUhoQakOAQcsVztJNYogt1q5B6c9ii/IAr77KGEBgrHkLI6XMX0zY5QzFB26CUjbclFMKObZPIUfoT4HojOFYmzeV4gvVJk2bKwI5T892YwNuub+Oyim/J1toqSGNpxHXuYfEIpKTJQ9qH+MDhQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=T6bfA0ev; 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="T6bfA0ev" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0D561C2BC9E; Mon, 30 Mar 2026 10:12:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865527; bh=KXI5bMAAl/JthbpgvCWeg45Zai/zTi+2zYeJ12CGtXQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=T6bfA0evPK6ZnzfPf76G9AX7uo/ynI67VOh0Hf/CutXRlQb3EPL88lYCZz2+4TSrP sJIISfG6sHGFw9HmV1v6gYmvdowqir8TX8d8mVJ/4DEkDjbTAn/ki/j6G+sfRNp0B6 kwhAYekUhsiVtrkqnoS4dm0lHKcwvmgPQYM/gnGBppKnBXxrSP6yyL0Xg8OweIPT4v 5zpDdT7M1DsnFNq69Jo6hbg0Mi7dvmXWEqFSwddaGdz4kK+9pwBX4rs1PUXtm990SK Lx6kEfL6C59ZHFzVD49d8upGPQanG343lclCQnvD+Gur37QQ6dxQatn5d5pgJbJ9Oj AnFSQd2dspewA== 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 v3 06/15] userfaultfd: move vma_can_userfault out of line Date: Mon, 30 Mar 2026 13:11:07 +0300 Message-ID: <20260330101116.1117699-7-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 30A753BADA1; Mon, 30 Mar 2026 10:12: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=1774865534; cv=none; b=aNMQpBfj9zgboUcSjZCWJ2SxaTNOcqZC/OjZTc/m8eAA25xHch6P94biM/cXykcVMUhkaXj4ahfQWU4QCD+lPbnqOp/LU3+Urbz4LUtr3lJBSBrnmS+r08EZywG+FUpbUURZV7HOkxykNuefZ8ljtXfIGxozwaTyasbJwcrd9wo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865534; c=relaxed/simple; bh=u+ZOOG8J/28GYuaj/GsT8WwOacoYSYHh4DClfNVux6U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rCyfFf9HcbqeWLEs1XfEFJnc34p7Z1ymS1diPacp4pyPjRLgkSeb5lz/4mYMfTu5d2krCrRvJuwCyhi5Ye3SPLX76aq2OvsTZqjxspQamuncLUPZAOFutQVjAv7Nzt7xLvhGw3b58ExsKeJ+5GbMg9x+0CtLpzNtCeOJ6flLNNA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ey/Bug5Q; 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="Ey/Bug5Q" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BFD6EC4CEF7; Mon, 30 Mar 2026 10:12:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865534; bh=u+ZOOG8J/28GYuaj/GsT8WwOacoYSYHh4DClfNVux6U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ey/Bug5QrGP9V2IgRa0MSqT9jLiKN1z+1t1GyNFGLTkOqS1opMCTgDdbe48AefLMh uRhgWjJ+4jGvSGiAqviMJS9hJwJVMLrpIcMtj0kxiYgOMCGzXba6s7b7BEp5Ph4TcG vcxWCNz79bccbB8n0xzREh8YuHHoU0yv6yjfUzKdgPSj6GNq/5KlQAtPvMk8E0mNLy cH082ASpezuITTOxHFDp21Vi672lNcVrRcjkAZA+A9NUj8Bp7uitBGCGr6ca32S9YT a8Eqn6aoG5p14c3oIUbe0ZGUu6xr671VsulGVtpxazSy3/AGyoZvPkKP6RYUdyxDK5 Rzb5c6eoa05aA== 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 v3 07/15] userfaultfd: introduce vm_uffd_ops Date: Mon, 30 Mar 2026 13:11:08 +0300 Message-ID: <20260330101116.1117699-8-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 E34AC3BADA1; Mon, 30 Mar 2026 10: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=1774865541; cv=none; b=ILTQ2VShVeCkQY92UAEByo5GGiK+t4rBa8Q2dhTmvMvHbPevPuDHa/3YB+l4ir1dne8SVGFkS5qTkv7nrXs/ZJdQcW+4LnL1VCr7hdKg3meYtYYjBQnXlnqmM3laKDS5jpftfSv+f8vSw7elhbkhbmeHfIILUmqZLh7imUhyki4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865541; c=relaxed/simple; bh=lxXvlQh8xQ0EBf3to4Jw9wikE7NXPrSIjYbQ7Uoyt4w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LW1n0IAs59/3Y1sCm7sdqQGeM1gcHsfCAsFEqqUP/88ezMGPr4HpAdAT5QUZUb0YtcB74kfeB1IZKjziPNEynHIJzSJEL0y/e9WKK9RoqG+xzLShHZ4tyc9jEL+WNCRD6+dplr0thY8E22+dlF8zJGWdWksQRTWZLWA7Hbat/Y0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=aS25XSKy; 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="aS25XSKy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7DF22C4CEF7; Mon, 30 Mar 2026 10:12:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865540; bh=lxXvlQh8xQ0EBf3to4Jw9wikE7NXPrSIjYbQ7Uoyt4w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aS25XSKyFpLRnOPX75HHRFZnJUfjixxOpkn2kTiFc06LCUfoedxT59el843/Qqbu/ hdj96YV/8MKR8RoOHQO3cSOxL6i1h6/pKmv6pMaaVH09KWfD5LC5NpJMuxZyRhHNFv OPlzbePKfMxLK71YRJjPAYBlBZNj3ztqyu7ADE3u4UK3sB8wOqPG1x81pa84IfP5ck FAIYO65JKH8d2o209unwereERqUUjHXzSUydWn7nIIsEDVGMqCpbCsVMbTOPNpwHpk HHJWD57DRZBfCNIdGHt99Pagc9jwhjvb0DWX4TxU51aQTEKG4dga61EpmCXGcZTjfO ovs80lRwcCs4g== 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 v3 08/15] shmem, userfaultfd: use a VMA callback to handle UFFDIO_CONTINUE Date: Mon, 30 Mar 2026 13:11:09 +0300 Message-ID: <20260330101116.1117699-9-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 8EDA13BADA1; Mon, 30 Mar 2026 10: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=1774865547; cv=none; b=fjrUPvuc1zI9asqETIFOSbVZDTYw2XOQ/UC3UcekgNahCey7qnupBOVxEBSTmuWDR/9UQsXyKHoD7u3GLYzSkzgtxhAJBcVnsLzkVbZH2KrTB2t9f8i93XrUrT3+wjesBha2bSvvAgSn5DpO6g/79nQSk0YDiYTnGGhPRzOF0eM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865547; c=relaxed/simple; bh=Ye6Z7TrGAOPgV52cgsTw8VTKwOK2VRWtWU0AJXFZvYo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sTjSykPYgoTsLe5VHHO862a6he9k3emV+Iy1V2xh1pUn+6S8NvPXYlCh1oBD4H74rohpJnmQLWUTn0/+hX6L5vBzC6fNkrHnR0EjioFiomBJyamHSGSTyCfA3f8FFnQvqlSGKb6lTcs58ZIHdVVqOnf78IsS5nWWD73+d3T55zg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Lu59ZX2u; 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="Lu59ZX2u" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3C476C4CEF7; Mon, 30 Mar 2026 10:12:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865547; bh=Ye6Z7TrGAOPgV52cgsTw8VTKwOK2VRWtWU0AJXFZvYo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Lu59ZX2uIJSAIeywrbsygWg/5qzBa3IGe/RNVZ7WYLUz4Xw5N5r0bIAABe6VFbP8O NtvKLDmvOZTz7qbBMsAPf/3U5xw7mvmT14XtWNEFof/m2zDo3fr2yPWC1tOqk9f9+y ELzQHIM5cBjVZSVPFUkiFgzGXGWXPBRk6Ig9Kok7voNr2Q+48CH2wFCbk9Ppf7Odsp 4mBV6rOhbzSAfBYSXV9ppyddFAaRhWFeGAPj8AKJP6mU3btMnqVtJV9pHMdnAitQVX LE6JlgEMOx98MNXXHElnfiP4Ss2smtFP/c27xZsZopXraSOUFqIynaqSs07w2x3gdX ZFbtPlVAcNraQ== 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 v3 09/15] userfaultfd: introduce vm_uffd_ops->alloc_folio() Date: Mon, 30 Mar 2026 13:11:10 +0300 Message-ID: <20260330101116.1117699-10-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 4E7903B8D48; Mon, 30 Mar 2026 10: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=1774865554; cv=none; b=ols/z0VmwHYlVRR5t9DA8HQ8TOYy7xYDM5Xj6JutbrlfSICLVeMJe4j+k0ci+LcqYntg3VsKQBczyg1uctn3c9RHaqvBdX8M3cEfrEcxF1YcaD44xHEpjMMYveuMAi/dSTCbWeZeePaf27czhda9xB8GGpiWxm0Rx1E8gbqlQg0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865554; c=relaxed/simple; bh=nhN0tYK94rMrBIfGb6UL4j3n8or99nhslnrvimSOhI4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JplVRS/KxnSO8jtl60eyHOeKSWGQIZJmnjU0dvCj1p1MtysIAKwqUPv8Dwc1YJGeswVDuSwVxZgzXKukaZFtVjlTMfnkK16qeD1+M+V9rIosd603HFWKTFcubjXrisn00B6ATfD0yvAhfZnLd+lYbjme86goCCkJ9JML7k1hhgQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ThkjaJTu; 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="ThkjaJTu" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EFCEAC4CEF7; Mon, 30 Mar 2026 10:12:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865554; bh=nhN0tYK94rMrBIfGb6UL4j3n8or99nhslnrvimSOhI4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ThkjaJTuFem6TuFExjQ/bRSrQvEN2ShwvsnLDFnskpwC+As2zc2+W1xCpBk8pUpHe PSkZEKwVL9BRSLtckD5qwYsPGCTJz3rruSCPddPvkhFEzHVhyUSQerdp2uF9Pc8Rgh jaSk7yG5dGkbhHsyrIUTP7GPncYPmYq73Z06Sms87TQu58qBdeUdK4xYDO0/TYCzD8 YZ8SwkXd8vBH247GXfmvhdGVROZruLkmWL9q8LYhlWlHf3jNvrlwU0nWLVFgbj/Nii sh3TsYg7Qg/vKYuCmYez5HrlFwI3Zr2FYTAbuDjfgO6pu3ivJsi9kzYyCAeDeg5u8Z vHXdduKanK9lQ== 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 v3 10/15] shmem, userfaultfd: implement shmem uffd operations using vm_uffd_ops Date: Mon, 30 Mar 2026 13:11:11 +0300 Message-ID: <20260330101116.1117699-11-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 0B7293B8D48; Mon, 30 Mar 2026 10:12: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=1774865561; cv=none; b=a1kCXz6LO1UWEidXAPUSDcrmn3tn/FFr5JdQlbhCNaulKOjF/+8WCbRqwCFVulKexPi9mFAikDqSQWK7fbqMPdNezCVxgC9H/mc3rutsLhs/RJY2HZzXgjRB8VYI14LVqEnGGrd5bYBLyp1ZGC9O8S58RFEpwZsV8eZvahKrOdU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865561; c=relaxed/simple; bh=5vaQGQBc3neyldkli11Rg6Y+SDtyLj7BMfixF4/GPSE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=M2BNAbgaWuQWrUmUc44iMqrZo7y1a2beLcY4dCEp0U4utBJf7sZaEM+hS/LL2/NGJ0tsp8fHsE/nOjBMtUYIhx+d8qIJn2F3inTYMrKA+GmQWDuPl4NZv0rdB1Qw9Pr3uF0HmWFnO/limTbiXBsnhrBa23e9owsYk0GaGlrOnG8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Vz5P/Ehl; 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="Vz5P/Ehl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AF1C3C2BCB2; Mon, 30 Mar 2026 10:12:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865560; bh=5vaQGQBc3neyldkli11Rg6Y+SDtyLj7BMfixF4/GPSE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Vz5P/EhlcbGrf+EmyrZRJlmlJMQol7XOzQNEor1Xg8Btoz8JyeEVcJwg+zd3jjia9 79uY/smDhLCPcI2vi/sG50tuN1FQiIJ5GJEFQZFBRzB/UNYlGVAxij9dSHQvV3kn16 UrQNH9lW2NvzWOT4w/05YnP6ez3HMLGLcwuNdxcv8+SXpC4Tt7TPIf6aUcQR+L25Wk z02GZvjWjqt/TTkKbwfaurOmdXFBzGou6+Y4yglTGV6i/CqRIqc37p4nniK0sH/Ytw 6mGvTJTgBidX783DjCgVKlho/aslUpIMMPjjG2rFpGM3akpXKXCB1ht1u/v3+8bRPO BpF9ymuGtg1bw== 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 v3 11/15] userfaultfd: mfill_atomic(): remove retry logic Date: Mon, 30 Mar 2026 13:11:12 +0300 Message-ID: <20260330101116.1117699-12-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 BE5833BE649; Mon, 30 Mar 2026 10: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=1774865567; cv=none; b=CmeMIaOFtR6A4nrLGTe+L7yiMYcMUN7vKTaWpc+rHH4/KxPJYhOuLXi+3NHWKUlRI1m0SXX3klo/EzFUZZDQh8gI4IpqZfe8OD6fNEpQ2C8jIOTzDndNCNoPlI941FdbgpqhJzNxjVWOSQ0EN9Xma+C35hJxWwAhVrENStAIF70= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865567; c=relaxed/simple; bh=OBbhkJ8YK27hN9vtg3QQXqgXyuXTuw2IdpJE89KAMWE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lYMuU4flt56ppRVpQEmKqNu/r0FrjXmAktWGBgq8JHl4X9yQ+DlTaw7BBCDavWdzaKanERnd+AAtWNAQH0L8MGSBqBReoSYpGa+hmlytsSpO4ZVBnZW6GjD1azEhkTcMuVUnzl4a81pbokjDxO/VsN5dAcUkE+kctYpJm25Lqcs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hgyyW8q8; 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="hgyyW8q8" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6D5F6C4CEF7; Mon, 30 Mar 2026 10:12:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865567; bh=OBbhkJ8YK27hN9vtg3QQXqgXyuXTuw2IdpJE89KAMWE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hgyyW8q8iYNIvZzvOQWoy03Lk7pJYY5Q3wOADiHx+O/Cemi7Y4kfbemX9edKP+Ipn SYJmjqhLXwWRpxKtGf8hWgXRI2CsPYArsol3/ezvJcWpv3Ql27Nddo827jppV5PNLa WHWG44EhUI/PgzreVTi/vguFrEesMAyqg6/oEqTwuMLQ2fykrIT1Yw13pSeBHDPJIt sKJf4ah6p9X9s/ypsiSK+oa4nQW+iKKa1L1vfd891+vuzaVF2CT7VNaE/PiMKdkrvm hg+f9cwpSE8m9qDQWFSiQOzSN5skxuMOkUyE1rhucWBqDyTFlRhBYkqDNIcVGulYCJ KumxlfCmzJ/5Q== 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 v3 12/15] mm: generalize handling of userfaults in __do_fault() Date: Mon, 30 Mar 2026 13:11:13 +0300 Message-ID: <20260330101116.1117699-13-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 8257C3BAD8B; Mon, 30 Mar 2026 10: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=1774865574; cv=none; b=Y2dVD5B2rB1NTcrI3bInGTvioi1a81gf5b6a2FJMpEZPAM+HqPjxS/+VSk+A1kQBChRwgN2QBSuHg01TpnCVZJN85cQp6RUDT9zQ/eg95swsDRiKmowV8Kjg89nLHuK8VDxpAyr+4T+RNS21zK32uW7CGHFl7uhvWORvEbLXxls= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865574; c=relaxed/simple; bh=sgqKlJf5BnGeophpkDo1XgppS/PlXJsOousdP1fvs+M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BFimt04A51MC/aKe9xW7o354GkB0QtoBHLUp1V/gUly9mM80Blqz6mG9478uV08ghfdEcnbNdbWlMj944q+Yoj0EaV5eszkn/Fwe1cp2PwG/tiHkcLw7XJdr1nf6YBuBphj19OQpMeVtVIH7SdcRS5UlJR6P1F4a/hyc9WE/hE0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hILcN0Xe; 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="hILcN0Xe" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2AFAAC4CEF7; Mon, 30 Mar 2026 10:12:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865574; bh=sgqKlJf5BnGeophpkDo1XgppS/PlXJsOousdP1fvs+M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hILcN0XeFbdlxHlruu2Besehj8PrHaiqK2pb5lcwSpeVPh4meHqmzavwE3/p7aJt0 MokaJ2fCycJrIYhZY6Ie+dyEedEdXN+hvlrNFeS2KLvGsYJitJY9EvJ2iZTQ3e+kKE 3wqe0tgp54GvzlHGrcrn8SGkf8ikX4AvblOSQiobkJ7Zg90ggh8y1BDJkqH/SkPRKn 3hV8fHIVNx7T2coeFmjILwXqtVcXJjiqjOwvLcCHlTsWdFbPL/0xNn1nrdV646OgkM 8U7MFS2K3y1tJH4yhnB3ILQUJjBvSqvGMhai2e8ofZhGWHI1RgRyDgbl0C0H1i+WpN Xeyx6IIGE6ETQ== 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 v3 13/15] KVM: guest_memfd: implement userfaultfd operations Date: Mon, 30 Mar 2026 13:11:14 +0300 Message-ID: <20260330101116.1117699-14-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 384B53C9ED5; Mon, 30 Mar 2026 10: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=1774865581; cv=none; b=fdLViVyMC/iSvqUJ7yNpvpf+a/VqnT5GYnX5+blaxrjHj5U73iWySxMfPHgXCF0tcWu9suD2rcgCw9kzupPwkSCQbv5rfuMaTPM8f59RPfLUpCIvsNn9XCXhkEIs14qSRn1WU3gM5KI7mLCQK1IQYMY4ZB3bUEce52jfVbmFYeg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865581; c=relaxed/simple; bh=vrai7iUOkplMitPBGsDeCd9GnVnwQZI906Ju/ms0Utg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VeH7p7rFXXvRqqF/Y2v7UHPmtv1/fdvDRc/e6lNQGCNw9llPo9e0sq23I3+cNWgQX/lzH0eD8xHSc9D2ot0rXGRl2/wnWwyg2TdKbnLuVxrW8V32HetPMGxd9rQyaIP5nk3HhBYOi6nBCMgyFG54hQgxAa4Rwx8VfVsIs8FdDQc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SLdJ7SF2; 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="SLdJ7SF2" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DC896C4CEF7; Mon, 30 Mar 2026 10:12:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865581; bh=vrai7iUOkplMitPBGsDeCd9GnVnwQZI906Ju/ms0Utg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SLdJ7SF21kWnRDDIGtJ65jNvNC/yPFzrBLTee8qMaBWvOw5YcRqVANo/wPZlY1P+N DZta0UtCRNtnQwU/023UCx6GnGjy9e9NUxTSJNPlWJ5xllr8BEDCfO0V/Pz5SAmRSd 42i3cH7qLRURuLVkfmjlZm9iXCkv33JAA+DafTaK5viTjhFpVsfbey9M6YvQndcHXq UUfZS7pA163lnmZzSeFsudxste4NafS8WT8QCvCvhQVD+Y8AU3+bKokHohpFa+prkO UHs6TWBbM06CQ86CGqW/Rmq1sCM9XW03K7btxDvXy9yngaOs413pfq4m+zH/7LEx1o 8Wg2LMxv2DwqQ== 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 v3 14/15] KVM: selftests: test userfaultfd minor for guest_memfd Date: Mon, 30 Mar 2026 13:11:15 +0300 Message-ID: <20260330101116.1117699-15-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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 10:31:30 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 ED2A33BED77; Mon, 30 Mar 2026 10: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=1774865588; cv=none; b=JJ8XucR+WrxQnVScXx3PX/VhtPTubtEVKBQWRmlvcKi+uyxTJbWC6xLkoyc1BQKH1H5V2aBc5S4NRHMUh4vIbUQq110GwhuwboKmV4/VntLXi2G/EYY+CzNSArsKcEKl8EawW7ZkEeFNHUaM55BGZqf+M5NkwetRRpHkS8wZ7nU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774865588; c=relaxed/simple; bh=z+HQ68nm/PN2NlXH0rkdfjkI55JODdIviAntTrnHsgk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FLn9CQmuGRpE/fAc6XGIlnwOl0SMPKwIgpGv5RM8EmKtjkAzJBAkxBjg7MJGIBycaa2Lso5aSoKC9APZ6xN9GqMpYETmPvZXrmcCSWt4HZ2uZOqvdol39g6Dt8UWHojhlogAW6VA2pw26ZCb7CjPXPiMhAH4P5z8T4hb+3+m8OI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Qd78A1P9; 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="Qd78A1P9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9A609C4CEF7; Mon, 30 Mar 2026 10:13:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774865587; bh=z+HQ68nm/PN2NlXH0rkdfjkI55JODdIviAntTrnHsgk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Qd78A1P9UWhpE5pfNF5/si0YUJqDBpx6QABMZxnbsGfRv1tputO1o/Fj0hI6yR2fH FvsnZ3AoBWYzWJ6RcLz9fOYYD1ih9BqPFODUF8l6Vh3PzjLcOB5dH2WtsPAsSXEgpG 2+ChD4WcGvre9pwNNmv+CFQ0efph6oYKrdKUJ93AefdcAxed2DAnXngn0RfTKB/hzH PLKhreYyx3SRJHaKu3Ob1dWqrvWyNNiReXe6WM02HJLPe3DzUBff3z58LQnwRvWy4I JHLJmjIAFYy7zsrRy7Gj+f+bVK9sOlM4R0u+cgTVjfLpeclDzbfvWJeXXyhPaDh+NF fuwMYcrpMklmQ== 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 v3 15/15] KVM: selftests: test userfaultfd missing for guest_memfd Date: Mon, 30 Mar 2026 13:11:16 +0300 Message-ID: <20260330101116.1117699-16-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260330101116.1117699-1-rppt@kernel.org> References: <20260330101116.1117699-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