本文主要是介绍Global kva allocator,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Global kva allocator 数据结构
/*** Global kva allocator ***/#define VM_LAZY_FREE 0x01
#define VM_LAZY_FREEING 0x02
#define VM_VM_AREA 0x04struct vmap_area {unsigned long va_start;unsigned long va_end;unsigned long flags; /* 参见上面,如VM_VM_AREA */struct rb_node rb_node; /* address sorted rbtree */struct list_head list; /* address sorted list */struct list_head purge_list; /* "lazy purge" list */void *private;struct rcu_head rcu_head;
};struct vm_struct {struct vm_struct *next;void *addr;unsigned long size;unsigned long flags;struct page **pages;unsigned int nr_pages;unsigned long phys_addr;void *caller;
};
两者关系
static void setup_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,unsigned long flags, void *caller)
{vm->flags = flags;vm->addr = (void *)va->va_start;vm->size = va->va_end - va->va_start;vm->caller = caller;va->private = vm;va->flags |= VM_VM_AREA;
}
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.*/
void *vmalloc(unsigned long size)
{return __vmalloc_node(size, 1, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL,-1, __builtin_return_address(0));
}
__vmalloc_node
/*** __vmalloc_node - allocate virtually contiguous memory* @size: allocation size* @align: desired alignment* @gfp_mask: flags for the page level allocator* @prot: protection mask for the allocated pages* @node: node to use for allocation or -1* @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.*/
static void *__vmalloc_node(unsigned long size, unsigned long align,gfp_t gfp_mask, pgprot_t prot,int node, void *caller)
{struct vm_struct *area;void *addr;unsigned long real_size = size;size = PAGE_ALIGN(size);if (!size || (size >> PAGE_SHIFT) > totalram_pages)return NULL;area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNLIST,VMALLOC_START, VMALLOC_END, node,gfp_mask, caller);if (!area)return NULL;addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller);/** In this function, newly allocated vm_struct is not added* to vmlist at __get_vm_area_node(). so, it is added here.*/insert_vmalloc_vmlist(area);/** A ref_count = 3 is needed because the vm_struct and vmap_area* structures allocated in the __get_vm_area_node() function contain* references to the virtual address of the vmalloc'ed block.*/kmemleak_alloc(addr, real_size, 3, gfp_mask);return addr;
}
__get_vm_area_node
static struct vm_struct *__get_vm_area_node(unsigned long size,unsigned long align, unsigned long flags, unsigned long start,unsigned long end, int node, gfp_t gfp_mask, void *caller)
{static struct vmap_area *va;struct vm_struct *area;BUG_ON(in_interrupt());if (flags & VM_IOREMAP) {int bit = fls(size);if (bit > IOREMAP_MAX_ORDER)bit = IOREMAP_MAX_ORDER;else if (bit < PAGE_SHIFT)bit = PAGE_SHIFT;align = 1ul << bit;}size = PAGE_ALIGN(size);if (unlikely(!size))return NULL;area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);if (unlikely(!area))return NULL;/** We always allocate a guard page.*/size += PAGE_SIZE;/** Allocate a region of KVA of the specified size and alignment, within the* vstart and vend.*//* 用rb树管理vmalloc地址空间,找到一个大小合适的空洞,也就是一个连续的虚拟地址空间 */va = alloc_vmap_area(size, align, start, end, node, gfp_mask); /* 找到一个大小合适的空洞 */if (IS_ERR(va)) {kfree(area);return NULL;}/** When this function is called from __vmalloc_node,* we do not add vm_struct to vmlist here to avoid* accessing uninitialized members of vm_struct such as* pages and nr_pages fields. They will be set later.* To distinguish it from others, we use a VM_UNLIST flag.*/if (flags & VM_UNLIST)setup_vmalloc_vm(area, va, flags, caller);elseinsert_vmalloc_vm(area, va, flags, caller);return area;
}
__vmalloc_area_node
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,pgprot_t prot, int node, void *caller)
{struct page **pages;unsigned int nr_pages, array_size, i;nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT; /* 有一页空闲,作为gap */array_size = (nr_pages * sizeof(struct page *)); /* 分配一个page数组*/area->nr_pages = nr_pages;/* Please note that the recursion is strictly bounded. */if (array_size > PAGE_SIZE) { /* 数组太大,用vmalloc分配,注意递归 */pages = __vmalloc_node(array_size, 1, gfp_mask | __GFP_ZERO,PAGE_KERNEL, node, caller);area->flags |= VM_VPAGES;} else {pages = kmalloc_node(array_size, /* 数组小于1page, 用kmalloc分配 */(gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,node);}area->pages = pages;area->caller = caller;if (!area->pages) {remove_vm_area(area->addr);kfree(area);return NULL;}for (i = 0; i < area->nr_pages; i++) {struct page *page;if (node < 0) /* 一页一页分配物理内存,注意,完全不保证物理内存连续性 */page = alloc_page(gfp_mask); /* 记住,标志中有__GFP_HIGHMEM */elsepage = alloc_pages_node(node, gfp_mask, 0);if (unlikely(!page)) {/* Successfully allocated i pages, free them in __vunmap() */area->nr_pages = i;goto fail;}area->pages[i] = page;}if (map_vm_area(area, prot, &pages)) /* 一页一页分配,但是映射到一个连续的kva范围上去 */goto fail;return area->addr;fail:vfree(area->addr);return NULL;
}
这篇关于Global kva allocator的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!