From nobody Mon Sep 28 21:01:29 2026 Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (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 54E363F9A11; Mon, 17 Aug 2026 11:33:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.156.1 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786966395; cv=none; b=uDZ2zJYR441AIp5TtQLh8wA6XJryoy9dSC30M6uCzzWpOZ0x9kgOFTkwTJmp4AkitazKCz46XHdKN9L/e6gDCQbATWMfD+j1v78JeEqm/JcsUBR+ptW9zPrjvERVN7ota6XB44GT91xVLtcnCVJuPHMHQECcl01yXATlOiz6eSs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786966395; c=relaxed/simple; bh=UAwoDPkW16ZGhYDE8FT+1cN97RJlMalzAWf9aZMXMCs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iaZeN79K/SIJiJL+JDV3aKTALycvJGG8zpd7sqJSa+Qb7prmcLQlhCLMgHf2eYQVe7xap3Qiu/TyAYQX+th/yeh3GB3HglJMq4nQEsD+FhPMZrcwDCo/BRiXucIzRds3/7/r3R9U5mEyFOiQfQrEbCj/vzV2LfgCyJdCmK452XY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com; spf=pass smtp.mailfrom=linux.ibm.com; dkim=pass (2048-bit key) header.d=ibm.com header.i=@ibm.com header.b=duHF3NqX; arc=none smtp.client-ip=148.163.156.1 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.ibm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ibm.com header.i=@ibm.com header.b="duHF3NqX" Received: from pps.filterd (m0353729.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 67HAx4s11866223; Mon, 17 Aug 2026 11:33:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=pp1; bh=pqm0dcxSc6epEG0/m 6x+642lVxaYm7+JEs64OBL0PS4=; b=duHF3NqXJFLMCGM27S0MG+L+XXGg6A6Gm 8PJ0l4h0so/j4SuRJFohqXnzaknCwwP/cZZJWWC88oYKdVLY0FDFVa8nULGV68UX 65wj94Nf2o5K+sxtvE+GlhlmFOMCZ0Bj5DD0xbjQjRtey4ivaCdhGGy3eVL8B5C9 rPd0WRo3lNx3bZQeNE2IzTtJtpZS/vy3937uyym08tWTUICdNTdzyNeLAedcCB+b pTSlJiri7uE2cMgp1x0wsYKFmlKBw6NG3/z17QQzawh067iM32/5q+iaay78FVc1 O2tQ0rlPyGJWdQdX+caFv+a4eKtdVpcHiN3bYIatbIZB1QCTMYIEA== Received: from ppma23.wdc07v.mail.ibm.com (5d.69.3da9.ip4.static.sl-reverse.com [169.61.105.93]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 4g2fsqhy0m-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 17 Aug 2026 11:33:07 +0000 (GMT) Received: from pps.filterd (ppma23.wdc07v.mail.ibm.com [127.0.0.1]) by ppma23.wdc07v.mail.ibm.com (8.18.1.7/8.18.1.7) with ESMTP id 67HBR1PA029809; Mon, 17 Aug 2026 11:33:06 GMT Received: from smtprelay07.fra02v.mail.ibm.com ([9.218.2.229]) by ppma23.wdc07v.mail.ibm.com (PPS) with ESMTPS id 4g33xgwsa6-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 17 Aug 2026 11:33:06 +0000 (GMT) Received: from smtpav01.fra02v.mail.ibm.com (smtpav01.fra02v.mail.ibm.com [10.20.54.100]) by smtprelay07.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 67HBX2YD49676768 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 17 Aug 2026 11:33:02 GMT Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 8E17F20043; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 5980B20040; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: from tuxmaker.boeblingen.de.ibm.com (unknown [9.87.85.9]) by smtpav01.fra02v.mail.ibm.com (Postfix) with SMTP; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: by tuxmaker.boeblingen.de.ibm.com (Postfix, from userid 55669) id 34E54161127; Mon, 17 Aug 2026 13:33:02 +0200 (CEST) From: Alexander Gordeev To: Gerald Schaefer , Heiko Carstens , Christian Borntraeger , Vasily Gorbik , Claudio Imbrenda , Andrey Ryabinin Cc: linux-s390@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com Subject: [PATCH v7 1/4] mm: Make lazy MMU mode context-aware Date: Mon, 17 Aug 2026 13:32:59 +0200 Message-ID: <5ee7bee9fa47e623a8cecb11b97392d38adb843a.1786956464.git.agordeev@linux.ibm.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: 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 X-TM-AS-GCONF: 00 X-Proofpoint-Reinject: loops=2 maxloops=12 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODE3MDA4NyBTYWx0ZWRfX3/eZHtIWFnP4 PuCa5h6D9ZUCunAv9FKxM+CM9q2qpIdGWGZnWmFOM2Jej1YvizDaP2QMCQ3NfzzeWnqP7r4CpgG LFa3IElU1G8xzcbnI/i13XMolNjnYk9ZXM6zVCeM0BmfGuDcAvwNPBy2/j3YjxdIJwT7yzUHHkn zFmC0/WphwpKZk2YKYiSbWgTd9bYcGrKFdwXnmhubq9EC5HMzrwfdu88b48GYqS5Lz3LE1uiUfP la7tOAmNrUHOnu9vQjNbIVBVIrxTo7+pGKJ4+Mrk5STeQql7bMNPYsxvEYhh+c0UC1Cgsw5juDI e8KyQGr4rbD4khcIofqdXGhrm5cvOt8r2OfeQiLqUTyrG0iJcdjRArYiNrsrDLzFWKKA+mpUvdR B/r+BFYF3MejmjHFzbNkX5yQkdam4Ht3zSvaxY9GUraPfHXoWlQO270ixB2V0YWUc5ZMPkQWOuK mnG9ZVoRSrdyHXAjJ5w== X-Proofpoint-ORIG-GUID: cKCGY1sMoU3W3YsUu4BiI2uzH1JgykKt X-Proofpoint-Spam-Info: AW1haW4tMjYwODE3MDA4NyBTYWx0ZWRfX5G/Hkx1I1cKK c894Zd26/Qzn3slY4/eGZTvKvDxeP+A7fzUC1H1tsA5zHZwBmaV3zwXAU7FDXnDOuQO9C0r2t1a lZDC6oOZIbhZdkxI+hzduO1yefPdCZQ= X-Authority-Analysis: v=2.4 cv=DJe/JSNb c=1 sm=1 tr=0 ts=6a82f174 cx=c_pps a=3Bg1Hr4SwmMryq2xdFQyZA==:117 a=3Bg1Hr4SwmMryq2xdFQyZA==:17 a=Sv0fKeRqtYgA:10 a=VkNPw1HP01LnGYTKEx00:22 a=RnoormkPH1_aCDwRdu11:22 a=uAbxVGIbfxUO_5tXvNgY:22 a=7CQSdrXTAAAA:8 a=VwQbUJbxAAAA:8 a=VnNF1IyMAAAA:8 a=ovIRSG1zLJ1dbapjHkcA:9 a=a-qgeE7W1pNrGK8U0ZQC:22 X-Proofpoint-GUID: Nv18bAn1CQOTUvcU6gV7b0OeT-NWzffM X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-08-16_06,2026-08-12_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 impostorscore=0 spamscore=0 clxscore=1015 bulkscore=0 suspectscore=0 malwarescore=0 phishscore=0 adultscore=0 lowpriorityscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608170087 Content-Type: text/plain; charset="utf-8" Lazy MMU mode is assumed to be context-independent, in the sense that it does not need any additional information while operating. However, the s390 architecture benefits from knowing the exact page table entries being modified. Introduce lazy_mmu_mode_enable_with_ptes(), which is provided with the process address space and the page table being operated on. This information is required to enable s390-specific optimizations. The function takes parameters that are typically passed to page- table level walkers, which implies that the span of PTE entries never crosses a page table boundary. Architectures that do not require such information simply do not need to define the lazy_mmu_mode_enable_with_ptes() callback. Reviewed-by: Kevin Brodsky Acked-by: David Hildenbrand (Arm) Signed-off-by: Alexander Gordeev --- fs/proc/task_mmu.c | 2 +- include/linux/pgtable.h | 46 +++++++++++++++++++++++++++++++++++++++++ mm/madvise.c | 8 +++---- mm/memory.c | 8 +++---- mm/mprotect.c | 2 +- mm/mremap.c | 2 +- mm/vmalloc.c | 6 +++--- 7 files changed, 60 insertions(+), 14 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 229d1fc3d7f1..916785c3b2e0 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -2852,7 +2852,7 @@ static int pagemap_scan_pmd_entry(pmd_t *pmd, unsigne= d long start, return 0; } =20 - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(vma->vm_mm, start, end, start_pte); =20 if ((p->arg.flags & PM_SCAN_WP_MATCHING) && !p->vec_out) { /* Fast path for performing exclusive WP */ diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h index 2981e386da7b..cc85daf30739 100644 --- a/include/linux/pgtable.h +++ b/include/linux/pgtable.h @@ -271,6 +271,50 @@ static inline void lazy_mmu_mode_enable(void) arch_enter_lazy_mmu_mode(); } =20 +#ifndef arch_enter_lazy_mmu_mode_with_ptes +static inline void arch_enter_lazy_mmu_mode_with_ptes(struct mm_struct *mm, + unsigned long addr, unsigned long end, pte_t *ptep) +{ + arch_enter_lazy_mmu_mode(); +} +#endif + +/** + * lazy_mmu_mode_enable_with_ptes() - Enable the lazy MMU mode with a spee= dup hint. + * @mm: Address space the pages are mapped into. + * @addr: Start address of the range. + * @end: End address of the range. + * @ptep: Page table pointer for the first entry. + * + * Enters a new lazy MMU mode section; if the mode was not already enabled, + * enables it and calls arch_enter_lazy_mmu_mode_with_ptes(). + * + * PTEs that fall within the specified range might observe update speedups. + * The PTEs must belong to the specified address space and be in the same = PMD. + * + * There are no requirements on the order or range completeness of PTE + * updates for the specified range. + * + * Must be paired with a call to lazy_mmu_mode_disable(). + * + * Has no effect if called: + * - While paused - see lazy_mmu_mode_pause() + * - In interrupt context + */ +static inline void lazy_mmu_mode_enable_with_ptes(struct mm_struct *mm, + unsigned long addr, unsigned long end, pte_t *ptep) +{ + struct lazy_mmu_state *state =3D ¤t->lazy_mmu_state; + + if (in_interrupt() || state->pause_count > 0) + return; + + VM_WARN_ON_ONCE(state->enable_count =3D=3D U8_MAX); + + if (state->enable_count++ =3D=3D 0) + arch_enter_lazy_mmu_mode_with_ptes(mm, addr, end, ptep); +} + /** * lazy_mmu_mode_disable() - Disable the lazy MMU mode. * @@ -387,6 +431,8 @@ static inline void lazy_mmu_mode_resume(void) } #else static inline void lazy_mmu_mode_enable(void) {} +static inline void lazy_mmu_mode_enable_with_ptes(struct mm_struct *mm, + unsigned long addr, unsigned long end, pte_t *ptep) {} static inline void lazy_mmu_mode_disable(void) {} static inline void lazy_mmu_mode_pause(void) {} static inline void lazy_mmu_mode_resume(void) {} diff --git a/mm/madvise.c b/mm/madvise.c index 77552b03d318..4419d16b39f9 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -452,7 +452,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd, if (!start_pte) return 0; flush_tlb_batched_pending(mm); - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(mm, addr, end, start_pte); for (; addr < end; pte +=3D nr, addr +=3D nr * PAGE_SIZE) { nr =3D 1; ptent =3D ptep_get(pte); @@ -507,7 +507,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd, if (!start_pte) break; flush_tlb_batched_pending(mm); - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(mm, addr, end, start_pte); if (!err) nr =3D 0; continue; @@ -674,7 +674,7 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned = long addr, if (!start_pte) return 0; flush_tlb_batched_pending(mm); - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(mm, addr, end, start_pte); for (; addr !=3D end; pte +=3D nr, addr +=3D PAGE_SIZE * nr) { nr =3D 1; ptent =3D ptep_get(pte); @@ -734,7 +734,7 @@ static int madvise_free_pte_range(pmd_t *pmd, unsigned = long addr, if (!start_pte) break; flush_tlb_batched_pending(mm); - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(mm, addr, end, pte); if (!err) nr =3D 0; continue; diff --git a/mm/memory.c b/mm/memory.c index 6b8280cfc1db..0cc07b043384 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1272,7 +1272,7 @@ copy_pte_range(struct vm_area_struct *dst_vma, struct= vm_area_struct *src_vma, spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING); orig_src_pte =3D src_pte; orig_dst_pte =3D dst_pte; - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(src_mm, addr, end, src_pte); =20 do { nr =3D 1; @@ -1922,7 +1922,7 @@ static unsigned long zap_pte_range(struct mmu_gather = *tlb, return addr; =20 flush_tlb_batched_pending(mm); - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(mm, addr, end, start_pte); do { bool any_skipped =3D false; =20 @@ -2919,7 +2919,7 @@ static int remap_pte_range(struct mm_struct *mm, pmd_= t *pmd, mapped_pte =3D pte =3D pte_alloc_map_lock(mm, pmd, addr, &ptl); if (!pte) return -ENOMEM; - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(mm, addr, end, mapped_pte); do { BUG_ON(!pte_none(ptep_get(pte))); if (!pfn_modify_allowed(pfn, prot)) { @@ -3330,7 +3330,7 @@ static int apply_to_pte_range(struct mm_struct *mm, p= md_t *pmd, return -EINVAL; } =20 - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(mm, addr, end, mapped_pte); =20 if (fn) { do { diff --git a/mm/mprotect.c b/mm/mprotect.c index 9cbf932b028c..3fc26418e837 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -337,7 +337,7 @@ static long change_pte_range(struct mmu_gather *tlb, is_private_single_threaded =3D vma_is_single_threaded_private(vma); =20 flush_tlb_batched_pending(vma->vm_mm); - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(vma->vm_mm, addr, end, pte); do { nr_ptes =3D 1; oldpte =3D ptep_get(pte); diff --git a/mm/mremap.c b/mm/mremap.c index e9c8b1d05832..0dfe3de39ccc 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -260,7 +260,7 @@ static int move_ptes(struct pagetable_move_control *pmc, if (new_ptl !=3D old_ptl) spin_lock_nested(new_ptl, SINGLE_DEPTH_NESTING); flush_tlb_batched_pending(vma->vm_mm); - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(mm, old_addr, old_end, old_ptep); =20 for (; old_addr < old_end; old_ptep +=3D nr_ptes, old_addr +=3D nr_ptes *= PAGE_SIZE, new_ptep +=3D nr_ptes, new_addr +=3D nr_ptes * PAGE_SIZE) { diff --git a/mm/vmalloc.c b/mm/vmalloc.c index f4fa227a8d7f..1c6ef8fd079e 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -109,7 +109,7 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long add= r, unsigned long end, if (!pte) return -ENOMEM; =20 - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(&init_mm, addr, end, pte); =20 do { if (unlikely(!pte_none(ptep_get(pte)))) { @@ -393,7 +393,7 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long = addr, unsigned long end, unsigned long size =3D PAGE_SIZE; =20 pte =3D pte_offset_kernel(pmd, addr); - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(&init_mm, addr, end, pte); =20 do { #ifdef CONFIG_HUGETLB_PAGE @@ -560,7 +560,7 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned lo= ng addr, if (!pte) return -ENOMEM; =20 - lazy_mmu_mode_enable(); + lazy_mmu_mode_enable_with_ptes(&init_mm, addr, end, pte); =20 do { struct page *page =3D pages[*nr]; --=20 2.53.0 From nobody Mon Sep 28 21:01:29 2026 Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (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 E05AA3F4DC3; Mon, 17 Aug 2026 11:33:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.156.1 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786966395; cv=none; b=evo+VusYEFkcOf0VUKAqRyo6ucOchIjeGTLPubcZ+pBCIUfxA/u4EtGeerWDdCbRnk7HnRTK8AztcjmB+gUw3bFx1YtHLb/J/NX6RJn/gn35/g8d0kDiMOZRsQ1k7UXH0H6kIqd0KEuzuCvPVOb3F+rWSOkC6rS4nuJ9SBjwIAg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786966395; c=relaxed/simple; bh=OYhpnJQjet6ciiON4XHbrIGiNbV1QZK1bV4puh2Og30=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=caWE0TRUHGyMIf+nZUFUai6pCc3VRF8Msqs6U3XZ/KF4I8NNx7EsRGX01ZKPMjdsICLMl4iR3+uGQLNDFRabOAA0x+RYyxAf5RuYmtRb7J6WJTiP7pFlmukqBbtXiRM0eYWRI7/Sh77XbhvodzHarsIoZX0AjP813SUxGTxL4H8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com; spf=pass smtp.mailfrom=linux.ibm.com; dkim=pass (2048-bit key) header.d=ibm.com header.i=@ibm.com header.b=Foq1uI4N; arc=none smtp.client-ip=148.163.156.1 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.ibm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ibm.com header.i=@ibm.com header.b="Foq1uI4N" Received: from pps.filterd (m0356517.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 67H9qLFE3727949; Mon, 17 Aug 2026 11:33:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=pp1; bh=mJwL6JP2vCyeQf6Dw udqtBk64GFr5Du4cvK0t/JQQrY=; b=Foq1uI4NVJ3qiKBJeXydvEvZny1thzcud aLjERjwTsKz/zVSRGfaI0nTg4vxBHxg3Lnp9Axk+0Dd2vpcysb77tcdVnlmWCcJq vbkF0hfkYRIj++tQhC8bzWAxS8FgvbsW+cbfd9WDkh/r2kJNWlk3iKTdIhVXATgL rdB+QQeTCoCTybBFC7VIzdLygHEs2D5T++A8pNeILG4/KTRmv3gcrfkEA+hxNi3/ SFncExWIT+834Rknxwb6yUA1uowyFk5WMhDZafijWoP43+xyHf694zMUjwjzR06H Ju8OMo0T2o8ikwz9oFun9JToXvLc4YPhV+HkPYQH1q2bzB3IkFAag== Received: from ppma12.dal12v.mail.ibm.com (dc.9e.1632.ip4.static.sl-reverse.com [50.22.158.220]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 4g2fu4hyvq-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 17 Aug 2026 11:33:07 +0000 (GMT) Received: from pps.filterd (ppma12.dal12v.mail.ibm.com [127.0.0.1]) by ppma12.dal12v.mail.ibm.com (8.18.1.7/8.18.1.7) with ESMTP id 67HBQMrF007554; Mon, 17 Aug 2026 11:33:06 GMT Received: from smtprelay03.fra02v.mail.ibm.com ([9.218.2.224]) by ppma12.dal12v.mail.ibm.com (PPS) with ESMTPS id 4g32epx01b-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 17 Aug 2026 11:33:06 +0000 (GMT) Received: from smtpav07.fra02v.mail.ibm.com (smtpav07.fra02v.mail.ibm.com [10.20.54.106]) by smtprelay03.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 67HBX2WL26542542 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 17 Aug 2026 11:33:02 GMT Received: from smtpav07.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 8FF4820043; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: from smtpav07.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 58ABF20040; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: from tuxmaker.boeblingen.de.ibm.com (unknown [9.87.85.9]) by smtpav07.fra02v.mail.ibm.com (Postfix) with SMTP; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: by tuxmaker.boeblingen.de.ibm.com (Postfix, from userid 55669) id 3EF1C1612F1; Mon, 17 Aug 2026 13:33:02 +0200 (CEST) From: Alexander Gordeev To: Gerald Schaefer , Heiko Carstens , Christian Borntraeger , Vasily Gorbik , Claudio Imbrenda , Andrey Ryabinin Cc: linux-s390@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com Subject: [PATCH v7 2/4] s390/mm: Batch PTE updates in lazy MMU mode Date: Mon, 17 Aug 2026 13:33:00 +0200 Message-ID: X-Mailer: git-send-email 2.53.0 In-Reply-To: References: 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 X-TM-AS-GCONF: 00 X-Proofpoint-Reinject: loops=2 maxloops=12 X-Proofpoint-GUID: 69zM09s9i1vGLiV9dcjcZbcvVULaBRzf X-Authority-Analysis: v=2.4 cv=NLLlPU6g c=1 sm=1 tr=0 ts=6a82f173 cx=c_pps a=bLidbwmWQ0KltjZqbj+ezA==:117 a=bLidbwmWQ0KltjZqbj+ezA==:17 a=Sv0fKeRqtYgA:10 a=VkNPw1HP01LnGYTKEx00:22 a=RnoormkPH1_aCDwRdu11:22 a=U7nrCbtTmkRpXpFmAIza:22 a=VnNF1IyMAAAA:8 a=C-ONzq7a6YYeur7YfcQA:9 X-Proofpoint-Spam-Info: AW1haW4tMjYwODE3MDA4NyBTYWx0ZWRfX5sKepB5+/xP0 W97irfU32buFgK4yENNqgJLKbjv/fopLlBlPYoq1ty/7x07ct8YRogg9gv/9FRiD3i2rkD/sWVG rnnE9YmcI4MJfnDogV0NukYXh1N1F4M= X-Proofpoint-ORIG-GUID: Tt-r7c6LnIzfGlhhgGHmEOxlaYKQlXGC X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODE3MDA4NyBTYWx0ZWRfXzR+2WA8B6Ka8 +4SaMVQ7T6Np154JNwUlFpvGJocNDJkLc/GwoTvRSEDWSgZm6DFiDCYtj8aBJnvAvzIlpBg8Vaw GXUlk+WFgCksH9k6qqRodCYFLxKZe/HvaHRwQARO33v8mPuYxaYm48PNbNwOT58uiHHqoPNclM2 3g7Wjuzl0Du01rGQo62iNXpfUHfEjnmJCAoxy+VkdFWMPzalFJg03e0DLLgsSIlfAHPEft3MJ5n mjIAYawphpOQzYgZalWVQtVmWLM0hVDaCfxRmrfz0UtwVXaxbjU0CsqLiMaRibVb6kyYfUt6MXM Rb14yDJSdd44Z6P4ycvx9U9C0UFER8KC01Nhn0lY4XlfaIWKiFPhrj5NQCnPS7o4PmCjgQN9Ng9 tyKUNI0Rta3CGBnAff8GI5YxPFj61em8MzQWc6pDnkVyKVz71sfiPpqOhFy8mUNCFNUlo3QM6uu OUfiDhqse+uxYzl/saw== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-08-16_06,2026-08-12_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 spamscore=0 lowpriorityscore=0 phishscore=0 impostorscore=0 malwarescore=0 suspectscore=0 clxscore=1015 adultscore=0 priorityscore=1501 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608170087 Content-Type: text/plain; charset="utf-8" Make use of the IPTE instruction's "Additional Entries" field to invalidate multiple PTEs in one go while in lazy MMU mode. This is the mode in which many memory-management system calls (like mremap(), mprotect(), etc.) update memory attributes. To achieve that, the set_pte() and ptep_get() primitives use a per-CPU cache to store and retrieve PTE values and apply the cached values to the real page table once lazy MMU mode is left. The same is done for memory-management platform callbacks that would otherwise cause intense per-PTE IPTE traffic, reducing the number of IPTE instructions from up to PTRS_PER_PTE to a single instruction in the best case. The average reduction is of course smaller. Since all existing page table iterators called in lazy MMU mode handle one table at a time, the per-CPU cache does not need to be larger than PTRS_PER_PTE entries. That also naturally aligns with the IPTE instruction, which must not cross a page table boundary. Before this change, the system calls did: lazy_mmu_mode_enable_pte() ... // up to PTRS_PER_PTE single-IPTEs ... lazy_mmu_mode_disable() With this change, the system calls do: lazy_mmu_mode_enable_pte() ... ... lazy_mmu_mode_disable() // apply cache with one multi-IPTE When applied to large memory ranges, some system calls show significant speedups: mprotect() ~15x munmap() ~3x mremap() ~28x The overall results depend on memory size and access patterns, but the change generally does not degrade performance. In addition to a process-wide impact, the rework affects the whole Central Electronics Complex (CEC). Each (global) IPTE instruction initiates a quiesce state in a CEC, so reducing the number of IPTE calls relieves CEC-wide quiesce traffic. In an extreme case of mprotect() contiguously triggering the quiesce state on four LPARs in parallel, measurements show ~25x fewer quiesce events. Signed-off-by: Alexander Gordeev --- arch/s390/Kconfig | 1 + arch/s390/include/asm/lazy_mmu.h | 9 + arch/s390/include/asm/lowcore.h | 2 +- arch/s390/include/asm/pgtable.h | 151 ++++++++++-- arch/s390/kernel/setup.c | 2 + arch/s390/kernel/smp.c | 7 + arch/s390/mm/Makefile | 1 + arch/s390/mm/lazy_mmu.c | 399 +++++++++++++++++++++++++++++++ arch/s390/mm/pgtable.c | 8 +- 9 files changed, 557 insertions(+), 23 deletions(-) create mode 100644 arch/s390/include/asm/lazy_mmu.h create mode 100644 arch/s390/mm/lazy_mmu.c diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 84404e6778d5..7846332dcd0a 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -97,6 +97,7 @@ config S390 select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_GIGANTIC_PAGE select ARCH_HAS_KCOV + select ARCH_HAS_LAZY_MMU_MODE select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_MEM_ENCRYPT select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS diff --git a/arch/s390/include/asm/lazy_mmu.h b/arch/s390/include/asm/lazy_= mmu.h new file mode 100644 index 000000000000..98366e9de9bc --- /dev/null +++ b/arch/s390/include/asm/lazy_mmu.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LAZY_MMU_H +#define __LAZY_MMU_H + +void lazy_mmu_online_boot_cpu(void); +int lazy_mmu_online_cpu(gfp_t gfp, unsigned int cpu); +void lazy_mmu_offline_cpu(unsigned int cpu); + +#endif /* __LAZY_MMU_H */ diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcor= e.h index 3b3ecc647993..dba236664da9 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -163,7 +163,7 @@ struct lowcore { __s32 preempt_count; /* 0x03a8 */ __u32 spinlock_lockval; /* 0x03ac */ __u32 spinlock_index; /* 0x03b0 */ - __u8 pad_0x03b4[0x03b8-0x03b4]; /* 0x03b4 */ + __s32 lazy_mmu_count; /* 0x03b4 */ __u64 percpu_offset; /* 0x03b8 */ __u8 percpu_register; /* 0x03c0 */ __u8 pad_0x03c1[0x0400-0x03c1]; /* 0x03c1 */ diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtabl= e.h index 859ce7c7d454..bf54202a0630 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -39,6 +39,64 @@ enum { =20 extern atomic_long_t direct_pages_count[PG_DIRECT_MAP_MAX]; =20 +bool __lazy_mmu_ptep_test_and_clear_young(unsigned long addr, pte_t *ptep,= int *res); +bool __lazy_mmu_ptep_get_and_clear(unsigned long addr, pte_t *ptep, pte_t = *res); +bool __lazy_mmu_ptep_modify_prot_start(unsigned long addr, pte_t *ptep, pt= e_t *res); +bool __lazy_mmu_ptep_modify_prot_commit(unsigned long addr, pte_t *ptep, p= te_t old_pte, pte_t pte); +bool __lazy_mmu_ptep_set_wrprotect(unsigned long addr, pte_t *ptep); +bool __lazy_mmu_set_pte(pte_t *ptep, pte_t pte); +bool __lazy_mmu_ptep_get(pte_t *ptep, pte_t *res); + +static __always_inline bool is_lazy_mmu_active(void) +{ + if (__is_defined(__DECOMPRESSOR)) + return false; + if (!get_lowcore()->lazy_mmu_count) + return false; + return true; +} + +static inline +bool lazy_mmu_ptep_test_and_clear_young(unsigned long addr, pte_t *ptep, i= nt *res) +{ + if (!is_lazy_mmu_active()) + return false; + return __lazy_mmu_ptep_test_and_clear_young(addr, ptep, res); +} + +static inline +bool lazy_mmu_ptep_get_and_clear(unsigned long addr, pte_t *ptep, pte_t *r= es) +{ + if (!is_lazy_mmu_active()) + return false; + return __lazy_mmu_ptep_get_and_clear(addr, ptep, res); +} + +static inline +bool lazy_mmu_ptep_modify_prot_start(unsigned long addr, pte_t *ptep, pte_= t *res) +{ + if (!is_lazy_mmu_active()) + return false; + return __lazy_mmu_ptep_modify_prot_start(addr, ptep, res); +} + +static inline +bool lazy_mmu_ptep_modify_prot_commit(unsigned long addr, pte_t *ptep, + pte_t old_pte, pte_t pte) +{ + if (!is_lazy_mmu_active()) + return false; + return __lazy_mmu_ptep_modify_prot_commit(addr, ptep, old_pte, pte); +} + +static inline +bool lazy_mmu_ptep_set_wrprotect(unsigned long addr, pte_t *ptep) +{ + if (!is_lazy_mmu_active()) + return false; + return __lazy_mmu_ptep_set_wrprotect(addr, ptep); +} + static inline void update_page_count(int level, long count) { if (IS_ENABLED(CONFIG_PROC_FS)) @@ -978,17 +1036,32 @@ static inline void set_pmd(pmd_t *pmdp, pmd_t pmd) WRITE_ONCE(*pmdp, pmd); } =20 -static inline void set_pte(pte_t *ptep, pte_t pte) +static inline void __set_pte(pte_t *ptep, pte_t pte) { if (pte_present(pte)) pte =3D clear_pte_bit(pte, __pgprot(_PAGE_UNUSED)); WRITE_ONCE(*ptep, pte); } =20 +static inline void set_pte(pte_t *ptep, pte_t pte) +{ + if (!is_lazy_mmu_active() || !__lazy_mmu_set_pte(ptep, pte)) + __set_pte(ptep, pte); +} + +static inline pte_t __ptep_get(pte_t *ptep) +{ + return READ_ONCE(*ptep); +} + #define ptep_get ptep_get static inline pte_t ptep_get(pte_t *ptep) { - return READ_ONCE(*ptep); + pte_t res; + + if (!is_lazy_mmu_active() || !__lazy_mmu_ptep_get(ptep, &res)) + res =3D __ptep_get(ptep); + return res; } =20 #define pmdp_get pmdp_get @@ -1181,6 +1254,15 @@ static __always_inline void __ptep_ipte_range(unsign= ed long address, int nr, } while (nr !=3D 255); } =20 +void arch_enter_lazy_mmu_mode_with_ptes(struct mm_struct *mm, + unsigned long addr, unsigned long end, + pte_t *pte); +#define arch_enter_lazy_mmu_mode_with_ptes arch_enter_lazy_mmu_mode_with_p= tes + +void arch_enter_lazy_mmu_mode(void); +void arch_leave_lazy_mmu_mode(void); +void arch_flush_lazy_mmu_mode(void); + /* * This is hard to understand. ptep_get_and_clear and ptep_clear_flush * both clear the TLB for the unmapped pte. The reason is that @@ -1201,10 +1283,16 @@ pte_t ptep_xchg_lazy(struct mm_struct *, unsigned l= ong, pte_t *, pte_t); static inline bool ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) { - pte_t pte =3D ptep_get(ptep); + pte_t pte; + int res; =20 - pte =3D ptep_xchg_direct(vma->vm_mm, addr, ptep, pte_mkold(pte)); - return pte_young(pte); + if (!lazy_mmu_ptep_test_and_clear_young(addr, ptep, &res)) { + pte =3D __ptep_get(ptep); + pte =3D pte_mkold(pte); + pte =3D ptep_xchg_direct(vma->vm_mm, addr, ptep, pte); + res =3D pte_young(pte); + } + return res; } =20 #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH @@ -1220,7 +1308,8 @@ static inline pte_t ptep_get_and_clear(struct mm_stru= ct *mm, { pte_t res; =20 - res =3D ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID)); + if (!lazy_mmu_ptep_get_and_clear(addr, ptep, &res)) + res =3D ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID)); page_table_check_pte_clear(mm, addr, res); /* At this point the reference through the mapping is still present */ if (mm_is_protected(mm) && pte_present(res)) @@ -1229,9 +1318,28 @@ static inline pte_t ptep_get_and_clear(struct mm_str= uct *mm, } =20 #define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION -pte_t ptep_modify_prot_start(struct vm_area_struct *, unsigned long, pte_t= *); -void ptep_modify_prot_commit(struct vm_area_struct *, unsigned long, - pte_t *, pte_t, pte_t); +pte_t ___ptep_modify_prot_start(struct vm_area_struct *, unsigned long, pt= e_t *); +void ___ptep_modify_prot_commit(struct vm_area_struct *, unsigned long, + pte_t *, pte_t, pte_t); + +static inline +pte_t ptep_modify_prot_start(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ + pte_t res; + + if (!lazy_mmu_ptep_modify_prot_start(addr, ptep, &res)) + res =3D ___ptep_modify_prot_start(vma, addr, ptep); + return res; +} + +static inline +void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long add= r, + pte_t *ptep, pte_t old_pte, pte_t pte) +{ + if (!lazy_mmu_ptep_modify_prot_commit(addr, ptep, old_pte, pte)) + ___ptep_modify_prot_commit(vma, addr, ptep, old_pte, pte); +} =20 #define __HAVE_ARCH_PTEP_CLEAR_FLUSH static inline pte_t ptep_clear_flush(struct vm_area_struct *vma, @@ -1261,11 +1369,13 @@ static inline pte_t ptep_get_and_clear_full(struct = mm_struct *mm, { pte_t res; =20 - if (full) { - res =3D ptep_get(ptep); - set_pte(ptep, __pte(_PAGE_INVALID)); - } else { - res =3D ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID)); + if (!lazy_mmu_ptep_get_and_clear(addr, ptep, &res)) { + if (full) { + res =3D __ptep_get(ptep); + __set_pte(ptep, __pte(_PAGE_INVALID)); + } else { + res =3D ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID)); + } } page_table_check_pte_clear(mm, addr, res); /* At this point the reference through the mapping is still present */ @@ -1291,10 +1401,15 @@ static inline pte_t ptep_get_and_clear_full(struct = mm_struct *mm, static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - pte_t pte =3D ptep_get(ptep); + pte_t pte; =20 - if (pte_write(pte)) - ptep_xchg_lazy(mm, addr, ptep, pte_wrprotect(pte)); + if (!lazy_mmu_ptep_set_wrprotect(addr, ptep)) { + pte =3D __ptep_get(ptep); + if (pte_write(pte)) { + pte =3D pte_wrprotect(pte); + ptep_xchg_lazy(mm, addr, ptep, pte); + } + } } =20 /* @@ -1327,7 +1442,7 @@ static inline void flush_tlb_fix_spurious_fault(struc= t vm_area_struct *vma, * PTE does not have _PAGE_PROTECT set, to avoid unnecessary overhead. * A local RDP can be used to do the flush. */ - if (cpu_has_rdp() && !(pte_val(ptep_get(ptep)) & _PAGE_PROTECT)) + if (cpu_has_rdp() && !(pte_val(__ptep_get(ptep)) & _PAGE_PROTECT)) __ptep_rdp(address, ptep, 1); } #define flush_tlb_fix_spurious_fault flush_tlb_fix_spurious_fault diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index b60284328fe3..f5a3c9e1b6b8 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -77,6 +77,7 @@ #include #include #include +#include #include "entry.h" =20 /* @@ -1012,5 +1013,6 @@ void __init setup_arch(char **cmdline_p) =20 void __init arch_cpu_finalize_init(void) { + lazy_mmu_online_boot_cpu(); sclp_init(); } diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 0ba7f89b8161..0a826bbaf1dd 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -59,6 +59,7 @@ #include #include #include +#include #include "entry.h" =20 enum { @@ -866,6 +867,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *tid= le) rc =3D pcpu_alloc_lowcore(pcpu, cpu); if (rc) return rc; + rc =3D lazy_mmu_online_cpu(GFP_KERNEL, cpu); + if (rc) { + pcpu_free_lowcore(pcpu, cpu); + return rc; + } /* * Make sure global control register contents do not change * until new CPU has initialized control registers. @@ -921,6 +927,7 @@ void __cpu_die(unsigned int cpu) pcpu =3D per_cpu_ptr(&pcpu_devices, cpu); while (!pcpu_stopped(pcpu)) cpu_relax(); + lazy_mmu_offline_cpu(cpu); pcpu_free_lowcore(pcpu, cpu); cpumask_clear_cpu(cpu, mm_cpumask(&init_mm)); cpumask_clear_cpu(cpu, &init_mm.context.cpu_attach_mask); diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile index 193899c39ca7..96cac3e99f51 100644 --- a/arch/s390/mm/Makefile +++ b/arch/s390/mm/Makefile @@ -4,6 +4,7 @@ # =20 obj-y :=3D init.o fault.o extmem.o mmap.o vmem.o maccess.o +obj-y :=3D init.o fault.o extmem.o mmap.o vmem.o maccess.o lazy_mmu.o obj-y +=3D page-states.o pageattr.o pgtable.o pgalloc.o extable.o =20 obj-$(CONFIG_CMM) +=3D cmm.o diff --git a/arch/s390/mm/lazy_mmu.c b/arch/s390/mm/lazy_mmu.c new file mode 100644 index 000000000000..002ee766b391 --- /dev/null +++ b/arch/s390/mm/lazy_mmu.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + +#define PTE_POISON _PAGE_LARGE + +struct ipte_range { + struct mm_struct *mm; + unsigned long base_addr; + unsigned long base_end; + pte_t *base_pte; + pte_t *start_pte; + pte_t *end_pte; + pte_t cache[PTRS_PER_PTE]; +}; + +static DEFINE_PER_CPU(struct ipte_range *, ipte_range); + +static int count_contiguous(pte_t *start, pte_t *end, bool *valid) +{ + unsigned long page_invalid_bit; + pte_t *ptep, pte; + + pte =3D __ptep_get(start); + page_invalid_bit =3D pte_val(pte) & _PAGE_INVALID; + + for (ptep =3D start + 1; ptep < end; ptep++) { + pte =3D __ptep_get(ptep); + if ((pte_val(pte) & _PAGE_INVALID) !=3D page_invalid_bit) + break; + } + + *valid =3D !(page_invalid_bit); + return ptep - start; +} + +static void __invalidate_pte_range(struct mm_struct *mm, unsigned long add= r, + int nr_ptes, pte_t *ptep) +{ + atomic_inc(&mm->context.flush_count); + if (cpu_has_tlb_lc() && cpumask_equal(mm_cpumask(mm), cpumask_of(smp_proc= essor_id()))) + __ptep_ipte_range(addr, nr_ptes - 1, ptep, IPTE_LOCAL); + else + __ptep_ipte_range(addr, nr_ptes - 1, ptep, IPTE_GLOBAL); + atomic_dec(&mm->context.flush_count); +} + +static int invalidate_pte_range(struct mm_struct *mm, unsigned long addr, + pte_t *start, pte_t *end) +{ + int nr_ptes; + bool valid; + + nr_ptes =3D count_contiguous(start, end, &valid); + if (valid) + __invalidate_pte_range(mm, addr, nr_ptes, start); + + return nr_ptes; +} + +static void set_pte_range(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t *end, pte_t *cache) +{ + int i, nr_ptes; + + while (ptep < end) { + nr_ptes =3D invalidate_pte_range(mm, addr, ptep, end); + + for (i =3D 0; i < nr_ptes; i++, ptep++, cache++) { + __set_pte(ptep, *cache); + *cache =3D __pte(PTE_POISON); + } + + addr +=3D nr_ptes * PAGE_SIZE; + } +} + +static void enter_ipte_norange(void) +{ + struct ipte_range __maybe_unused *range; + + if (!test_facility(13)) + return; + + local_bh_disable(); + + range =3D get_cpu_var(ipte_range); + get_lowcore()->lazy_mmu_count++; + + local_bh_enable(); +} + +static void enter_ipte_range(struct mm_struct *mm, + unsigned long addr, unsigned long end, pte_t *pte) +{ + struct ipte_range *range; + + if (!test_facility(13)) + return; + + local_bh_disable(); + + range =3D get_cpu_var(ipte_range); + get_lowcore()->lazy_mmu_count++; + + if (mm_is_protected(mm)) { + local_bh_enable(); + return; + } + + range->mm =3D mm; + range->base_addr =3D addr; + range->base_end =3D end; + range->base_pte =3D pte; + + local_bh_enable(); +} + +static void leave_ipte_range(void) +{ + pte_t *ptep, *start, *start_cache, *cache; + unsigned long start_addr, addr; + struct ipte_range *range; + int start_idx; + + if (!test_facility(13)) + return; + + local_bh_disable(); + + lockdep_assert_preemption_disabled(); + range =3D this_cpu_read(ipte_range); + if (!range->mm) + goto norange; + if (!range->start_pte) + goto done; + + start =3D range->start_pte; + start_idx =3D range->start_pte - range->base_pte; + start_addr =3D range->base_addr + start_idx * PAGE_SIZE; + addr =3D start_addr; + start_cache =3D &range->cache[start_idx]; + cache =3D start_cache; + for (ptep =3D start; ptep < range->end_pte; ptep++, cache++, addr +=3D PA= GE_SIZE) { + if (pte_val(*cache) =3D=3D PTE_POISON) { + if (start) { + set_pte_range(range->mm, start_addr, start, ptep, start_cache); + start =3D NULL; + } + } else if (!start) { + start =3D ptep; + start_addr =3D addr; + start_cache =3D cache; + } + } + set_pte_range(range->mm, start_addr, start, ptep, start_cache); + + range->start_pte =3D NULL; + range->end_pte =3D NULL; + +done: + range->mm =3D NULL; + range->base_addr =3D 0; + range->base_end =3D 0; + range->base_pte =3D NULL; + +norange: + get_lowcore()->lazy_mmu_count--; + put_cpu_var(ipte_range); + + local_bh_enable(); +} + +static void flush_lazy_mmu_mode(void) +{ + unsigned long addr, end; + struct ipte_range *range; + struct mm_struct *mm; + pte_t *pte; + + if (!test_facility(13)) + return; + + range =3D get_cpu_var(ipte_range); + if (range->mm) { + mm =3D range->mm; + addr =3D range->base_addr; + end =3D range->base_end; + pte =3D range->base_pte; + + leave_ipte_range(); + enter_ipte_range(mm, addr, end, pte); + } + put_cpu_var(ipte_range); +} + +void arch_enter_lazy_mmu_mode(void) +{ + enter_ipte_norange(); +} +EXPORT_SYMBOL_IF_KUNIT(arch_enter_lazy_mmu_mode); + +void arch_enter_lazy_mmu_mode_with_ptes(struct mm_struct *mm, + unsigned long addr, unsigned long end, + pte_t *pte) +{ + enter_ipte_range(mm, addr, end, pte); +} +EXPORT_SYMBOL_IF_KUNIT(arch_enter_lazy_mmu_mode_with_ptes); + +void arch_leave_lazy_mmu_mode(void) +{ + leave_ipte_range(); +} +EXPORT_SYMBOL_IF_KUNIT(arch_leave_lazy_mmu_mode); + +void arch_flush_lazy_mmu_mode(void) +{ + flush_lazy_mmu_mode(); +} +EXPORT_SYMBOL_IF_KUNIT(arch_flush_lazy_mmu_mode); + +static void __ipte_range_set_pte(struct ipte_range *range, pte_t *ptep, pt= e_t pte) +{ + unsigned int idx =3D ptep - range->base_pte; + + lockdep_assert_preemption_disabled(); + range->cache[idx] =3D pte; + + if (!range->start_pte) { + range->start_pte =3D ptep; + range->end_pte =3D ptep + 1; + } else if (ptep < range->start_pte) { + range->start_pte =3D ptep; + } else if (ptep + 1 > range->end_pte) { + range->end_pte =3D ptep + 1; + } +} + +static pte_t __ipte_range_ptep_get(struct ipte_range *range, pte_t *ptep) +{ + unsigned int idx =3D ptep - range->base_pte; + + lockdep_assert_preemption_disabled(); + if (pte_val(range->cache[idx]) =3D=3D PTE_POISON) + return __ptep_get(ptep); + return range->cache[idx]; +} + +static struct ipte_range *this_ipte_range(pte_t *ptep) +{ + struct ipte_range *range; + unsigned int nr_ptes; + + range =3D this_cpu_read(ipte_range); + if (ptep < range->base_pte) + return NULL; + nr_ptes =3D (range->base_end - range->base_addr) / PAGE_SIZE; + if (ptep >=3D range->base_pte + nr_ptes) + return NULL; + + return range; +} + +bool __lazy_mmu_set_pte(pte_t *ptep, pte_t pte) +{ + struct ipte_range *range; + + range =3D this_ipte_range(ptep); + if (!range) + return false; + + __ipte_range_set_pte(range, ptep, pte); + + return true; +} + +bool __lazy_mmu_ptep_get(pte_t *ptep, pte_t *res) +{ + struct ipte_range *range; + + range =3D this_ipte_range(ptep); + if (!range) + return false; + + *res =3D __ipte_range_ptep_get(range, ptep); + + return true; +} + +bool __lazy_mmu_ptep_test_and_clear_young(unsigned long addr, pte_t *ptep,= int *res) +{ + struct ipte_range *range; + pte_t pte, old; + + range =3D this_ipte_range(ptep); + if (!range) + return false; + + old =3D __ipte_range_ptep_get(range, ptep); + pte =3D pte_mkold(old); + __ipte_range_set_pte(range, ptep, pte); + *res =3D pte_young(old); + + return true; +} + +bool __lazy_mmu_ptep_get_and_clear(unsigned long addr, pte_t *ptep, pte_t = *res) +{ + struct ipte_range *range; + pte_t pte, old; + + range =3D this_ipte_range(ptep); + if (!range) + return false; + + old =3D __ipte_range_ptep_get(range, ptep); + pte =3D __pte(_PAGE_INVALID); + __ipte_range_set_pte(range, ptep, pte); + *res =3D old; + + return true; +} + +bool __lazy_mmu_ptep_modify_prot_start(unsigned long addr, pte_t *ptep, pt= e_t *res) +{ + return __lazy_mmu_ptep_get_and_clear(addr, ptep, res); +} + +bool __lazy_mmu_ptep_modify_prot_commit(unsigned long addr, pte_t *ptep, + pte_t old_pte, pte_t pte) +{ + struct ipte_range *range; + + range =3D this_ipte_range(ptep); + if (!range) + return false; + + __ipte_range_set_pte(range, ptep, pte); + + return true; +} + +bool __lazy_mmu_ptep_set_wrprotect(unsigned long addr, pte_t *ptep) +{ + struct ipte_range *range; + pte_t pte; + + range =3D this_ipte_range(ptep); + if (!range) + return false; + + pte =3D __ipte_range_ptep_get(range, ptep); + if (pte_write(pte)) { + pte =3D pte_wrprotect(pte); + __ipte_range_set_pte(range, ptep, pte); + } + + return true; +} + +int lazy_mmu_online_cpu(gfp_t gfp, unsigned int cpu) +{ + struct ipte_range *range; + int i; + + if (!test_facility(13)) + return 0; + + range =3D kzalloc_obj(*range, gfp); + if (!range) + return -ENOMEM; + for (i =3D 0; i < ARRAY_SIZE(range->cache); i++) + range->cache[i] =3D __pte(PTE_POISON); + per_cpu(ipte_range, cpu) =3D range; + + return 0; +} + +void lazy_mmu_offline_cpu(unsigned int cpu) +{ + struct ipte_range *range; + + if (!test_facility(13)) + return; + + range =3D per_cpu(ipte_range, cpu); + per_cpu(ipte_range, cpu) =3D NULL; + kfree(range); +} + +void __init lazy_mmu_online_boot_cpu(void) +{ + lazy_mmu_online_cpu(GFP_ATOMIC, 0); +} diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 4acd8b140c4b..df36523bcbbb 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -166,14 +166,14 @@ pte_t ptep_xchg_lazy(struct mm_struct *mm, unsigned l= ong addr, } EXPORT_SYMBOL(ptep_xchg_lazy); =20 -pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long add= r, - pte_t *ptep) +pte_t ___ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long = addr, + pte_t *ptep) { return ptep_flush_lazy(vma->vm_mm, addr, ptep, 1); } =20 -void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long add= r, - pte_t *ptep, pte_t old_pte, pte_t pte) +void ___ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long = addr, + pte_t *ptep, pte_t old_pte, pte_t pte) { set_pte(ptep, pte); } --=20 2.53.0 From nobody Mon Sep 28 21:01:29 2026 Received: from mx0b-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) (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 3319F3E44E8; Mon, 17 Aug 2026 11:33:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.158.5 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786966393; cv=none; b=Fb3/ivetlNdIZm4f+hFz4sNG8zHsaeFH4SR+moV016R+xlb45T0i3GI+YATF79tbbsFv4mG0hTZFu1ypLr7t5s5Qrdap+QKuileL9uWxs3zYr3Zq2ozmlFaIb9cT5lmtozMYJsr9Mt1oQtIPyqe/9fQJAsvd4nPTkrS0PrPlreg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786966393; c=relaxed/simple; bh=Fl19eDf0nRz1TYbpcDjdLq5dOHgIrFO3gJe0jDoLT6s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uI+0lBTs0FULHFxjQaRV3DPB0hLhI+hULOYWWjlc2ru5iZal/FHGUED0/J3Q/2ZnkIyxgqveaZ0/IGTriWPxV2fbm+NAk4bIk0kYzrOgyseG366MiAiwDzvIHLKyKsz8EWMt4WX8Uv3ydCJIdejJFlQZd8m/7QdSMgPKIva+Za8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com; spf=pass smtp.mailfrom=linux.ibm.com; dkim=pass (2048-bit key) header.d=ibm.com header.i=@ibm.com header.b=KTHToFiC; arc=none smtp.client-ip=148.163.158.5 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.ibm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ibm.com header.i=@ibm.com header.b="KTHToFiC" Received: from pps.filterd (m0360072.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 67GLWEcj3780952; Mon, 17 Aug 2026 11:33:07 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=pp1; bh=jCoOcIlPM4QZnTVbE 1S4wqZbwstR12KhLcPHqV9Sp1U=; b=KTHToFiCYd4xiQmJko2La7ytq2r+UbFKx PqgvbOB/dhrRHa4moMw96nA8UF0eS8NQlGiQ4qtNx09/Lc2g4bMyJ5QEMuJEo0oA jbPtefyOq8J9sD5L4/rkgk3M7GdKVyguSZgP7WGzmTMu03xtgTQhBZFRyy63jB+/ CjWWa/PbLNIva1eOhpMHdHFHeJ5wqXYFOULM+jFbA8yDRdp6NKS3gn65xWUTf1y0 jxD8FaoZyVbxM7lAw4NKUUTm1FbkKFG07mKSposYseVpU4baTEddUsxII8Ae2lTT 9/rRvVfbgrQ6NtN1cIDnln8CKJCbyLUXMP/AAAaymCCaT8GzWE5YA== Received: from ppma21.wdc07v.mail.ibm.com (5b.69.3da9.ip4.static.sl-reverse.com [169.61.105.91]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 4g2frt1m4p-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 17 Aug 2026 11:33:07 +0000 (GMT) Received: from pps.filterd (ppma21.wdc07v.mail.ibm.com [127.0.0.1]) by ppma21.wdc07v.mail.ibm.com (8.18.1.7/8.18.1.7) with ESMTP id 67HBQWrI022977; Mon, 17 Aug 2026 11:33:06 GMT Received: from smtprelay02.fra02v.mail.ibm.com ([9.218.2.226]) by ppma21.wdc07v.mail.ibm.com (PPS) with ESMTPS id 4g33ejwtvp-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 17 Aug 2026 11:33:06 +0000 (GMT) Received: from smtpav02.fra02v.mail.ibm.com (smtpav02.fra02v.mail.ibm.com [10.20.54.101]) by smtprelay02.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 67HBX2bZ49217894 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 17 Aug 2026 11:33:02 GMT Received: from smtpav02.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 8DEC020043; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: from smtpav02.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 637D720040; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: from tuxmaker.boeblingen.de.ibm.com (unknown [9.87.85.9]) by smtpav02.fra02v.mail.ibm.com (Postfix) with SMTP; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: by tuxmaker.boeblingen.de.ibm.com (Postfix, from userid 55669) id 4520F16158E; Mon, 17 Aug 2026 13:33:02 +0200 (CEST) From: Alexander Gordeev To: Gerald Schaefer , Heiko Carstens , Christian Borntraeger , Vasily Gorbik , Claudio Imbrenda , Andrey Ryabinin Cc: linux-s390@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com Subject: [PATCH v7 3/4] mm/kasan: Introduce helpers for lazy MMU mode sanitizer Date: Mon, 17 Aug 2026 13:33:01 +0200 Message-ID: <52c9be0cf71a8896a5d76c148c38fb24a2005dd9.1786956464.git.agordeev@linux.ibm.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: 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 X-TM-AS-GCONF: 00 X-Proofpoint-Reinject: loops=2 maxloops=12 X-Authority-Analysis: v=2.4 cv=OfaoyBTY c=1 sm=1 tr=0 ts=6a82f173 cx=c_pps a=GFwsV6G8L6GxiO2Y/PsHdQ==:117 a=GFwsV6G8L6GxiO2Y/PsHdQ==:17 a=Sv0fKeRqtYgA:10 a=VkNPw1HP01LnGYTKEx00:22 a=RnoormkPH1_aCDwRdu11:22 a=RzCfie-kr_QcCd8fBx8p:22 a=VnNF1IyMAAAA:8 a=IbWDVZlrxelGz4sJbtUA:9 X-Proofpoint-Spam-Info: AW1haW4tMjYwODE3MDA4NyBTYWx0ZWRfX/+4fCm+gB8G5 2RiCBOZ5NlJZvsXwGYWFydnkDo34qYfthGKQCJbUOyHaple9d98NY3YLKRwJcf8gpEWbHvnrMeH 6JGJFsmPNeRAJne6CexoFGMml+6ch3M= X-Proofpoint-GUID: MvG7Ke4FAqGi8_-4CsoJlgvNnyj77p6- X-Proofpoint-ORIG-GUID: 4FUd8_M_4TIG6n8IU5tDLQ4GcyZLYqRk X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODE3MDA4NyBTYWx0ZWRfX2eQZRCnxwWvx fnOALfM7tJTiqmdqDCg2Sh3ONESzXkCr+X1GA28FyKgpDC5mykDFi9/t/tWux/SVU0/VKfTKo/4 hQe/fvG15tB0Y93meOzvepWHC7N4N/Pn6taHrReNlTpwQs0t414PSXNzlxqLFRr4OnLUHv6BNHd RBnp5EvYzvRa9EnXIBDTnBo6waavwhrorFitsQbqvyCQyJO3xAryNiRmn/7vw13gY4PhrX2D14Q 48ACbzqux7ufXqtCg/MvzYSidJSpy/k8w2Ah9AuEW0tgKVVi0v573LgwY1FclMVYUsLKbp7XKoI Iptwv1An+252M8/k05KhTS3/57P6hkuM+qM7A6uaXLxiL5XRFwGO7z0mfk8HvCqL5IaE0IDbeDe u6bd6Z6VFaDVFKAkJkh1KCfhtl6lN+BvUQriRuelMo+le0w2E4nW6+YnDk+Zud0BG+8pWxDHJgu nvqBVX+EBEq+16SJyxA== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-08-16_06,2026-08-12_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 adultscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 malwarescore=0 bulkscore=0 phishscore=0 priorityscore=1501 spamscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608170087 Content-Type: text/plain; charset="utf-8" Provide helpers that allow architectures implement illegitimate PTE direct accesses while the lazy MMU mode is enabled, such as: pte_t pte =3D *ptep; *ptep =3D pte; By contrast, these would have to be: pte_t pte =3D ptep_get(ptep); set_pte(ptep, pte); The direct PTE accesses pose a real issue on s390. Suggested-by: Ilya Leoshkevich Signed-off-by: Alexander Gordeev --- include/linux/kasan.h | 19 +++++++++++++++++-- mm/kasan/common.c | 14 ++++++++++++++ mm/kasan/kasan.h | 2 ++ mm/kasan/report_generic.c | 3 +++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/include/linux/kasan.h b/include/linux/kasan.h index bf233bde68c7..1aa4d9dd2485 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include =20 @@ -35,8 +36,6 @@ typedef unsigned int __bitwise kasan_vmalloc_flags_t; =20 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) =20 -#include - /* Software KASAN implementations use shadow memory. */ =20 #ifdef CONFIG_KASAN_SW_TAGS @@ -134,6 +133,20 @@ static __always_inline void kasan_poison_slab(struct s= lab *slab) __kasan_poison_slab(slab); } =20 +void __kasan_poison_pte(pte_t *pte, int nr); +static __always_inline void kasan_poison_pte(pte_t *pte, int nr) +{ + if (kasan_enabled()) + __kasan_poison_pte(pte, nr); +} + +void __kasan_unpoison_pte(pte_t *pte, int nr); +static __always_inline void kasan_unpoison_pte(pte_t *pte, int nr) +{ + if (kasan_enabled()) + __kasan_unpoison_pte(pte, nr); +} + void __kasan_unpoison_new_object(struct kmem_cache *cache, void *object); /** * kasan_unpoison_new_object - Temporarily unpoison a new slab object. @@ -414,6 +427,8 @@ static inline bool kasan_unpoison_pages(struct page *pa= ge, unsigned int order, return false; } static inline void kasan_poison_slab(struct slab *slab) {} +static inline void kasan_poison_pte(pte_t *pte, int nr) {} +static inline void kasan_unpoison_pte(pte_t *pte, int nr) {} static inline void kasan_unpoison_new_object(struct kmem_cache *cache, void *object) {} static inline void kasan_poison_new_object(struct kmem_cache *cache, diff --git a/mm/kasan/common.c b/mm/kasan/common.c index b7d05c2a6d93..94d106094989 100644 --- a/mm/kasan/common.c +++ b/mm/kasan/common.c @@ -163,6 +163,20 @@ void __kasan_poison_slab(struct slab *slab) KASAN_SLAB_REDZONE, false); } =20 +void __kasan_poison_pte(pte_t *pte, int nr) +{ + if (IS_ALIGNED(sizeof(*pte), KASAN_GRANULE_SIZE)) + kasan_poison(pte, sizeof(*pte) * nr, KASAN_LAZY_MMU_PTE, false); +} +EXPORT_SYMBOL_GPL(__kasan_poison_pte); + +void __kasan_unpoison_pte(pte_t *pte, int nr) +{ + if (IS_ALIGNED(sizeof(*pte), KASAN_GRANULE_SIZE)) + kasan_unpoison(pte, sizeof(*pte) * nr, false); +} +EXPORT_SYMBOL_GPL(__kasan_unpoison_pte); + void __kasan_unpoison_new_object(struct kmem_cache *cache, void *object) { kasan_unpoison(object, cache->object_size, false); diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h index fc9169a54766..1a2d18cdb21d 100644 --- a/mm/kasan/kasan.h +++ b/mm/kasan/kasan.h @@ -144,12 +144,14 @@ static inline bool kasan_requires_meta(void) #define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocation */ #define KASAN_SLAB_REDZONE 0xFC /* redzone for slab object */ #define KASAN_SLAB_FREE 0xFB /* freed slab object */ +#define KASAN_LAZY_MMU_PTE 0xFD /* direct pte access in lazy mmu mode */ #define KASAN_VMALLOC_INVALID 0xF8 /* inaccessible space in vmap area */ #else #define KASAN_PAGE_FREE KASAN_TAG_INVALID #define KASAN_PAGE_REDZONE KASAN_TAG_INVALID #define KASAN_SLAB_REDZONE KASAN_TAG_INVALID #define KASAN_SLAB_FREE KASAN_TAG_INVALID +#define KASAN_LAZY_MMU_PTE KASAN_TAG_INVALID #define KASAN_VMALLOC_INVALID KASAN_TAG_INVALID /* only used for SW_TAGS */ #endif =20 diff --git a/mm/kasan/report_generic.c b/mm/kasan/report_generic.c index f5b8e37b3805..489d4a8d6902 100644 --- a/mm/kasan/report_generic.c +++ b/mm/kasan/report_generic.c @@ -113,6 +113,9 @@ static const char *get_shadow_bug_type(struct kasan_rep= ort_info *info) case KASAN_SLAB_FREE_META: bug_type =3D "slab-use-after-free"; break; + case KASAN_LAZY_MMU_PTE: + bug_type =3D "lazy-mmu-pte-access"; + break; case KASAN_ALLOCA_LEFT: case KASAN_ALLOCA_RIGHT: bug_type =3D "alloca-out-of-bounds"; --=20 2.53.0 From nobody Mon Sep 28 21:01:29 2026 Received: from mx0b-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) (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 130A53DDAF8; Mon, 17 Aug 2026 11:33:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.158.5 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786966393; cv=none; b=jXaKa//QOKhGoY0kbXL5IIQohSG7P/eub0mk/B1WIrPKL/szzI9453Fv/59Bz6m54Zq58t65FR8zkCK65gCTg9dkwXUUVcYHpTldkDT1W6jZFJnyuhK0TV0dCOp4PYtJLB0bShskptsrgyt4aqOskX2FqCz6ca90ywT0ux5Cym4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786966393; c=relaxed/simple; bh=788KiRQX9FjO9MgH4kw64oibWzlyzy5xl2vhJJRKFew=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iOVIWhurNsirDrWn1OWBz1UtOmVRqFP56tXLlh7Z4Z65YEnP0CUQiPrbMY/QLHLmkus9Z7dBYNEmIzCMR0iqkZdbeShDiNKcNizpkwZDks2TUT1wTxQQQCjEMyHKT7QmWQ2DBPXMmeV0O35Cx/R3UNtjeIrNzC/z6799QqKnRzw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com; spf=pass smtp.mailfrom=linux.ibm.com; dkim=pass (2048-bit key) header.d=ibm.com header.i=@ibm.com header.b=lINPbSdO; arc=none smtp.client-ip=148.163.158.5 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.ibm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.ibm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ibm.com header.i=@ibm.com header.b="lINPbSdO" Received: from pps.filterd (m0360072.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 67H1ZFFs071008; Mon, 17 Aug 2026 11:33:07 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=pp1; bh=7uXokoF4PUm6dgm6R UNuJHv7jlLk70lExgHTtA8VB0c=; b=lINPbSdO2D6p0xZueWMxWwgpinIGVIO93 b0pjxsfSSw449x3RnL8P61PzVZSBLEq8SkZbeW6GRwYiJqbnRy6IAEljPTaVbzu6 1JOKxC2WVWPgRTmqdm04rdZdHjX4zwGPzRlRszDvaptxQYjDNYLdoZrhmSFDBHs6 olrBHDmn1vL+d65zrHVXaLqVcTnhppP9QW2mMLltOf/1ZxeBlW1XWX82BLCOdVlZ G6M9klCgQrDbTRFIKYMXAKh8j46pais7AELpAfERiiUITXPcN9WY0cBgp2fF700d yNLAJAtMX5NOQADUHGl+nwT0q1BrdVnVHYjMpdjWmvQ/MLa7K9ihg== Received: from ppma12.dal12v.mail.ibm.com (dc.9e.1632.ip4.static.sl-reverse.com [50.22.158.220]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 4g2frt1m4n-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 17 Aug 2026 11:33:07 +0000 (GMT) Received: from pps.filterd (ppma12.dal12v.mail.ibm.com [127.0.0.1]) by ppma12.dal12v.mail.ibm.com (8.18.1.7/8.18.1.7) with ESMTP id 67HBQMrE007554; Mon, 17 Aug 2026 11:33:06 GMT Received: from smtprelay05.fra02v.mail.ibm.com ([9.218.2.225]) by ppma12.dal12v.mail.ibm.com (PPS) with ESMTPS id 4g32epx019-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 17 Aug 2026 11:33:06 +0000 (GMT) Received: from smtpav03.fra02v.mail.ibm.com (smtpav03.fra02v.mail.ibm.com [10.20.54.102]) by smtprelay05.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 67HBX2bL45351356 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 17 Aug 2026 11:33:02 GMT Received: from smtpav03.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 8EFF42004D; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: from smtpav03.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 68D6A20043; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: from tuxmaker.boeblingen.de.ibm.com (unknown [9.87.85.9]) by smtpav03.fra02v.mail.ibm.com (Postfix) with SMTP; Mon, 17 Aug 2026 11:33:02 +0000 (GMT) Received: by tuxmaker.boeblingen.de.ibm.com (Postfix, from userid 55669) id 4A9F41615E3; Mon, 17 Aug 2026 13:33:02 +0200 (CEST) From: Alexander Gordeev To: Gerald Schaefer , Heiko Carstens , Christian Borntraeger , Vasily Gorbik , Claudio Imbrenda , Andrey Ryabinin Cc: linux-s390@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com Subject: [PATCH v7 4/4] s390/mm: Lazy MMU mode sanitizer Date: Mon, 17 Aug 2026 13:33:02 +0200 Message-ID: <064436ca2fcc3221f12a37acd0a41d995a794cf7.1786956464.git.agordeev@linux.ibm.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: 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 X-TM-AS-GCONF: 00 X-Proofpoint-Reinject: loops=2 maxloops=12 X-Authority-Analysis: v=2.4 cv=OfaoyBTY c=1 sm=1 tr=0 ts=6a82f173 cx=c_pps a=bLidbwmWQ0KltjZqbj+ezA==:117 a=bLidbwmWQ0KltjZqbj+ezA==:17 a=Sv0fKeRqtYgA:10 a=VkNPw1HP01LnGYTKEx00:22 a=RnoormkPH1_aCDwRdu11:22 a=RzCfie-kr_QcCd8fBx8p:22 a=VnNF1IyMAAAA:8 a=DuHJb5c9rUSq-GGRdagA:9 X-Proofpoint-Spam-Info: AW1haW4tMjYwODE3MDA4NyBTYWx0ZWRfX/F6hRpjEFYcG b2EKkTZ1UshVTNvaYejVDVzsIiH6v03MoggxVV/PN50hr1mCBJ7CaKxBmrmiuEQjxBOwb54+2tp lCJ2wBy+TpKqngSZWO4E9/nrxqFhkmU= X-Proofpoint-GUID: dY7S0-hVddtmiyTGPRj2gkH2IcX9yDWG X-Proofpoint-ORIG-GUID: NlL45JyIS0IkwGwb4bRlbgWKq2rZGFzy X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwODE3MDA4NyBTYWx0ZWRfXz0XKPB/T+VdT WLVzTc4RdKa363ytu9yJj+uBgKXWDUHWlOJGGTET3zGzEJ79qldEmorLO8jMivk+VL4RxjCoj0n OVOQSyetzW0woAXie+ZxH2P522PlPTO6+otTbZIEglCmWS09LBSBoevc3iO2b0L5va1DhTBZ8qh Fb2iA4JfPRiWKIN+QtbsfWVvI62fSG4tafP9Be1CPwsg6NKKhtuLokKhapS4+TzCBA+M2UsyN80 dwQpFR+UxfxGuB1xemf0QhdS6VdtF/zR6zF8jq3Xan/Vtu1mIuafJCn8at0Nh6ldgzIuYKFsXTb 96tCpov5W7rryNPmlR1eXM4/XNJ9TybCGT92jqSGo5GPaRQh+QpJn0t0I2/5HhFkk0pCkHme5Vz khcdUVAPlhW0hang7m3xWiNOwk4N7DxELPqtmDA8r25I3QFGZ2uZyTUFFG+RTsiKpQWsgqcdotX aIan644huX/rjukai4Q== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-08-16_06,2026-08-12_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 adultscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 malwarescore=0 bulkscore=0 phishscore=0 priorityscore=1501 spamscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2608170087 Content-Type: text/plain; charset="utf-8" Detect PTE entries access in lazy MMU mode by means other than set_pte() and ptep_get() primitives, which would be a read hazard. The access to kasan shadow memory from ptep_get_lockless() mistakenly hits invalid access in case a concurrent lazy MMU access to the same PTE is happening. To avoid that disable instrumentation for ptep_get_lockless() altogether. Suggested-by: Ilya Leoshkevich Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/pgtable.h | 6 ++++++ arch/s390/mm/lazy_mmu.c | 27 +++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtabl= e.h index bf54202a0630..1e8a79989ed6 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -1049,6 +1049,12 @@ static inline void set_pte(pte_t *ptep, pte_t pte) __set_pte(ptep, pte); } =20 +#define ptep_get_lockless ptep_get_lockless +static inline __no_sanitize_address pte_t ptep_get_lockless(pte_t *ptep) +{ + return READ_ONCE(*ptep); +} + static inline pte_t __ptep_get(pte_t *ptep) { return READ_ONCE(*ptep); diff --git a/arch/s390/mm/lazy_mmu.c b/arch/s390/mm/lazy_mmu.c index 002ee766b391..7e3a4115ea04 100644 --- a/arch/s390/mm/lazy_mmu.c +++ b/arch/s390/mm/lazy_mmu.c @@ -63,10 +63,13 @@ static int invalidate_pte_range(struct mm_struct *mm, u= nsigned long addr, } =20 static void set_pte_range(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t *end, pte_t *cache) + pte_t *start, pte_t *end, pte_t *cache) { - int i, nr_ptes; + int nr_ptes, nr_total =3D end - start; + pte_t *ptep =3D start; + int i; =20 + kasan_unpoison_pte(start, nr_total); while (ptep < end) { nr_ptes =3D invalidate_pte_range(mm, addr, ptep, end); =20 @@ -77,6 +80,7 @@ static void set_pte_range(struct mm_struct *mm, unsigned = long addr, =20 addr +=3D nr_ptes * PAGE_SIZE; } + kasan_poison_pte(start, nr_total); } =20 static void enter_ipte_norange(void) @@ -98,6 +102,7 @@ static void enter_ipte_range(struct mm_struct *mm, unsigned long addr, unsigned long end, pte_t *pte) { struct ipte_range *range; + unsigned int nr_ptes; =20 if (!test_facility(13)) return; @@ -117,6 +122,9 @@ static void enter_ipte_range(struct mm_struct *mm, range->base_end =3D end; range->base_pte =3D pte; =20 + nr_ptes =3D (range->base_end - range->base_addr) / PAGE_SIZE; + kasan_poison_pte(range->base_pte, nr_ptes); + local_bh_enable(); } =20 @@ -125,6 +133,7 @@ static void leave_ipte_range(void) pte_t *ptep, *start, *start_cache, *cache; unsigned long start_addr, addr; struct ipte_range *range; + unsigned int nr_ptes; int start_idx; =20 if (!test_facility(13)) @@ -163,6 +172,9 @@ static void leave_ipte_range(void) range->end_pte =3D NULL; =20 done: + nr_ptes =3D (range->base_end - range->base_addr) / PAGE_SIZE; + kasan_unpoison_pte(range->base_pte, nr_ptes); + range->mm =3D NULL; range->base_addr =3D 0; range->base_end =3D 0; @@ -244,10 +256,17 @@ static void __ipte_range_set_pte(struct ipte_range *r= ange, pte_t *ptep, pte_t pt static pte_t __ipte_range_ptep_get(struct ipte_range *range, pte_t *ptep) { unsigned int idx =3D ptep - range->base_pte; + pte_t pte; =20 lockdep_assert_preemption_disabled(); - if (pte_val(range->cache[idx]) =3D=3D PTE_POISON) - return __ptep_get(ptep); + if (pte_val(range->cache[idx]) =3D=3D PTE_POISON) { + kasan_unpoison_pte(ptep, 1); + pte =3D __ptep_get(ptep); + kasan_poison_pte(ptep, 1); + + return pte; + } + return range->cache[idx]; } =20 --=20 2.53.0