From nobody Mon Feb 9 07:20:41 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (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 07429294A0C for ; Mon, 12 May 2025 12:34:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747053284; cv=none; b=NIbaCsm9WlTzEbdJHlFVJQmGGsjlQOrtQ8FwnGefLVjRYMHHGSdSu1CjPRNn8i5oATw/sSU0JSE5ZQZOA0xTXOqr02IL8zPjQb1RV9LikgJL0cEfEk13rLF7xd5gmw91X2xv2tHmdlcnH6Ti9shKMT5fkcUcy8r+nLvPRULK8E4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747053284; c=relaxed/simple; bh=2GaZjzn3CoqrV8e+uJviIdXRB1JEBQSNA1uHS4huF4c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Fl8Z0VDF1FUU+TF+0bARI92+gf9R9CVCRBwfl8iBC5g/vGn7B3/tKiBHNf/iXxx7Ovw73HtI8pQqO20bHUn196ccYHEiBwtsBawQN4MNMj5zkXwadr38SY0sOUwKjSW8+mWp46SRtRbhiCXQTYoI7Q/RgYhcDjriMqqk9uIKRQ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=SsNn0uul; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="SsNn0uul" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1747053280; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NYDrLvG5XDZK2S7Vzjb3pmEnm43v74kkrfTla5+cPZI=; b=SsNn0uulAPj3HprWE5BGzjkYGXpBbHfHOV0kiIF9kf+knbJZ4GF95sKRntqQX2SpxrpWFL VxqQbKVCtC5Zmz0kbIE5Po+9rIn2SAwOztgF5Q4XJml6dagT0Lbg3m4iWWQLi4M2GO20Jj XLyVd0p5p4QEMPEaQZU//FRMT4IUanY= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-371--Rfgbxl6M2qFwQ9SvvNq4Q-1; Mon, 12 May 2025 08:34:39 -0400 X-MC-Unique: -Rfgbxl6M2qFwQ9SvvNq4Q-1 X-Mimecast-MFC-AGG-ID: -Rfgbxl6M2qFwQ9SvvNq4Q_1747053278 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-3a0bd786a53so1767583f8f.0 for ; Mon, 12 May 2025 05:34:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747053278; x=1747658078; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NYDrLvG5XDZK2S7Vzjb3pmEnm43v74kkrfTla5+cPZI=; b=TcnPtClyJqxqaEcGcDPHQ3XncRqxYVic2mFb9VN6To81+mozqkxIMBxPoQqL87FeD6 Q4HlLAacAdidGUDx+PsIdICC6LkT3dBOzNcl9K7zSdyJa9LOThQbZ6/kX/VTi0MYPWlQ xhSFd0b2pQ9exW2PsrNrVo/xubYvS5NrNB9X3GuqvFRqH+aNt+aKbZp0Lm2mlsP/j522 TUGOGU9+MkBILZiCBbxPTftVvnsCvKWr3e+XFKH7u0f0KdgaAIpPE15t/t1qWMEe7B+8 q/G2uIijfKujCe3R7AWlQjjGlrL9mkYLEtbVdaPZbh/9UsLpCxjXzTsw2zCMD/hwtJEe h5IA== X-Gm-Message-State: AOJu0YwZDaT+HWibAfKATRZ2U/VxAJ71/c+rVY75KDVxaEeLKgsDkyAT KiC4rGQMnpMP+LDVFYihQPjFkR/7czuofOZvlFQ3MBxqZ6UlQPH7LZGhlY769eOL1otdDW8GUj0 kJwR078QGgBORRRlR1HfYDv8gRMHrcMWoVTufOHlMONX/BD/bqavSVODoAfOkyhdDqK2obvTUL8 2OS6KDLnYTmKqQBmBl/VejR2cTrCCJ9LPQR1UJh53tfZOP X-Gm-Gg: ASbGncu/kzwNoeG5JS1x5aA4JoyVltfjHa39Q6441at3WLPRmsMUIU5BQN9icDnYaBD 7VANXjjjb8mR3OdGtPJi70UYq6wdz0tNnfstSOVHvyFht0fOP1/hKicjQ+T/lXeWShVDnM4tqVJ mQNPwdzWih1+SN6PVQj9AnqCSApfI+upsTm5TR30poKZXm4AK9yUIuc1RP9aXjxTHw5hnwoN2/c Hq9pM7XHFFDuLWpxA6WLA9osPqxhpfkUYSxJjc6T5Fdl/Zl8TkLcO4a427Xaz55MnQM2f2Cu7HK Nn9/lcv+dYzzQ4piY+bamnVRi7hO0xwxwhHG9Qn+1Ue93j6ygoDK+PuLvN5lVp5LQABGDvxi X-Received: by 2002:a05:6000:430d:b0:3a0:b9a9:2fd9 with SMTP id ffacd0b85a97d-3a1f64a34c6mr10772134f8f.51.1747053278176; Mon, 12 May 2025 05:34:38 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEhxNuxHtjAVwepTb/c5WYjIAvKZdsEfHNK/tmk5VSXnQqEoBOKdiBfd2UyPw5vI1dksu9fng== X-Received: by 2002:a05:6000:430d:b0:3a0:b9a9:2fd9 with SMTP id ffacd0b85a97d-3a1f64a34c6mr10772084f8f.51.1747053277633; Mon, 12 May 2025 05:34:37 -0700 (PDT) Received: from localhost (p200300d82f4a5800f1ae8e20d7f451b0.dip0.t-ipconnect.de. [2003:d8:2f4a:5800:f1ae:8e20:d7f4:51b0]) by smtp.gmail.com with UTF8SMTPSA id 5b1f17b1804b1-442d687ae10sm123566305e9.37.2025.05.12.05.34.34 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 12 May 2025 05:34:36 -0700 (PDT) From: David Hildenbrand To: linux-kernel@vger.kernel.org Cc: linux-mm@kvack.org, x86@kernel.org, intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-trace-kernel@vger.kernel.org, David Hildenbrand , Dave Hansen , Andy Lutomirski , Peter Zijlstra , Thomas Gleixner , Ingo Molnar , Borislav Petkov , "H. Peter Anvin" , Jani Nikula , Joonas Lahtinen , Rodrigo Vivi , Tvrtko Ursulin , David Airlie , Simona Vetter , Andrew Morton , Steven Rostedt , Masami Hiramatsu , Mathieu Desnoyers , "Liam R. Howlett" , Lorenzo Stoakes , Vlastimil Babka , Jann Horn , Pedro Falcato , Peter Xu , Ingo Molnar Subject: [PATCH v2 04/11] mm: convert VM_PFNMAP tracking to pfnmap_track() + pfnmap_untrack() Date: Mon, 12 May 2025 14:34:17 +0200 Message-ID: <20250512123424.637989-5-david@redhat.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250512123424.637989-1-david@redhat.com> References: <20250512123424.637989-1-david@redhat.com> 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" Let's use our new interface. In remap_pfn_range(), we'll now decide whether we have to track (full VMA covered) or only lookup the cachemode (partial VMA covered). Remember what we have to untrack by linking it from the VMA. When duplicating VMAs (e.g., splitting, mremap, fork), we'll handle it similar to anon VMA names, and use a kref to share the tracking. Once the last VMA un-refs our tracking data, we'll do the untracking, which simplifies things a lot and should sort our various issues we saw recently, for example, when partially unmapping/zapping a tracked VMA. This change implies that we'll keep tracking the original PFN range even after splitting + partially unmapping it: not too bad, because it was not working reliably before. The only thing that kind-of worked before was shrinking such a mapping using mremap(): we managed to adjust the reservation in a hacky way, now we won't adjust the reservation but leave it around until all involved VMAs are gone. If that ever turns out to be an issue, we could hook into VM splitting code and split the tracking; however, that adds complexity that might not be required, so we'll keep it simple for now. Acked-by: Ingo Molnar # x86 bits Signed-off-by: David Hildenbrand Reviewed-by: Liam R. Howlett Reviewed-by: Lorenzo Stoakes --- include/linux/mm_inline.h | 2 + include/linux/mm_types.h | 11 ++++++ mm/memory.c | 82 +++++++++++++++++++++++++++++++-------- mm/mmap.c | 5 --- mm/mremap.c | 4 -- mm/vma_init.c | 50 ++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 25 deletions(-) diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h index f9157a0c42a5c..89b518ff097e6 100644 --- a/include/linux/mm_inline.h +++ b/include/linux/mm_inline.h @@ -447,6 +447,8 @@ static inline bool anon_vma_name_eq(struct anon_vma_nam= e *anon_name1, =20 #endif /* CONFIG_ANON_VMA_NAME */ =20 +void pfnmap_track_ctx_release(struct kref *ref); + static inline void init_tlb_flush_pending(struct mm_struct *mm) { atomic_set(&mm->tlb_flush_pending, 0); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 15808cad2bc1a..3e934dc6057c4 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -763,6 +763,14 @@ struct vma_numab_state { int prev_scan_seq; }; =20 +#ifdef __HAVE_PFNMAP_TRACKING +struct pfnmap_track_ctx { + struct kref kref; + unsigned long pfn; + unsigned long size; /* in bytes */ +}; +#endif + /* * Describes a VMA that is about to be mmap()'ed. Drivers may choose to * manipulate mutable fields which will cause those fields to be updated i= n the @@ -900,6 +908,9 @@ struct vm_area_struct { struct anon_vma_name *anon_name; #endif struct vm_userfaultfd_ctx vm_userfaultfd_ctx; +#ifdef __HAVE_PFNMAP_TRACKING + struct pfnmap_track_ctx *pfnmap_track_ctx; +#endif } __randomize_layout; =20 #ifdef CONFIG_NUMA diff --git a/mm/memory.c b/mm/memory.c index 064fc55d8eab9..4cf4adb0de266 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1371,7 +1371,7 @@ copy_page_range(struct vm_area_struct *dst_vma, struc= t vm_area_struct *src_vma) struct mm_struct *dst_mm =3D dst_vma->vm_mm; struct mm_struct *src_mm =3D src_vma->vm_mm; struct mmu_notifier_range range; - unsigned long next, pfn =3D 0; + unsigned long next; bool is_cow; int ret; =20 @@ -1381,12 +1381,6 @@ copy_page_range(struct vm_area_struct *dst_vma, stru= ct vm_area_struct *src_vma) if (is_vm_hugetlb_page(src_vma)) return copy_hugetlb_page_range(dst_mm, src_mm, dst_vma, src_vma); =20 - if (unlikely(src_vma->vm_flags & VM_PFNMAP)) { - ret =3D track_pfn_copy(dst_vma, src_vma, &pfn); - if (ret) - return ret; - } - /* * We need to invalidate the secondary MMU mappings only when * there could be a permission downgrade on the ptes of the @@ -1428,8 +1422,6 @@ copy_page_range(struct vm_area_struct *dst_vma, struc= t vm_area_struct *src_vma) raw_write_seqcount_end(&src_mm->write_protect_seq); mmu_notifier_invalidate_range_end(&range); } - if (ret && unlikely(src_vma->vm_flags & VM_PFNMAP)) - untrack_pfn_copy(dst_vma, pfn); return ret; } =20 @@ -1924,9 +1916,6 @@ static void unmap_single_vma(struct mmu_gather *tlb, if (vma->vm_file) uprobe_munmap(vma, start, end); =20 - if (unlikely(vma->vm_flags & VM_PFNMAP)) - untrack_pfn(vma, 0, 0, mm_wr_locked); - if (start !=3D end) { if (unlikely(is_vm_hugetlb_page(vma))) { /* @@ -2872,6 +2861,36 @@ int remap_pfn_range_notrack(struct vm_area_struct *v= ma, unsigned long addr, return error; } =20 +#ifdef __HAVE_PFNMAP_TRACKING +static inline struct pfnmap_track_ctx *pfnmap_track_ctx_alloc(unsigned lon= g pfn, + unsigned long size, pgprot_t *prot) +{ + struct pfnmap_track_ctx *ctx; + + if (pfnmap_track(pfn, size, prot)) + return ERR_PTR(-EINVAL); + + ctx =3D kmalloc(sizeof(*ctx), GFP_KERNEL); + if (unlikely(!ctx)) { + pfnmap_untrack(pfn, size); + return ERR_PTR(-ENOMEM); + } + + ctx->pfn =3D pfn; + ctx->size =3D size; + kref_init(&ctx->kref); + return ctx; +} + +void pfnmap_track_ctx_release(struct kref *ref) +{ + struct pfnmap_track_ctx *ctx =3D container_of(ref, struct pfnmap_track_ct= x, kref); + + pfnmap_untrack(ctx->pfn, ctx->size); + kfree(ctx); +} +#endif /* __HAVE_PFNMAP_TRACKING */ + /** * remap_pfn_range - remap kernel memory to userspace * @vma: user vma to map to @@ -2884,20 +2903,51 @@ int remap_pfn_range_notrack(struct vm_area_struct *= vma, unsigned long addr, * * Return: %0 on success, negative error code otherwise. */ +#ifdef __HAVE_PFNMAP_TRACKING int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot) { + struct pfnmap_track_ctx *ctx =3D NULL; int err; =20 - err =3D track_pfn_remap(vma, &prot, pfn, addr, PAGE_ALIGN(size)); - if (err) + size =3D PAGE_ALIGN(size); + + /* + * If we cover the full VMA, we'll perform actual tracking, and + * remember to untrack when the last reference to our tracking + * context from a VMA goes away. We'll keep tracking the whole pfn + * range even during VMA splits and partial unmapping. + * + * If we only cover parts of the VMA, we'll only setup the cachemode + * in the pgprot for the pfn range. + */ + if (addr =3D=3D vma->vm_start && addr + size =3D=3D vma->vm_end) { + if (vma->pfnmap_track_ctx) + return -EINVAL; + ctx =3D pfnmap_track_ctx_alloc(pfn, size, &prot); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + } else if (pfnmap_setup_cachemode(pfn, size, &prot)) { return -EINVAL; + } =20 err =3D remap_pfn_range_notrack(vma, addr, pfn, size, prot); - if (err) - untrack_pfn(vma, pfn, PAGE_ALIGN(size), true); + if (ctx) { + if (err) + kref_put(&ctx->kref, pfnmap_track_ctx_release); + else + vma->pfnmap_track_ctx =3D ctx; + } return err; } + +#else +int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, + unsigned long pfn, unsigned long size, pgprot_t prot) +{ + return remap_pfn_range_notrack(vma, addr, pfn, size, prot); +} +#endif EXPORT_SYMBOL(remap_pfn_range); =20 /** diff --git a/mm/mmap.c b/mm/mmap.c index 50f902c08341a..09c563c951123 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1784,11 +1784,6 @@ __latent_entropy int dup_mmap(struct mm_struct *mm, = struct mm_struct *oldmm) tmp =3D vm_area_dup(mpnt); if (!tmp) goto fail_nomem; - - /* track_pfn_copy() will later take care of copying internal state. */ - if (unlikely(tmp->vm_flags & VM_PFNMAP)) - untrack_pfn_clear(tmp); - retval =3D vma_dup_policy(mpnt, tmp); if (retval) goto fail_nomem_policy; diff --git a/mm/mremap.c b/mm/mremap.c index 7db9da609c84f..6e78e02f74bd3 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -1191,10 +1191,6 @@ static int copy_vma_and_data(struct vma_remap_struct= *vrm, if (is_vm_hugetlb_page(vma)) clear_vma_resv_huge_pages(vma); =20 - /* Tell pfnmap has moved from this vma */ - if (unlikely(vma->vm_flags & VM_PFNMAP)) - untrack_pfn_clear(vma); - *new_vma_ptr =3D new_vma; return err; } diff --git a/mm/vma_init.c b/mm/vma_init.c index 967ca85179864..8e53c7943561e 100644 --- a/mm/vma_init.c +++ b/mm/vma_init.c @@ -71,7 +71,51 @@ static void vm_area_init_from(const struct vm_area_struc= t *src, #ifdef CONFIG_NUMA dest->vm_policy =3D src->vm_policy; #endif +#ifdef __HAVE_PFNMAP_TRACKING + dest->pfnmap_track_ctx =3D NULL; +#endif +} + +#ifdef __HAVE_PFNMAP_TRACKING +static inline int vma_pfnmap_track_ctx_dup(struct vm_area_struct *orig, + struct vm_area_struct *new) +{ + struct pfnmap_track_ctx *ctx =3D orig->pfnmap_track_ctx; + + if (likely(!ctx)) + return 0; + + /* + * We don't expect to ever hit this. If ever required, we would have + * to duplicate the tracking. + */ + if (unlikely(kref_read(&ctx->kref) >=3D REFCOUNT_MAX)) + return -ENOMEM; + kref_get(&ctx->kref); + new->pfnmap_track_ctx =3D ctx; + return 0; +} + +static inline void vma_pfnmap_track_ctx_release(struct vm_area_struct *vma) +{ + struct pfnmap_track_ctx *ctx =3D vma->pfnmap_track_ctx; + + if (likely(!ctx)) + return; + + kref_put(&ctx->kref, pfnmap_track_ctx_release); + vma->pfnmap_track_ctx =3D NULL; +} +#else +static inline int vma_pfnmap_track_ctx_dup(struct vm_area_struct *orig, + struct vm_area_struct *new) +{ + return 0; } +static inline void vma_pfnmap_track_ctx_release(struct vm_area_struct *vma) +{ +} +#endif =20 struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig) { @@ -83,6 +127,11 @@ struct vm_area_struct *vm_area_dup(struct vm_area_struc= t *orig) ASSERT_EXCLUSIVE_WRITER(orig->vm_flags); ASSERT_EXCLUSIVE_WRITER(orig->vm_file); vm_area_init_from(orig, new); + + if (vma_pfnmap_track_ctx_dup(orig, new)) { + kmem_cache_free(vm_area_cachep, new); + return NULL; + } vma_lock_init(new, true); INIT_LIST_HEAD(&new->anon_vma_chain); vma_numab_state_init(new); @@ -97,5 +146,6 @@ void vm_area_free(struct vm_area_struct *vma) vma_assert_detached(vma); vma_numab_state_free(vma); free_anon_vma_name(vma); + vma_pfnmap_track_ctx_release(vma); kmem_cache_free(vm_area_cachep, vma); } --=20 2.49.0