分类: LINUX
2014-03-13 21:09:06
原文地址:Vi Linux内存 之 bootmem分配器(三) 作者:palals
bootmem内存的释放
free_bootmem
释放一块内存给bootmem分配器。
参数:
1) addr:待释放内存的物理地址
2) size:待释放内存的大小
void __init free_bootmem(unsigned long addr, unsigned long size)
{
#ifdef CONFIG_NO_BOOTMEM
kmemleak_free_part(__va(addr), size);
memblock_x86_free_range(addr, addr + size);
#else
unsigned long start, end;
/* 内存泄露检测相关,不去管它 */
kmemleak_free_part(__va(addr), size);
/* 获得起始pfn */
start = PFN_UP(addr);
/* 获得截止pfn */
end = PFN_DOWN(addr + size);
/* 将这部分空间对应的位图比特位清0 */
mark_bootmem(start, end, 0, 0);
#endif
}
mark_bootmem
设置某块内存区对应的位图比特位。
参数:
1) start:起始pfn
2) end:截止pfn。start与end间的bootmem可能分布在不同内存节点的bootmem上。
3) reserve:比特位置0还是1
4) flags:标志,参见BOOTMEM_EXCLUSIVE
static int __init mark_bootmem(unsigned long start, unsigned long end,
int reserve, int flags)
{
unsigned long pos;
bootmem_data_t *bdata;
/* pos记录当前标记的位置 */
pos = start;
/* 遍历所有节点的bootmem,待标记的内存区可能跨越多个节点 */
list_for_each_entry(bdata, &bdata_list, list) {
int err;
unsigned long max;
/* 如果bootmem跨越了多个节点,那么不同节点的bootmem管理的内存区在地址空间上必须是连续的。bdata_list链表是按地址高低有序的,所以只有在初始时(pos==start)pos可能不在bootmem范围内,一旦开始标记,pos必定落在某个节点的bootmem范围内 */
if (pos < bdata->node_min_pfn ||
pos >= bdata->node_low_pfn) {
BUG_ON(pos != start);
continue;
}
/* 获得此次标记的截止pfn */
max = min(bdata->node_low_pfn, end);
/* 标记此内存节点bootmem范围内的内存区 */
err = mark_bootmem_node(bdata, pos, max, reserve, flags);
/* 如果此次标记是分配内存,并且中途放生了错误,释放之前已经分配的内存 */
if (reserve && err) {
mark_bootmem(start, pos, 0, 0);
return err;
}
/* 如果待标记内存区截止于此节点bootmem范围内,无需扫描下一个节点 */
if (max == end)
return 0;
/* pos指向本节点bootmem的截止pfn,即下一个节点的起始pfn */
pos = bdata->node_low_pfn;
}
BUG();
}
mark_bootmem_node
标记某个内存节点的bootmem,可能是分配也可能是释放。
static int __init mark_bootmem_node(bootmem_data_t *bdata,
unsigned long start, unsigned long end,
int reserve, int flags)
{
unsigned long sidx, eidx;
bdebug("nid=%td start=%lx end=%lx reserve=%d flags=%x\n",
bdata - bootmem_node_data, start, end, reserve, flags);
/* 待标记的区间必须位于此节点bootmem内 */
BUG_ON(start < bdata->node_min_pfn);
BUG_ON(end > bdata->node_low_pfn);
/* 计算起止pfn偏移 */
sidx = start - bdata->node_min_pfn;
eidx = end - bdata->node_min_pfn;
if (reserve)
/* 保留这块bootmem空间*/
return __reserve(bdata, sidx, eidx, flags);
else
/* 释放这块bootmem空间 */
__free(bdata, sidx, eidx);
return 0;
}
__free
释放一块bootmem空间,即将位图比特位清0。
static void __init __free(bootmem_data_t *bdata,
unsigned long sidx, unsigned long eidx)
{
unsigned long idx;
bdebug("nid=%td start=%lx end=%lx\n", bdata - bootmem_node_data,
sidx + bdata->node_min_pfn,
eidx + bdata->node_min_pfn);
/* 如果分配bootmem时的优先扫描位置高于释放空间的起始位置,更新优先扫描位置为释放空间的起始位置,这样做有助于减少碎片,使高地址尽可能存在更多的连续的大块空闲空间 */
if (bdata->hint_idx > sidx)
bdata->hint_idx = sidx;
/* 将sidx与eidx之间的比特位清0 */
for (idx = sidx; idx < eidx; idx++)
if (!test_and_clear_bit(idx, bdata->node_bootmem_map))
BUG();
}