alloc_vmap_area()函数,负责从vmalloc区取出一段虚拟地址
下边是虚拟地址的分布信息,关注vmalloc段
vmalloc : 0xf0800000 - 0xff800000 ( 240 MB)
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xffc00000 - 0xfff00000 (3072 kB)
vmalloc : 0xf0800000 - 0xff800000 ( 240 MB)
lowmem : 0xc0000000 - 0xf0000000 ( 768 MB)
pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB)
modules : 0xbf000000 - 0xbfe00000 ( 14 MB)
.text : 0xc0008000 - 0xc0700000 (7136 kB)
.init : 0xc0900000 - 0xc0b00000 (2048 kB)
.data : 0xc0b00000 - 0xc0b64dd8 ( 404 kB)
.bss : 0xc0b6bd54 - 0xc0b98df0 ( 181 kB)
Hierarchical RCU implementation.
加log打印了一些信息:
cached_hole_size=00000000 cached_vstart=00000000 cached_align=00000000 vend=ff800000
cached_hole_size=00000000 cached_vstart=f0800000 cached_align=00001000 vend=ff800000
addr=f0802000
size=00002000 addr=f0802000
addr=f0802000 ddr+size=f0804000 first->va_start=f0800000 first->va_end=f0802000
cached_hole_size=00000000 cached_vstart=f0800000 cached_align=00001000 vend=ff800000
addr=f0804000
size=00002000 addr=f0804000
addr=f0804000 ddr+size=f0806000 first->va_start=f0802000 first->va_end=f0804000
cached_hole_size=00000000 cached_vstart=f0800000 cached_align=00001000 vend=ff800000
addr=f0806000
size=00002000 addr=f0806000
addr=f0806000 ddr+size=f0808000 first->va_start=f0804000 first->va_end=f0806000
cached_hole_size=00000000 cached_vstart=f0800000 cached_align=00001000 vend=ff800000
addr=f0808000
size=00002000 addr=f0808000
addr=f0808000 ddr+size=f080a000 first->va_start=f0806000 first->va_end=f0808000
smp_twd: clock not found -2
cached_hole_size=00000000 cached_vstart=f0800000 cached_align=00001000 vend=ff800000
addr=f080a000
size=00002000 addr=f080a000
addr=f080a000 ddr+size=f080c000 first->va_start=f0808000 first->va_end=f080a000
sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 89478484971ns
cached_hole_size=00000000 cached_vstart=f0800000 cached_align=00001000 vend=ff800000
addr=f080c000
size=00002000 addr=f080c000
addr=f080c000 ddr+size=f080e000 first->va_start=f080a000 first->va_end=f080c000
/*
* 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;
struct rb_node *n;
unsigned long addr;
int purged = 0;
struct vmap_area *first;
BUG_ON(!size); // 长度为0不行
BUG_ON(offset_in_page(size)); // 长度不是页面整数倍不行
BUG_ON(!is_power_of_2(align)); // 长度不是2的n次方不行
might_sleep(); // 查看当前是否在不可睡眠的区域
va = kmalloc_node(sizeof(struct vmap_area), // vmap_area结构管理管理KVA地址空间,
gfp_mask & GFP_RECLAIM_MASK, node); // vm_struct仅代表一个与vmalloc对应的地址空间
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 & GFP_RECLAIM_MASK);
retry:
spin_lock(&vmap_area_lock);
/*
* Invalidate cache if we have more permissive parameters.
* cached_hole_size notes the largest hole noticed _below_
* the vmap_area cached in free_vmap_cache: if size fits
* into that hole, we want to scan from vstart to reuse
* the hole instead of allocating above free_vmap_cache.
* Note that __free_vmap_area may update free_vmap_cache
* without updating cached_hole_size or cached_align.
*/
if (!free_vmap_cache ||
size < cached_hole_size ||
vstart < cached_vstart ||
align < cached_align) { // 一般情况下第一次会进入这个判断,
nocache: // 设置以下变量的值
cached_hole_size = 0;
free_vmap_cache = NULL;
}
/* record if we encounter less permissive parameters */
cached_vstart = vstart; // cached_vstart 一直会是vmalloc的起始地址 f0800000
cached_align = align; // vstart 传进来就是vmalloc的起始地址
/* find starting point for our search */
if (free_vmap_cache) { // 第一次不会进入这里
first = rb_entry(free_vmap_cache, struct vmap_area, rb_node);
addr = ALIGN(first->va_end, align); // 上次分配的结束地址向上对齐,得到本次可用的地址
if (addr < vstart) // 可用地址小于起始地址,缓存失效
goto nocache;
if (addr + size < addr) // 溢出会retry的,会从头查起
goto overflow;
} else { // 第一次或free_vmap_cache为空会走这个流程
addr = ALIGN(vstart, align); // 将虚拟地址向上对齐,对vmalloc来说,vstart表示可以使用的虚拟地址空间起始地址,也就是从头开始搜
if (addr + size < addr)
goto overflow;
n = vmap_area_root.rb_node;
first = NULL;
while (n) { // 搜索红黑树,这里都是虚拟地址
struct vmap_area *tmp;
tmp = rb_entry(n, struct vmap_area, rb_node); // 获取当前红黑树节点n对应的vmap_area描述符
if (tmp->va_end >= addr) { // 该区域结束地址大于要搜索的起始地址
first = tmp; // 记录下有这样一个结束地址符合的区
if (tmp->va_start <= addr) // 如果该区的起始虚拟地址小于要搜索的起始地址,说明该区落在了查找的起始地址范围内
break; // 从当前节点开始向后搜索符合的虚拟地址空洞
n = n->rb_left; // 该区的起始虚拟地址也大于要搜索的起始地址,这个区处的地址太高,需要向树的左侧查询
} else // 该区的虚拟地址太小,需要向树的右侧查询
n = n->rb_right;
}
if (!first)
goto found;
}
// 从找到的节点开始搜索空洞
/* from the starting point, walk areas until a suitable hole is found */
while (addr + size > first->va_start && addr + size <= vend) { // 要求的地址空间与当前地址空间重叠
if (addr + cached_hole_size < first->va_start) // cached_hole_size 保存的是搜索过的最大空洞
cached_hole_size = first->va_start - addr; // 计算当前节点与要搜索的地址(addr)之间的空洞大小
addr = ALIGN(first->va_end, align); // 搜索地址往后挪,挪到本区的结束位置
if (addr + size < addr) // 溢出说明空间不足,会retry的,从头查起
goto overflow;
if (list_is_last(&first->list, &vmap_area_list))// 链表空,或已循环到表尾
goto found;
first = list_next_entry(first, list); // 寻找下一个节点
}
found:
if (addr + size > vend) // 如果表循环一周或为空则到overflow 从头查起
goto overflow;
va->va_start = addr;
va->va_end = addr + size;
va->flags = 0;
__insert_vmap_area(va);
free_vmap_cache = &va->rb_node;
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);
return va;
overflow:
spin_unlock(&vmap_area_lock);
if (!purged) { // 如果还没有进行KVA地址空间清理则进行KVA的清理,可能会释放出一些空间
purge_vmap_area_lazy(); // 清理懒模式下没有及时释放的KVA空间
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= to increase size\n",
size);
kfree(va);
return ERR_PTR(-EBUSY);
}
阅读(4453) | 评论(0) | 转发(0) |