mirror of
https://github.com/torvalds/linux.git
synced 2024-11-30 16:11:38 +00:00
7ca3027b72
In commit121e6f3258
("mm/vmalloc: hugepage vmalloc mappings"), __vmalloc_node_range was changed such that __get_vm_area_node was no longer called with the requested/real size of the vmalloc allocation, but rather with a rounded-up size. This means that __get_vm_area_node called kasan_unpoision_vmalloc() with a rounded up size rather than the real size. This led to it allowing access to too much memory and so missing vmalloc OOBs and failing the kasan kunit tests. Pass the real size and the desired shift into __get_vm_area_node. This allows it to round up the size for the underlying allocators while still unpoisioning the correct quantity of shadow memory. Adjust the other call-sites to pass in PAGE_SHIFT for the shift value. Link: https://lkml.kernel.org/r/20210617081330.98629-1-dja@axtens.net Link: https://bugzilla.kernel.org/show_bug.cgi?id=213335 Fixes:121e6f3258
("mm/vmalloc: hugepage vmalloc mappings") Signed-off-by: Daniel Axtens <dja@axtens.net> Tested-by: David Gow <davidgow@google.com> Reviewed-by: Nicholas Piggin <npiggin@gmail.com> Reviewed-by: Uladzislau Rezki (Sony) <urezki@gmail.com> Tested-by: Andrey Konovalov <andreyknvl@gmail.com> Acked-by: Andrey Konovalov <andreyknvl@gmail.com> Cc: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
3865 lines
98 KiB
C
3865 lines
98 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/rcupdate.h>
|
|
#include <linux/pfn.h>
|
|
#include <linux/kmemleak.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/compiler.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 <asm/tlbflush.h>
|
|
#include <asm/shmparam.h>
|
|
|
|
#include "internal.h"
|
|
#include "pgalloc-track.h"
|
|
|
|
#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)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);
|
|
|
|
static void __vunmap(const void *, int);
|
|
|
|
static void free_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))
|
|
__vunmap((void *)llnode, 1);
|
|
}
|
|
|
|
/*** 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,
|
|
pgtbl_mod_mask *mask)
|
|
{
|
|
pte_t *pte;
|
|
u64 pfn;
|
|
|
|
pfn = phys_addr >> PAGE_SHIFT;
|
|
pte = pte_alloc_kernel_track(pmd, addr, mask);
|
|
if (!pte)
|
|
return -ENOMEM;
|
|
do {
|
|
BUG_ON(!pte_none(*pte));
|
|
set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
|
|
pfn++;
|
|
} while (pte++, addr += PAGE_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, 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 vmap_range(unsigned long addr, unsigned long end,
|
|
phys_addr_t phys_addr, pgprot_t prot,
|
|
unsigned int max_page_shift)
|
|
{
|
|
int err;
|
|
|
|
err = vmap_range_noflush(addr, end, phys_addr, prot, max_page_shift);
|
|
flush_cache_vmap(addr, end);
|
|
|
|
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;
|
|
int cleared;
|
|
|
|
p4d = p4d_offset(pgd, addr);
|
|
do {
|
|
next = p4d_addr_end(addr, end);
|
|
|
|
cleared = p4d_clear_huge(p4d);
|
|
if (cleared || p4d_bad(*p4d))
|
|
*mask |= PGTBL_P4D_MODIFIED;
|
|
|
|
if (cleared)
|
|
continue;
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
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),
|
|
__pa(page_address(pages[i])), prot,
|
|
page_shift);
|
|
if (err)
|
|
return err;
|
|
|
|
addr += 1UL << page_shift;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 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)x;
|
|
if (addr >= MODULES_VADDR && addr < MODULES_END)
|
|
return 1;
|
|
#endif
|
|
return is_vmalloc_addr(x);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* 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));
|
|
}
|
|
|
|
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 unsigned long lazy_max_pages(void);
|
|
|
|
static atomic_long_t nr_vmalloc_pages;
|
|
|
|
unsigned long vmalloc_nr_pages(void)
|
|
{
|
|
return atomic_long_read(&nr_vmalloc_pages);
|
|
}
|
|
|
|
static struct vmap_area *__find_vmap_area(unsigned long addr)
|
|
{
|
|
struct rb_node *n = vmap_area_root.rb_node;
|
|
|
|
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_start < tmp_va->va_end &&
|
|
va->va_end <= tmp_va->va_start)
|
|
link = &(*link)->rb_left;
|
|
else if (va->va_end > tmp_va->va_start &&
|
|
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)
|
|
{
|
|
/*
|
|
* 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 (root == &free_vmap_area_root) {
|
|
/*
|
|
* 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 of 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
|
|
unlink_va(struct vmap_area *va, struct rb_root *root)
|
|
{
|
|
if (WARN_ON(RB_EMPTY_NODE(&va->rb_node)))
|
|
return;
|
|
|
|
if (root == &free_vmap_area_root)
|
|
rb_erase_augmented(&va->rb_node,
|
|
root, &free_vmap_area_rb_augment_cb);
|
|
else
|
|
rb_erase(&va->rb_node, root);
|
|
|
|
list_del(&va->list);
|
|
RB_CLEAR_NODE(&va->rb_node);
|
|
}
|
|
|
|
#if DEBUG_AUGMENT_PROPAGATE_CHECK
|
|
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(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)
|
|
{
|
|
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);
|
|
|
|
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);
|
|
|
|
return va;
|
|
}
|
|
|
|
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);
|
|
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.
|
|
*/
|
|
static __always_inline struct vmap_area *
|
|
find_vmap_lowest_match(unsigned long size,
|
|
unsigned long align, unsigned long vstart)
|
|
{
|
|
struct vmap_area *va;
|
|
struct rb_node *node;
|
|
unsigned long length;
|
|
|
|
/* Start from the root. */
|
|
node = free_vmap_area_root.rb_node;
|
|
|
|
/* Adjust the search size for alignment overhead. */
|
|
length = size + align - 1;
|
|
|
|
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
|
|
* only once due to "vstart" restriction.
|
|
*/
|
|
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) {
|
|
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(unsigned long size,
|
|
unsigned long align, unsigned long vstart)
|
|
{
|
|
struct vmap_area *va;
|
|
|
|
list_for_each_entry(va, &free_vmap_area_list, list) {
|
|
if (!is_within_this_va(va, size, align, vstart))
|
|
continue;
|
|
|
|
return va;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
find_vmap_lowest_match_check(unsigned long size)
|
|
{
|
|
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(size, 1, vstart);
|
|
va_2 = find_vmap_lowest_linear_match(size, 1, 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 vmap_area *va,
|
|
unsigned long nva_start_addr, unsigned long size,
|
|
enum fit_type type)
|
|
{
|
|
struct vmap_area *lva = NULL;
|
|
|
|
if (type == FL_FIT_TYPE) {
|
|
/*
|
|
* No need to split VA, it fully fits.
|
|
*
|
|
* | |
|
|
* V NVA V
|
|
* |---------------|
|
|
*/
|
|
unlink_va(va, &free_vmap_area_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,
|
|
&free_vmap_area_root, &free_vmap_area_list);
|
|
}
|
|
|
|
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(unsigned long size, unsigned long align,
|
|
unsigned long vstart, unsigned long vend)
|
|
{
|
|
unsigned long nva_start_addr;
|
|
struct vmap_area *va;
|
|
enum fit_type type;
|
|
int ret;
|
|
|
|
va = find_vmap_lowest_match(size, align, vstart);
|
|
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;
|
|
|
|
/* Classify what we have found. */
|
|
type = classify_va_fit_type(va, nva_start_addr, size);
|
|
if (WARN_ON_ONCE(type == NOTHING_FIT))
|
|
return vend;
|
|
|
|
/* Update the free vmap_area. */
|
|
ret = adjust_va_to_fit_type(va, nva_start_addr, size, type);
|
|
if (ret)
|
|
return vend;
|
|
|
|
#if DEBUG_AUGMENT_LOWEST_MATCH_CHECK
|
|
find_vmap_lowest_match_check(size);
|
|
#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)
|
|
{
|
|
struct vmap_area *va;
|
|
unsigned long addr;
|
|
int purged = 0;
|
|
int ret;
|
|
|
|
BUG_ON(!size);
|
|
BUG_ON(offset_in_page(size));
|
|
BUG_ON(!is_power_of_2(align));
|
|
|
|
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(size, align, vstart, vend);
|
|
spin_unlock(&free_vmap_area_lock);
|
|
|
|
/*
|
|
* 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;
|
|
|
|
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;
|
|
}
|
|
|
|
if (gfpflags_allow_blocking(gfp_mask)) {
|
|
unsigned long 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 look, 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);
|
|
|
|
/*
|
|
* called before a call to iounmap() if the caller wants vm_area_struct's
|
|
* immediately freed.
|
|
*/
|
|
void set_iounmap_nonlazy(void)
|
|
{
|
|
atomic_long_set(&vmap_lazy_nr, lazy_max_pages()+1);
|
|
}
|
|
|
|
/*
|
|
* Purges all lazily-freed vmap areas.
|
|
*/
|
|
static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end)
|
|
{
|
|
unsigned long resched_threshold;
|
|
struct list_head local_pure_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_pure_list);
|
|
spin_unlock(&purge_vmap_area_lock);
|
|
|
|
if (unlikely(list_empty(&local_pure_list)))
|
|
return false;
|
|
|
|
start = min(start,
|
|
list_first_entry(&local_pure_list,
|
|
struct vmap_area, list)->va_start);
|
|
|
|
end = max(end,
|
|
list_last_entry(&local_pure_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_pure_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);
|
|
|
|
if (atomic_long_read(&vmap_lazy_nr) < resched_threshold)
|
|
cond_resched_lock(&free_vmap_area_lock);
|
|
}
|
|
spin_unlock(&free_vmap_area_lock);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Kick off a purge of the outstanding lazy areas. Don't bother if somebody
|
|
* is already purging.
|
|
*/
|
|
static void try_purge_vmap_area_lazy(void)
|
|
{
|
|
if (mutex_trylock(&vmap_purge_lock)) {
|
|
__purge_vmap_area_lazy(ULONG_MAX, 0);
|
|
mutex_unlock(&vmap_purge_lock);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* Free a vmap area, caller ensuring that the area has been unmapped
|
|
* 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;
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
unlink_va(va, &vmap_area_root);
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
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);
|
|
|
|
/* After this point, we may free va at any time */
|
|
if (unlikely(nr_lazy > lazy_max_pages()))
|
|
try_purge_vmap_area_lazy();
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
static struct vmap_area *find_vmap_area(unsigned long addr)
|
|
{
|
|
struct vmap_area *va;
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
va = __find_vmap_area(addr);
|
|
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)
|
|
|
|
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;
|
|
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);
|
|
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));
|
|
vb->free = VMAP_BBMAP_BITS - (1UL << order);
|
|
vb->dirty = 0;
|
|
vb->dirty_min = VMAP_BBMAP_BITS;
|
|
vb->dirty_max = 0;
|
|
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 = &get_cpu_var(vmap_block_queue);
|
|
spin_lock(&vbq->lock);
|
|
list_add_tail_rcu(&vb->free_list, &vbq->free);
|
|
spin_unlock(&vbq->lock);
|
|
put_cpu_var(vmap_block_queue);
|
|
|
|
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);
|
|
|
|
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 = &get_cpu_var(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;
|
|
if (vb->free == 0) {
|
|
spin_lock(&vbq->lock);
|
|
list_del_rcu(&vb->free_list);
|
|
spin_unlock(&vbq->lock);
|
|
}
|
|
|
|
spin_unlock(&vb->lock);
|
|
break;
|
|
}
|
|
|
|
put_cpu_var(vmap_block_queue);
|
|
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));
|
|
|
|
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)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_vmap_area(addr);
|
|
BUG_ON(!va);
|
|
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);
|
|
if (IS_ERR(va))
|
|
return NULL;
|
|
|
|
addr = va->va_start;
|
|
mem = (void *)addr;
|
|
}
|
|
|
|
kasan_unpoison_vmalloc(mem, size);
|
|
|
|
if (vmap_pages_range(addr, addr + size, PAGE_KERNEL,
|
|
pages, PAGE_SHIFT) < 0) {
|
|
vm_unmap_ram(mem, count);
|
|
return NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
static size_t vm_init_off __initdata;
|
|
unsigned long addr;
|
|
|
|
addr = ALIGN(VMALLOC_START + vm_init_off, align);
|
|
vm_init_off = PFN_ALIGN(addr + vm->size) - VMALLOC_START;
|
|
|
|
vm->addr = (void *)addr;
|
|
|
|
vm_area_add_early(vm);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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, free_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;
|
|
}
|
|
|
|
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);
|
|
if (IS_ERR(va)) {
|
|
kfree(area);
|
|
return NULL;
|
|
}
|
|
|
|
kasan_unpoison_vmalloc((void *)va->va_start, requested_size);
|
|
|
|
setup_vmalloc_vm(area, va, flags, caller);
|
|
|
|
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;
|
|
|
|
might_sleep();
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
va = __find_vmap_area((unsigned long)addr);
|
|
if (va && va->vm) {
|
|
struct vm_struct *vm = va->vm;
|
|
|
|
va->vm = NULL;
|
|
spin_unlock(&vmap_area_lock);
|
|
|
|
kasan_free_shadow(vm);
|
|
free_unmap_vmap_area(va);
|
|
|
|
return vm;
|
|
}
|
|
|
|
spin_unlock(&vmap_area_lock);
|
|
return NULL;
|
|
}
|
|
|
|
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]);
|
|
}
|
|
|
|
/* Handle removing and resetting vm mappings related to the vm_struct. */
|
|
static void vm_remove_mappings(struct vm_struct *area, int deallocate_pages)
|
|
{
|
|
unsigned long start = ULONG_MAX, end = 0;
|
|
unsigned int page_order = vm_area_page_order(area);
|
|
int flush_reset = area->flags & VM_FLUSH_RESET_PERMS;
|
|
int flush_dmap = 0;
|
|
int i;
|
|
|
|
remove_vm_area(area->addr);
|
|
|
|
/* If this is not VM_FLUSH_RESET_PERMS memory, no need for the below. */
|
|
if (!flush_reset)
|
|
return;
|
|
|
|
/*
|
|
* If not deallocating pages, just do the flush of the VM area and
|
|
* return.
|
|
*/
|
|
if (!deallocate_pages) {
|
|
vm_unmap_aliases();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If execution gets here, flush the vm mapping and reset the direct
|
|
* map. Find the start and end range of the direct mappings to make sure
|
|
* 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 __vunmap(const void *addr, int deallocate_pages)
|
|
{
|
|
struct vm_struct *area;
|
|
|
|
if (!addr)
|
|
return;
|
|
|
|
if (WARN(!PAGE_ALIGNED(addr), "Trying to vfree() bad address (%p)\n",
|
|
addr))
|
|
return;
|
|
|
|
area = find_vm_area(addr);
|
|
if (unlikely(!area)) {
|
|
WARN(1, KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",
|
|
addr);
|
|
return;
|
|
}
|
|
|
|
debug_check_no_locks_freed(area->addr, get_vm_area_size(area));
|
|
debug_check_no_obj_freed(area->addr, get_vm_area_size(area));
|
|
|
|
kasan_poison_vmalloc(area->addr, get_vm_area_size(area));
|
|
|
|
vm_remove_mappings(area, deallocate_pages);
|
|
|
|
if (deallocate_pages) {
|
|
unsigned int page_order = vm_area_page_order(area);
|
|
int i;
|
|
|
|
for (i = 0; i < area->nr_pages; i += 1U << page_order) {
|
|
struct page *page = area->pages[i];
|
|
|
|
BUG_ON(!page);
|
|
__free_pages(page, page_order);
|
|
}
|
|
atomic_long_sub(area->nr_pages, &nr_vmalloc_pages);
|
|
|
|
kvfree(area->pages);
|
|
}
|
|
|
|
kfree(area);
|
|
}
|
|
|
|
static inline void __vfree_deferred(const void *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.
|
|
*/
|
|
struct vfree_deferred *p = raw_cpu_ptr(&vfree_deferred);
|
|
|
|
if (llist_add((struct llist_node *)addr, &p->list))
|
|
schedule_work(&p->wq);
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
BUG_ON(in_nmi());
|
|
|
|
kmemleak_free(addr);
|
|
|
|
if (!addr)
|
|
return;
|
|
__vfree_deferred(addr);
|
|
}
|
|
|
|
static void __vfree(const void *addr)
|
|
{
|
|
if (unlikely(in_interrupt()))
|
|
__vfree_deferred(addr);
|
|
else
|
|
__vunmap(addr, 1);
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
BUG_ON(in_nmi());
|
|
|
|
kmemleak_free(addr);
|
|
|
|
might_sleep_if(!in_interrupt());
|
|
|
|
if (!addr)
|
|
return;
|
|
|
|
__vfree(addr);
|
|
}
|
|
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)
|
|
{
|
|
BUG_ON(in_interrupt());
|
|
might_sleep();
|
|
if (addr)
|
|
__vunmap(addr, 0);
|
|
}
|
|
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 (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 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;
|
|
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;
|
|
struct page **pages;
|
|
unsigned int i;
|
|
|
|
array_size = (unsigned long)nr_small_pages * sizeof(struct page *);
|
|
gfp_mask |= __GFP_NOWARN;
|
|
if (!(gfp_mask & (GFP_DMA | GFP_DMA32)))
|
|
gfp_mask |= __GFP_HIGHMEM;
|
|
|
|
/* Please note that the recursion is strictly bounded. */
|
|
if (array_size > PAGE_SIZE) {
|
|
pages = __vmalloc_node(array_size, 1, nested_gfp, node,
|
|
area->caller);
|
|
} else {
|
|
pages = kmalloc_node(array_size, nested_gfp, node);
|
|
}
|
|
|
|
if (!pages) {
|
|
free_vm_area(area);
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc size %lu allocation failure: "
|
|
"page array size %lu allocation failed",
|
|
nr_small_pages * PAGE_SIZE, array_size);
|
|
return NULL;
|
|
}
|
|
|
|
area->pages = pages;
|
|
area->nr_pages = nr_small_pages;
|
|
set_vm_area_page_order(area, page_shift - PAGE_SHIFT);
|
|
|
|
page_order = vm_area_page_order(area);
|
|
|
|
/*
|
|
* 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 < area->nr_pages; i += 1U << page_order) {
|
|
struct page *page;
|
|
int p;
|
|
|
|
/* Compound pages required for remap_vmalloc_page */
|
|
page = alloc_pages_node(node, gfp_mask | __GFP_COMP, page_order);
|
|
if (unlikely(!page)) {
|
|
/* Successfully allocated i pages, free them in __vfree() */
|
|
area->nr_pages = i;
|
|
atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc size %lu allocation failure: "
|
|
"page order %u allocation failed",
|
|
area->nr_pages * PAGE_SIZE, page_order);
|
|
goto fail;
|
|
}
|
|
|
|
for (p = 0; p < (1U << page_order); p++)
|
|
area->pages[i + p] = page + p;
|
|
|
|
if (gfpflags_allow_blocking(gfp_mask))
|
|
cond_resched();
|
|
}
|
|
atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
|
|
|
|
if (vmap_pages_range(addr, addr + size, prot, pages, page_shift) < 0) {
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc size %lu allocation failure: "
|
|
"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. 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 *addr;
|
|
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 size %lu allocation failure: "
|
|
"exceeds total pages", real_size);
|
|
return NULL;
|
|
}
|
|
|
|
if (vmap_allow_huge && !(vm_flags & VM_NO_HUGE_VMAP) &&
|
|
arch_vmap_pmd_supported(prot)) {
|
|
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 (size_per_node >= PMD_SIZE) {
|
|
shift = PMD_SHIFT;
|
|
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) {
|
|
warn_alloc(gfp_mask, NULL,
|
|
"vmalloc size %lu allocation failure: "
|
|
"vm_struct allocation failed", real_size);
|
|
goto fail;
|
|
}
|
|
|
|
addr = __vmalloc_area_node(area, gfp_mask, prot, shift, node);
|
|
if (!addr)
|
|
goto fail;
|
|
|
|
/*
|
|
* 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);
|
|
kmemleak_vmalloc(area, size, gfp_mask);
|
|
|
|
return 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_no_huge - allocate virtually contiguous memory using small pages
|
|
* @size: allocation size
|
|
*
|
|
* Allocate enough non-huge 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_no_huge(unsigned long size)
|
|
{
|
|
return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END,
|
|
GFP_KERNEL, PAGE_KERNEL, VM_NO_HUGE_VMAP,
|
|
NUMA_NO_NODE, __builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(vmalloc_no_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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/* Don't allow overflow */
|
|
if ((unsigned long) addr + count < count)
|
|
count = -(unsigned long) addr;
|
|
|
|
spin_lock(&vmap_area_lock);
|
|
va = __find_vmap_area((unsigned long)addr);
|
|
if (!va)
|
|
goto finished;
|
|
list_for_each_entry_from(va, &vmap_area_list, list) {
|
|
if (!count)
|
|
break;
|
|
|
|
if (!va->vm)
|
|
continue;
|
|
|
|
vm = va->vm;
|
|
vaddr = (char *) vm->addr;
|
|
if (addr >= vaddr + get_vm_area_size(vm))
|
|
continue;
|
|
while (addr < vaddr) {
|
|
if (count == 0)
|
|
goto finished;
|
|
*buf = '\0';
|
|
buf++;
|
|
addr++;
|
|
count--;
|
|
}
|
|
n = vaddr + get_vm_area_size(vm) - addr;
|
|
if (n > count)
|
|
n = count;
|
|
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);
|
|
|
|
vma->vm_flags |= 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;
|
|
enum fit_type type;
|
|
|
|
/* 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;
|
|
|
|
type = classify_va_fit_type(va, start, size);
|
|
if (WARN_ON_ONCE(type == NOTHING_FIT))
|
|
/* It is a BUG(), but trigger recovery instead. */
|
|
goto recovery;
|
|
|
|
ret = adjust_va_to_fit_type(va, start, size, type);
|
|
if (unlikely(ret))
|
|
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;
|
|
|
|
kasan_unpoison_vmalloc((void *)vas[area]->va_start,
|
|
sizes[area]);
|
|
}
|
|
|
|
/* 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);
|
|
|
|
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;
|
|
|
|
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++)
|
|
counters[page_to_nid(v->pages[nr])]++;
|
|
|
|
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);
|
|
|
|
/*
|
|
* s_show can encounter race with remove_vm_area, !vm on behalf
|
|
* of vmap area is being tear down or vm_map_ram allocation.
|
|
*/
|
|
if (!va->vm) {
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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.
|
|
*/
|
|
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
|