mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
mm/mlock: eliminate weird VMA_IO_BIT abuse and simplify
When performing mlock() or munlock() otherwise normal VMAs have VMA_IO_BIT solely to fix a race with migration which might otherwise double-count mlock VMAs. This is unnecessary - at the point of applying folio mlock state, whether setting or clearing PG_mlocked, we know whether or not we are locking. Solve this in two ways - thread a boolean through the page table walk indicating whether a lock or unlock is being performed, and run a locking walk with VMA_LOCKONFAULT_BIT set and VMA_LOCKED_BIT cleared. This state never occurs otherwise, as VMA_LOCKONFAULT_BIT always implies VMA_LOCKED_BIT. These are also always cleared together. Then, update folio_add_lru_vma() and mlock_folio() to check only for VMA_LOCKED_BIT, and update try_to_unmap_one() to check for VMA_LOCKED_MASK instead. Also remove the useless invocation of allow_mlock_munlock() which simply returns true if unlocking and instead rename it to allow_mlock() and only call it when locking. With the other mlock abuse of VMA_IO_BIT addressed, update mlock_vma_folio() and folio_add_lru_vma() to simply test for VMA_LOCKED_BIT. The munlock_vma_folio() function tests VMA_LOCKED_MASK instead, as an unmap racing with the locking walk must still munlock folios the walk has already counted. Additionally, in order to ensure a racing unmap/migration releases the reference munlock_folio() took (whose put was batched), check VMA_LOCKED_MASK before calling mlock_drain_local() in try_to_unmap_one() and try_to_migrate_one(). While here, also replace some deprecated VMA flag predicates. Link: https://lore.kernel.org/20261013-b4-mmap-prepare-vma-flag-sanify-v6-24-1aa3c9b139f9@kernel.org Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Zi Yan <ziy@nvidia.com> Acked-by: David Hildenbrand (Arm) <david@kernel.org> Cc: Liam R. Howlett <liam@infradead.org> Cc: Vlastimil Babka <vbabka@kernel.org> Cc: Jann Horn <jannh@google.com> Cc: Pedro Falcato <pfalcato@suse.de> Cc: Mike Rapoport <rppt@kernel.org> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Michal Hocko <mhocko@suse.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com> Cc: Jason Gunthorpe <jgg@ziepe.ca> Cc: Leon Romanovsky <leon@kernel.org> Cc: Paul Moore <paul@paul-moore.com> Cc: Stephen Smalley <stephen.smalley.work@gmail.com> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Andrii Nakryiko <andrii@kernel.org> Cc: Eduard Zingerman <eddyz87@gmail.com> Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Nico Pache <nico.pache@linux.dev> Cc: Ryan Roberts <ryan.roberts@arm.com> Cc: Dev Jain <dev.jain@arm.com> Cc: Barry Song <baohua@kernel.org> Cc: Lance Yang <lance.yang@linux.dev> Cc: Usama Arif <usama.arif@linux.dev> Cc: Kiryl Shutsemau <kas@kernel.org> Cc: Doug Gilbert <dgilbert@interlog.com> Cc: James Bottomley <james.bottomley@hansenpartnership.com> Cc: Martin K. Petersen <mkp@kernel.org> Cc: Simona Vetter <simona@ffwll.ch> Cc: Helge Deller <deller@gmx.de> Cc: Sebastian Reichel <sre@kernel.org> Cc: John Hubbard <jhubbard@nvidia.com> Cc: Peter Xu <peterx@redhat.com> Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Rik van Riel <riel@surriel.com> Cc: Harry Yoo <harry@kernel.org> Cc: Juri Lelli <juri.lelli@redhat.com> Cc: Vincent Guittot <vincent.guittot@linaro.org> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Thomas Zimmermann <tzimmermann@suse.de> Cc: David Airlie <airlied@gmail.com> Cc: Will Deacon <will@kernel.org> Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Muchun Song <muchun.song@linux.dev> Cc: Oscar Salvador <osalvador@suse.de> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Jan Kara <jack@suse.cz> Cc: Marc Zyngier <maz@kernel.org> Cc: Oliver Upton <oupton@kernel.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Madhavan Srinivasan <maddy@linux.ibm.com> Cc: Anup Patel <anup@brainfault.org> Cc: Paul Walmsley <pjw@kernel.org> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Albert Ou <aou@eecs.berkeley.edu> Cc: Christian Borntraeger <borntraeger@linux.ibm.com> Cc: Janosch Frank <frankja@linux.ibm.com> Cc: Claudio Imbrenda <imbrenda@linux.ibm.com> Cc: Alexander Gordeev <agordeev@linux.ibm.com> Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com> Cc: Heiko Carstens <hca@linux.ibm.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: David S. Miller <davem@davemloft.net> Cc: Andreas Larsson <andreas@gaisler.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Christian Brauner <brauner@kernel.org> Cc: Matthew Brost <matthew.brost@intel.com> Cc: Joshua Hahn <joshua.hahnjy@gmail.com> Cc: Rakie Kim <rakie.kim@sk.com> Cc: Byungchul Park <byungchul@sk.com> Cc: Gregory Price <gourry@gourry.net> Cc: Ying Huang <ying.huang@linux.alibaba.com> Cc: Alistair Popple <apopple@nvidia.com> Cc: Chris Li <chrisl@kernel.org> Cc: Kairui Song <kasong@tencent.com> Cc: Kemeng Shi <shikemeng@huaweicloud.com> Cc: Nhat Pham <nphamcs@gmail.com> Cc: Baoquan He <baoquan.he@linux.dev> Cc: Youngjun Park <youngjun.park@lge.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Qi Zheng <qi.zheng@linux.dev> Cc: Shakeel Butt <shakeel.butt@linux.dev> Cc: Axel Rasmussen <axelrasmussen@google.com> Cc: Yuanchu Xie <yuanchu@google.com> Cc: Wei Xu <weixugc@google.com> Cc: Chengming Zhou <chengming.zhou@linux.dev> Cc: Michal Hocko <mhocko@kernel.org> Cc: Miklos Szeredi <miklos@szeredi.hu> Cc: Xu Xin <xu.xin@linux.dev> Cc: Ihor Solodrai <ihor.solodrai@linux.dev> Cc: Bert Karwatzki <spasswolf@web.de>
This commit is contained in:
parent
61e012ab9a
commit
aea83f2694
|
|
@ -505,7 +505,7 @@ void folio_add_lru_vma(struct folio *folio, struct vm_area_struct *vma)
|
|||
{
|
||||
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
|
||||
|
||||
if (unlikely((vma->vm_flags & (VM_LOCKED | VM_SPECIAL)) == VM_LOCKED))
|
||||
if (vma_test(vma, VMA_LOCKED_BIT))
|
||||
mlock_new_folio(folio);
|
||||
else
|
||||
folio_add_lru(folio);
|
||||
|
|
|
|||
|
|
@ -970,8 +970,7 @@ void mlock_folio(struct folio *folio);
|
|||
static inline void mlock_vma_folio(struct folio *folio,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
/* The VM_IO check prevents migration from double-counting during mlock. */
|
||||
if (unlikely((vma->vm_flags & (VM_LOCKED|VM_SPECIAL)) == VM_LOCKED))
|
||||
if (vma_test(vma, VMA_LOCKED_BIT))
|
||||
mlock_folio(folio);
|
||||
}
|
||||
|
||||
|
|
@ -988,7 +987,12 @@ static inline void munlock_vma_folio(struct folio *folio,
|
|||
* always munlock the folio and page reclaim will correct it
|
||||
* if it's wrong.
|
||||
*/
|
||||
if (unlikely(vma->vm_flags & VM_LOCKED))
|
||||
/*
|
||||
* VMA_LOCKONFAULT_BIT alone marks an mlock walk in progress, see
|
||||
* mlock_vma_pages_range(). An unmap racing with the walk must still
|
||||
* munlock folios the walk has already counted.
|
||||
*/
|
||||
if (unlikely(vma_test_any_mask(vma, VMA_LOCKED_MASK)))
|
||||
munlock_folio(folio);
|
||||
}
|
||||
|
||||
|
|
|
|||
61
mm/mlock.c
61
mm/mlock.c
|
|
@ -316,22 +316,10 @@ static inline unsigned int folio_mlock_step(struct folio *folio,
|
|||
return folio_pte_batch(folio, pte, ptent, count);
|
||||
}
|
||||
|
||||
static inline bool allow_mlock_munlock(struct folio *folio,
|
||||
static inline bool allow_mlock(struct folio *folio,
|
||||
struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end, unsigned int step)
|
||||
{
|
||||
/*
|
||||
* For unlock, allow munlock large folio which is partially
|
||||
* mapped to VMA. As it's possible that large folio is
|
||||
* mlocked and VMA is split later.
|
||||
*
|
||||
* During memory pressure, such kind of large folio can
|
||||
* be split. And the pages are not in VM_LOCKed VMA
|
||||
* can be reclaimed.
|
||||
*/
|
||||
if (!vma_test(vma, VMA_LOCKED_BIT))
|
||||
return true;
|
||||
|
||||
/* folio_within_range() cannot take KSM, but any small folio is OK */
|
||||
if (!folio_test_large(folio))
|
||||
return true;
|
||||
|
|
@ -352,6 +340,7 @@ static int mlock_pte_range(pmd_t *pmd, unsigned long addr,
|
|||
|
||||
{
|
||||
struct vm_area_struct *vma = walk->vma;
|
||||
const bool lock = walk->private;
|
||||
spinlock_t *ptl;
|
||||
pte_t *start_pte, *pte;
|
||||
pte_t ptent;
|
||||
|
|
@ -368,7 +357,7 @@ static int mlock_pte_range(pmd_t *pmd, unsigned long addr,
|
|||
folio = pmd_folio(*pmd);
|
||||
if (folio_is_zone_device(folio))
|
||||
goto out;
|
||||
if (vma_test(vma, VMA_LOCKED_BIT))
|
||||
if (lock)
|
||||
mlock_folio(folio);
|
||||
else
|
||||
munlock_folio(folio);
|
||||
|
|
@ -390,10 +379,10 @@ static int mlock_pte_range(pmd_t *pmd, unsigned long addr,
|
|||
continue;
|
||||
|
||||
step = folio_mlock_step(folio, pte, addr, end);
|
||||
if (!allow_mlock_munlock(folio, vma, start, end, step))
|
||||
if (lock && !allow_mlock(folio, vma, start, end, step))
|
||||
goto next_entry;
|
||||
|
||||
if (vma_test(vma, VMA_LOCKED_BIT))
|
||||
if (lock)
|
||||
mlock_folio(folio);
|
||||
else
|
||||
munlock_folio(folio);
|
||||
|
|
@ -428,31 +417,29 @@ static void mlock_vma_pages_range(struct vm_area_struct *vma,
|
|||
.pmd_entry = mlock_pte_range,
|
||||
.walk_lock = PGWALK_WRLOCK_VERIFY,
|
||||
};
|
||||
const bool lock = vma_flags_test(new_vma_flags, VMA_LOCKED_BIT);
|
||||
vma_flags_t walk_flags = *new_vma_flags;
|
||||
|
||||
/*
|
||||
* There is a slight chance that concurrent page migration,
|
||||
* or page reclaim finding a page of this now-VMA_LOCKED_BIT vma,
|
||||
* will call mlock_vma_folio() and raise page's mlock_count:
|
||||
* double counting, leaving the page unevictable indefinitely.
|
||||
* Communicate this danger to mlock_vma_folio() with VMA_IO_BIT,
|
||||
* which is a VMA_SPECIAL_FLAGS flag not allowed on VMA_LOCKED_BIT vmas.
|
||||
* mmap_lock is held in write mode here, so this weird
|
||||
* combination should not be visible to other mmap_lock users;
|
||||
* but WRITE_ONCE so rmap walkers must see VMA_IO_BIT if VMA_LOCKED_BIT.
|
||||
* LOCKONFAULT without LOCKED never otherwise occurs: it marks a walk in
|
||||
* progress so that rmap-side callers, which test VMA_LOCKED_BIT, do not
|
||||
* count folios, while try_to_unmap_one(), which tests VMA_LOCKED_MASK,
|
||||
* still refuses to unmap them.
|
||||
*/
|
||||
if (vma_flags_test(new_vma_flags, VMA_LOCKED_BIT))
|
||||
vma_flags_set(new_vma_flags, VMA_IO_BIT);
|
||||
vma_start_write(vma);
|
||||
vma_flags_reset_once(vma, new_vma_flags);
|
||||
|
||||
lru_add_drain();
|
||||
walk_page_range_vma(vma, start, end, &mlock_walk_ops, NULL);
|
||||
lru_add_drain();
|
||||
|
||||
if (vma_flags_test(new_vma_flags, VMA_IO_BIT)) {
|
||||
vma_flags_clear(new_vma_flags, VMA_IO_BIT);
|
||||
vma_flags_reset_once(vma, new_vma_flags);
|
||||
if (lock) {
|
||||
vma_flags_clear(&walk_flags, VMA_LOCKED_BIT);
|
||||
vma_flags_set(&walk_flags, VMA_LOCKONFAULT_BIT);
|
||||
}
|
||||
|
||||
vma_start_write(vma);
|
||||
vma_flags_reset_once(vma, &walk_flags);
|
||||
|
||||
lru_add_drain();
|
||||
walk_page_range_vma(vma, start, end, &mlock_walk_ops, (void *)lock);
|
||||
lru_add_drain();
|
||||
|
||||
if (lock)
|
||||
vma_flags_reset_once(vma, new_vma_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2239,9 +2239,11 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
|
|||
|
||||
/*
|
||||
* If the folio is in an mlock()d vma, we must not swap it out.
|
||||
* VMA_LOCKONFAULT_BIT alone marks an mlock walk in progress, see
|
||||
* mlock_vma_pages_range().
|
||||
*/
|
||||
if (!(flags & TTU_IGNORE_MLOCK) &&
|
||||
(vma->vm_flags & VM_LOCKED)) {
|
||||
vma_test_any_mask(vma, VMA_LOCKED_MASK)) {
|
||||
ptes++;
|
||||
|
||||
/*
|
||||
|
|
@ -2395,7 +2397,7 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,
|
|||
}
|
||||
finish_unmap:
|
||||
folio_remove_rmap_ptes(folio, page, nr_pages, vma);
|
||||
if (vma->vm_flags & VM_LOCKED)
|
||||
if (vma_test_any_mask(vma, VMA_LOCKED_MASK))
|
||||
mlock_drain_local();
|
||||
folio_put_refs(folio, nr_pages);
|
||||
|
||||
|
|
@ -2770,7 +2772,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,
|
|||
hugetlb_remove_rmap(folio);
|
||||
else
|
||||
folio_remove_rmap_pte(folio, subpage, vma);
|
||||
if (vma->vm_flags & VM_LOCKED)
|
||||
if (vma_test_any_mask(vma, VMA_LOCKED_MASK))
|
||||
mlock_drain_local();
|
||||
folio_put(folio);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user