mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-10-28 04:45:43 +01:00
Patch series "mm/rmap: index MAP_PRIVATE file-backed folios by anonymous pgoff", v5. In memory management we've managed to manufacture a great deal of confusion around the concept of anonymous memory. We have: 1. 'Pure anon' memory - anonymous VMAs whose folios are anonymous and swap-backed (thus for reclaim purposes, treated as anonymous). These are simple enough. 2. shmem - file-backed VMAs, file-backed folios (from rmap perspective) so present in the page cache and mapped by an address_space object, but whose folios are also swap-backed (thus treated as anonymous for reclaim purposes). 3. MAP_PRIVATE-mapped /dev/zero - a strange beast whose VMAs have vma->vm_file set, but which clears vma->vm_ops to satisfy vma_is_anonymous(), resulting in VMAs that were mmap()'d referencing a file, but are in every other sense anonymous, including the folios. 4. Other MAP_PRIVATE-file backed mappings - These possess file-backed VMAs and have file-backed folios until CoW'd, at which point those CoW'd folios are anonymous. This series fixes issue 3. In order for us to traverse VMAs using the reverse mapping, we require two fields - folio->mapping and folio->index. The first tells the rmap code where to look for VMAs, and the second tells it at which offset the folio starts within the referenced object. For anonymous folios, folio->mapping points at an anon_vma object. For file-backed folios, it points at an address_space. And: * For file-backed folios folio->index is simply the page offset of the start of the folio within the file. * For anonymous folios belonging to pure anon mappings, folio->index is equal to the anonymous page offset of the folio. * For anonymous folios belonging to file-backed mappings (i.e. CoW'd folios of a MAP_PRIVATE file-backed mapping), folio->index is equal to the file page offset. This series establishes a new anonymous page offset property of VMAs to allow us to map anonymous folios at their anonymous page offset, consistent with pure anon. The purpose of doing so is to lay the foundations for the scalable CoW work. This is necessary because scalable CoW looks in the maple tree for the VMA located at folio->index << PAGE_SHIFT, before falling back to looking up tracked remaps if necessary. The MAP_PRIVATE file-backed case means that folio indices will very often conflict with one another and this remap tracking becomes substantially more contended, and of course the fast path can never be used. This also makes it possible, in future, to unshare anonymously mapped folios with deep fork hierarchies on remap, eliminating the need for remap tracking in the vast majority of cases. Similar to page offset of pure anonymous VMAs, we update the anonymous page offset of unfaulted file-backed VMAs on remap, but do not once CoW'd (i.e. vma->anon_vma is non-NULL). Overall, there is little impact on mergeability, which remains exactly the same for pure anonymous and shared file-backed mappings, with the only impact being on MAP_PRIVATE-mapped file-backed mappings, which must now match on anonymous page offset as well as file page offset to be merged. To fail to merge like this would require CoW'ing the mapping, then finding another VMA with identical file and compatible page offset to remap next to. This is therefore very much an edge case that should have very little impact (and which scalable CoW may very well address in any case). This patch (of 16): Establish fields in vm_area_struct to store the anonymous page offset of VMAs. Initially, the anonymous page offset of a VMA is vma->vm_start >> PAGE_SHIFT. When a VMA is remapped to new_address its anonymous page offset is either updated to new_address >> PAGE_SHIFT if unfaulted or, if faulted, remains equal to the anonymous page offset it had when first faulted. Currently, anonymous folios belonging to CoW'd MAP_PRIVATE-mapped file-backed VMAs are tracked by their file offsets. By adding anonymous offset as a property of VMAs, we can now track them by their anonymous page offset instead. By tracking this, we provide the means by which to eliminate this inconsistency, and more importantly lay the foundations for future work for the scalable CoW anonymous rmap rework. This patch simply adds the fields and some simple helpers. Subsequent patches will update mm code to make use of these fields correctly. The fields chosen are packed in the VMA such that, for 64-bit kernel builds, no additional space is taken up. The first field is present on cacheline 0 containing key VMA fields, and the second on cacheline 3, which contains file-backed reverse mapping fields. Given the relative time spent accessing reverse mapping fields as well as updating them, there shouldn't be any performance impact here from false sharing. Update the VMA userland tests to account for this change. No callsites are updated yet, so no functional change intended. Link: https://lore.kernel.org/[email protected] Link: https://lore.kernel.org/[email protected] Signed-off-by: Lorenzo Stoakes (ARM) <[email protected]> Acked-by: David Hildenbrand (Arm) <[email protected]> Reviewed-by: Gregory Price (Meta) <[email protected]> Reviewed-by: Xu Xin <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Alexander Deucher <[email protected]> Cc: Alexander Gordeev <[email protected]> Cc: Alexander Shishkin <[email protected]> Cc: Alistair Popple <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Baolin Wang <[email protected]> Cc: Baoquan He <[email protected]> Cc: Barry Song <[email protected]> Cc: Boris Brezillon <[email protected]> Cc: Byungchul Park <[email protected]> Cc: Chengming Zhou <[email protected]> Cc: Chris Li <[email protected]> Cc: Christan König <[email protected]> Cc: Christian Borntraeger <[email protected]> Cc: Claudio Imbrenda <[email protected]> Cc: Dave Airlie <[email protected]> Cc: Dev Jain <[email protected]> Cc: Gerald Schaefer <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Cc: Harry Yoo <[email protected]> Cc: Heiko Carstens <[email protected]> Cc: Huang Ray <[email protected]> Cc: "Huang, Ying" <[email protected]> Cc: Ian Rogers <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: James Clark <[email protected]> Cc: Jan Kara <[email protected]> Cc: Jann Horn <[email protected]> Cc: Janosch Frank <[email protected]> Cc: Jason Gunthorpe <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: John Hubbard <[email protected]> Cc: Joshua Hahn <[email protected]> Cc: Kairui Song <[email protected]> Cc: Kees Cook <[email protected]> Cc: Kemeng Shi <[email protected]> Cc: Lance Yang <[email protected]> Cc: Liam R. Howlett <[email protected]> Cc: Liviu Dudau <[email protected]> Cc: Maarten Lankhorst <[email protected]> Cc: Marc Rutland <[email protected]> Cc: "Masami Hiramatsu (Google)" <[email protected]> Cc: Matthew Auld <[email protected]> Cc: Matthew Brost <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Cc: Maxime Ripard <[email protected]> Cc: Miaohe Lin <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: Muchun Song <[email protected]> Cc: Namhyung kim <[email protected]> Cc: Naoya Horiguchi <[email protected]> Cc: Nhat Pham <[email protected]> Cc: Nico Pache <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Oscar Salvador <[email protected]> Cc: Pedro Falcato <[email protected]> Cc: Peter Xu <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Rakie Kim <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Rodrigo Vivi <[email protected]> Cc: Ryan Roberts <[email protected]> Cc: Steven Price <[email protected]> Cc: Suren Baghdasaryan <[email protected]> Cc: Sven Schnelle <[email protected]> Cc: Thomas Hellström <[email protected]> Cc: Thomas Zimemrmann <[email protected]> Cc: Vasily Gorbik <[email protected]> Cc: Vlastimil Babka <[email protected]> Cc: xu xin <[email protected]> Cc: Zi Yan <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
158 lines
3.9 KiB
C
158 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/*
|
|
* Functions for initializing, allocating, freeing and duplicating VMAs. Shared
|
|
* between CONFIG_MMU and non-CONFIG_MMU kernel configurations.
|
|
*/
|
|
|
|
/*
|
|
* To allow for userland testing we place internal dependencies in
|
|
* vma_internal.h and external VMA API declarations in vma.h.
|
|
*/
|
|
#include "vma_internal.h"
|
|
#include "vma.h"
|
|
|
|
/* SLAB cache for vm_area_struct structures */
|
|
static struct kmem_cache *vm_area_cachep;
|
|
|
|
void __init vma_state_init(void)
|
|
{
|
|
struct kmem_cache_args args = {
|
|
.use_freeptr_offset = true,
|
|
.freeptr_offset = offsetof(struct vm_area_struct, vm_freeptr),
|
|
.sheaf_capacity = 32,
|
|
};
|
|
|
|
vm_area_cachep = kmem_cache_create("vm_area_struct",
|
|
sizeof(struct vm_area_struct), &args,
|
|
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_TYPESAFE_BY_RCU|
|
|
SLAB_ACCOUNT);
|
|
}
|
|
|
|
struct vm_area_struct *vm_area_alloc(struct mm_struct *mm)
|
|
{
|
|
struct vm_area_struct *vma;
|
|
|
|
vma = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
|
|
if (!vma)
|
|
return NULL;
|
|
|
|
vma_init(vma, mm);
|
|
|
|
return vma;
|
|
}
|
|
|
|
static void vm_area_init_from(const struct vm_area_struct *src,
|
|
struct vm_area_struct *dest)
|
|
{
|
|
dest->vm_mm = src->vm_mm;
|
|
dest->vm_ops = src->vm_ops;
|
|
dest->vm_start = src->vm_start;
|
|
dest->vm_end = src->vm_end;
|
|
dest->anon_vma = src->anon_vma;
|
|
dest->vm_pgoff = vma_start_pgoff(src);
|
|
__vma_set_anon_pgoff(dest, vma_start_anon_pgoff(src));
|
|
dest->vm_file = src->vm_file;
|
|
dest->vm_private_data = src->vm_private_data;
|
|
vm_flags_init(dest, src->vm_flags);
|
|
memcpy(&dest->vm_page_prot, &src->vm_page_prot,
|
|
sizeof(dest->vm_page_prot));
|
|
/*
|
|
* src->shared.rb may be modified concurrently when called from
|
|
* dup_mmap(), but the clone will reinitialize it.
|
|
*/
|
|
data_race(memcpy(&dest->shared, &src->shared, sizeof(dest->shared)));
|
|
memcpy(&dest->vm_userfaultfd_ctx, &src->vm_userfaultfd_ctx,
|
|
sizeof(dest->vm_userfaultfd_ctx));
|
|
#ifdef CONFIG_ANON_VMA_NAME
|
|
dest->anon_name = src->anon_name;
|
|
#endif
|
|
#ifdef CONFIG_SWAP
|
|
memcpy(&dest->swap_readahead_info, &src->swap_readahead_info,
|
|
sizeof(dest->swap_readahead_info));
|
|
#endif
|
|
#ifndef CONFIG_MMU
|
|
dest->vm_region = src->vm_region;
|
|
#endif
|
|
#ifdef CONFIG_NUMA
|
|
dest->vm_policy = src->vm_policy;
|
|
#endif
|
|
#ifdef __HAVE_PFNMAP_TRACKING
|
|
dest->pfnmap_track_ctx = 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 = 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) >= REFCOUNT_MAX))
|
|
return -ENOMEM;
|
|
kref_get(&ctx->kref);
|
|
new->pfnmap_track_ctx = ctx;
|
|
return 0;
|
|
}
|
|
|
|
static inline void vma_pfnmap_track_ctx_release(struct vm_area_struct *vma)
|
|
{
|
|
struct pfnmap_track_ctx *ctx = vma->pfnmap_track_ctx;
|
|
|
|
if (likely(!ctx))
|
|
return;
|
|
|
|
kref_put(&ctx->kref, pfnmap_track_ctx_release);
|
|
vma->pfnmap_track_ctx = 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
|
|
|
|
struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig)
|
|
{
|
|
struct vm_area_struct *new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
|
|
|
|
if (!new)
|
|
return NULL;
|
|
|
|
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);
|
|
dup_anon_vma_name(orig, new);
|
|
|
|
return new;
|
|
}
|
|
|
|
void vm_area_free(struct vm_area_struct *vma)
|
|
{
|
|
/* The vma should be detached while being destroyed. */
|
|
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);
|
|
}
|