mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
3822a7c409
F_SEAL_EXEC") which permits the setting of the memfd execute bit at memfd creation time, with the option of sealing the state of the X bit. - Peter Xu adds a patch series ("mm/hugetlb: Make huge_pte_offset() thread-safe for pmd unshare") which addresses a rare race condition related to PMD unsharing. - Several folioification patch serieses from Matthew Wilcox, Vishal Moola, Sidhartha Kumar and Lorenzo Stoakes - Johannes Weiner has a series ("mm: push down lock_page_memcg()") which does perform some memcg maintenance and cleanup work. - SeongJae Park has added DAMOS filtering to DAMON, with the series "mm/damon/core: implement damos filter". These filters provide users with finer-grained control over DAMOS's actions. SeongJae has also done some DAMON cleanup work. - Kairui Song adds a series ("Clean up and fixes for swap"). - Vernon Yang contributed the series "Clean up and refinement for maple tree". - Yu Zhao has contributed the "mm: multi-gen LRU: memcg LRU" series. It adds to MGLRU an LRU of memcgs, to improve the scalability of global reclaim. - David Hildenbrand has added some userfaultfd cleanup work in the series "mm: uffd-wp + change_protection() cleanups". - Christoph Hellwig has removed the generic_writepages() library function in the series "remove generic_writepages". - Baolin Wang has performed some maintenance on the compaction code in his series "Some small improvements for compaction". - Sidhartha Kumar is doing some maintenance work on struct page in his series "Get rid of tail page fields". - David Hildenbrand contributed some cleanup, bugfixing and generalization of pte management and of pte debugging in his series "mm: support __HAVE_ARCH_PTE_SWP_EXCLUSIVE on all architectures with swap PTEs". - Mel Gorman and Neil Brown have removed the __GFP_ATOMIC allocation flag in the series "Discard __GFP_ATOMIC". - Sergey Senozhatsky has improved zsmalloc's memory utilization with his series "zsmalloc: make zspage chain size configurable". - Joey Gouly has added prctl() support for prohibiting the creation of writeable+executable mappings. The previous BPF-based approach had shortcomings. See "mm: In-kernel support for memory-deny-write-execute (MDWE)". - Waiman Long did some kmemleak cleanup and bugfixing in the series "mm/kmemleak: Simplify kmemleak_cond_resched() & fix UAF". - T.J. Alumbaugh has contributed some MGLRU cleanup work in his series "mm: multi-gen LRU: improve". - Jiaqi Yan has provided some enhancements to our memory error statistics reporting, mainly by presenting the statistics on a per-node basis. See the series "Introduce per NUMA node memory error statistics". - Mel Gorman has a second and hopefully final shot at fixing a CPU-hog regression in compaction via his series "Fix excessive CPU usage during compaction". - Christoph Hellwig does some vmalloc maintenance work in the series "cleanup vfree and vunmap". - Christoph Hellwig has removed block_device_operations.rw_page() in ths series "remove ->rw_page". - We get some maple_tree improvements and cleanups in Liam Howlett's series "VMA tree type safety and remove __vma_adjust()". - Suren Baghdasaryan has done some work on the maintainability of our vm_flags handling in the series "introduce vm_flags modifier functions". - Some pagemap cleanup and generalization work in Mike Rapoport's series "mm, arch: add generic implementation of pfn_valid() for FLATMEM" and "fixups for generic implementation of pfn_valid()" - Baoquan He has done some work to make /proc/vmallocinfo and /proc/kcore better represent the real state of things in his series "mm/vmalloc.c: allow vread() to read out vm_map_ram areas". - Jason Gunthorpe rationalized the GUP system's interface to the rest of the kernel in the series "Simplify the external interface for GUP". - SeongJae Park wishes to migrate people from DAMON's debugfs interface over to its sysfs interface. To support this, we'll temporarily be printing warnings when people use the debugfs interface. See the series "mm/damon: deprecate DAMON debugfs interface". - Andrey Konovalov provided the accurately named "lib/stackdepot: fixes and clean-ups" series. - Huang Ying has provided a dramatic reduction in migration's TLB flush IPI rates with the series "migrate_pages(): batch TLB flushing". - Arnd Bergmann has some objtool fixups in "objtool warning fixes". -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCY/PoPQAKCRDdBJ7gKXxA jlvpAPsFECUBBl20qSue2zCYWnHC7Yk4q9ytTkPB/MMDrFEN9wD/SNKEm2UoK6/K DmxHkn0LAitGgJRS/W9w81yrgig9tAQ= =MlGs -----END PGP SIGNATURE----- Merge tag 'mm-stable-2023-02-20-13-37' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm Pull MM updates from Andrew Morton: - Daniel Verkamp has contributed a memfd series ("mm/memfd: add F_SEAL_EXEC") which permits the setting of the memfd execute bit at memfd creation time, with the option of sealing the state of the X bit. - Peter Xu adds a patch series ("mm/hugetlb: Make huge_pte_offset() thread-safe for pmd unshare") which addresses a rare race condition related to PMD unsharing. - Several folioification patch serieses from Matthew Wilcox, Vishal Moola, Sidhartha Kumar and Lorenzo Stoakes - Johannes Weiner has a series ("mm: push down lock_page_memcg()") which does perform some memcg maintenance and cleanup work. - SeongJae Park has added DAMOS filtering to DAMON, with the series "mm/damon/core: implement damos filter". These filters provide users with finer-grained control over DAMOS's actions. SeongJae has also done some DAMON cleanup work. - Kairui Song adds a series ("Clean up and fixes for swap"). - Vernon Yang contributed the series "Clean up and refinement for maple tree". - Yu Zhao has contributed the "mm: multi-gen LRU: memcg LRU" series. It adds to MGLRU an LRU of memcgs, to improve the scalability of global reclaim. - David Hildenbrand has added some userfaultfd cleanup work in the series "mm: uffd-wp + change_protection() cleanups". - Christoph Hellwig has removed the generic_writepages() library function in the series "remove generic_writepages". - Baolin Wang has performed some maintenance on the compaction code in his series "Some small improvements for compaction". - Sidhartha Kumar is doing some maintenance work on struct page in his series "Get rid of tail page fields". - David Hildenbrand contributed some cleanup, bugfixing and generalization of pte management and of pte debugging in his series "mm: support __HAVE_ARCH_PTE_SWP_EXCLUSIVE on all architectures with swap PTEs". - Mel Gorman and Neil Brown have removed the __GFP_ATOMIC allocation flag in the series "Discard __GFP_ATOMIC". - Sergey Senozhatsky has improved zsmalloc's memory utilization with his series "zsmalloc: make zspage chain size configurable". - Joey Gouly has added prctl() support for prohibiting the creation of writeable+executable mappings. The previous BPF-based approach had shortcomings. See "mm: In-kernel support for memory-deny-write-execute (MDWE)". - Waiman Long did some kmemleak cleanup and bugfixing in the series "mm/kmemleak: Simplify kmemleak_cond_resched() & fix UAF". - T.J. Alumbaugh has contributed some MGLRU cleanup work in his series "mm: multi-gen LRU: improve". - Jiaqi Yan has provided some enhancements to our memory error statistics reporting, mainly by presenting the statistics on a per-node basis. See the series "Introduce per NUMA node memory error statistics". - Mel Gorman has a second and hopefully final shot at fixing a CPU-hog regression in compaction via his series "Fix excessive CPU usage during compaction". - Christoph Hellwig does some vmalloc maintenance work in the series "cleanup vfree and vunmap". - Christoph Hellwig has removed block_device_operations.rw_page() in ths series "remove ->rw_page". - We get some maple_tree improvements and cleanups in Liam Howlett's series "VMA tree type safety and remove __vma_adjust()". - Suren Baghdasaryan has done some work on the maintainability of our vm_flags handling in the series "introduce vm_flags modifier functions". - Some pagemap cleanup and generalization work in Mike Rapoport's series "mm, arch: add generic implementation of pfn_valid() for FLATMEM" and "fixups for generic implementation of pfn_valid()" - Baoquan He has done some work to make /proc/vmallocinfo and /proc/kcore better represent the real state of things in his series "mm/vmalloc.c: allow vread() to read out vm_map_ram areas". - Jason Gunthorpe rationalized the GUP system's interface to the rest of the kernel in the series "Simplify the external interface for GUP". - SeongJae Park wishes to migrate people from DAMON's debugfs interface over to its sysfs interface. To support this, we'll temporarily be printing warnings when people use the debugfs interface. See the series "mm/damon: deprecate DAMON debugfs interface". - Andrey Konovalov provided the accurately named "lib/stackdepot: fixes and clean-ups" series. - Huang Ying has provided a dramatic reduction in migration's TLB flush IPI rates with the series "migrate_pages(): batch TLB flushing". - Arnd Bergmann has some objtool fixups in "objtool warning fixes". * tag 'mm-stable-2023-02-20-13-37' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (505 commits) include/linux/migrate.h: remove unneeded externs mm/memory_hotplug: cleanup return value handing in do_migrate_range() mm/uffd: fix comment in handling pte markers mm: change to return bool for isolate_movable_page() mm: hugetlb: change to return bool for isolate_hugetlb() mm: change to return bool for isolate_lru_page() mm: change to return bool for folio_isolate_lru() objtool: add UACCESS exceptions for __tsan_volatile_read/write kmsan: disable ftrace in kmsan core code kasan: mark addr_has_metadata __always_inline mm: memcontrol: rename memcg_kmem_enabled() sh: initialize max_mapnr m68k/nommu: add missing definition of ARCH_PFN_OFFSET mm: percpu: fix incorrect size in pcpu_obj_full_size() maple_tree: reduce stack usage with gcc-9 and earlier mm: page_alloc: call panic() when memoryless node allocation fails mm: multi-gen LRU: avoid futile retries migrate_pages: move THP/hugetlb migration support check to simplify code migrate_pages: batch flushing TLB migrate_pages: share more code between _unmap and _move ...
4277 lines
110 KiB
C
4277 lines
110 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 1993 Linus Torvalds
|
|
* Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999
|
|
* SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian <tigran@veritas.com>, May 2000
|
|
* Major rework to support vmap/vunmap, Christoph Hellwig, SGI, August 2002
|
|
* Numa awareness, Christoph Lameter, SGI, June 2005
|
|
* Improving global KVA allocator, Uladzislau Rezki, Sony, May 2019
|
|
*/
|
|
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/set_memory.h>
|
|
#include <linux/debugobjects.h>
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/list.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/rbtree.h>
|
|
#include <linux/xarray.h>
|
|
#include <linux/io.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/pfn.h>
|
|
#include <linux/kmemleak.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/memcontrol.h>
|
|
#include <linux/llist.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/rbtree_augmented.h>
|
|
#include <linux/overflow.h>
|
|
#include <linux/pgtable.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/sched/mm.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/shmparam.h>
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/vmalloc.h>
|
|
|
|
#include "internal.h"
|
|
#include "pgalloc-track.h"
|
|
|
|
#ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
|
|
static unsigned int __ro_after_init ioremap_max_page_shift = BITS_PER_LONG - 1;
|
|
|
|
static int __init set_nohugeiomap(char *str)
|
|
{
|
|
ioremap_max_page_shift = PAGE_SHIFT;
|
|
return 0;
|
|
}
|
|
early_param("nohugeiomap", set_nohugeiomap);
|
|
#else /* CONFIG_HAVE_ARCH_HUGE_VMAP */
|
|
static const unsigned int ioremap_max_page_shift = PAGE_SHIFT;
|
|
#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
|
|
|
|
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
|
|
static bool __ro_after_init vmap_allow_huge = true;
|
|
|
|
static int __init set_nohugevmalloc(char *str)
|
|
{
|
|
vmap_allow_huge = false;
|
|
return 0;
|
|
}
|
|
early_param("nohugevmalloc", set_nohugevmalloc);
|
|
#else /* CONFIG_HAVE_ARCH_HUGE_VMALLOC */
|
|
static const bool vmap_allow_huge = false;
|
|
#endif /* CONFIG_HAVE_ARCH_HUGE_VMALLOC */
|
|
|
|
bool is_vmalloc_addr(const void *x)
|
|
{
|
|
unsigned long addr = (unsigned long)kasan_reset_tag(x);
|
|
|
|
return addr >= VMALLOC_START && addr < VMALLOC_END;
|
|
}
|
|
EXPORT_SYMBOL(is_vmalloc_addr);
|
|
|
|
struct vfree_deferred {
|
|
struct llist_head list;
|
|
struct work_struct wq;
|
|
};
|
|
static DEFINE_PER_CPU(struct vfree_deferred, vfree_deferred);
|
|
|
|
/*** Page table manipulation functions ***/
|
|
static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift, pgtbl_mod_mask *mask)
|
|
{
|
|
pte_t *pte;
|
|
u64 pfn;
|
|
unsigned long size = PAGE_SIZE;
|
|
|
|
pfn = phys_addr >> PAGE_SHIFT;
|
|
pte = pte_alloc_kernel_track(pmd, addr, mask);
|
|
if (!pte)
|
|
return -ENOMEM;
|
|
do {
|
|
BUG_ON(!pte_none(*pte));
|
|
|
|
#ifdef CONFIG_HUGETLB_PAGE
|
|
size = arch_vmap_pte_range_map_size(addr, end, pfn, max_page_shift);
|
|
if (size != PAGE_SIZE) {
|
|
pte_t entry = pfn_pte(pfn, prot);
|
|
|
|
entry = arch_make_huge_pte(entry, ilog2(size), 0);
|
|
set_huge_pte_at(&init_mm, addr, pte, entry);
|
|
pfn += PFN_DOWN(size);
|
|
continue;
|
|
}
|
|
#endif
|
|
set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
|
|
pfn++;
|
|
} while (pte += PFN_DOWN(size), addr += size, addr != end);
|
|
*mask |= PGTBL_PTE_MODIFIED;
|
|
return 0;
|
|
}
|
|
|
|
static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift)
|
|
{
|
|
if (max_page_shift < PMD_SHIFT)
|
|
return 0;
|
|
|
|
if (!arch_vmap_pmd_supported(prot))
|
|
return 0;
|
|
|
|
if ((end - addr) != PMD_SIZE)
|
|
return 0;
|
|
|
|
if (!IS_ALIGNED(addr, PMD_SIZE))
|
|
return 0;
|
|
|
|
if (!IS_ALIGNED(phys_addr, PMD_SIZE))
|
|
return 0;
|
|
|
|
if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
|
|
return 0;
|
|
|
|
return pmd_set_huge(pmd, phys_addr, prot);
|
|
}
|
|
|
|
static int vmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift, pgtbl_mod_mask *mask)
|
|
{
|
|
pmd_t *pmd;
|
|
unsigned long next;
|
|
|
|
pmd = pmd_alloc_track(&init_mm, pud, addr, mask);
|
|
if (!pmd)
|
|
return -ENOMEM;
|
|
do {
|
|
next = pmd_addr_end(addr, end);
|
|
|
|
if (vmap_try_huge_pmd(pmd, addr, next, phys_addr, prot,
|
|
max_page_shift)) {
|
|
*mask |= PGTBL_PMD_MODIFIED;
|
|
continue;
|
|
}
|
|
|
|
if (vmap_pte_range(pmd, addr, next, phys_addr, prot, max_page_shift, mask))
|
|
return -ENOMEM;
|
|
} while (pmd++, phys_addr += (next - addr), addr = next, addr != end);
|
|
return 0;
|
|
}
|
|
|
|
static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift)
|
|
{
|
|
if (max_page_shift < PUD_SHIFT)
|
|
return 0;
|
|
|
|
if (!arch_vmap_pud_supported(prot))
|
|
return 0;
|
|
|
|
if ((end - addr) != PUD_SIZE)
|
|
return 0;
|
|
|
|
if (!IS_ALIGNED(addr, PUD_SIZE))
|
|
return 0;
|
|
|
|
if (!IS_ALIGNED(phys_addr, PUD_SIZE))
|
|
return 0;
|
|
|
|
if (pud_present(*pud) && !pud_free_pmd_page(pud, addr))
|
|
return 0;
|
|
|
|
return pud_set_huge(pud, phys_addr, prot);
|
|
}
|
|
|
|
static int vmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift, pgtbl_mod_mask *mask)
|
|
{
|
|
pud_t *pud;
|
|
unsigned long next;
|
|
|
|
pud = pud_alloc_track(&init_mm, p4d, addr, mask);
|
|
if (!pud)
|
|
return -ENOMEM;
|
|
do {
|
|
next = pud_addr_end(addr, end);
|
|
|
|
if (vmap_try_huge_pud(pud, addr, next, phys_addr, prot,
|
|
max_page_shift)) {
|
|
*mask |= PGTBL_PUD_MODIFIED;
|
|
continue;
|
|
}
|
|
|
|
if (vmap_pmd_range(pud, addr, next, phys_addr, prot,
|
|
max_page_shift, mask))
|
|
return -ENOMEM;
|
|
} while (pud++, phys_addr += (next - addr), addr = next, addr != end);
|
|
return 0;
|
|
}
|
|
|
|
static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift)
|
|
{
|
|
if (max_page_shift < P4D_SHIFT)
|
|
return 0;
|
|
|
|
if (!arch_vmap_p4d_supported(prot))
|
|
return 0;
|
|
|
|
if ((end - addr) != P4D_SIZE)
|
|
return 0;
|
|
|
|
if (!IS_ALIGNED(addr, P4D_SIZE))
|
|
return 0;
|
|
|
|
if (!IS_ALIGNED(phys_addr, P4D_SIZE))
|
|
return 0;
|
|
|
|
if (p4d_present(*p4d) && !p4d_free_pud_page(p4d, addr))
|
|
return 0;
|
|
|
|
return p4d_set_huge(p4d, phys_addr, prot);
|
|
}
|
|
|
|
static int vmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift, pgtbl_mod_mask *mask)
|
|
{
|
|
p4d_t *p4d;
|
|
unsigned long next;
|
|
|
|
p4d = p4d_alloc_track(&init_mm, pgd, addr, mask);
|
|
if (!p4d)
|
|
return -ENOMEM;
|
|
do {
|
|
next = p4d_addr_end(addr, end);
|
|
|
|
if (vmap_try_huge_p4d(p4d, addr, next, phys_addr, prot,
|
|
max_page_shift)) {
|
|
*mask |= PGTBL_P4D_MODIFIED;
|
|
continue;
|
|
}
|
|
|
|
if (vmap_pud_range(p4d, addr, next, phys_addr, prot,
|
|
max_page_shift, mask))
|
|
return -ENOMEM;
|
|
} while (p4d++, phys_addr += (next - addr), addr = next, addr != end);
|
|
return 0;
|
|
}
|
|
|
|
static int vmap_range_noflush(unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift)
|
|
{
|
|
pgd_t *pgd;
|
|
unsigned long start;
|
|
unsigned long next;
|
|
int err;
|
|
pgtbl_mod_mask mask = 0;
|
|
|
|
might_sleep();
|
|
BUG_ON(addr >= end);
|
|
|
|
start = addr;
|
|
pgd = pgd_offset_k(addr);
|
|
do {
|
|
next = pgd_addr_end(addr, end);
|
|
err = vmap_p4d_range(pgd, addr, next, phys_addr, prot,
|
|
max_page_shift, &mask);
|
|
if (err)
|
|
break;
|
|
} while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
|
|
|
|
if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
|
|
arch_sync_kernel_mappings(start, end);
|
|
|
|
return err;
|
|
}
|
|
|
|
int ioremap_page_range(unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot)
|
|
{
|
|
int err;
|
|
|
|
err = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),
|
|
ioremap_max_page_shift);
|
|
flush_cache_vmap(addr, end);
|
|
if (!err)
|
|
kmsan_ioremap_page_range(addr, end, phys_addr, prot,
|
|
ioremap_max_page_shift);
|
|
return err;
|
|
}
|
|
|
|
static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
pte_t *pte;
|
|
|
|
pte = pte_offset_kernel(pmd, addr);
|
|
do {
|
|
pte_t ptent = ptep_get_and_clear(&init_mm, addr, pte);
|
|
WARN_ON(!pte_none(ptent) && !pte_present(ptent));
|
|
} while (pte++, addr += PAGE_SIZE, addr != end);
|
|
*mask |= PGTBL_PTE_MODIFIED;
|
|
}
|
|
|
|
static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
pmd_t *pmd;
|
|
unsigned long next;
|
|
int cleared;
|
|
|
|
pmd = pmd_offset(pud, addr);
|
|
do {
|
|
next = pmd_addr_end(addr, end);
|
|
|
|
cleared = pmd_clear_huge(pmd);
|
|
if (cleared || pmd_bad(*pmd))
|
|
*mask |= PGTBL_PMD_MODIFIED;
|
|
|
|
if (cleared)
|
|
continue;
|
|
if (pmd_none_or_clear_bad(pmd))
|
|
continue;
|
|
vunmap_pte_range(pmd, addr, next, mask);
|
|
|
|
cond_resched();
|
|
} while (pmd++, addr = next, addr != end);
|
|
}
|
|
|
|
static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
pud_t *pud;
|
|
unsigned long next;
|
|
int cleared;
|
|
|
|
pud = pud_offset(p4d, addr);
|
|
do {
|
|
next = pud_addr_end(addr, end);
|
|
|
|
cleared = pud_clear_huge(pud);
|
|
if (cleared || pud_bad(*pud))
|
|
*mask |= PGTBL_PUD_MODIFIED;
|
|
|
|
if (cleared)
|
|
continue;
|
|
if (pud_none_or_clear_bad(pud))
|
|
continue;
|
|
vunmap_pmd_range(pud, addr, next, mask);
|
|
} while (pud++, addr = next, addr != end);
|
|
}
|
|
|
|
static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
p4d_t *p4d;
|
|
unsigned long next;
|
|
|
|
p4d = p4d_offset(pgd, addr);
|
|
do {
|
|
next = p4d_addr_end(addr, end);
|
|
|
|
p4d_clear_huge(p4d);
|
|
if (p4d_bad(*p4d))
|
|
*mask |= PGTBL_P4D_MODIFIED;
|
|
|
|
if (p4d_none_or_clear_bad(p4d))
|
|
continue;
|
|
vunmap_pud_range(p4d, addr, next, mask);
|
|
} while (p4d++, addr = next, addr != end);
|
|
}
|
|
|
|
/*
|
|
* vunmap_range_noflush is similar to vunmap_range, but does not
|
|
* flush caches or TLBs.
|
|
*
|
|
* The caller is responsible for calling flush_cache_vmap() before calling
|
|
* this function, and flush_tlb_kernel_range after it has returned
|
|
* successfully (and before the addresses are expected to cause a page fault
|
|
* or be re-mapped for something else, if TLB flushes are being delayed or
|
|
* coalesced).
|
|
*
|
|
* This is an internal function only. Do not use outside mm/.
|
|
*/
|
|
void __vunmap_range_noflush(unsigned long start, unsigned long end)
|
|
{
|
|
unsigned long next;
|
|
pgd_t *pgd;
|
|
unsigned long addr = start;
|
|
pgtbl_mod_mask mask = 0;
|
|
|
|
BUG_ON(addr >= end);
|
|
pgd = pgd_offset_k(addr);
|
|
do {
|
|
next = pgd_addr_end(addr, end);
|
|
if (pgd_bad(*pgd))
|
|
mask |= PGTBL_PGD_MODIFIED;
|
|
if (pgd_none_or_clear_bad(pgd))
|
|
continue;
|
|
vunmap_p4d_range(pgd, addr, next, &mask);
|
|
} while (pgd++, addr = next, addr != end);
|
|
|
|
if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
|
|
arch_sync_kernel_mappings(start, end);
|
|
}
|
|
|
|
void vunmap_range_noflush(unsigned long start, unsigned long end)
|
|
{
|
|
kmsan_vunmap_range_noflush(start, end);
|
|
__vunmap_range_noflush(start, end);
|
|
}
|
|
|
|
/**
|
|
* vunmap_range - unmap kernel virtual addresses
|
|
* @addr: start of the VM area to unmap
|
|
* @end: end of the VM area to unmap (non-inclusive)
|
|
*
|
|
* Clears any present PTEs in the virtual address range, flushes TLBs and
|
|
* caches. Any subsequent access to the address before it has been re-mapped
|
|
* is a kernel bug.
|
|
*/
|
|
void vunmap_range(unsigned long addr, unsigned long end)
|
|
{
|
|
flush_cache_vunmap(addr, end);
|
|
vunmap_range_noflush(addr, end);
|
|
flush_tlb_kernel_range(addr, end);
|
|
}
|
|
|
|
static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
|
|
unsigned long end, pgprot_t prot, struct page **pages, int *nr,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
pte_t *pte;
|
|
|
|
/*
|
|
* nr is a running index into the array which helps higher level
|
|
* callers keep track of where we're up to.
|
|
*/
|
|
|
|
pte = pte_alloc_kernel_track(pmd, addr, mask);
|
|
if (!pte)
|
|
return -ENOMEM;
|
|
do {
|
|
struct page *page = pages[*nr];
|
|
|
|
if (WARN_ON(!pte_none(*pte)))
|
|
return -EBUSY;
|
|
if (WARN_ON(!page))
|
|
return -ENOMEM;
|
|
if (WARN_ON(!pfn_valid(page_to_pfn(page))))
|
|
return -EINVAL;
|
|
|
|
set_pte_at(&init_mm, addr, pte, mk_pte(page, prot));
|
|
(*nr)++;
|
|
} while (pte++, addr += PAGE_SIZE, addr != end);
|
|
*mask |= PGTBL_PTE_MODIFIED;
|
|
return 0;
|
|
}
|
|
|
|
static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
|
|
unsigned long end, pgprot_t prot, struct page **pages, int *nr,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
pmd_t *pmd;
|
|
unsigned long next;
|
|
|
|
pmd = pmd_alloc_track(&init_mm, pud, addr, mask);
|
|
if (!pmd)
|
|
return -ENOMEM;
|
|
do {
|
|
next = pmd_addr_end(addr, end);
|
|
if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask))
|
|
return -ENOMEM;
|
|
} while (pmd++, addr = next, addr != end);
|
|
return 0;
|
|
}
|
|
|
|
static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
|
|
unsigned long end, pgprot_t prot, struct page **pages, int *nr,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
pud_t *pud;
|
|
unsigned long next;
|
|
|
|
pud = pud_alloc_track(&init_mm, p4d, addr, mask);
|
|
if (!pud)
|
|
return -ENOMEM;
|
|
do {
|
|
next = pud_addr_end(addr, end);
|
|
if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask))
|
|
return -ENOMEM;
|
|
} while (pud++, addr = next, addr != end);
|
|
return 0;
|
|
}
|
|
|
|
static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
|
|
unsigned long end, pgprot_t prot, struct page **pages, int *nr,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
p4d_t *p4d;
|
|
unsigned long next;
|
|
|
|
p4d = p4d_alloc_track(&init_mm, pgd, addr, mask);
|
|
if (!p4d)
|
|
return -ENOMEM;
|
|
do {
|
|
next = p4d_addr_end(addr, end);
|
|
if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask))
|
|
return -ENOMEM;
|
|
} while (p4d++, addr = next, addr != end);
|
|
return 0;
|
|
}
|
|
|
|
static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
|
|
pgprot_t prot, struct page **pages)
|
|
{
|
|
unsigned long start = addr;
|
|
pgd_t *pgd;
|
|
unsigned long next;
|
|
int err = 0;
|
|
int nr = 0;
|
|
pgtbl_mod_mask mask = 0;
|
|
|
|
BUG_ON(addr >= end);
|
|
pgd = pgd_offset_k(addr);
|
|
do {
|
|
next = pgd_addr_end(addr, end);
|
|
if (pgd_bad(*pgd))
|
|
mask |= PGTBL_PGD_MODIFIED;
|
|
err = vmap_pages_p4d_range(pgd, addr, next, prot, pages, &nr, &mask);
|
|
if (err)
|
|
return err;
|
|
} while (pgd++, addr = next, addr != end);
|
|
|
|
if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
|
|
arch_sync_kernel_mappings(start, end);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* vmap_pages_range_noflush is similar to vmap_pages_range, but does not
|
|
* flush caches.
|
|
*
|
|
* The caller is responsible for calling flush_cache_vmap() after this
|
|
* function returns successfully and before the addresses are accessed.
|
|
*
|
|
* This is an internal function only. Do not use outside mm/.
|
|
*/
|
|
int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
|
|
pgprot_t prot, struct page **pages, unsigned int page_shift)
|
|
{
|
|
unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
|
|
|
|
WARN_ON(page_shift < PAGE_SHIFT);
|
|
|
|
if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
|
|
page_shift == PAGE_SHIFT)
|
|
return vmap_small_pages_range_noflush(addr, end, prot, pages);
|
|
|
|
for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
|
|
int err;
|
|
|
|
err = vmap_range_noflush(addr, addr + (1UL << page_shift),
|
|
page_to_phys(pages[i]), prot,
|
|
page_shift);
|
|
if (err)
|
|
return err;
|
|
|
|
addr += 1UL << page_shift;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
|
|
pgprot_t prot, struct page **pages, unsigned int page_shift)
|
|
{
|
|
kmsan_vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
|
|
return __vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
|
|
}
|
|
|
|
/**
|
|
* vmap_pages_range - map pages to a kernel virtual address
|
|
* @addr: start of the VM area to map
|
|
* @end: end of the VM area to map (non-inclusive)
|
|
* @prot: page protection flags to use
|
|
* @pages: pages to map (always PAGE_SIZE pages)
|
|
* @page_shift: maximum shift that the pages may be mapped with, @pages must
|
|
* be aligned and contiguous up to at least this shift.
|
|
*
|
|
* RETURNS:
|
|
* 0 on success, -errno on failure.
|
|
*/
|
|
static int vmap_pages_range(unsigned long addr, unsigned long end,
|
|
pgprot_t prot, struct page **pages, unsigned int page_shift)
|
|
{
|
|
int err;
|
|
|
|
err = vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
|
|
flush_cache_vmap(addr, end);
|
|
return err;
|
|
}
|
|
|
|
int is_vmalloc_or_module_addr(const void *x)
|
|
{
|
|
/*
|
|
* ARM, x86-64 and sparc64 put modules in a special place,
|
|
* and fall back on vmalloc() if that fails. Others
|
|
* just put it in the vmalloc space.
|
|
*/
|
|
#if defined(CONFIG_MODULES) && defined(MODULES_VADDR)
|
|
unsigned long addr = (unsigned long)kasan_reset_tag(x);
|
|
if (addr >= MODULES_VADDR && addr < MODULES_END)
|
|
return 1;
|
|
#endif
|
|
return is_vmalloc_addr(x);
|
|
}
|
|
EXPORT_SYMBOL_GPL(is_vmalloc_or_module_addr);
|
|
|
|
/*
|
|
* Walk a vmap address to the struct page it maps. Huge vmap mappings will
|
|
* return the tail page that corresponds to the base page address, which
|
|
* matches small vmap mappings.
|
|
*/
|
|
struct page *vmalloc_to_page(const void *vmalloc_addr)
|
|
{
|
|
unsigned long addr = (unsigned long) vmalloc_addr;
|
|
struct page *page = NULL;
|
|
pgd_t *pgd = pgd_offset_k(addr);
|
|
p4d_t *p4d;
|
|
pud_t *pud;
|
|
pmd_t *pmd;
|
|
pte_t *ptep, pte;
|
|
|
|
/*
|
|
* XXX we might need to change this if we add VIRTUAL_BUG_ON for
|
|
* architectures that do not vmalloc module space
|
|
*/
|
|
VIRTUAL_BUG_ON(!is_vmalloc_or_module_addr(vmalloc_addr));
|
|
|
|
if (pgd_none(*pgd))
|
|
return NULL;
|
|
if (WARN_ON_ONCE(pgd_leaf(*pgd)))
|
|
return NULL; /* XXX: no allowance for huge pgd */
|
|
if (WARN_ON_ONCE(pgd_bad(*pgd)))
|
|
return NULL;
|
|
|
|
p4d = p4d_offset(pgd, addr);
|
|
if (p4d_none(*p4d))
|
|
return NULL;
|
|
if (p4d_leaf(*p4d))
|
|
return p4d_page(*p4d) + ((addr & ~P4D_MASK) >> PAGE_SHIFT);
|
|
if (WARN_ON_ONCE(p4d_bad(*p4d)))
|
|
return NULL;
|
|
|
|
pud = pud_offset(p4d, addr);
|
|
if (pud_none(*pud))
|
|
return NULL;
|
|
if (pud_leaf(*pud))
|
|
return pud_page(*pud) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
|
|
if (WARN_ON_ONCE(pud_bad(*pud)))
|
|
return NULL;
|
|
|
|
pmd = pmd_offset(pud, addr);
|
|
if (pmd_none(*pmd))
|
|
return NULL;
|
|
if (pmd_leaf(*pmd))
|
|
return pmd_page(*pmd) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
|
|
if (WARN_ON_ONCE(pmd_bad(*pmd)))
|
|
return NULL;
|
|
|
|
ptep = pte_offset_map(pmd, addr);
|
|
pte = *ptep;
|
|
if (pte_present(pte))
|
|
page = pte_page(pte);
|
|
pte_unmap(ptep);
|
|
|
|
return page;
|
|
}
|
|
EXPORT_SYMBOL(vmalloc_to_page);
|
|
|
|
/*
|
|
* Map a vmalloc()-space virtual address to the physical page frame number.
|
|
*/
|
|
unsigned long vmalloc_to_pfn(const void *vmalloc_addr)
|
|
{
|
|
return page_to_pfn(vmalloc_to_page(vmalloc_addr));
|
|
}
|
|
EXPORT_SYMBOL(vmalloc_to_pfn);
|
|
|
|
|
|
/*** Global kva allocator ***/
|
|
|
|
#define DEBUG_AUGMENT_PROPAGATE_CHECK 0
|
|
#define DEBUG_AUGMENT_LOWEST_MATCH_CHECK 0
|
|
|
|
|
|
static DEFINE_SPINLOCK(vmap_area_lock);
|
|
static DEFINE_SPINLOCK(free_vmap_area_lock);
|
|
/* Export for kexec only */
|
|
LIST_HEAD(vmap_area_list);
|
|
static struct rb_root vmap_area_root = RB_ROOT;
|
|
static bool vmap_initialized __read_mostly;
|
|
|
|
static struct rb_root purge_vmap_area_root = RB_ROOT;
|
|
static LIST_HEAD(purge_vmap_area_list);
|
|
static DEFINE_SPINLOCK(purge_vmap_area_lock);
|
|
|
|
/*
|
|
* This kmem_cache is used for vmap_area objects. Instead of
|
|
* allocating from slab we reuse an object from this cache to
|
|
* make things faster. Especially in "no edge" splitting of
|
|
* free block.
|
|
*/
|
|
static struct kmem_cache *vmap_area_cachep;
|
|
|
|
/*
|
|
* This linked list is used in pair with free_vmap_area_root.
|
|
* It gives O(1) access to prev/next to perform fast coalescing.
|
|
*/
|
|
static LIST_HEAD(free_vmap_area_list);
|
|
|
|
/*
|
|
* This augment red-black tree represents the free vmap space.
|
|
* All vmap_area objects in this tree are sorted by va->va_start
|
|
* address. It is used for allocation and merging when a vmap
|
|
* object is released.
|
|
*
|
|
* Each vmap_area node contains a maximum available free block
|
|
* of its sub-tree, right or left. Therefore it is possible to
|
|
* find a lowest match of free area.
|
|
*/
|
|
static struct rb_root free_vmap_area_root = RB_ROOT;
|
|
|
|
/*
|
|
* Preload a CPU with one object for "no edge" split case. The
|
|
* aim is to get rid of allocations from the atomic context, thus
|
|
* to use more permissive allocation masks.
|
|
*/
|
|
static DEFINE_PER_CPU(struct vmap_area *, ne_fit_preload_node);
|
|
|
|
static __always_inline unsigned long
|
|
va_size(struct vmap_area *va)
|
|
{
|
|
return (va->va_end - va->va_start);
|
|
}
|
|
|
|
static __always_inline unsigned long
|
|
get_subtree_max_size(struct rb_node *node)
|
|
{
|
|
struct vmap_area *va;
|
|
|
|
va = rb_entry_safe(node, struct vmap_area, rb_node);
|
|
return va ? va->subtree_max_size : 0;
|
|
}
|
|
|
|
RB_DECLARE_CALLBACKS_MAX(static, free_vmap_area_rb_augment_cb,
|
|
struct vmap_area, rb_node, unsigned long, subtree_max_size, va_size)
|
|
|
|
static void purge_vmap_area_lazy(void);
|
|
static BLOCKING_NOTIFIER_HEAD(vmap_notify_list);
|
|
static void drain_vmap_area_work(struct work_struct *work);
|
|
static DECLARE_WORK(drain_vmap_work, drain_vmap_area_work);
|
|
|
|
static atomic_long_t nr_vmalloc_pages;
|
|
|
|
unsigned long vmalloc_nr_pages(void)
|
|
{
|
|
return atomic_long_read(&nr_vmalloc_pages);
|
|
}
|
|
|
|
/* Look up the first VA which satisfies addr < va_end, NULL if none. */
|
|
static struct vmap_area *find_vmap_area_exceed_addr(unsigned long addr)
|
|
{
|
|
struct vmap_area *va = NULL;
|
|
struct rb_node *n = vmap_area_root.rb_node;
|
|
|
|
addr = (unsigned long)kasan_reset_tag((void *)addr);
|
|
|
|
while (n) {
|
|
struct vmap_area *tmp;
|
|
|
|
tmp = rb_entry(n, struct vmap_area, rb_node);
|
|
if (tmp->va_end > addr) {
|
|
va = tmp;
|
|
if (tmp->va_start <= addr)
|
|
break;
|
|
|
|
n = n->rb_left;
|
|
} else
|
|
n = n->rb_right;
|
|
}
|
|
|
|
return va;
|
|
}
|
|
|
|
static struct vmap_area *__find_vmap_area(unsigned long addr, struct rb_root *root)
|
|
{
|
|
struct rb_node *n = root->rb_node;
|
|
|
|
addr = (unsigned long)kasan_reset_tag((void *)addr);
|
|
|
|
while (n) {
|
|
struct vmap_area *va;
|
|
|
|
va = rb_entry(n, struct vmap_area, rb_node);
|
|
if (addr < va->va_start)
|
|
n = n->rb_left;
|
|
else if (addr >= va->va_end)
|
|
n = n->rb_right;
|
|
else
|
|
return va;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* This function returns back addresses of parent node
|
|
* and its left or right link for further processing.
|
|
*
|
|
* Otherwise NULL is returned. In that case all further
|
|
* steps regarding inserting of conflicting overlap range
|
|
* have to be declined and actually considered as a bug.
|
|
*/
|
|
static __always_inline struct rb_node **
|
|
find_va_links(struct vmap_area *va,
|
|
struct rb_root *root, struct rb_node *from,
|
|
struct rb_node **parent)
|
|
{
|
|
struct vmap_area *tmp_va;
|
|
struct rb_node **link;
|
|
|
|
if (root) {
|
|
link = &root->rb_node;
|
|
if (unlikely(!*link)) {
|
|
*parent = NULL;
|
|
return link;
|
|
}
|
|
} else {
|
|
link = &from;
|
|
}
|
|
|
|
/*
|
|
* Go to the bottom of the tree. When we hit the last point
|
|
* we end up with parent rb_node and correct direction, i name
|
|
* it link, where the new va->rb_node will be attached to.
|
|
*/
|
|
do {
|
|
tmp_va = rb_entry(*link, struct vmap_area, rb_node);
|
|
|
|
/*
|
|
* During the traversal we also do some sanity check.
|
|
* Trigger the BUG() if there are sides(left/right)
|
|
* or full overlaps.
|
|
*/
|
|
if (va->va_end <= tmp_va->va_start)
|
|
link = &(*link)->rb_left;
|
|
else if (va->va_start >= tmp_va->va_end)
|
|
link = &(*link)->rb_right;
|
|
else {
|
|
WARN(1, "vmalloc bug: 0x%lx-0x%lx overlaps with 0x%lx-0x%lx\n",
|
|
va->va_start, va->va_end, tmp_va->va_start, tmp_va->va_end);
|
|
|
|
return NULL;
|
|
}
|
|
} while (*link);
|
|
|
|
*parent = &tmp_va->rb_node;
|
|
return link;
|
|
}
|
|
|
|
static __always_inline struct list_head *
|
|
get_va_next_sibling(struct rb_node *parent, struct rb_node **link)
|
|
{
|
|
struct list_head *list;
|
|
|
|
if (unlikely(!parent))
|
|
/*
|
|
* The red-black tree where we try to find VA neighbors
|
|
* before merging or inserting is empty, i.e. it means
|
|
* there is no free vmap space. Normally it does not
|
|
* happen but we handle this case anyway.
|
|
*/
|
|
return NULL;
|
|
|
|
list = &rb_entry(parent, struct vmap_area, rb_node)->list;
|
|
return (&parent->rb_right == link ? list->next : list);
|
|
}
|
|
|
|
static __always_inline void
|
|
__link_va(struct vmap_area *va, struct rb_root *root,
|
|
struct rb_node *parent, struct rb_node **link,
|
|
struct list_head *head, bool augment)
|
|
{
|
|
/*
|
|
* VA is still not in the list, but we can
|
|
* identify its future previous list_head node.
|
|
*/
|
|
if (likely(parent)) {
|
|
head = &rb_entry(parent, struct vmap_area, rb_node)->list;
|
|
if (&parent->rb_right != link)
|
|
head = head->prev;
|
|
}
|
|
|
|
/* Insert to the rb-tree */
|
|
rb_link_node(&va->rb_node, parent, link);
|
|
if (augment) {
|
|
/*
|
|
* Some explanation here. Just perform simple insertion
|
|
* to the tree. We do not set va->subtree_max_size to
|
|
* its current size before calling rb_insert_augmented().
|
|
* It is because we populate the tree from the bottom
|
|
* to parent levels when the node _is_ in the tree.
|
|
*
|
|
* Therefore we set subtree_max_size to zero after insertion,
|
|
* to let __augment_tree_propagate_from() puts everything to
|
|
* the correct order later on.
|
|
*/
|
|
rb_insert_augmented(&va->rb_node,
|
|
root, &free_vmap_area_rb_augment_cb);
|
|
va->subtree_max_size = 0;
|
|
} else {
|
|
rb_insert_color(&va->rb_node, root);
|
|
}
|
|
|
|
/* Address-sort this list */
|
|
list_add(&va->list, head);
|
|
}
|
|
|
|
static __always_inline void
|
|
link_va(struct vmap_area *va, struct rb_root *root,
|
|
struct rb_node *parent, struct rb_node **link,
|
|
struct list_head *head)
|
|
{
|
|
__link_va(va, root, parent, link, head, false);
|
|
}
|
|
|
|
static __always_inline void
|
|
link_va_augment(struct vmap_area *va, struct rb_root *root,
|
|
struct rb_node *parent, struct rb_node **link,
|
|
struct list_head *head)
|
|
{
|
|
__link_va(va, root, parent, link, head, true);
|
|
}
|
|
|
|
static __always_inline void
|
|
__unlink_va(struct vmap_area *va, struct rb_root *root, bool augment)
|
|
{
|
|
if (WARN_ON(RB_EMPTY_NODE(&va->rb_node)))
|
|
return;
|
|
|
|
if (augment)
|
|
rb_erase_augmented(&va->rb_node,
|
|
root, &free_vmap_area_rb_augment_cb);
|
|
else
|
|
rb_erase(&va->rb_node, root);
|
|
|
|
list_del_init(&va->list);
|
|
RB_CLEAR_NODE(&va->rb_node);
|
|
}
|
|
|
|
static __always_inline void
|
|
unlink_va(struct vmap_area *va, struct rb_root *root)
|
|
{
|
|
__unlink_va(va, root, false);
|
|
}
|
|
|
|
static __always_inline void
|
|
unlink_va_augment(struct vmap_area *va, struct rb_root *root)
|
|
{
|
|
__unlink_va(va, root, true);
|
|
}
|
|
|
|
#if DEBUG_AUGMENT_PROPAGATE_CHECK
|
|
/*
|
|
* Gets called when remove the node and rotate.
|
|
*/
|
|
static __always_inline unsigned long
|
|
compute_subtree_max_size(struct vmap_area *va)
|
|
{
|
|
return max3(va_size(va),
|
|
get_subtree_max_size(va->rb_node.rb_left),
|
|
get_subtree_max_size(va->rb_node.rb_right));
|
|
}
|
|
|
|
static void
|
|
augment_tree_propagate_check(void)
|
|
{
|
|
struct vmap_area *va;
|
|
unsigned long computed_size;
|
|
|
|
list_for_each_entry(va, &free_vmap_area_list, list) {
|
|
computed_size = compute_subtree_max_size(va);
|
|
if (computed_size != va->subtree_max_size)
|
|
pr_emerg("tree is corrupted: %lu, %lu\n",
|
|
va_size(va), va->subtree_max_size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* This function populates subtree_max_size from bottom to upper
|
|
* levels starting from VA point. The propagation must be done
|
|
* when VA size is modified by changing its va_start/va_end. Or
|
|
* in case of newly inserting of VA to the tree.
|
|
*
|
|
* It means that __augment_tree_propagate_from() must be called:
|
|
* - After VA has been inserted to the tree(free path);
|
|
* - After VA has been shrunk(allocation path);
|
|
* - After VA has been increased(merging path).
|
|
*
|
|
* Please note that, it does not mean that upper parent nodes
|
|
* and their subtree_max_size are recalculated all the time up
|
|
* to the root node.
|
|
*
|
|
* 4--8
|
|
* /\
|
|
* / \
|
|
* / \
|
|
* 2--2 8--8
|
|
*
|
|
* For example if we modify the node 4, shrinking it to 2, then
|
|
* no any modification is required. If we shrink the node 2 to 1
|
|
* its subtree_max_size is updated only, and set to 1. If we shrink
|
|
* the node 8 to 6, then its subtree_max_size is set to 6 and parent
|
|
* node becomes 4--6.
|
|
*/
|
|
static __always_inline void
|
|
augment_tree_propagate_from(struct vmap_area *va)
|
|
{
|
|
/*
|
|
* Populate the tree from bottom towards the root until
|
|
* the calculated maximum available size of checked node
|
|
* is equal to its current one.
|
|
*/
|
|
free_vmap_area_rb_augment_cb_propagate(&va->rb_node, NULL);
|
|
|
|
#if DEBUG_AUGMENT_PROPAGATE_CHECK
|
|
augment_tree_propagate_check();
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
insert_vmap_area(struct vmap_area *va,
|
|
struct rb_root *root, struct list_head *head)
|
|
{
|
|
struct rb_node **link;
|
|
struct rb_node *parent;
|
|
|
|
link = find_va_links(va, root, NULL, &parent);
|
|
if (link)
|
|
link_va(va, root, parent, link, head);
|
|
}
|
|
|
|
static void
|
|
insert_vmap_area_augment(struct vmap_area *va,
|
|
struct rb_node *from, struct rb_root *root,
|
|
struct list_head *head)
|
|
{
|
|
struct rb_node **link;
|
|
struct rb_node *parent;
|
|
|
|
if (from)
|
|
link = find_va_links(va, NULL, from, &parent);
|
|
else
|
|
link = find_va_links(va, root, NULL, &parent);
|
|
|
|
if (link) {
|
|
link_va_augment(va, root, parent, link, head);
|
|
augment_tree_propagate_from(va);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Merge de-allocated chunk of VA memory with previous
|
|
* and next free blocks. If coalesce is not done a new
|
|
* free area is inserted. If VA has been merged, it is
|
|
* freed.
|
|
*
|
|
* Please note, it can return NULL in case of overlap
|
|
* ranges, followed by WARN() report. Despite it is a
|
|
* buggy behaviour, a system can be alive and keep
|
|
* ongoing.
|
|
*/
|
|
static __always_inline struct vmap_area *
|
|
__merge_or_add_vmap_area(struct vmap_area *va,
|
|
struct rb_root *root, struct list_head *head, bool augment)
|
|
{
|
|
struct vmap_area *sibling;
|
|
struct list_head *next;
|
|
struct rb_node **link;
|
|
struct rb_node *parent;
|
|
bool merged = false;
|
|
|
|
/*
|
|
* Find a place in the tree where VA potentially will be
|
|
* inserted, unless it is merged with its sibling/siblings.
|
|
*/
|
|
link = find_va_links(va, root, NULL, &parent);
|
|
if (!link)
|
|
return NULL;
|
|
|
|
/*
|
|
* Get next node of VA to check if merging can be done.
|
|
*/
|
|
next = get_va_next_sibling(parent, link);
|
|
if (unlikely(next == NULL))
|
|
goto insert;
|
|
|
|
/*
|
|
* start end
|
|
* | |
|
|
* |<------VA------>|<-----Next----->|
|
|
* | |
|
|
* start end
|
|
*/
|
|
if (next != head) {
|
|
sibling = list_entry(next, struct vmap_area, list);
|
|
if (sibling->va_start == va->va_end) {
|
|
sibling->va_start = va->va_start;
|
|
|
|
/* Free vmap_area object. */
|
|
kmem_cache_free(vmap_area_cachep, va);
|
|
|
|
/* Point to the new merged area. */
|
|
va = sibling;
|
|
merged = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* start end
|
|
* | |
|
|
* |<-----Prev----->|<------VA------>|
|
|
* | |
|
|
* start end
|
|
*/
|
|
if (next->prev != head) {
|
|
sibling = list_entry(next->prev, struct vmap_area, list);
|
|
if (sibling->va_end == va->va_start) {
|
|
/*
|
|
* If both neighbors are coalesced, it is important
|
|
* to unlink the "next" node first, followed by merging
|
|
* with "previous" one. Otherwise the tree might not be
|
|
* fully populated if a sibling's augmented value is
|
|
* "normalized" because of rotation operations.
|
|
*/
|
|
if (merged)
|
|
__unlink_va(va, root, augment);
|
|
|
|
sibling->va_end = va->va_end;
|
|
|
|
/* Free vmap_area object. */
|
|
kmem_cache_free(vmap_area_cachep, va);
|
|
|
|
/* Point to the new merged area. */
|
|
va = sibling;
|
|
merged = true;
|
|
}
|
|
}
|
|
|
|
insert:
|
|
if (!merged)
|
|
__link_va(va, root, parent, link, head, augment);
|
|
|
|
return va;
|
|
}
|
|
|
|
static __always_inline struct vmap_area *
|
|
merge_or_add_vmap_area(struct vmap_area *va,
|
|
struct rb_root *root, struct list_head *head)
|
|
{
|
|
return __merge_or_add_vmap_area(va, root, head, false);
|
|
}
|
|
|
|
static __always_inline struct vmap_area *
|
|
merge_or_add_vmap_area_augment(struct vmap_area *va,
|
|
struct rb_root *root, struct list_head *head)
|
|
{
|
|
va = __merge_or_add_vmap_area(va, root, head, true);
|
|
if (va)
|
|
augment_tree_propagate_from(va);
|
|
|
|
return va;
|
|
}
|
|
|
|
static __always_inline bool
|
|
is_within_this_va(struct vmap_area *va, unsigned long size,
|
|
unsigned long align, unsigned long vstart)
|
|
{
|
|
unsigned long nva_start_addr;
|
|
|
|
if (va->va_start > vstart)
|
|
nva_start_addr = ALIGN(va->va_start, align);
|
|
else
|
|
nva_start_addr = ALIGN(vstart, align);
|
|
|
|
/* Can be overflowed due to big size or alignment. */
|
|
if (nva_start_addr + size < nva_start_addr ||
|
|
nva_start_addr < vstart)
|
|
return false;
|
|
|
|
return (nva_start_addr + size <= va->va_end);
|
|
}
|
|
|
|
/*
|
|
* Find the first free block(lowest start address) in the tree,
|
|
* that will accomplish the request corresponding to passing
|
|
* parameters. Please note, with an alignment bigger than PAGE_SIZE,
|
|
* a search length is adjusted to account for worst case alignment
|
|
* overhead.
|
|
*/
|
|
static __always_inline struct vmap_area *
|
|
find_vmap_lowest_match(struct rb_root *root, unsigned long size,
|
|
unsigned long align, unsigned long vstart, bool adjust_search_size)
|
|
{
|
|
struct vmap_area *va;
|
|
struct rb_node *node;
|
|
unsigned long length;
|
|
|
|
/* Start from the root. */
|
|
node = root->rb_node;
|
|
|
|
/* Adjust the search size for alignment overhead. */
|
|
length = adjust_search_size ? size + align - 1 : size;
|
|
|
|
while (node) {
|
|
va = rb_entry(node, struct vmap_area, rb_node);
|
|
|
|
if (get_subtree_max_size(node->rb_left) >= length &&
|
|
vstart < va->va_start) {
|
|
node = node->rb_left;
|
|
} else {
|
|
if (is_within_this_va(va, size, align, vstart))
|
|
return va;
|
|
|
|
/*
|
|
* Does not make sense to go deeper towards the right
|
|
* sub-tree if it does not have a free block that is
|
|
* equal or bigger to the requested search length.
|
|
*/
|
|
if (get_subtree_max_size(node->rb_right) >= length) {
|
|
node = node->rb_right;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* OK. We roll back and find the first right sub-tree,
|
|
* that will satisfy the search criteria. It can happen
|
|
* due to "vstart" restriction or an alignment overhead
|
|
* that is bigger then PAGE_SIZE.
|
|
*/
|
|
while ((node = rb_parent(node))) {
|
|
va = rb_entry(node, struct vmap_area, rb_node);
|
|
if (is_within_this_va(va, size, align, vstart))
|
|
return va;
|
|
|
|
if (get_subtree_max_size(node->rb_right) >= length &&
|
|
vstart <= va->va_start) {
|
|
/*
|
|
* Shift the vstart forward. Please note, we update it with
|
|
* parent's start address adding "1" because we do not want
|
|
* to enter same sub-tree after it has already been checked
|
|
* and no suitable free block found there.
|
|
*/
|
|
vstart = va->va_start + 1;
|
|
node = node->rb_right;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if DEBUG_AUGMENT_LOWEST_MATCH_CHECK
|
|
#include <linux/random.h>
|
|
|
|
static struct vmap_area *
|
|
find_vmap_lowest_linear_match(struct list_head *head, unsigned long size,
|
|
unsigned long align, unsigned long vstart)
|
|
{
|
|
struct vmap_area *va;
|
|
|
|
list_for_each_entry(va, head, list) {
|
|
if (!is_within_this_va(va, size, align, vstart))
|
|
continue;
|
|
|
|
return va;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
find_vmap_lowest_match_check(struct rb_root *root, struct list_head *head,
|
|
unsigned long size, unsigned long align)
|
|
{
|
|
struct vmap_area *va_1, *va_2;
|
|
unsigned long vstart;
|
|
unsigned int rnd;
|
|
|
|
get_random_bytes(&rnd, sizeof(rnd));
|
|
vstart = VMALLOC_START + rnd;
|
|
|
|
va_1 = find_vmap_lowest_match(root, size, align, vstart, false);
|
|
va_2 = find_vmap_lowest_linear_match(head, size, align, vstart);
|
|
|
|
if (va_1 != va_2)
|
|
pr_emerg("not lowest: t: 0x%p, l: 0x%p, v: 0x%lx\n",
|
|
va_1, va_2, vstart);
|
|
}
|
|
#endif
|
|
|
|
enum fit_type {
|
|
NOTHING_FIT = 0,
|
|
FL_FIT_TYPE = 1, /* full fit */
|
|
LE_FIT_TYPE = 2, /* left edge fit */
|
|
RE_FIT_TYPE = 3, /* right edge fit */
|
|
NE_FIT_TYPE = 4 /* no edge fit */
|
|
};
|
|
|
|
static __always_inline enum fit_type
|
|
classify_va_fit_type(struct vmap_area *va,
|
|
unsigned long nva_start_addr, unsigned long size)
|
|
{
|
|
enum fit_type type;
|
|
|
|
/* Check if it is within VA. */
|
|
if (nva_start_addr < va->va_start ||
|
|
nva_start_addr + size > va->va_end)
|
|
return NOTHING_FIT;
|
|
|
|
/* Now classify. */
|
|
if (va->va_start == nva_start_addr) {
|
|
if (va->va_end == nva_start_addr + size)
|
|
type = FL_FIT_TYPE;
|
|
else
|
|
type = LE_FIT_TYPE;
|
|
} else if (va->va_end == nva_start_addr + size) {
|
|
type = RE_FIT_TYPE;
|
|
} else {
|
|
type = NE_FIT_TYPE;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
static __always_inline int
|
|
adjust_va_to_fit_type(struct rb_root *root, struct list_head *head,
|
|
struct vmap_area *va, unsigned long nva_start_addr,
|
|
unsigned long size)
|
|
{
|
|
struct vmap_area *lva = NULL;
|
|
enum fit_type type = classify_va_fit_type(va, nva_start_addr, size);
|
|
|
|
if (type == FL_FIT_TYPE) {
|
|
/*
|
|
* No need to split VA, it fully fits.
|
|
*
|
|
* | |
|
|
* V NVA V
|
|
* |---------------|
|
|
*/
|
|
unlink_va_augment(va, root);
|
|
kmem_cache_free(vmap_area_cachep, va);
|
|
} else if (type == LE_FIT_TYPE) {
|
|
/*
|
|
* Split left edge of fit VA.
|
|
*
|
|
* | |
|
|
* V NVA V R
|
|
* |-------|-------|
|
|
*/
|
|
va->va_start += size;
|
|
} else if (type == RE_FIT_TYPE) {
|
|
/*
|
|
* Split right edge of fit VA.
|
|
*
|
|
* | |
|
|
* L V NVA V
|
|
* |-------|-------|
|
|
*/
|
|
va->va_end = nva_start_addr;
|
|
} else if (type == NE_FIT_TYPE) {
|
|
/*
|
|
* Split no edge of fit VA.
|
|
*
|
|
* | |
|
|
* L V NVA V R
|
|
* |---|-------|---|
|
|
*/
|
|
lva = __this_cpu_xchg(ne_fit_preload_node, NULL);
|
|
if (unlikely(!lva)) {
|
|
/*
|
|
* For percpu allocator we do not do any pre-allocation
|
|
* and leave it as it is. The reason is it most likely
|
|
* never ends up with NE_FIT_TYPE splitting. In case of
|
|
* percpu allocations offsets and sizes are aligned to
|
|
* fixed align request, i.e. RE_FIT_TYPE and FL_FIT_TYPE
|
|
* are its main fitting cases.
|
|
*
|
|
* There are a few exceptions though, as an example it is
|
|
* a first allocation (early boot up) when we have "one"
|
|
* big free space that has to be split.
|
|
*
|
|
* Also we can hit this path in case of regular "vmap"
|
|
* allocations, if "this" current CPU was not preloaded.
|
|
* See the comment in alloc_vmap_area() why. If so, then
|
|
* GFP_NOWAIT is used instead to get an extra object for
|
|
* split purpose. That is rare and most time does not
|
|
* occur.
|
|
*
|
|
* What happens if an allocation gets failed. Basically,
|
|
* an "overflow" path is triggered to purge lazily freed
|
|
* areas to free some memory, then, the "retry" path is
|
|
* triggered to repeat one more time. See more details
|
|
* in alloc_vmap_area() function.
|
|
*/
|
|
lva = kmem_cache_alloc(vmap_area_cachep, GFP_NOWAIT);
|
|
if (!lva)
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Build the remainder.
|
|
*/
|
|
lva->va_start = va->va_start;
|
|
lva->va_end = nva_start_addr;
|
|
|
|
/*
|
|
* Shrink this VA to remaining size.
|
|
*/
|
|
va->va_start = nva_start_addr + size;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
if (type != FL_FIT_TYPE) {
|
|
augment_tree_propagate_from(va);
|
|
|
|
if (lva) /* type == NE_FIT_TYPE */
|
|
insert_vmap_area_augment(lva, &va->rb_node, root, head);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Returns a start address of the newly allocated area, if success.
|
|
* Otherwise a vend is returned that indicates failure.
|
|
*/
|
|
static __always_inline unsigned long
|
|
__alloc_vmap_area(struct rb_root *root, struct list_head *head,
|
|
unsigned long size, unsigned long align,
|
|
unsigned long vstart, unsigned long vend)
|
|
{
|
|
bool adjust_search_size = true;
|
|
unsigned long nva_start_addr;
|
|
struct vmap_area *va;
|
|
int ret;
|
|
|
|
/*
|
|
* Do not adjust when:
|
|
* a) align <= PAGE_SIZE, because it does not make any sense.
|
|
* All blocks(their start addresses) are at least PAGE_SIZE
|
|
* aligned anyway;
|
|
* b) a short range where a requested size corresponds to exactly
|
|
* specified [vstart:vend] interval and an alignment > PAGE_SIZE.
|
|
* With adjusted search length an allocation would not succeed.
|
|
*/
|
|
if (align <= PAGE_SIZE || (align > PAGE_SIZE && (vend - vstart) == size))
|
|
adjust_search_size = false;
|
|
|
|
va = find_vmap_lowest_match(root, size, align, vstart, adjust_search_size);
|
|
if (unlikely(!va))
|
|
return vend;
|
|
|
|
if (va->va_start > vstart)
|
|
nva_start_addr = ALIGN(va->va_start, align);
|
|
else
|
|
nva_start_addr = ALIGN(vstart, align);
|
|
|
|
/* Check the "vend" restriction. */
|
|
if (nva_start_addr + size > vend)
|
|
return vend;
|
|
|
|
/* Update the free vmap_area. */
|
|
ret = adjust_va_to_fit_type(root, head, va, nva_start_addr, size);
|
|
if (WARN_ON_ONCE(ret))
|
|
return vend;
|
|
|
|
#if DEBUG_AUGMENT_LOWEST_MATCH_CHECK
|
|
find_vmap_lowest_match_check(root, head, size, align);
|
|
#endif
|
|
|
|
return nva_start_addr;
|
|
}
|
|
|
|
/*
|
|
* Free a region of KVA allocated by alloc_vmap_area
|
|
*/
|
|
static void free_vmap_area(struct vmap_area *va)
|
|
{
|
|
/*
|
|
* Remove from the busy tree/list.
|
|
*/
|
|
spin_lock(&vmap_area_lock);
|
|
unlink_va(va, &vmap_area_root);
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
/*
|
|
* Insert/Merge it back to the free tree/list.
|
|
*/
|
|
spin_lock(&free_vmap_area_lock);
|
|
merge_or_add_vmap_area_augment(va, &free_vmap_area_root, &free_vmap_area_list);
|
|
spin_unlock(&free_vmap_area_lock);
|
|
}
|
|
|
|
static inline void
|
|
preload_this_cpu_lock(spinlock_t *lock, gfp_t gfp_mask, int node)
|
|
{
|
|
struct vmap_area *va = NULL;
|
|
|
|
/*
|
|
* Preload this CPU with one extra vmap_area object. It is used
|
|
* when fit type of free area is NE_FIT_TYPE. It guarantees that
|
|
* a CPU that does an allocation is preloaded.
|
|
*
|
|
* We do it in non-atomic context, thus it allows us to use more
|
|
* permissive allocation masks to be more stable under low memory
|
|
* condition and high memory pressure.
|
|
*/
|
|
if (!this_cpu_read(ne_fit_preload_node))
|
|
va = kmem_cache_alloc_node(vmap_area_cachep, gfp_mask, node);
|
|
|
|
spin_lock(lock);
|
|
|
|
if (va && __this_cpu_cmpxchg(ne_fit_preload_node, NULL, va))
|
|
kmem_cache_free(vmap_area_cachep, va);
|
|
}
|
|
|
|
/*
|
|
* Allocate a region of KVA of the specified size and alignment, within the
|
|
* vstart and vend.
|
|
*/
|
|
static struct vmap_area *alloc_vmap_area(unsigned long size,
|
|
unsigned long align,
|
|
unsigned long vstart, unsigned long vend,
|
|
int node, gfp_t gfp_mask,
|
|
unsigned long va_flags)
|
|
{
|
|
struct vmap_area *va;
|
|
unsigned long freed;
|
|
unsigned long addr;
|
|
int purged = 0;
|
|
int ret;
|
|
|
|
if (unlikely(!size || offset_in_page(size) || !is_power_of_2(align)))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
if (unlikely(!vmap_initialized))
|
|
return ERR_PTR(-EBUSY);
|
|
|
|
might_sleep();
|
|
gfp_mask = gfp_mask & GFP_RECLAIM_MASK;
|
|
|
|
va = kmem_cache_alloc_node(vmap_area_cachep, gfp_mask, node);
|
|
if (unlikely(!va))
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
/*
|
|
* Only scan the relevant parts containing pointers to other objects
|
|
* to avoid false negatives.
|
|
*/
|
|
kmemleak_scan_area(&va->rb_node, SIZE_MAX, gfp_mask);
|
|
|
|
retry:
|
|
preload_this_cpu_lock(&free_vmap_area_lock, gfp_mask, node);
|
|
addr = __alloc_vmap_area(&free_vmap_area_root, &free_vmap_area_list,
|
|
size, align, vstart, vend);
|
|
spin_unlock(&free_vmap_area_lock);
|
|
|
|
trace_alloc_vmap_area(addr, size, align, vstart, vend, addr == vend);
|
|
|
|
/*
|
|
* If an allocation fails, the "vend" address is
|
|
* returned. Therefore trigger the overflow path.
|
|
*/
|
|
if (unlikely(addr == vend))
|
|
goto overflow;
|
|
|
|
va->va_start = addr;
|
|
va->va_end = addr + size;
|
|
va->vm = NULL;
|
|
va->flags = va_flags;
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
insert_vmap_area(va, &vmap_area_root, &vmap_area_list);
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
BUG_ON(!IS_ALIGNED(va->va_start, align));
|
|
BUG_ON(va->va_start < vstart);
|
|
BUG_ON(va->va_end > vend);
|
|
|
|
ret = kasan_populate_vmalloc(addr, size);
|
|
if (ret) {
|
|
free_vmap_area(va);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
return va;
|
|
|
|
overflow:
|
|
if (!purged) {
|
|
purge_vmap_area_lazy();
|
|
purged = 1;
|
|
goto retry;
|
|
}
|
|
|
|
freed = 0;
|
|
blocking_notifier_call_chain(&vmap_notify_list, 0, &freed);
|
|
|
|
if (freed > 0) {
|
|
purged = 0;
|
|
goto retry;
|
|
}
|
|
|
|
if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit())
|
|
pr_warn("vmap allocation for size %lu failed: use vmalloc=<size> to increase size\n",
|
|
size);
|
|
|
|
kmem_cache_free(vmap_area_cachep, va);
|
|
return ERR_PTR(-EBUSY);
|
|
}
|
|
|
|
int register_vmap_purge_notifier(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_register(&vmap_notify_list, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(register_vmap_purge_notifier);
|
|
|
|
int unregister_vmap_purge_notifier(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_unregister(&vmap_notify_list, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(unregister_vmap_purge_notifier);
|
|
|
|
/*
|
|
* lazy_max_pages is the maximum amount of virtual address space we gather up
|
|
* before attempting to purge with a TLB flush.
|
|
*
|
|
* There is a tradeoff here: a larger number will cover more kernel page tables
|
|
* and take slightly longer to purge, but it will linearly reduce the number of
|
|
* global TLB flushes that must be performed. It would seem natural to scale
|
|
* this number up linearly with the number of CPUs (because vmapping activity
|
|
* could also scale linearly with the number of CPUs), however it is likely
|
|
* that in practice, workloads might be constrained in other ways that mean
|
|
* vmap activity will not scale linearly with CPUs. Also, I want to be
|
|
* conservative and not introduce a big latency on huge systems, so go with
|
|
* a less aggressive log scale. It will still be an improvement over the old
|
|
* code, and it will be simple to change the scale factor if we find that it
|
|
* becomes a problem on bigger systems.
|
|
*/
|
|
static unsigned long lazy_max_pages(void)
|
|
{
|
|
unsigned int log;
|
|
|
|
log = fls(num_online_cpus());
|
|
|
|
return log * (32UL * 1024 * 1024 / PAGE_SIZE);
|
|
}
|
|
|
|
static atomic_long_t vmap_lazy_nr = ATOMIC_LONG_INIT(0);
|
|
|
|
/*
|
|
* Serialize vmap purging. There is no actual critical section protected
|
|
* by this lock, but we want to avoid concurrent calls for performance
|
|
* reasons and to make the pcpu_get_vm_areas more deterministic.
|
|
*/
|
|
static DEFINE_MUTEX(vmap_purge_lock);
|
|
|
|
/* for per-CPU blocks */
|
|
static void purge_fragmented_blocks_allcpus(void);
|
|
|
|
/*
|
|
* Purges all lazily-freed vmap areas.
|
|
*/
|
|
static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end)
|
|
{
|
|
unsigned long resched_threshold;
|
|
unsigned int num_purged_areas = 0;
|
|
struct list_head local_purge_list;
|
|
struct vmap_area *va, *n_va;
|
|
|
|
lockdep_assert_held(&vmap_purge_lock);
|
|
|
|
spin_lock(&purge_vmap_area_lock);
|
|
purge_vmap_area_root = RB_ROOT;
|
|
list_replace_init(&purge_vmap_area_list, &local_purge_list);
|
|
spin_unlock(&purge_vmap_area_lock);
|
|
|
|
if (unlikely(list_empty(&local_purge_list)))
|
|
goto out;
|
|
|
|
start = min(start,
|
|
list_first_entry(&local_purge_list,
|
|
struct vmap_area, list)->va_start);
|
|
|
|
end = max(end,
|
|
list_last_entry(&local_purge_list,
|
|
struct vmap_area, list)->va_end);
|
|
|
|
flush_tlb_kernel_range(start, end);
|
|
resched_threshold = lazy_max_pages() << 1;
|
|
|
|
spin_lock(&free_vmap_area_lock);
|
|
list_for_each_entry_safe(va, n_va, &local_purge_list, list) {
|
|
unsigned long nr = (va->va_end - va->va_start) >> PAGE_SHIFT;
|
|
unsigned long orig_start = va->va_start;
|
|
unsigned long orig_end = va->va_end;
|
|
|
|
/*
|
|
* Finally insert or merge lazily-freed area. It is
|
|
* detached and there is no need to "unlink" it from
|
|
* anything.
|
|
*/
|
|
va = merge_or_add_vmap_area_augment(va, &free_vmap_area_root,
|
|
&free_vmap_area_list);
|
|
|
|
if (!va)
|
|
continue;
|
|
|
|
if (is_vmalloc_or_module_addr((void *)orig_start))
|
|
kasan_release_vmalloc(orig_start, orig_end,
|
|
va->va_start, va->va_end);
|
|
|
|
atomic_long_sub(nr, &vmap_lazy_nr);
|
|
num_purged_areas++;
|
|
|
|
if (atomic_long_read(&vmap_lazy_nr) < resched_threshold)
|
|
cond_resched_lock(&free_vmap_area_lock);
|
|
}
|
|
spin_unlock(&free_vmap_area_lock);
|
|
|
|
out:
|
|
trace_purge_vmap_area_lazy(start, end, num_purged_areas);
|
|
return num_purged_areas > 0;
|
|
}
|
|
|
|
/*
|
|
* Kick off a purge of the outstanding lazy areas.
|
|
*/
|
|
static void purge_vmap_area_lazy(void)
|
|
{
|
|
mutex_lock(&vmap_purge_lock);
|
|
purge_fragmented_blocks_allcpus();
|
|
__purge_vmap_area_lazy(ULONG_MAX, 0);
|
|
mutex_unlock(&vmap_purge_lock);
|
|
}
|
|
|
|
static void drain_vmap_area_work(struct work_struct *work)
|
|
{
|
|
unsigned long nr_lazy;
|
|
|
|
do {
|
|
mutex_lock(&vmap_purge_lock);
|
|
__purge_vmap_area_lazy(ULONG_MAX, 0);
|
|
mutex_unlock(&vmap_purge_lock);
|
|
|
|
/* Recheck if further work is required. */
|
|
nr_lazy = atomic_long_read(&vmap_lazy_nr);
|
|
} while (nr_lazy > lazy_max_pages());
|
|
}
|
|
|
|
/*
|
|
* Free a vmap area, caller ensuring that the area has been unmapped,
|
|
* unlinked and flush_cache_vunmap had been called for the correct
|
|
* range previously.
|
|
*/
|
|
static void free_vmap_area_noflush(struct vmap_area *va)
|
|
{
|
|
unsigned long nr_lazy_max = lazy_max_pages();
|
|
unsigned long va_start = va->va_start;
|
|
unsigned long nr_lazy;
|
|
|
|
if (WARN_ON_ONCE(!list_empty(&va->list)))
|
|
return;
|
|
|
|
nr_lazy = atomic_long_add_return((va->va_end - va->va_start) >>
|
|
PAGE_SHIFT, &vmap_lazy_nr);
|
|
|
|
/*
|
|
* Merge or place it to the purge tree/list.
|
|
*/
|
|
spin_lock(&purge_vmap_area_lock);
|
|
merge_or_add_vmap_area(va,
|
|
&purge_vmap_area_root, &purge_vmap_area_list);
|
|
spin_unlock(&purge_vmap_area_lock);
|
|
|
|
trace_free_vmap_area_noflush(va_start, nr_lazy, nr_lazy_max);
|
|
|
|
/* After this point, we may free va at any time */
|
|
if (unlikely(nr_lazy > nr_lazy_max))
|
|
schedule_work(&drain_vmap_work);
|
|
}
|
|
|
|
/*
|
|
* Free and unmap a vmap area
|
|
*/
|
|
static void free_unmap_vmap_area(struct vmap_area *va)
|
|
{
|
|
flush_cache_vunmap(va->va_start, va->va_end);
|
|
vunmap_range_noflush(va->va_start, va->va_end);
|
|
if (debug_pagealloc_enabled_static())
|
|
flush_tlb_kernel_range(va->va_start, va->va_end);
|
|
|
|
free_vmap_area_noflush(va);
|
|
}
|
|
|
|
struct vmap_area *find_vmap_area(unsigned long addr)
|
|
{
|
|
struct vmap_area *va;
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
va = __find_vmap_area(addr, &vmap_area_root);
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
return va;
|
|
}
|
|
|
|
static struct vmap_area *find_unlink_vmap_area(unsigned long addr)
|
|
{
|
|
struct vmap_area *va;
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
va = __find_vmap_area(addr, &vmap_area_root);
|
|
if (va)
|
|
unlink_va(va, &vmap_area_root);
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
return va;
|
|
}
|
|
|
|
/*** Per cpu kva allocator ***/
|
|
|
|
/*
|
|
* vmap space is limited especially on 32 bit architectures. Ensure there is
|
|
* room for at least 16 percpu vmap blocks per CPU.
|
|
*/
|
|
/*
|
|
* If we had a constant VMALLOC_START and VMALLOC_END, we'd like to be able
|
|
* to #define VMALLOC_SPACE (VMALLOC_END-VMALLOC_START). Guess
|
|
* instead (we just need a rough idea)
|
|
*/
|
|
#if BITS_PER_LONG == 32
|
|
#define VMALLOC_SPACE (128UL*1024*1024)
|
|
#else
|
|
#define VMALLOC_SPACE (128UL*1024*1024*1024)
|
|
#endif
|
|
|
|
#define VMALLOC_PAGES (VMALLOC_SPACE / PAGE_SIZE)
|
|
#define VMAP_MAX_ALLOC BITS_PER_LONG /* 256K with 4K pages */
|
|
#define VMAP_BBMAP_BITS_MAX 1024 /* 4MB with 4K pages */
|
|
#define VMAP_BBMAP_BITS_MIN (VMAP_MAX_ALLOC*2)
|
|
#define VMAP_MIN(x, y) ((x) < (y) ? (x) : (y)) /* can't use min() */
|
|
#define VMAP_MAX(x, y) ((x) > (y) ? (x) : (y)) /* can't use max() */
|
|
#define VMAP_BBMAP_BITS \
|
|
VMAP_MIN(VMAP_BBMAP_BITS_MAX, \
|
|
VMAP_MAX(VMAP_BBMAP_BITS_MIN, \
|
|
VMALLOC_PAGES / roundup_pow_of_two(NR_CPUS) / 16))
|
|
|
|
#define VMAP_BLOCK_SIZE (VMAP_BBMAP_BITS * PAGE_SIZE)
|
|
|
|
#define VMAP_RAM 0x1 /* indicates vm_map_ram area*/
|
|
#define VMAP_BLOCK 0x2 /* mark out the vmap_block sub-type*/
|
|
#define VMAP_FLAGS_MASK 0x3
|
|
|
|
struct vmap_block_queue {
|
|
spinlock_t lock;
|
|
struct list_head free;
|
|
};
|
|
|
|
struct vmap_block {
|
|
spinlock_t lock;
|
|
struct vmap_area *va;
|
|
unsigned long free, dirty;
|
|
DECLARE_BITMAP(used_map, VMAP_BBMAP_BITS);
|
|
unsigned long dirty_min, dirty_max; /*< dirty range */
|
|
struct list_head free_list;
|
|
struct rcu_head rcu_head;
|
|
struct list_head purge;
|
|
};
|
|
|
|
/* Queue of free and dirty vmap blocks, for allocation and flushing purposes */
|
|
static DEFINE_PER_CPU(struct vmap_block_queue, vmap_block_queue);
|
|
|
|
/*
|
|
* XArray of vmap blocks, indexed by address, to quickly find a vmap block
|
|
* in the free path. Could get rid of this if we change the API to return a
|
|
* "cookie" from alloc, to be passed to free. But no big deal yet.
|
|
*/
|
|
static DEFINE_XARRAY(vmap_blocks);
|
|
|
|
/*
|
|
* We should probably have a fallback mechanism to allocate virtual memory
|
|
* out of partially filled vmap blocks. However vmap block sizing should be
|
|
* fairly reasonable according to the vmalloc size, so it shouldn't be a
|
|
* big problem.
|
|
*/
|
|
|
|
static unsigned long addr_to_vb_idx(unsigned long addr)
|
|
{
|
|
addr -= VMALLOC_START & ~(VMAP_BLOCK_SIZE-1);
|
|
addr /= VMAP_BLOCK_SIZE;
|
|
return addr;
|
|
}
|
|
|
|
static void *vmap_block_vaddr(unsigned long va_start, unsigned long pages_off)
|
|
{
|
|
unsigned long addr;
|
|
|
|
addr = va_start + (pages_off << PAGE_SHIFT);
|
|
BUG_ON(addr_to_vb_idx(addr) != addr_to_vb_idx(va_start));
|
|
return (void *)addr;
|
|
}
|
|
|
|
/**
|
|
* new_vmap_block - allocates new vmap_block and occupies 2^order pages in this
|
|
* block. Of course pages number can't exceed VMAP_BBMAP_BITS
|
|
* @order: how many 2^order pages should be occupied in newly allocated block
|
|
* @gfp_mask: flags for the page level allocator
|
|
*
|
|
* Return: virtual address in a newly allocated block or ERR_PTR(-errno)
|
|
*/
|
|
static void *new_vmap_block(unsigned int order, gfp_t gfp_mask)
|
|
{
|
|
struct vmap_block_queue *vbq;
|
|
struct vmap_block *vb;
|
|
struct vmap_area *va;
|
|
unsigned long vb_idx;
|
|
int node, err;
|
|
void *vaddr;
|
|
|
|
node = numa_node_id();
|
|
|
|
vb = kmalloc_node(sizeof(struct vmap_block),
|
|
gfp_mask & GFP_RECLAIM_MASK, node);
|
|
if (unlikely(!vb))
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
va = alloc_vmap_area(VMAP_BLOCK_SIZE, VMAP_BLOCK_SIZE,
|
|
VMALLOC_START, VMALLOC_END,
|
|
node, gfp_mask,
|
|
VMAP_RAM|VMAP_BLOCK);
|
|
if (IS_ERR(va)) {
|
|
kfree(vb);
|
|
return ERR_CAST(va);
|
|
}
|
|
|
|
vaddr = vmap_block_vaddr(va->va_start, 0);
|
|
spin_lock_init(&vb->lock);
|
|
vb->va = va;
|
|
/* At least something should be left free */
|
|
BUG_ON(VMAP_BBMAP_BITS <= (1UL << order));
|
|
bitmap_zero(vb->used_map, VMAP_BBMAP_BITS);
|
|
vb->free = VMAP_BBMAP_BITS - (1UL << order);
|
|
vb->dirty = 0;
|
|
vb->dirty_min = VMAP_BBMAP_BITS;
|
|
vb->dirty_max = 0;
|
|
bitmap_set(vb->used_map, 0, (1UL << order));
|
|
INIT_LIST_HEAD(&vb->free_list);
|
|
|
|
vb_idx = addr_to_vb_idx(va->va_start);
|
|
err = xa_insert(&vmap_blocks, vb_idx, vb, gfp_mask);
|
|
if (err) {
|
|
kfree(vb);
|
|
free_vmap_area(va);
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
vbq = raw_cpu_ptr(&vmap_block_queue);
|
|
spin_lock(&vbq->lock);
|
|
list_add_tail_rcu(&vb->free_list, &vbq->free);
|
|
spin_unlock(&vbq->lock);
|
|
|
|
return vaddr;
|
|
}
|
|
|
|
static void free_vmap_block(struct vmap_block *vb)
|
|
{
|
|
struct vmap_block *tmp;
|
|
|
|
tmp = xa_erase(&vmap_blocks, addr_to_vb_idx(vb->va->va_start));
|
|
BUG_ON(tmp != vb);
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
unlink_va(vb->va, &vmap_area_root);
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
free_vmap_area_noflush(vb->va);
|
|
kfree_rcu(vb, rcu_head);
|
|
}
|
|
|
|
static void purge_fragmented_blocks(int cpu)
|
|
{
|
|
LIST_HEAD(purge);
|
|
struct vmap_block *vb;
|
|
struct vmap_block *n_vb;
|
|
struct vmap_block_queue *vbq = &per_cpu(vmap_block_queue, cpu);
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(vb, &vbq->free, free_list) {
|
|
|
|
if (!(vb->free + vb->dirty == VMAP_BBMAP_BITS && vb->dirty != VMAP_BBMAP_BITS))
|
|
continue;
|
|
|
|
spin_lock(&vb->lock);
|
|
if (vb->free + vb->dirty == VMAP_BBMAP_BITS && vb->dirty != VMAP_BBMAP_BITS) {
|
|
vb->free = 0; /* prevent further allocs after releasing lock */
|
|
vb->dirty = VMAP_BBMAP_BITS; /* prevent purging it again */
|
|
vb->dirty_min = 0;
|
|
vb->dirty_max = VMAP_BBMAP_BITS;
|
|
spin_lock(&vbq->lock);
|
|
list_del_rcu(&vb->free_list);
|
|
spin_unlock(&vbq->lock);
|
|
spin_unlock(&vb->lock);
|
|
list_add_tail(&vb->purge, &purge);
|
|
} else
|
|
spin_unlock(&vb->lock);
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
list_for_each_entry_safe(vb, n_vb, &purge, purge) {
|
|
list_del(&vb->purge);
|
|
free_vmap_block(vb);
|
|
}
|
|
}
|
|
|
|
static void purge_fragmented_blocks_allcpus(void)
|
|
{
|
|
int cpu;
|
|
|
|
for_each_possible_cpu(cpu)
|
|
purge_fragmented_blocks(cpu);
|
|
}
|
|
|
|
static void *vb_alloc(unsigned long size, gfp_t gfp_mask)
|
|
{
|
|
struct vmap_block_queue *vbq;
|
|
struct vmap_block *vb;
|
|
void *vaddr = NULL;
|
|
unsigned int order;
|
|
|
|
BUG_ON(offset_in_page(size));
|
|
BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC);
|
|
if (WARN_ON(size == 0)) {
|
|
/*
|
|
* Allocating 0 bytes isn't what caller wants since
|
|
* get_order(0) returns funny result. Just warn and terminate
|
|
* early.
|
|
*/
|
|
return NULL;
|
|
}
|
|
order = get_order(size);
|
|
|
|
rcu_read_lock();
|
|
vbq = raw_cpu_ptr(&vmap_block_queue);
|
|
list_for_each_entry_rcu(vb, &vbq->free, free_list) {
|
|
unsigned long pages_off;
|
|
|
|
spin_lock(&vb->lock);
|
|
if (vb->free < (1UL << order)) {
|
|
spin_unlock(&vb->lock);
|
|
continue;
|
|
}
|
|
|
|
pages_off = VMAP_BBMAP_BITS - vb->free;
|
|
vaddr = vmap_block_vaddr(vb->va->va_start, pages_off);
|
|
vb->free -= 1UL << order;
|
|
bitmap_set(vb->used_map, pages_off, (1UL << order));
|
|
if (vb->free == 0) {
|
|
spin_lock(&vbq->lock);
|
|
list_del_rcu(&vb->free_list);
|
|
spin_unlock(&vbq->lock);
|
|
}
|
|
|
|
spin_unlock(&vb->lock);
|
|
break;
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
/* Allocate new block if nothing was found */
|
|
if (!vaddr)
|
|
vaddr = new_vmap_block(order, gfp_mask);
|
|
|
|
return vaddr;
|
|
}
|
|
|
|
static void vb_free(unsigned long addr, unsigned long size)
|
|
{
|
|
unsigned long offset;
|
|
unsigned int order;
|
|
struct vmap_block *vb;
|
|
|
|
BUG_ON(offset_in_page(size));
|
|
BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC);
|
|
|
|
flush_cache_vunmap(addr, addr + size);
|
|
|
|
order = get_order(size);
|
|
offset = (addr & (VMAP_BLOCK_SIZE - 1)) >> PAGE_SHIFT;
|
|
vb = xa_load(&vmap_blocks, addr_to_vb_idx(addr));
|
|
spin_lock(&vb->lock);
|
|
bitmap_clear(vb->used_map, offset, (1UL << order));
|
|
spin_unlock(&vb->lock);
|
|
|
|
vunmap_range_noflush(addr, addr + size);
|
|
|
|
if (debug_pagealloc_enabled_static())
|
|
flush_tlb_kernel_range(addr, addr + size);
|
|
|
|
spin_lock(&vb->lock);
|
|
|
|
/* Expand dirty range */
|
|
vb->dirty_min = min(vb->dirty_min, offset);
|
|
vb->dirty_max = max(vb->dirty_max, offset + (1UL << order));
|
|
|
|
vb->dirty += 1UL << order;
|
|
if (vb->dirty == VMAP_BBMAP_BITS) {
|
|
BUG_ON(vb->free);
|
|
spin_unlock(&vb->lock);
|
|
free_vmap_block(vb);
|
|
} else
|
|
spin_unlock(&vb->lock);
|
|
}
|
|
|
|
static void _vm_unmap_aliases(unsigned long start, unsigned long end, int flush)
|
|
{
|
|
int cpu;
|
|
|
|
if (unlikely(!vmap_initialized))
|
|
return;
|
|
|
|
might_sleep();
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
struct vmap_block_queue *vbq = &per_cpu(vmap_block_queue, cpu);
|
|
struct vmap_block *vb;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(vb, &vbq->free, free_list) {
|
|
spin_lock(&vb->lock);
|
|
if (vb->dirty && vb->dirty != VMAP_BBMAP_BITS) {
|
|
unsigned long va_start = vb->va->va_start;
|
|
unsigned long s, e;
|
|
|
|
s = va_start + (vb->dirty_min << PAGE_SHIFT);
|
|
e = va_start + (vb->dirty_max << PAGE_SHIFT);
|
|
|
|
start = min(s, start);
|
|
end = max(e, end);
|
|
|
|
flush = 1;
|
|
}
|
|
spin_unlock(&vb->lock);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
mutex_lock(&vmap_purge_lock);
|
|
purge_fragmented_blocks_allcpus();
|
|
if (!__purge_vmap_area_lazy(start, end) && flush)
|
|
flush_tlb_kernel_range(start, end);
|
|
mutex_unlock(&vmap_purge_lock);
|
|
}
|
|
|
|
/**
|
|
* vm_unmap_aliases - unmap outstanding lazy aliases in the vmap layer
|
|
*
|
|
* The vmap/vmalloc layer lazily flushes kernel virtual mappings primarily
|
|
* to amortize TLB flushing overheads. What this means is that any page you
|
|
* have now, may, in a former life, have been mapped into kernel virtual
|
|
* address by the vmap layer and so there might be some CPUs with TLB entries
|
|
* still referencing that page (additional to the regular 1:1 kernel mapping).
|
|
*
|
|
* vm_unmap_aliases flushes all such lazy mappings. After it returns, we can
|
|
* be sure that none of the pages we have control over will have any aliases
|
|
* from the vmap layer.
|
|
*/
|
|
void vm_unmap_aliases(void)
|
|
{
|
|
unsigned long start = ULONG_MAX, end = 0;
|
|
int flush = 0;
|
|
|
|
_vm_unmap_aliases(start, end, flush);
|
|
}
|
|
EXPORT_SYMBOL_GPL(vm_unmap_aliases);
|
|
|
|
/**
|
|
* vm_unmap_ram - unmap linear kernel address space set up by vm_map_ram
|
|
* @mem: the pointer returned by vm_map_ram
|
|
* @count: the count passed to that vm_map_ram call (cannot unmap partial)
|
|
*/
|
|
void vm_unmap_ram(const void *mem, unsigned int count)
|
|
{
|
|
unsigned long size = (unsigned long)count << PAGE_SHIFT;
|
|
unsigned long addr = (unsigned long)kasan_reset_tag(mem);
|
|
struct vmap_area *va;
|
|
|
|
might_sleep();
|
|
BUG_ON(!addr);
|
|
BUG_ON(addr < VMALLOC_START);
|
|
BUG_ON(addr > VMALLOC_END);
|
|
BUG_ON(!PAGE_ALIGNED(addr));
|
|
|
|
kasan_poison_vmalloc(mem, size);
|
|
|
|
if (likely(count <= VMAP_MAX_ALLOC)) {
|
|
debug_check_no_locks_freed(mem, size);
|
|
vb_free(addr, size);
|
|
return;
|
|
}
|
|
|
|
va = find_unlink_vmap_area(addr);
|
|
if (WARN_ON_ONCE(!va))
|
|
return;
|
|
|
|
debug_check_no_locks_freed((void *)va->va_start,
|
|
(va->va_end - va->va_start));
|
|
free_unmap_vmap_area(va);
|
|
}
|
|
EXPORT_SYMBOL(vm_unmap_ram);
|
|
|
|
/**
|
|
* vm_map_ram - map pages linearly into kernel virtual address (vmalloc space)
|
|
* @pages: an array of pointers to the pages to be mapped
|
|
* @count: number of pages
|
|
* @node: prefer to allocate data structures on this node
|
|
*
|
|
* If you use this function for less than VMAP_MAX_ALLOC pages, it could be
|
|
* faster than vmap so it's good. But if you mix long-life and short-life
|
|
* objects with vm_map_ram(), it could consume lots of address space through
|
|
* fragmentation (especially on a 32bit machine). You could see failures in
|
|
* the end. Please use this function for short-lived objects.
|
|
*
|
|
* Returns: a pointer to the address that has been mapped, or %NULL on failure
|
|
*/
|
|
void *vm_map_ram(struct page **pages, unsigned int count, int node)
|
|
{
|
|
unsigned long size = (unsigned long)count << PAGE_SHIFT;
|
|
unsigned long addr;
|
|
void *mem;
|
|
|
|
if (likely(count <= VMAP_MAX_ALLOC)) {
|
|
mem = vb_alloc(size, GFP_KERNEL);
|
|
if (IS_ERR(mem))
|
|
return NULL;
|
|
addr = (unsigned long)mem;
|
|
} else {
|
|
struct vmap_area *va;
|
|
va = alloc_vmap_area(size, PAGE_SIZE,
|
|
VMALLOC_START, VMALLOC_END,
|
|
node, GFP_KERNEL, VMAP_RAM);
|
|
if (IS_ERR(va))
|
|
return NULL;
|
|
|
|
addr = va->va_start;
|
|
mem = (void *)addr;
|
|
}
|
|
|
|
if (vmap_pages_range(addr, addr + size, PAGE_KERNEL,
|
|
pages, PAGE_SHIFT) < 0) {
|
|
vm_unmap_ram(mem, count);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Mark the pages as accessible, now that they are mapped.
|
|
* With hardware tag-based KASAN, marking is skipped for
|
|
* non-VM_ALLOC mappings, see __kasan_unpoison_vmalloc().
|
|
*/
|
|
mem = kasan_unpoison_vmalloc(mem, size, KASAN_VMALLOC_PROT_NORMAL);
|
|
|
|
return mem;
|
|
}
|
|
EXPORT_SYMBOL(vm_map_ram);
|
|
|
|
static struct vm_struct *vmlist __initdata;
|
|
|
|
static inline unsigned int vm_area_page_order(struct vm_struct *vm)
|
|
{
|
|
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
|
|
return vm->page_order;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static inline void set_vm_area_page_order(struct vm_struct *vm, unsigned int order)
|
|
{
|
|
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
|
|
vm->page_order = order;
|
|
#else
|
|
BUG_ON(order != 0);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* vm_area_add_early - add vmap area early during boot
|
|
* @vm: vm_struct to add
|
|
*
|
|
* This function is used to add fixed kernel vm area to vmlist before
|
|
* vmalloc_init() is called. @vm->addr, @vm->size, and @vm->flags
|
|
* should contain proper values and the other fields should be zero.
|
|
*
|
|
* DO NOT USE THIS FUNCTION UNLESS YOU KNOW WHAT YOU'RE DOING.
|
|
*/
|
|
void __init vm_area_add_early(struct vm_struct *vm)
|
|
{
|
|
struct vm_struct *tmp, **p;
|
|
|
|
BUG_ON(vmap_initialized);
|
|
for (p = &vmlist; (tmp = *p) != NULL; p = &tmp->next) {
|
|
if (tmp->addr >= vm->addr) {
|
|
BUG_ON(tmp->addr < vm->addr + vm->size);
|
|
break;
|
|
} else
|
|
BUG_ON(tmp->addr + tmp->size > vm->addr);
|
|
}
|
|
vm->next = *p;
|
|
*p = vm;
|
|
}
|
|
|
|
/**
|
|
* vm_area_register_early - register vmap area early during boot
|
|
* @vm: vm_struct to register
|
|
* @align: requested alignment
|
|
*
|
|
* This function is used to register kernel vm area before
|
|
* vmalloc_init() is called. @vm->size and @vm->flags should contain
|
|
* proper values on entry and other fields should be zero. On return,
|
|
* vm->addr contains the allocated address.
|
|
*
|
|
* DO NOT USE THIS FUNCTION UNLESS YOU KNOW WHAT YOU'RE DOING.
|
|
*/
|
|
void __init vm_area_register_early(struct vm_struct *vm, size_t align)
|
|
{
|
|
unsigned long addr = ALIGN(VMALLOC_START, align);
|
|
struct vm_struct *cur, **p;
|
|
|
|
BUG_ON(vmap_initialized);
|
|
|
|
for (p = &vmlist; (cur = *p) != NULL; p = &cur->next) {
|
|
if ((unsigned long)cur->addr - addr >= vm->size)
|
|
break;
|
|
addr = ALIGN((unsigned long)cur->addr + cur->size, align);
|
|
}
|
|
|
|
BUG_ON(addr > VMALLOC_END - vm->size);
|
|
vm->addr = (void *)addr;
|
|
vm->next = *p;
|
|
*p = vm;
|
|
kasan_populate_early_vm_area_shadow(vm->addr, vm->size);
|
|
}
|
|
|
|
static void vmap_init_free_space(void)
|
|
{
|
|
unsigned long vmap_start = 1;
|
|
const unsigned long vmap_end = ULONG_MAX;
|
|
struct vmap_area *busy, *free;
|
|
|
|
/*
|
|
* B F B B B F
|
|
* -|-----|.....|-----|-----|-----|.....|-
|
|
* | The KVA space |
|
|
* |<--------------------------------->|
|
|
*/
|
|
list_for_each_entry(busy, &vmap_area_list, list) {
|
|
if (busy->va_start - vmap_start > 0) {
|
|
free = kmem_cache_zalloc(vmap_area_cachep, GFP_NOWAIT);
|
|
if (!WARN_ON_ONCE(!free)) {
|
|
free->va_start = vmap_start;
|
|
free->va_end = busy->va_start;
|
|
|
|
insert_vmap_area_augment(free, NULL,
|
|
&free_vmap_area_root,
|
|
&free_vmap_area_list);
|
|
}
|
|
}
|
|
|
|
vmap_start = busy->va_end;
|
|
}
|
|
|
|
if (vmap_end - vmap_start > 0) {
|
|
free = kmem_cache_zalloc(vmap_area_cachep, GFP_NOWAIT);
|
|
if (!WARN_ON_ONCE(!free)) {
|
|
free->va_start = vmap_start;
|
|
free->va_end = vmap_end;
|
|
|
|
insert_vmap_area_augment(free, NULL,
|
|
&free_vmap_area_root,
|
|
&free_vmap_area_list);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void setup_vmalloc_vm_locked(struct vm_struct *vm,
|
|
struct vmap_area *va, unsigned long flags, const void *caller)
|
|
{
|
|
vm->flags = flags;
|
|
vm->addr = (void *)va->va_start;
|
|
vm->size = va->va_end - va->va_start;
|
|
vm->caller = caller;
|
|
va->vm = vm;
|
|
}
|
|
|
|
static void setup_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,
|
|
unsigned long flags, const void *caller)
|
|
{
|
|
spin_lock(&vmap_area_lock);
|
|
setup_vmalloc_vm_locked(vm, va, flags, caller);
|
|
spin_unlock(&vmap_area_lock);
|
|
}
|
|
|
|
static void clear_vm_uninitialized_flag(struct vm_struct *vm)
|
|
{
|
|
/*
|
|
* Before removing VM_UNINITIALIZED,
|
|
* we should make sure that vm has proper values.
|
|
* Pair with smp_rmb() in show_numa_info().
|
|
*/
|
|
smp_wmb();
|
|
vm->flags &= ~VM_UNINITIALIZED;
|
|
}
|
|
|
|
static struct vm_struct *__get_vm_area_node(unsigned long size,
|
|
unsigned long align, unsigned long shift, unsigned long flags,
|
|
unsigned long start, unsigned long end, int node,
|
|
gfp_t gfp_mask, const void *caller)
|
|
{
|
|
struct vmap_area *va;
|
|
struct vm_struct *area;
|
|
unsigned long requested_size = size;
|
|
|
|
BUG_ON(in_interrupt());
|
|
size = ALIGN(size, 1ul << shift);
|
|
if (unlikely(!size))
|
|
return NULL;
|
|
|
|
if (flags & VM_IOREMAP)
|
|
align = 1ul << clamp_t(int, get_count_order_long(size),
|
|
PAGE_SHIFT, IOREMAP_MAX_ORDER);
|
|
|
|
area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
|
|
if (unlikely(!area))
|
|
return NULL;
|
|
|
|
if (!(flags & VM_NO_GUARD))
|
|
size += PAGE_SIZE;
|
|
|
|
va = alloc_vmap_area(size, align, start, end, node, gfp_mask, 0);
|
|
if (IS_ERR(va)) {
|
|
kfree(area);
|
|
return NULL;
|
|
}
|
|
|
|
setup_vmalloc_vm(area, va, flags, caller);
|
|
|
|
/*
|
|
* Mark pages for non-VM_ALLOC mappings as accessible. Do it now as a
|
|
* best-effort approach, as they can be mapped outside of vmalloc code.
|
|
* For VM_ALLOC mappings, the pages are marked as accessible after
|
|
* getting mapped in __vmalloc_node_range().
|
|
* With hardware tag-based KASAN, marking is skipped for
|
|
* non-VM_ALLOC mappings, see __kasan_unpoison_vmalloc().
|
|
*/
|
|
if (!(flags & VM_ALLOC))
|
|
area->addr = kasan_unpoison_vmalloc(area->addr, requested_size,
|
|
KASAN_VMALLOC_PROT_NORMAL);
|
|
|
|
return area;
|
|
}
|
|
|
|
struct vm_struct *__get_vm_area_caller(unsigned long size, unsigned long flags,
|
|
unsigned long start, unsigned long end,
|
|
const void *caller)
|
|
{
|
|
return __get_vm_area_node(size, 1, PAGE_SHIFT, flags, start, end,
|
|
NUMA_NO_NODE, GFP_KERNEL, caller);
|
|
}
|
|
|
|
/**
|
|
* get_vm_area - reserve a contiguous kernel virtual area
|
|
* @size: size of the area
|
|
* @flags: %VM_IOREMAP for I/O mappings or VM_ALLOC
|
|
*
|
|
* Search an area of @size in the kernel virtual mapping area,
|
|
* and reserved it for out purposes. Returns the area descriptor
|
|
* on success or %NULL on failure.
|
|
*
|
|
* Return: the area descriptor on success or %NULL on failure.
|
|
*/
|
|
struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
|
|
{
|
|
return __get_vm_area_node(size, 1, PAGE_SHIFT, flags,
|
|
VMALLOC_START, VMALLOC_END,
|
|
NUMA_NO_NODE, GFP_KERNEL,
|
|
__builtin_return_address(0));
|
|
}
|
|
|
|
struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,
|
|
const void *caller)
|
|
{
|
|
return __get_vm_area_node(size, 1, PAGE_SHIFT, flags,
|
|
VMALLOC_START, VMALLOC_END,
|
|
NUMA_NO_NODE, GFP_KERNEL, caller);
|
|
}
|
|
|
|
/**
|
|
* find_vm_area - find a continuous kernel virtual area
|
|
* @addr: base address
|
|
*
|
|
* Search for the kernel VM area starting at @addr, and return it.
|
|
* It is up to the caller to do all required locking to keep the returned
|
|
* pointer valid.
|
|
*
|
|
* Return: the area descriptor on success or %NULL on failure.
|
|
*/
|
|
struct vm_struct *find_vm_area(const void *addr)
|
|
{
|
|
struct vmap_area *va;
|
|
|
|
va = find_vmap_area((unsigned long)addr);
|
|
if (!va)
|
|
return NULL;
|
|
|
|
return va->vm;
|
|
}
|
|
|
|
/**
|
|
* remove_vm_area - find and remove a continuous kernel virtual area
|
|
* @addr: base address
|
|
*
|
|
* Search for the kernel VM area starting at @addr, and remove it.
|
|
* This function returns the found VM area, but using it is NOT safe
|
|
* on SMP machines, except for its size or flags.
|
|
*
|
|
* Return: the area descriptor on success or %NULL on failure.
|
|
*/
|
|
struct vm_struct *remove_vm_area(const void *addr)
|
|
{
|
|
struct vmap_area *va;
|
|
struct vm_struct *vm;
|
|
|
|
might_sleep();
|
|
|
|
if (WARN(!PAGE_ALIGNED(addr), "Trying to vfree() bad address (%p)\n",
|
|
addr))
|
|
return NULL;
|
|
|
|
va = find_unlink_vmap_area((unsigned long)addr);
|
|
if (!va || !va->vm)
|
|
return NULL;
|
|
vm = va->vm;
|
|
|
|
debug_check_no_locks_freed(vm->addr, get_vm_area_size(vm));
|
|
debug_check_no_obj_freed(vm->addr, get_vm_area_size(vm));
|
|
kasan_free_module_shadow(vm);
|
|
kasan_poison_vmalloc(vm->addr, get_vm_area_size(vm));
|
|
|
|
free_unmap_vmap_area(va);
|
|
return vm;
|
|
}
|
|
|
|
static inline void set_area_direct_map(const struct vm_struct *area,
|
|
int (*set_direct_map)(struct page *page))
|
|
{
|
|
int i;
|
|
|
|
/* HUGE_VMALLOC passes small pages to set_direct_map */
|
|
for (i = 0; i < area->nr_pages; i++)
|
|
if (page_address(area->pages[i]))
|
|
set_direct_map(area->pages[i]);
|
|
}
|
|
|
|
/*
|
|
* Flush the vm mapping and reset the direct map.
|
|
*/
|
|
static void vm_reset_perms(struct vm_struct *area)
|
|
{
|
|
unsigned long start = ULONG_MAX, end = 0;
|
|
unsigned int page_order = vm_area_page_order(area);
|
|
int flush_dmap = 0;
|
|
int i;
|
|
|
|
/*
|
|
* Find the start and end range of the direct mappings to make sure that
|
|
* the vm_unmap_aliases() flush includes the direct map.
|
|
*/
|
|
for (i = 0; i < area->nr_pages; i += 1U << page_order) {
|
|
unsigned long addr = (unsigned long)page_address(area->pages[i]);
|
|
|
|
if (addr) {
|
|
unsigned long page_size;
|
|
|
|
page_size = PAGE_SIZE << page_order;
|
|
start = min(addr, start);
|
|
end = max(addr + page_size, end);
|
|
flush_dmap = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set direct map to something invalid so that it won't be cached if
|
|
* there are any accesses after the TLB flush, then flush the TLB and
|
|
* reset the direct map permissions to the default.
|
|
*/
|
|
set_area_direct_map(area, set_direct_map_invalid_noflush);
|
|
_vm_unmap_aliases(start, end, flush_dmap);
|
|
set_area_direct_map(area, set_direct_map_default_noflush);
|
|
}
|
|
|
|
static void delayed_vfree_work(struct work_struct *w)
|
|
{
|
|
struct vfree_deferred *p = container_of(w, struct vfree_deferred, wq);
|
|
struct llist_node *t, *llnode;
|
|
|
|
llist_for_each_safe(llnode, t, llist_del_all(&p->list))
|
|
vfree(llnode);
|
|
}
|
|
|
|
/**
|
|
* vfree_atomic - release memory allocated by vmalloc()
|
|
* @addr: memory base address
|
|
*
|
|
* This one is just like vfree() but can be called in any atomic context
|
|
* except NMIs.
|
|
*/
|
|
void vfree_atomic(const void *addr)
|
|
{
|
|
struct vfree_deferred *p = raw_cpu_ptr(&vfree_deferred);
|
|
|
|
BUG_ON(in_nmi());
|
|
kmemleak_free(addr);
|
|
|
|
/*
|
|
* Use raw_cpu_ptr() because this can be called from preemptible
|
|
* context. Preemption is absolutely fine here, because the llist_add()
|
|
* implementation is lockless, so it works even if we are adding to
|
|
* another cpu's list. schedule_work() should be fine with this too.
|
|
*/
|
|
if (addr && llist_add((struct llist_node *)addr, &p->list))
|
|
schedule_work(&p->wq);
|
|
}
|
|
|
|
/**
|
|
* vfree - Release memory allocated by vmalloc()
|
|
* @addr: Memory base address
|
|
*
|
|
* Free the virtually continuous memory area starting at @addr, as obtained
|
|
* from one of the vmalloc() family of APIs. This will usually also free the
|
|
* physical memory underlying the virtual allocation, but that memory is
|
|
* reference counted, so it will not be freed until the last user goes away.
|
|
*
|
|
* If @addr is NULL, no operation is performed.
|
|
*
|
|
* Context:
|
|
* May sleep if called *not* from interrupt context.
|
|
* Must not be called in NMI context (strictly speaking, it could be
|
|
* if we have CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG, but making the calling
|
|
* conventions for vfree() arch-dependent would be a really bad idea).
|
|
*/
|
|
void vfree(const void *addr)
|
|
{
|
|
struct vm_struct *vm;
|
|
int i;
|
|
|
|
if (unlikely(in_interrupt())) {
|
|
vfree_atomic(addr);
|
|
return;
|
|
}
|
|
|
|
BUG_ON(in_nmi());
|
|
kmemleak_free(addr);
|
|
might_sleep();
|
|
|
|
if (!addr)
|
|
return;
|
|
|
|
vm = remove_vm_area(addr);
|
|
if (unlikely(!vm)) {
|
|
WARN(1, KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",
|
|
addr);
|
|
return;
|
|
}
|
|
|
|
if (unlikely(vm->flags & VM_FLUSH_RESET_PERMS))
|
|
vm_reset_perms(vm);
|
|
for (i = 0; i < vm->nr_pages; i++) {
|
|
struct page *page = vm->pages[i];
|
|
|
|
BUG_ON(!page);
|
|
mod_memcg_page_state(page, MEMCG_VMALLOC, -1);
|
|
/*
|
|
* High-order allocs for huge vmallocs are split, so
|
|
* can be freed as an array of order-0 allocations
|
|
*/
|
|
__free_pages(page, 0);
|
|
cond_resched();
|
|
}
|
|
atomic_long_sub(vm->nr_pages, &nr_vmalloc_pages);
|
|
kvfree(vm->pages);
|
|
kfree(vm);
|
|
}
|
|
EXPORT_SYMBOL(vfree);
|
|
|
|
/**
|
|
* vunmap - release virtual mapping obtained by vmap()
|
|
* @addr: memory base address
|
|
*
|
|
* Free the virtually contiguous memory area starting at @addr,
|
|
* which was created from the page array passed to vmap().
|
|
*
|
|
* Must not be called in interrupt context.
|
|
*/
|
|
void vunmap(const void *addr)
|
|
{
|
|
struct vm_struct *vm;
|
|
|
|
BUG_ON(in_interrupt());
|
|
might_sleep();
|
|
|
|
if (!addr)
|
|
return;
|
|
vm = remove_vm_area(addr);
|
|
if (unlikely(!vm)) {
|
|
WARN(1, KERN_ERR "Trying to vunmap() nonexistent vm area (%p)\n",
|
|
addr);
|
|
return;
|
|
}
|
|
kfree(vm);
|
|
}
|
|
EXPORT_SYMBOL(vunmap);
|
|
|
|
/**
|
|
* vmap - map an array of pages into virtually contiguous space
|
|
* @pages: array of page pointers
|
|
* @count: number of pages to map
|
|
* @flags: vm_area->flags
|
|
* @prot: page protection for the mapping
|
|
*
|
|
* Maps @count pages from @pages into contiguous kernel virtual space.
|
|
* If @flags contains %VM_MAP_PUT_PAGES the ownership of the pages array itself
|
|
* (which must be kmalloc or vmalloc memory) and one reference per pages in it
|
|
* are transferred from the caller to vmap(), and will be freed / dropped when
|
|
* vfree() is called on the return value.
|
|
*
|
|
* Return: the address of the area or %NULL on failure
|
|
*/
|
|
void *vmap(struct page **pages, unsigned int count,
|
|
unsigned long flags, pgprot_t prot)
|
|
{
|
|
struct vm_struct *area;
|
|
unsigned long addr;
|
|
unsigned long size; /* In bytes */
|
|
|
|
might_sleep();
|
|
|
|
if (WARN_ON_ONCE(flags & VM_FLUSH_RESET_PERMS))
|
|
return NULL;
|
|
|
|
/*
|
|
* Your top guard is someone else's bottom guard. Not having a top
|
|
* guard compromises someone else's mappings too.
|
|
*/
|
|
if (WARN_ON_ONCE(flags & VM_NO_GUARD))
|
|
flags &= ~VM_NO_GUARD;
|
|
|
|
if (count > totalram_pages())
|
|
return NULL;
|
|
|
|
size = (unsigned long)count << PAGE_SHIFT;
|
|
area = get_vm_area_caller(size, flags, __builtin_return_address(0));
|
|
if (!area)
|
|
return NULL;
|
|
|
|
addr = (unsigned long)area->addr;
|
|
if (vmap_pages_range(addr, addr + size, pgprot_nx(prot),
|
|
pages, PAGE_SHIFT) < 0) {
|
|
vunmap(area->addr);
|
|
return NULL;
|
|
}
|
|
|
|
if (flags & VM_MAP_PUT_PAGES) {
|
|
area->pages = pages;
|
|
area->nr_pages = count;
|
|
}
|
|
return area->addr;
|
|
}
|
|
EXPORT_SYMBOL(vmap);
|
|
|
|
#ifdef CONFIG_VMAP_PFN
|
|
struct vmap_pfn_data {
|
|
unsigned long *pfns;
|
|
pgprot_t prot;
|
|
unsigned int idx;
|
|
};
|
|
|
|
static int vmap_pfn_apply(pte_t *pte, unsigned long addr, void *private)
|
|
{
|
|
struct vmap_pfn_data *data = private;
|
|
|
|
if (WARN_ON_ONCE(pfn_valid(data->pfns[data->idx])))
|
|
return -EINVAL;
|
|
*pte = pte_mkspecial(pfn_pte(data->pfns[data->idx++], data->prot));
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* vmap_pfn - map an array of PFNs into virtually contiguous space
|
|
* @pfns: array of PFNs
|
|
* @count: number of pages to map
|
|
* @prot: page protection for the mapping
|
|
*
|
|
* Maps @count PFNs from @pfns into contiguous kernel virtual space and returns
|
|
* the start address of the mapping.
|
|
*/
|
|
void *vmap_pfn(unsigned long *pfns, unsigned int count, pgprot_t prot)
|
|
{
|
|
struct vmap_pfn_data data = { .pfns = pfns, .prot = pgprot_nx(prot) };
|
|
struct vm_struct *area;
|
|
|
|
area = get_vm_area_caller(count * PAGE_SIZE, VM_IOREMAP,
|
|
__builtin_return_address(0));
|
|
if (!area)
|
|
return NULL;
|
|
if (apply_to_page_range(&init_mm, (unsigned long)area->addr,
|
|
count * PAGE_SIZE, vmap_pfn_apply, &data)) {
|
|
free_vm_area(area);
|
|
return NULL;
|
|
}
|
|
return area->addr;
|
|
}
|
|
EXPORT_SYMBOL_GPL(vmap_pfn);
|
|
#endif /* CONFIG_VMAP_PFN */
|
|
|
|
static inline unsigned int
|
|
vm_area_alloc_pages(gfp_t gfp, int nid,
|
|
unsigned int order, unsigned int nr_pages, struct page **pages)
|
|
{
|
|
unsigned int nr_allocated = 0;
|
|
struct page *page;
|
|
int i;
|
|
|
|
/*
|
|
* For order-0 pages we make use of bulk allocator, if
|
|
* the page array is partly or not at all populated due
|
|
* to fails, fallback to a single page allocator that is
|
|
* more permissive.
|
|
*/
|
|
if (!order) {
|
|
gfp_t bulk_gfp = gfp & ~__GFP_NOFAIL;
|
|
|
|
while (nr_allocated < nr_pages) {
|
|
unsigned int nr, nr_pages_request;
|
|
|
|
/*
|
|
* A maximum allowed request is hard-coded and is 100
|
|
* pages per call. That is done in order to prevent a
|
|
* long preemption off scenario in the bulk-allocator
|
|
* so the range is [1:100].
|
|
*/
|
|
nr_pages_request = min(100U, nr_pages - nr_allocated);
|
|
|
|
/* memory allocation should consider mempolicy, we can't
|
|
* wrongly use nearest node when nid == NUMA_NO_NODE,
|
|
* otherwise memory may be allocated in only one node,
|
|
* but mempolicy wants to alloc memory by interleaving.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_NUMA) && nid == NUMA_NO_NODE)
|
|
nr = alloc_pages_bulk_array_mempolicy(bulk_gfp,
|
|
nr_pages_request,
|
|
pages + nr_allocated);
|
|
|
|
else
|
|
nr = alloc_pages_bulk_array_node(bulk_gfp, nid,
|
|
nr_pages_request,
|
|
pages + nr_allocated);
|
|
|
|
nr_allocated += nr;
|
|
cond_resched();
|
|
|
|
/*
|
|
* If zero or pages were obtained partly,
|
|
* fallback to a single page allocator.
|
|
*/
|
|
if (nr != nr_pages_request)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* High-order pages or fallback path if "bulk" fails. */
|
|
|
|
while (nr_allocated < nr_pages) {
|
|
if (fatal_signal_pending(current))
|
|
break;
|
|
|
|
if (nid == NUMA_NO_NODE)
|
|
page = alloc_pages(gfp, order);
|
|
else
|
|
page = alloc_pages_node(nid, gfp, order);
|
|
if (unlikely(!page))
|
|
break;
|
|
/*
|
|
* Higher order allocations must be able to be treated as
|
|
* indepdenent small pages by callers (as they can with
|
|
* small-page vmallocs). Some drivers do their own refcounting
|
|
* on vmalloc_to_page() pages, some use page->mapping,
|
|
* page->lru, etc.
|
|
*/
|
|
if (order)
|
|
split_page(page, order);
|
|
|
|
/*
|
|
* Careful, we allocate and map page-order pages, but
|
|
* tracking is done per PAGE_SIZE page so as to keep the
|
|
* vm_struct APIs independent of the physical/mapped size.
|
|
*/
|
|
for (i = 0; i < (1U << order); i++)
|
|
pages[nr_allocated + i] = page + i;
|
|
|
|
cond_resched();
|
|
nr_allocated += 1U << order;
|
|
}
|
|
|
|
return nr_allocated;
|
|
}
|
|
|
|
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
|
|
pgprot_t prot, unsigned int page_shift,
|
|
int node)
|
|
{
|
|
const gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
|
|
bool nofail = gfp_mask & __GFP_NOFAIL;
|
|
unsigned long addr = (unsigned long)area->addr;
|
|
unsigned long size = get_vm_area_size(area);
|
|
unsigned long array_size;
|
|
unsigned int nr_small_pages = size >> PAGE_SHIFT;
|
|
unsigned int page_order;
|
|
unsigned int flags;
|
|
int ret;
|
|
|
|
array_size = (unsigned long)nr_small_pages * sizeof(struct page *);
|
|
|
|
if (!(gfp_mask & (GFP_DMA | GFP_DMA32)))
|
|
gfp_mask |= __GFP_HIGHMEM;
|
|
|
|
/* Please note that the recursion is strictly bounded. */
|
|
if (array_size > PAGE_SIZE) {
|
|
area->pages = __vmalloc_node(array_size, 1, nested_gfp, node,
|
|
area->caller);
|
|
} else {
|
|
area->pages = kmalloc_node(array_size, nested_gfp, node);
|
|
}
|
|
|
|
if (!area->pages) {
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc error: size %lu, failed to allocated page array size %lu",
|
|
nr_small_pages * PAGE_SIZE, array_size);
|
|
free_vm_area(area);
|
|
return NULL;
|
|
}
|
|
|
|
set_vm_area_page_order(area, page_shift - PAGE_SHIFT);
|
|
page_order = vm_area_page_order(area);
|
|
|
|
area->nr_pages = vm_area_alloc_pages(gfp_mask | __GFP_NOWARN,
|
|
node, page_order, nr_small_pages, area->pages);
|
|
|
|
atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
|
|
if (gfp_mask & __GFP_ACCOUNT) {
|
|
int i;
|
|
|
|
for (i = 0; i < area->nr_pages; i++)
|
|
mod_memcg_page_state(area->pages[i], MEMCG_VMALLOC, 1);
|
|
}
|
|
|
|
/*
|
|
* If not enough pages were obtained to accomplish an
|
|
* allocation request, free them via vfree() if any.
|
|
*/
|
|
if (area->nr_pages != nr_small_pages) {
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc error: size %lu, page order %u, failed to allocate pages",
|
|
area->nr_pages * PAGE_SIZE, page_order);
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* page tables allocations ignore external gfp mask, enforce it
|
|
* by the scope API
|
|
*/
|
|
if ((gfp_mask & (__GFP_FS | __GFP_IO)) == __GFP_IO)
|
|
flags = memalloc_nofs_save();
|
|
else if ((gfp_mask & (__GFP_FS | __GFP_IO)) == 0)
|
|
flags = memalloc_noio_save();
|
|
|
|
do {
|
|
ret = vmap_pages_range(addr, addr + size, prot, area->pages,
|
|
page_shift);
|
|
if (nofail && (ret < 0))
|
|
schedule_timeout_uninterruptible(1);
|
|
} while (nofail && (ret < 0));
|
|
|
|
if ((gfp_mask & (__GFP_FS | __GFP_IO)) == __GFP_IO)
|
|
memalloc_nofs_restore(flags);
|
|
else if ((gfp_mask & (__GFP_FS | __GFP_IO)) == 0)
|
|
memalloc_noio_restore(flags);
|
|
|
|
if (ret < 0) {
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc error: size %lu, failed to map pages",
|
|
area->nr_pages * PAGE_SIZE);
|
|
goto fail;
|
|
}
|
|
|
|
return area->addr;
|
|
|
|
fail:
|
|
vfree(area->addr);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* __vmalloc_node_range - allocate virtually contiguous memory
|
|
* @size: allocation size
|
|
* @align: desired alignment
|
|
* @start: vm area range start
|
|
* @end: vm area range end
|
|
* @gfp_mask: flags for the page level allocator
|
|
* @prot: protection mask for the allocated pages
|
|
* @vm_flags: additional vm area flags (e.g. %VM_NO_GUARD)
|
|
* @node: node to use for allocation or NUMA_NO_NODE
|
|
* @caller: caller's return address
|
|
*
|
|
* Allocate enough pages to cover @size from the page level
|
|
* allocator with @gfp_mask flags. Please note that the full set of gfp
|
|
* flags are not supported. GFP_KERNEL, GFP_NOFS and GFP_NOIO are all
|
|
* supported.
|
|
* Zone modifiers are not supported. From the reclaim modifiers
|
|
* __GFP_DIRECT_RECLAIM is required (aka GFP_NOWAIT is not supported)
|
|
* and only __GFP_NOFAIL is supported (i.e. __GFP_NORETRY and
|
|
* __GFP_RETRY_MAYFAIL are not supported).
|
|
*
|
|
* __GFP_NOWARN can be used to suppress failures messages.
|
|
*
|
|
* Map them into contiguous kernel virtual space, using a pagetable
|
|
* protection of @prot.
|
|
*
|
|
* Return: the address of the area or %NULL on failure
|
|
*/
|
|
void *__vmalloc_node_range(unsigned long size, unsigned long align,
|
|
unsigned long start, unsigned long end, gfp_t gfp_mask,
|
|
pgprot_t prot, unsigned long vm_flags, int node,
|
|
const void *caller)
|
|
{
|
|
struct vm_struct *area;
|
|
void *ret;
|
|
kasan_vmalloc_flags_t kasan_flags = KASAN_VMALLOC_NONE;
|
|
unsigned long real_size = size;
|
|
unsigned long real_align = align;
|
|
unsigned int shift = PAGE_SHIFT;
|
|
|
|
if (WARN_ON_ONCE(!size))
|
|
return NULL;
|
|
|
|
if ((size >> PAGE_SHIFT) > totalram_pages()) {
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc error: size %lu, exceeds total pages",
|
|
real_size);
|
|
return NULL;
|
|
}
|
|
|
|
if (vmap_allow_huge && (vm_flags & VM_ALLOW_HUGE_VMAP)) {
|
|
unsigned long size_per_node;
|
|
|
|
/*
|
|
* Try huge pages. Only try for PAGE_KERNEL allocations,
|
|
* others like modules don't yet expect huge pages in
|
|
* their allocations due to apply_to_page_range not
|
|
* supporting them.
|
|
*/
|
|
|
|
size_per_node = size;
|
|
if (node == NUMA_NO_NODE)
|
|
size_per_node /= num_online_nodes();
|
|
if (arch_vmap_pmd_supported(prot) && size_per_node >= PMD_SIZE)
|
|
shift = PMD_SHIFT;
|
|
else
|
|
shift = arch_vmap_pte_supported_shift(size_per_node);
|
|
|
|
align = max(real_align, 1UL << shift);
|
|
size = ALIGN(real_size, 1UL << shift);
|
|
}
|
|
|
|
again:
|
|
area = __get_vm_area_node(real_size, align, shift, VM_ALLOC |
|
|
VM_UNINITIALIZED | vm_flags, start, end, node,
|
|
gfp_mask, caller);
|
|
if (!area) {
|
|
bool nofail = gfp_mask & __GFP_NOFAIL;
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc error: size %lu, vm_struct allocation failed%s",
|
|
real_size, (nofail) ? ". Retrying." : "");
|
|
if (nofail) {
|
|
schedule_timeout_uninterruptible(1);
|
|
goto again;
|
|
}
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Prepare arguments for __vmalloc_area_node() and
|
|
* kasan_unpoison_vmalloc().
|
|
*/
|
|
if (pgprot_val(prot) == pgprot_val(PAGE_KERNEL)) {
|
|
if (kasan_hw_tags_enabled()) {
|
|
/*
|
|
* Modify protection bits to allow tagging.
|
|
* This must be done before mapping.
|
|
*/
|
|
prot = arch_vmap_pgprot_tagged(prot);
|
|
|
|
/*
|
|
* Skip page_alloc poisoning and zeroing for physical
|
|
* pages backing VM_ALLOC mapping. Memory is instead
|
|
* poisoned and zeroed by kasan_unpoison_vmalloc().
|
|
*/
|
|
gfp_mask |= __GFP_SKIP_KASAN_UNPOISON | __GFP_SKIP_ZERO;
|
|
}
|
|
|
|
/* Take note that the mapping is PAGE_KERNEL. */
|
|
kasan_flags |= KASAN_VMALLOC_PROT_NORMAL;
|
|
}
|
|
|
|
/* Allocate physical pages and map them into vmalloc space. */
|
|
ret = __vmalloc_area_node(area, gfp_mask, prot, shift, node);
|
|
if (!ret)
|
|
goto fail;
|
|
|
|
/*
|
|
* Mark the pages as accessible, now that they are mapped.
|
|
* The condition for setting KASAN_VMALLOC_INIT should complement the
|
|
* one in post_alloc_hook() with regards to the __GFP_SKIP_ZERO check
|
|
* to make sure that memory is initialized under the same conditions.
|
|
* Tag-based KASAN modes only assign tags to normal non-executable
|
|
* allocations, see __kasan_unpoison_vmalloc().
|
|
*/
|
|
kasan_flags |= KASAN_VMALLOC_VM_ALLOC;
|
|
if (!want_init_on_free() && want_init_on_alloc(gfp_mask) &&
|
|
(gfp_mask & __GFP_SKIP_ZERO))
|
|
kasan_flags |= KASAN_VMALLOC_INIT;
|
|
/* KASAN_VMALLOC_PROT_NORMAL already set if required. */
|
|
area->addr = kasan_unpoison_vmalloc(area->addr, real_size, kasan_flags);
|
|
|
|
/*
|
|
* In this function, newly allocated vm_struct has VM_UNINITIALIZED
|
|
* flag. It means that vm_struct is not fully initialized.
|
|
* Now, it is fully initialized, so remove this flag here.
|
|
*/
|
|
clear_vm_uninitialized_flag(area);
|
|
|
|
size = PAGE_ALIGN(size);
|
|
if (!(vm_flags & VM_DEFER_KMEMLEAK))
|
|
kmemleak_vmalloc(area, size, gfp_mask);
|
|
|
|
return area->addr;
|
|
|
|
fail:
|
|
if (shift > PAGE_SHIFT) {
|
|
shift = PAGE_SHIFT;
|
|
align = real_align;
|
|
size = real_size;
|
|
goto again;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* __vmalloc_node - allocate virtually contiguous memory
|
|
* @size: allocation size
|
|
* @align: desired alignment
|
|
* @gfp_mask: flags for the page level allocator
|
|
* @node: node to use for allocation or NUMA_NO_NODE
|
|
* @caller: caller's return address
|
|
*
|
|
* Allocate enough pages to cover @size from the page level allocator with
|
|
* @gfp_mask flags. Map them into contiguous kernel virtual space.
|
|
*
|
|
* Reclaim modifiers in @gfp_mask - __GFP_NORETRY, __GFP_RETRY_MAYFAIL
|
|
* and __GFP_NOFAIL are not supported
|
|
*
|
|
* Any use of gfp flags outside of GFP_KERNEL should be consulted
|
|
* with mm people.
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *__vmalloc_node(unsigned long size, unsigned long align,
|
|
gfp_t gfp_mask, int node, const void *caller)
|
|
{
|
|
return __vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,
|
|
gfp_mask, PAGE_KERNEL, 0, node, caller);
|
|
}
|
|
/*
|
|
* This is only for performance analysis of vmalloc and stress purpose.
|
|
* It is required by vmalloc test module, therefore do not use it other
|
|
* than that.
|
|
*/
|
|
#ifdef CONFIG_TEST_VMALLOC_MODULE
|
|
EXPORT_SYMBOL_GPL(__vmalloc_node);
|
|
#endif
|
|
|
|
void *__vmalloc(unsigned long size, gfp_t gfp_mask)
|
|
{
|
|
return __vmalloc_node(size, 1, gfp_mask, NUMA_NO_NODE,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(__vmalloc);
|
|
|
|
/**
|
|
* vmalloc - allocate virtually contiguous memory
|
|
* @size: allocation size
|
|
*
|
|
* Allocate enough pages to cover @size from the page level
|
|
* allocator and map them into contiguous kernel virtual space.
|
|
*
|
|
* For tight control over page level allocator and protection flags
|
|
* use __vmalloc() instead.
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *vmalloc(unsigned long size)
|
|
{
|
|
return __vmalloc_node(size, 1, GFP_KERNEL, NUMA_NO_NODE,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(vmalloc);
|
|
|
|
/**
|
|
* vmalloc_huge - allocate virtually contiguous memory, allow huge pages
|
|
* @size: allocation size
|
|
* @gfp_mask: flags for the page level allocator
|
|
*
|
|
* Allocate enough pages to cover @size from the page level
|
|
* allocator and map them into contiguous kernel virtual space.
|
|
* If @size is greater than or equal to PMD_SIZE, allow using
|
|
* huge pages for the memory
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *vmalloc_huge(unsigned long size, gfp_t gfp_mask)
|
|
{
|
|
return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END,
|
|
gfp_mask, PAGE_KERNEL, VM_ALLOW_HUGE_VMAP,
|
|
NUMA_NO_NODE, __builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL_GPL(vmalloc_huge);
|
|
|
|
/**
|
|
* vzalloc - allocate virtually contiguous memory with zero fill
|
|
* @size: allocation size
|
|
*
|
|
* Allocate enough pages to cover @size from the page level
|
|
* allocator and map them into contiguous kernel virtual space.
|
|
* The memory allocated is set to zero.
|
|
*
|
|
* For tight control over page level allocator and protection flags
|
|
* use __vmalloc() instead.
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *vzalloc(unsigned long size)
|
|
{
|
|
return __vmalloc_node(size, 1, GFP_KERNEL | __GFP_ZERO, NUMA_NO_NODE,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(vzalloc);
|
|
|
|
/**
|
|
* vmalloc_user - allocate zeroed virtually contiguous memory for userspace
|
|
* @size: allocation size
|
|
*
|
|
* The resulting memory area is zeroed so it can be mapped to userspace
|
|
* without leaking data.
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *vmalloc_user(unsigned long size)
|
|
{
|
|
return __vmalloc_node_range(size, SHMLBA, VMALLOC_START, VMALLOC_END,
|
|
GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL,
|
|
VM_USERMAP, NUMA_NO_NODE,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(vmalloc_user);
|
|
|
|
/**
|
|
* vmalloc_node - allocate memory on a specific node
|
|
* @size: allocation size
|
|
* @node: numa node
|
|
*
|
|
* Allocate enough pages to cover @size from the page level
|
|
* allocator and map them into contiguous kernel virtual space.
|
|
*
|
|
* For tight control over page level allocator and protection flags
|
|
* use __vmalloc() instead.
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *vmalloc_node(unsigned long size, int node)
|
|
{
|
|
return __vmalloc_node(size, 1, GFP_KERNEL, node,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(vmalloc_node);
|
|
|
|
/**
|
|
* vzalloc_node - allocate memory on a specific node with zero fill
|
|
* @size: allocation size
|
|
* @node: numa node
|
|
*
|
|
* Allocate enough pages to cover @size from the page level
|
|
* allocator and map them into contiguous kernel virtual space.
|
|
* The memory allocated is set to zero.
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *vzalloc_node(unsigned long size, int node)
|
|
{
|
|
return __vmalloc_node(size, 1, GFP_KERNEL | __GFP_ZERO, node,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(vzalloc_node);
|
|
|
|
#if defined(CONFIG_64BIT) && defined(CONFIG_ZONE_DMA32)
|
|
#define GFP_VMALLOC32 (GFP_DMA32 | GFP_KERNEL)
|
|
#elif defined(CONFIG_64BIT) && defined(CONFIG_ZONE_DMA)
|
|
#define GFP_VMALLOC32 (GFP_DMA | GFP_KERNEL)
|
|
#else
|
|
/*
|
|
* 64b systems should always have either DMA or DMA32 zones. For others
|
|
* GFP_DMA32 should do the right thing and use the normal zone.
|
|
*/
|
|
#define GFP_VMALLOC32 (GFP_DMA32 | GFP_KERNEL)
|
|
#endif
|
|
|
|
/**
|
|
* vmalloc_32 - allocate virtually contiguous memory (32bit addressable)
|
|
* @size: allocation size
|
|
*
|
|
* Allocate enough 32bit PA addressable pages to cover @size from the
|
|
* page level allocator and map them into contiguous kernel virtual space.
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *vmalloc_32(unsigned long size)
|
|
{
|
|
return __vmalloc_node(size, 1, GFP_VMALLOC32, NUMA_NO_NODE,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(vmalloc_32);
|
|
|
|
/**
|
|
* vmalloc_32_user - allocate zeroed virtually contiguous 32bit memory
|
|
* @size: allocation size
|
|
*
|
|
* The resulting memory area is 32bit addressable and zeroed so it can be
|
|
* mapped to userspace without leaking data.
|
|
*
|
|
* Return: pointer to the allocated memory or %NULL on error
|
|
*/
|
|
void *vmalloc_32_user(unsigned long size)
|
|
{
|
|
return __vmalloc_node_range(size, SHMLBA, VMALLOC_START, VMALLOC_END,
|
|
GFP_VMALLOC32 | __GFP_ZERO, PAGE_KERNEL,
|
|
VM_USERMAP, NUMA_NO_NODE,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(vmalloc_32_user);
|
|
|
|
/*
|
|
* small helper routine , copy contents to buf from addr.
|
|
* If the page is not present, fill zero.
|
|
*/
|
|
|
|
static int aligned_vread(char *buf, char *addr, unsigned long count)
|
|
{
|
|
struct page *p;
|
|
int copied = 0;
|
|
|
|
while (count) {
|
|
unsigned long offset, length;
|
|
|
|
offset = offset_in_page(addr);
|
|
length = PAGE_SIZE - offset;
|
|
if (length > count)
|
|
length = count;
|
|
p = vmalloc_to_page(addr);
|
|
/*
|
|
* To do safe access to this _mapped_ area, we need
|
|
* lock. But adding lock here means that we need to add
|
|
* overhead of vmalloc()/vfree() calls for this _debug_
|
|
* interface, rarely used. Instead of that, we'll use
|
|
* kmap() and get small overhead in this access function.
|
|
*/
|
|
if (p) {
|
|
/* We can expect USER0 is not used -- see vread() */
|
|
void *map = kmap_atomic(p);
|
|
memcpy(buf, map + offset, length);
|
|
kunmap_atomic(map);
|
|
} else
|
|
memset(buf, 0, length);
|
|
|
|
addr += length;
|
|
buf += length;
|
|
copied += length;
|
|
count -= length;
|
|
}
|
|
return copied;
|
|
}
|
|
|
|
static void vmap_ram_vread(char *buf, char *addr, int count, unsigned long flags)
|
|
{
|
|
char *start;
|
|
struct vmap_block *vb;
|
|
unsigned long offset;
|
|
unsigned int rs, re, n;
|
|
|
|
/*
|
|
* If it's area created by vm_map_ram() interface directly, but
|
|
* not further subdividing and delegating management to vmap_block,
|
|
* handle it here.
|
|
*/
|
|
if (!(flags & VMAP_BLOCK)) {
|
|
aligned_vread(buf, addr, count);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Area is split into regions and tracked with vmap_block, read out
|
|
* each region and zero fill the hole between regions.
|
|
*/
|
|
vb = xa_load(&vmap_blocks, addr_to_vb_idx((unsigned long)addr));
|
|
if (!vb)
|
|
goto finished;
|
|
|
|
spin_lock(&vb->lock);
|
|
if (bitmap_empty(vb->used_map, VMAP_BBMAP_BITS)) {
|
|
spin_unlock(&vb->lock);
|
|
goto finished;
|
|
}
|
|
for_each_set_bitrange(rs, re, vb->used_map, VMAP_BBMAP_BITS) {
|
|
if (!count)
|
|
break;
|
|
start = vmap_block_vaddr(vb->va->va_start, rs);
|
|
while (addr < start) {
|
|
if (count == 0)
|
|
goto unlock;
|
|
*buf = '\0';
|
|
buf++;
|
|
addr++;
|
|
count--;
|
|
}
|
|
/*it could start reading from the middle of used region*/
|
|
offset = offset_in_page(addr);
|
|
n = ((re - rs + 1) << PAGE_SHIFT) - offset;
|
|
if (n > count)
|
|
n = count;
|
|
aligned_vread(buf, start+offset, n);
|
|
|
|
buf += n;
|
|
addr += n;
|
|
count -= n;
|
|
}
|
|
unlock:
|
|
spin_unlock(&vb->lock);
|
|
|
|
finished:
|
|
/* zero-fill the left dirty or free regions */
|
|
if (count)
|
|
memset(buf, 0, count);
|
|
}
|
|
|
|
/**
|
|
* vread() - read vmalloc area in a safe way.
|
|
* @buf: buffer for reading data
|
|
* @addr: vm address.
|
|
* @count: number of bytes to be read.
|
|
*
|
|
* This function checks that addr is a valid vmalloc'ed area, and
|
|
* copy data from that area to a given buffer. If the given memory range
|
|
* of [addr...addr+count) includes some valid address, data is copied to
|
|
* proper area of @buf. If there are memory holes, they'll be zero-filled.
|
|
* IOREMAP area is treated as memory hole and no copy is done.
|
|
*
|
|
* If [addr...addr+count) doesn't includes any intersects with alive
|
|
* vm_struct area, returns 0. @buf should be kernel's buffer.
|
|
*
|
|
* Note: In usual ops, vread() is never necessary because the caller
|
|
* should know vmalloc() area is valid and can use memcpy().
|
|
* This is for routines which have to access vmalloc area without
|
|
* any information, as /proc/kcore.
|
|
*
|
|
* Return: number of bytes for which addr and buf should be increased
|
|
* (same number as @count) or %0 if [addr...addr+count) doesn't
|
|
* include any intersection with valid vmalloc area
|
|
*/
|
|
long vread(char *buf, char *addr, unsigned long count)
|
|
{
|
|
struct vmap_area *va;
|
|
struct vm_struct *vm;
|
|
char *vaddr, *buf_start = buf;
|
|
unsigned long buflen = count;
|
|
unsigned long n, size, flags;
|
|
|
|
addr = kasan_reset_tag(addr);
|
|
|
|
/* Don't allow overflow */
|
|
if ((unsigned long) addr + count < count)
|
|
count = -(unsigned long) addr;
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
va = find_vmap_area_exceed_addr((unsigned long)addr);
|
|
if (!va)
|
|
goto finished;
|
|
|
|
/* no intersects with alive vmap_area */
|
|
if ((unsigned long)addr + count <= va->va_start)
|
|
goto finished;
|
|
|
|
list_for_each_entry_from(va, &vmap_area_list, list) {
|
|
if (!count)
|
|
break;
|
|
|
|
vm = va->vm;
|
|
flags = va->flags & VMAP_FLAGS_MASK;
|
|
/*
|
|
* VMAP_BLOCK indicates a sub-type of vm_map_ram area, need
|
|
* be set together with VMAP_RAM.
|
|
*/
|
|
WARN_ON(flags == VMAP_BLOCK);
|
|
|
|
if (!vm && !flags)
|
|
continue;
|
|
|
|
if (vm && (vm->flags & VM_UNINITIALIZED))
|
|
continue;
|
|
/* Pair with smp_wmb() in clear_vm_uninitialized_flag() */
|
|
smp_rmb();
|
|
|
|
vaddr = (char *) va->va_start;
|
|
size = vm ? get_vm_area_size(vm) : va_size(va);
|
|
|
|
if (addr >= vaddr + size)
|
|
continue;
|
|
while (addr < vaddr) {
|
|
if (count == 0)
|
|
goto finished;
|
|
*buf = '\0';
|
|
buf++;
|
|
addr++;
|
|
count--;
|
|
}
|
|
n = vaddr + size - addr;
|
|
if (n > count)
|
|
n = count;
|
|
|
|
if (flags & VMAP_RAM)
|
|
vmap_ram_vread(buf, addr, n, flags);
|
|
else if (!(vm->flags & VM_IOREMAP))
|
|
aligned_vread(buf, addr, n);
|
|
else /* IOREMAP area is treated as memory hole */
|
|
memset(buf, 0, n);
|
|
buf += n;
|
|
addr += n;
|
|
count -= n;
|
|
}
|
|
finished:
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
if (buf == buf_start)
|
|
return 0;
|
|
/* zero-fill memory holes */
|
|
if (buf != buf_start + buflen)
|
|
memset(buf, 0, buflen - (buf - buf_start));
|
|
|
|
return buflen;
|
|
}
|
|
|
|
/**
|
|
* remap_vmalloc_range_partial - map vmalloc pages to userspace
|
|
* @vma: vma to cover
|
|
* @uaddr: target user address to start at
|
|
* @kaddr: virtual address of vmalloc kernel memory
|
|
* @pgoff: offset from @kaddr to start at
|
|
* @size: size of map area
|
|
*
|
|
* Returns: 0 for success, -Exxx on failure
|
|
*
|
|
* This function checks that @kaddr is a valid vmalloc'ed area,
|
|
* and that it is big enough to cover the range starting at
|
|
* @uaddr in @vma. Will return failure if that criteria isn't
|
|
* met.
|
|
*
|
|
* Similar to remap_pfn_range() (see mm/memory.c)
|
|
*/
|
|
int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long uaddr,
|
|
void *kaddr, unsigned long pgoff,
|
|
unsigned long size)
|
|
{
|
|
struct vm_struct *area;
|
|
unsigned long off;
|
|
unsigned long end_index;
|
|
|
|
if (check_shl_overflow(pgoff, PAGE_SHIFT, &off))
|
|
return -EINVAL;
|
|
|
|
size = PAGE_ALIGN(size);
|
|
|
|
if (!PAGE_ALIGNED(uaddr) || !PAGE_ALIGNED(kaddr))
|
|
return -EINVAL;
|
|
|
|
area = find_vm_area(kaddr);
|
|
if (!area)
|
|
return -EINVAL;
|
|
|
|
if (!(area->flags & (VM_USERMAP | VM_DMA_COHERENT)))
|
|
return -EINVAL;
|
|
|
|
if (check_add_overflow(size, off, &end_index) ||
|
|
end_index > get_vm_area_size(area))
|
|
return -EINVAL;
|
|
kaddr += off;
|
|
|
|
do {
|
|
struct page *page = vmalloc_to_page(kaddr);
|
|
int ret;
|
|
|
|
ret = vm_insert_page(vma, uaddr, page);
|
|
if (ret)
|
|
return ret;
|
|
|
|
uaddr += PAGE_SIZE;
|
|
kaddr += PAGE_SIZE;
|
|
size -= PAGE_SIZE;
|
|
} while (size > 0);
|
|
|
|
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* remap_vmalloc_range - map vmalloc pages to userspace
|
|
* @vma: vma to cover (map full range of vma)
|
|
* @addr: vmalloc memory
|
|
* @pgoff: number of pages into addr before first page to map
|
|
*
|
|
* Returns: 0 for success, -Exxx on failure
|
|
*
|
|
* This function checks that addr is a valid vmalloc'ed area, and
|
|
* that it is big enough to cover the vma. Will return failure if
|
|
* that criteria isn't met.
|
|
*
|
|
* Similar to remap_pfn_range() (see mm/memory.c)
|
|
*/
|
|
int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
|
|
unsigned long pgoff)
|
|
{
|
|
return remap_vmalloc_range_partial(vma, vma->vm_start,
|
|
addr, pgoff,
|
|
vma->vm_end - vma->vm_start);
|
|
}
|
|
EXPORT_SYMBOL(remap_vmalloc_range);
|
|
|
|
void free_vm_area(struct vm_struct *area)
|
|
{
|
|
struct vm_struct *ret;
|
|
ret = remove_vm_area(area->addr);
|
|
BUG_ON(ret != area);
|
|
kfree(area);
|
|
}
|
|
EXPORT_SYMBOL_GPL(free_vm_area);
|
|
|
|
#ifdef CONFIG_SMP
|
|
static struct vmap_area *node_to_va(struct rb_node *n)
|
|
{
|
|
return rb_entry_safe(n, struct vmap_area, rb_node);
|
|
}
|
|
|
|
/**
|
|
* pvm_find_va_enclose_addr - find the vmap_area @addr belongs to
|
|
* @addr: target address
|
|
*
|
|
* Returns: vmap_area if it is found. If there is no such area
|
|
* the first highest(reverse order) vmap_area is returned
|
|
* i.e. va->va_start < addr && va->va_end < addr or NULL
|
|
* if there are no any areas before @addr.
|
|
*/
|
|
static struct vmap_area *
|
|
pvm_find_va_enclose_addr(unsigned long addr)
|
|
{
|
|
struct vmap_area *va, *tmp;
|
|
struct rb_node *n;
|
|
|
|
n = free_vmap_area_root.rb_node;
|
|
va = NULL;
|
|
|
|
while (n) {
|
|
tmp = rb_entry(n, struct vmap_area, rb_node);
|
|
if (tmp->va_start <= addr) {
|
|
va = tmp;
|
|
if (tmp->va_end >= addr)
|
|
break;
|
|
|
|
n = n->rb_right;
|
|
} else {
|
|
n = n->rb_left;
|
|
}
|
|
}
|
|
|
|
return va;
|
|
}
|
|
|
|
/**
|
|
* pvm_determine_end_from_reverse - find the highest aligned address
|
|
* of free block below VMALLOC_END
|
|
* @va:
|
|
* in - the VA we start the search(reverse order);
|
|
* out - the VA with the highest aligned end address.
|
|
* @align: alignment for required highest address
|
|
*
|
|
* Returns: determined end address within vmap_area
|
|
*/
|
|
static unsigned long
|
|
pvm_determine_end_from_reverse(struct vmap_area **va, unsigned long align)
|
|
{
|
|
unsigned long vmalloc_end = VMALLOC_END & ~(align - 1);
|
|
unsigned long addr;
|
|
|
|
if (likely(*va)) {
|
|
list_for_each_entry_from_reverse((*va),
|
|
&free_vmap_area_list, list) {
|
|
addr = min((*va)->va_end & ~(align - 1), vmalloc_end);
|
|
if ((*va)->va_start < addr)
|
|
return addr;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* pcpu_get_vm_areas - allocate vmalloc areas for percpu allocator
|
|
* @offsets: array containing offset of each area
|
|
* @sizes: array containing size of each area
|
|
* @nr_vms: the number of areas to allocate
|
|
* @align: alignment, all entries in @offsets and @sizes must be aligned to this
|
|
*
|
|
* Returns: kmalloc'd vm_struct pointer array pointing to allocated
|
|
* vm_structs on success, %NULL on failure
|
|
*
|
|
* Percpu allocator wants to use congruent vm areas so that it can
|
|
* maintain the offsets among percpu areas. This function allocates
|
|
* congruent vmalloc areas for it with GFP_KERNEL. These areas tend to
|
|
* be scattered pretty far, distance between two areas easily going up
|
|
* to gigabytes. To avoid interacting with regular vmallocs, these
|
|
* areas are allocated from top.
|
|
*
|
|
* Despite its complicated look, this allocator is rather simple. It
|
|
* does everything top-down and scans free blocks from the end looking
|
|
* for matching base. While scanning, if any of the areas do not fit the
|
|
* base address is pulled down to fit the area. Scanning is repeated till
|
|
* all the areas fit and then all necessary data structures are inserted
|
|
* and the result is returned.
|
|
*/
|
|
struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
|
|
const size_t *sizes, int nr_vms,
|
|
size_t align)
|
|
{
|
|
const unsigned long vmalloc_start = ALIGN(VMALLOC_START, align);
|
|
const unsigned long vmalloc_end = VMALLOC_END & ~(align - 1);
|
|
struct vmap_area **vas, *va;
|
|
struct vm_struct **vms;
|
|
int area, area2, last_area, term_area;
|
|
unsigned long base, start, size, end, last_end, orig_start, orig_end;
|
|
bool purged = false;
|
|
|
|
/* verify parameters and allocate data structures */
|
|
BUG_ON(offset_in_page(align) || !is_power_of_2(align));
|
|
for (last_area = 0, area = 0; area < nr_vms; area++) {
|
|
start = offsets[area];
|
|
end = start + sizes[area];
|
|
|
|
/* is everything aligned properly? */
|
|
BUG_ON(!IS_ALIGNED(offsets[area], align));
|
|
BUG_ON(!IS_ALIGNED(sizes[area], align));
|
|
|
|
/* detect the area with the highest address */
|
|
if (start > offsets[last_area])
|
|
last_area = area;
|
|
|
|
for (area2 = area + 1; area2 < nr_vms; area2++) {
|
|
unsigned long start2 = offsets[area2];
|
|
unsigned long end2 = start2 + sizes[area2];
|
|
|
|
BUG_ON(start2 < end && start < end2);
|
|
}
|
|
}
|
|
last_end = offsets[last_area] + sizes[last_area];
|
|
|
|
if (vmalloc_end - vmalloc_start < last_end) {
|
|
WARN_ON(true);
|
|
return NULL;
|
|
}
|
|
|
|
vms = kcalloc(nr_vms, sizeof(vms[0]), GFP_KERNEL);
|
|
vas = kcalloc(nr_vms, sizeof(vas[0]), GFP_KERNEL);
|
|
if (!vas || !vms)
|
|
goto err_free2;
|
|
|
|
for (area = 0; area < nr_vms; area++) {
|
|
vas[area] = kmem_cache_zalloc(vmap_area_cachep, GFP_KERNEL);
|
|
vms[area] = kzalloc(sizeof(struct vm_struct), GFP_KERNEL);
|
|
if (!vas[area] || !vms[area])
|
|
goto err_free;
|
|
}
|
|
retry:
|
|
spin_lock(&free_vmap_area_lock);
|
|
|
|
/* start scanning - we scan from the top, begin with the last area */
|
|
area = term_area = last_area;
|
|
start = offsets[area];
|
|
end = start + sizes[area];
|
|
|
|
va = pvm_find_va_enclose_addr(vmalloc_end);
|
|
base = pvm_determine_end_from_reverse(&va, align) - end;
|
|
|
|
while (true) {
|
|
/*
|
|
* base might have underflowed, add last_end before
|
|
* comparing.
|
|
*/
|
|
if (base + last_end < vmalloc_start + last_end)
|
|
goto overflow;
|
|
|
|
/*
|
|
* Fitting base has not been found.
|
|
*/
|
|
if (va == NULL)
|
|
goto overflow;
|
|
|
|
/*
|
|
* If required width exceeds current VA block, move
|
|
* base downwards and then recheck.
|
|
*/
|
|
if (base + end > va->va_end) {
|
|
base = pvm_determine_end_from_reverse(&va, align) - end;
|
|
term_area = area;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If this VA does not fit, move base downwards and recheck.
|
|
*/
|
|
if (base + start < va->va_start) {
|
|
va = node_to_va(rb_prev(&va->rb_node));
|
|
base = pvm_determine_end_from_reverse(&va, align) - end;
|
|
term_area = area;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* This area fits, move on to the previous one. If
|
|
* the previous one is the terminal one, we're done.
|
|
*/
|
|
area = (area + nr_vms - 1) % nr_vms;
|
|
if (area == term_area)
|
|
break;
|
|
|
|
start = offsets[area];
|
|
end = start + sizes[area];
|
|
va = pvm_find_va_enclose_addr(base + end);
|
|
}
|
|
|
|
/* we've found a fitting base, insert all va's */
|
|
for (area = 0; area < nr_vms; area++) {
|
|
int ret;
|
|
|
|
start = base + offsets[area];
|
|
size = sizes[area];
|
|
|
|
va = pvm_find_va_enclose_addr(start);
|
|
if (WARN_ON_ONCE(va == NULL))
|
|
/* It is a BUG(), but trigger recovery instead. */
|
|
goto recovery;
|
|
|
|
ret = adjust_va_to_fit_type(&free_vmap_area_root,
|
|
&free_vmap_area_list,
|
|
va, start, size);
|
|
if (WARN_ON_ONCE(unlikely(ret)))
|
|
/* It is a BUG(), but trigger recovery instead. */
|
|
goto recovery;
|
|
|
|
/* Allocated area. */
|
|
va = vas[area];
|
|
va->va_start = start;
|
|
va->va_end = start + size;
|
|
}
|
|
|
|
spin_unlock(&free_vmap_area_lock);
|
|
|
|
/* populate the kasan shadow space */
|
|
for (area = 0; area < nr_vms; area++) {
|
|
if (kasan_populate_vmalloc(vas[area]->va_start, sizes[area]))
|
|
goto err_free_shadow;
|
|
}
|
|
|
|
/* insert all vm's */
|
|
spin_lock(&vmap_area_lock);
|
|
for (area = 0; area < nr_vms; area++) {
|
|
insert_vmap_area(vas[area], &vmap_area_root, &vmap_area_list);
|
|
|
|
setup_vmalloc_vm_locked(vms[area], vas[area], VM_ALLOC,
|
|
pcpu_get_vm_areas);
|
|
}
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
/*
|
|
* Mark allocated areas as accessible. Do it now as a best-effort
|
|
* approach, as they can be mapped outside of vmalloc code.
|
|
* With hardware tag-based KASAN, marking is skipped for
|
|
* non-VM_ALLOC mappings, see __kasan_unpoison_vmalloc().
|
|
*/
|
|
for (area = 0; area < nr_vms; area++)
|
|
vms[area]->addr = kasan_unpoison_vmalloc(vms[area]->addr,
|
|
vms[area]->size, KASAN_VMALLOC_PROT_NORMAL);
|
|
|
|
kfree(vas);
|
|
return vms;
|
|
|
|
recovery:
|
|
/*
|
|
* Remove previously allocated areas. There is no
|
|
* need in removing these areas from the busy tree,
|
|
* because they are inserted only on the final step
|
|
* and when pcpu_get_vm_areas() is success.
|
|
*/
|
|
while (area--) {
|
|
orig_start = vas[area]->va_start;
|
|
orig_end = vas[area]->va_end;
|
|
va = merge_or_add_vmap_area_augment(vas[area], &free_vmap_area_root,
|
|
&free_vmap_area_list);
|
|
if (va)
|
|
kasan_release_vmalloc(orig_start, orig_end,
|
|
va->va_start, va->va_end);
|
|
vas[area] = NULL;
|
|
}
|
|
|
|
overflow:
|
|
spin_unlock(&free_vmap_area_lock);
|
|
if (!purged) {
|
|
purge_vmap_area_lazy();
|
|
purged = true;
|
|
|
|
/* Before "retry", check if we recover. */
|
|
for (area = 0; area < nr_vms; area++) {
|
|
if (vas[area])
|
|
continue;
|
|
|
|
vas[area] = kmem_cache_zalloc(
|
|
vmap_area_cachep, GFP_KERNEL);
|
|
if (!vas[area])
|
|
goto err_free;
|
|
}
|
|
|
|
goto retry;
|
|
}
|
|
|
|
err_free:
|
|
for (area = 0; area < nr_vms; area++) {
|
|
if (vas[area])
|
|
kmem_cache_free(vmap_area_cachep, vas[area]);
|
|
|
|
kfree(vms[area]);
|
|
}
|
|
err_free2:
|
|
kfree(vas);
|
|
kfree(vms);
|
|
return NULL;
|
|
|
|
err_free_shadow:
|
|
spin_lock(&free_vmap_area_lock);
|
|
/*
|
|
* We release all the vmalloc shadows, even the ones for regions that
|
|
* hadn't been successfully added. This relies on kasan_release_vmalloc
|
|
* being able to tolerate this case.
|
|
*/
|
|
for (area = 0; area < nr_vms; area++) {
|
|
orig_start = vas[area]->va_start;
|
|
orig_end = vas[area]->va_end;
|
|
va = merge_or_add_vmap_area_augment(vas[area], &free_vmap_area_root,
|
|
&free_vmap_area_list);
|
|
if (va)
|
|
kasan_release_vmalloc(orig_start, orig_end,
|
|
va->va_start, va->va_end);
|
|
vas[area] = NULL;
|
|
kfree(vms[area]);
|
|
}
|
|
spin_unlock(&free_vmap_area_lock);
|
|
kfree(vas);
|
|
kfree(vms);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* pcpu_free_vm_areas - free vmalloc areas for percpu allocator
|
|
* @vms: vm_struct pointer array returned by pcpu_get_vm_areas()
|
|
* @nr_vms: the number of allocated areas
|
|
*
|
|
* Free vm_structs and the array allocated by pcpu_get_vm_areas().
|
|
*/
|
|
void pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nr_vms; i++)
|
|
free_vm_area(vms[i]);
|
|
kfree(vms);
|
|
}
|
|
#endif /* CONFIG_SMP */
|
|
|
|
#ifdef CONFIG_PRINTK
|
|
bool vmalloc_dump_obj(void *object)
|
|
{
|
|
struct vm_struct *vm;
|
|
void *objp = (void *)PAGE_ALIGN((unsigned long)object);
|
|
|
|
vm = find_vm_area(objp);
|
|
if (!vm)
|
|
return false;
|
|
pr_cont(" %u-page vmalloc region starting at %#lx allocated at %pS\n",
|
|
vm->nr_pages, (unsigned long)vm->addr, vm->caller);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
static void *s_start(struct seq_file *m, loff_t *pos)
|
|
__acquires(&vmap_purge_lock)
|
|
__acquires(&vmap_area_lock)
|
|
{
|
|
mutex_lock(&vmap_purge_lock);
|
|
spin_lock(&vmap_area_lock);
|
|
|
|
return seq_list_start(&vmap_area_list, *pos);
|
|
}
|
|
|
|
static void *s_next(struct seq_file *m, void *p, loff_t *pos)
|
|
{
|
|
return seq_list_next(p, &vmap_area_list, pos);
|
|
}
|
|
|
|
static void s_stop(struct seq_file *m, void *p)
|
|
__releases(&vmap_area_lock)
|
|
__releases(&vmap_purge_lock)
|
|
{
|
|
spin_unlock(&vmap_area_lock);
|
|
mutex_unlock(&vmap_purge_lock);
|
|
}
|
|
|
|
static void show_numa_info(struct seq_file *m, struct vm_struct *v)
|
|
{
|
|
if (IS_ENABLED(CONFIG_NUMA)) {
|
|
unsigned int nr, *counters = m->private;
|
|
unsigned int step = 1U << vm_area_page_order(v);
|
|
|
|
if (!counters)
|
|
return;
|
|
|
|
if (v->flags & VM_UNINITIALIZED)
|
|
return;
|
|
/* Pair with smp_wmb() in clear_vm_uninitialized_flag() */
|
|
smp_rmb();
|
|
|
|
memset(counters, 0, nr_node_ids * sizeof(unsigned int));
|
|
|
|
for (nr = 0; nr < v->nr_pages; nr += step)
|
|
counters[page_to_nid(v->pages[nr])] += step;
|
|
for_each_node_state(nr, N_HIGH_MEMORY)
|
|
if (counters[nr])
|
|
seq_printf(m, " N%u=%u", nr, counters[nr]);
|
|
}
|
|
}
|
|
|
|
static void show_purge_info(struct seq_file *m)
|
|
{
|
|
struct vmap_area *va;
|
|
|
|
spin_lock(&purge_vmap_area_lock);
|
|
list_for_each_entry(va, &purge_vmap_area_list, list) {
|
|
seq_printf(m, "0x%pK-0x%pK %7ld unpurged vm_area\n",
|
|
(void *)va->va_start, (void *)va->va_end,
|
|
va->va_end - va->va_start);
|
|
}
|
|
spin_unlock(&purge_vmap_area_lock);
|
|
}
|
|
|
|
static int s_show(struct seq_file *m, void *p)
|
|
{
|
|
struct vmap_area *va;
|
|
struct vm_struct *v;
|
|
|
|
va = list_entry(p, struct vmap_area, list);
|
|
|
|
if (!va->vm) {
|
|
if (va->flags & VMAP_RAM)
|
|
seq_printf(m, "0x%pK-0x%pK %7ld vm_map_ram\n",
|
|
(void *)va->va_start, (void *)va->va_end,
|
|
va->va_end - va->va_start);
|
|
|
|
goto final;
|
|
}
|
|
|
|
v = va->vm;
|
|
|
|
seq_printf(m, "0x%pK-0x%pK %7ld",
|
|
v->addr, v->addr + v->size, v->size);
|
|
|
|
if (v->caller)
|
|
seq_printf(m, " %pS", v->caller);
|
|
|
|
if (v->nr_pages)
|
|
seq_printf(m, " pages=%d", v->nr_pages);
|
|
|
|
if (v->phys_addr)
|
|
seq_printf(m, " phys=%pa", &v->phys_addr);
|
|
|
|
if (v->flags & VM_IOREMAP)
|
|
seq_puts(m, " ioremap");
|
|
|
|
if (v->flags & VM_ALLOC)
|
|
seq_puts(m, " vmalloc");
|
|
|
|
if (v->flags & VM_MAP)
|
|
seq_puts(m, " vmap");
|
|
|
|
if (v->flags & VM_USERMAP)
|
|
seq_puts(m, " user");
|
|
|
|
if (v->flags & VM_DMA_COHERENT)
|
|
seq_puts(m, " dma-coherent");
|
|
|
|
if (is_vmalloc_addr(v->pages))
|
|
seq_puts(m, " vpages");
|
|
|
|
show_numa_info(m, v);
|
|
seq_putc(m, '\n');
|
|
|
|
/*
|
|
* As a final step, dump "unpurged" areas.
|
|
*/
|
|
final:
|
|
if (list_is_last(&va->list, &vmap_area_list))
|
|
show_purge_info(m);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct seq_operations vmalloc_op = {
|
|
.start = s_start,
|
|
.next = s_next,
|
|
.stop = s_stop,
|
|
.show = s_show,
|
|
};
|
|
|
|
static int __init proc_vmalloc_init(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_NUMA))
|
|
proc_create_seq_private("vmallocinfo", 0400, NULL,
|
|
&vmalloc_op,
|
|
nr_node_ids * sizeof(unsigned int), NULL);
|
|
else
|
|
proc_create_seq("vmallocinfo", 0400, NULL, &vmalloc_op);
|
|
return 0;
|
|
}
|
|
module_init(proc_vmalloc_init);
|
|
|
|
#endif
|
|
|
|
void __init vmalloc_init(void)
|
|
{
|
|
struct vmap_area *va;
|
|
struct vm_struct *tmp;
|
|
int i;
|
|
|
|
/*
|
|
* Create the cache for vmap_area objects.
|
|
*/
|
|
vmap_area_cachep = KMEM_CACHE(vmap_area, SLAB_PANIC);
|
|
|
|
for_each_possible_cpu(i) {
|
|
struct vmap_block_queue *vbq;
|
|
struct vfree_deferred *p;
|
|
|
|
vbq = &per_cpu(vmap_block_queue, i);
|
|
spin_lock_init(&vbq->lock);
|
|
INIT_LIST_HEAD(&vbq->free);
|
|
p = &per_cpu(vfree_deferred, i);
|
|
init_llist_head(&p->list);
|
|
INIT_WORK(&p->wq, delayed_vfree_work);
|
|
}
|
|
|
|
/* Import existing vmlist entries. */
|
|
for (tmp = vmlist; tmp; tmp = tmp->next) {
|
|
va = kmem_cache_zalloc(vmap_area_cachep, GFP_NOWAIT);
|
|
if (WARN_ON_ONCE(!va))
|
|
continue;
|
|
|
|
va->va_start = (unsigned long)tmp->addr;
|
|
va->va_end = va->va_start + tmp->size;
|
|
va->vm = tmp;
|
|
insert_vmap_area(va, &vmap_area_root, &vmap_area_list);
|
|
}
|
|
|
|
/*
|
|
* Now we can initialize a free vmap space.
|
|
*/
|
|
vmap_init_free_space();
|
|
vmap_initialized = true;
|
|
}
|