From nobody Sun Feb 8 15:58:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BFFB6B67E for ; Fri, 16 Jan 2026 08:27:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768552071; cv=none; b=TyAl7KzXg0nmqq4jbEEAEmAqUn+P4s83KJnGobVab0mVR3pzTfcCw4IMJiLSD3dLKgn8QqsdBIF5zcA1yICsfTDzh292KBvensRigDnVf3qBjgE5DKtAAYXemnm92+rqwZEQl9RVKR/kEoET4CDyX7eTBbODoCJR/vrQVe+xiKY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768552071; c=relaxed/simple; bh=YofC2b2XU20cv5xXmAOE7WNprw+c3MGIP1Je4A3PBw4=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=Y8ZaNdSDq0fLWO6WuzL+SNSoKVmD0BPpoD/RQEgS8T0kZLrnN8HTGXpYh/rdt8sriRpzxmnbguHrXqe5cUw/Qi2DyCHDXQFkrWvufniEWMX2TtstzZn1sy1T2vBwcEpC9FZ9Dv0FFxZrfWFKLrG2GPNCQn9tSODBDANrG2NhVG4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 07186FEC; Fri, 16 Jan 2026 00:27:41 -0800 (PST) Received: from a080796.blr.arm.com (a080796.arm.com [10.164.21.51]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 37D7D3F694; Fri, 16 Jan 2026 00:27:41 -0800 (PST) From: Dev Jain To: akpm@linux-foundation.org Cc: axelrasmussen@google.com, yuanchu@google.com, weixugc@google.com, david@kernel.org, lorenzo.stoakes@oracle.com, Liam.Howlett@oracle.com, vbabka@suse.cz, rppt@kernel.org, surenb@google.com, mhocko@suse.com, riel@surriel.com, harry.yoo@oracle.com, jannh@google.com, ryan.roberts@arm.com, baohua@kernel.org, baolin.wang@linux.alibaba.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Dev Jain Subject: [PATCH mm-unstable] mm: Fix uffd-wp bit loss when batching file folio unmapping Date: Fri, 16 Jan 2026 13:57:21 +0530 Message-Id: <20260116082721.275178-1-dev.jain@arm.com> X-Mailer: git-send-email 2.34.1 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" The recently added file folio unmap batching support forgets to update pte_install_uffd_wp_if_needed(), which still updates a single pte. We end up jumping to the end of the folio in page_vma_mapped_walk(), thus setting the uffd-wp marker only on a single pte in the batch. Fix this by passing nr_pages into the function, and set the uffd-wp marker on all ptes. Note that, since the nr_pages passed to this function is always derived by some sort of batching, it is guaranteed that the set of old ptevals of the batch have uffd-wp bit on all ptes or no ptes, therefore it is safe to deri= ve the value of the local variable "arm_uffd_pte" from only the particular pteval passed to this function, but apply the result on all ptes of the bat= ch. Use set_pte_at() in a loop to set the markers - we cannot use set_ptes() as that will increment the PFN, but we don't have any PFN to update here. The userspace visible effect of the bug is inaccuracy observed by workloads relying on uffd-wp regions to install their own pages. Fixes: 8798e255b5ec ("mm: rmap: support batched unmapping for file large fo= lios") Signed-off-by: Dev Jain --- Patch applies on mm-unstable, commit f8ed52ac0cfb. I observed this bug during code inspection, but it turns out that the uffd-= wp-mremap selftest will skip some tests with a bogus complain that "MADV_PAGEOUT didn= 't work, is swap enabled?" even when swap is enabled. It first sets the region uffd-= wp, then swaps it out, then checks through pagemap whether it got swapped out. = For file folios, this check makes no sense since the ptes are simply cleared, b= ut in this particular case, because of uffd-wp preservation, we need to store PTE= _MARKER_UFFD_WP, which is stored as a swap entry, that is why the test works out on a non-bu= ggy kernel. include/linux/mm_inline.h | 7 ++++--- mm/memory.c | 14 +------------- mm/rmap.c | 2 +- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h index fa2d6ba811b5..adec1dcb8793 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h @@ -568,7 +568,7 @@ static inline pte_marker copy_pte_marker( */ static inline bool pte_install_uffd_wp_if_needed(struct vm_area_struct *vma, unsigned long ad= dr, - pte_t *pte, pte_t pteval) + pte_t *pte, pte_t pteval, unsigned int nr_pages) { bool arm_uffd_pte =3D false; =20 @@ -599,8 +599,9 @@ pte_install_uffd_wp_if_needed(struct vm_area_struct *vm= a, unsigned long addr, arm_uffd_pte =3D true; =20 if (unlikely(arm_uffd_pte)) { - set_pte_at(vma->vm_mm, addr, pte, - make_pte_marker(PTE_MARKER_UFFD_WP)); + for (int i =3D 0; i < nr_pages; ++i, ++pte, addr +=3D PAGE_SIZE) + set_pte_at(vma->vm_mm, addr, pte, + make_pte_marker(PTE_MARKER_UFFD_WP)); return true; } =20 diff --git a/mm/memory.c b/mm/memory.c index 6b22dd72ebc8..35ac86d29e77 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1588,8 +1588,6 @@ zap_install_uffd_wp_if_needed(struct vm_area_struct *= vma, unsigned long addr, pte_t *pte, int nr, struct zap_details *details, pte_t pteval) { - bool was_installed =3D false; - if (!uffd_supports_wp_marker()) return false; =20 @@ -1600,17 +1598,7 @@ zap_install_uffd_wp_if_needed(struct vm_area_struct = *vma, if (zap_drop_markers(details)) return false; =20 - for (;;) { - /* the PFN in the PTE is irrelevant. */ - if (pte_install_uffd_wp_if_needed(vma, addr, pte, pteval)) - was_installed =3D true; - if (--nr =3D=3D 0) - break; - pte++; - addr +=3D PAGE_SIZE; - } - - return was_installed; + return pte_install_uffd_wp_if_needed(vma, addr, pte, pteval, nr); } =20 static __always_inline void zap_present_folio_ptes(struct mmu_gather *tlb, diff --git a/mm/rmap.c b/mm/rmap.c index f13480cb9f2e..d6ca002bf79c 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -2171,7 +2171,7 @@ static bool try_to_unmap_one(struct folio *folio, str= uct vm_area_struct *vma, * we may want to replace a none pte with a marker pte if * it's file-backed, so we don't lose the tracking info. */ - pte_install_uffd_wp_if_needed(vma, address, pvmw.pte, pteval); + pte_install_uffd_wp_if_needed(vma, address, pvmw.pte, pteval, nr_pages); =20 /* Update high watermark before we lower rss */ update_hiwater_rss(mm); --=20 2.34.1