/***************************************************************************************************************/
/* bootmem.h */
/*
* Discontiguous memory support, Kanoj Sarcar, SGI, Nov 1999
*/
#ifndef _LINUX_BOOTMEM_H
#define _LINUX_BOOTMEM_H
#include
#include
#include
#include
#include
/*
* simple boot-time physical memory area allocator.
*/
extern unsigned long max_low_pfn;
extern unsigned long min_low_pfn;
extern unsigned long max_pfn;
/*
* node_bootmem_map is a map pointer - the bits represent all physical
* memory pages (including holes) on the node.
*/
typedef struct bootmem_data { /* 引导内存节点描述结构 */
unsigned long node_boot_start; /* 节点的起始物理地址 */
unsigned long node_low_pfn; /* 节点的结束页帧号,或就是该节点表示的ZONE_NORMAL的结束 */
void *node_bootmem_map; /* 以位表示的以分配和空闲页面的位图的地址 */
unsigned long last_offset; /* 最后一次分配所在页面的偏移,如果是0则表示该页全部使用 */
unsigned long last_pos; /* 最后一次分配时的页面帧数,使用last_offset字段可以检测在内存分配时,是否
可以合并上次分配所使用的页,而不用重新分配新页*/
} bootmem_data_t;
extern unsigned long __init bootmem_bootmap_pages (unsigned long);
extern unsigned long __init init_bootmem (unsigned long addr, unsigned long memend);
extern void __init reserve_bootmem (unsigned long addr, unsigned long size);
extern void __init free_bootmem (unsigned long addr, unsigned long size);
extern void * __init __alloc_bootmem (unsigned long size, unsigned long align, unsigned long goal);
/* 从ZONE_NORMAL分配size数量的字节,该分配的地址将对其L1硬件高速缓存以最充分地利用硬件高速缓存 */
#define alloc_bootmem(x) \
__alloc_bootmem((x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) /* 将L1硬件高速缓存页对齐,并在DMA中可用的最大地址后开始找一页 */
/* 从ZONE_NORMAL分配size数量的字节,该分配的地方将对其L1硬件高速缓存 */
#define alloc_bootmem_low(x) \
__alloc_bootmem((x), SMP_CACHE_BYTES, 0) /* 将L1硬件高速缓存页对齐,从0开始查找 */
/* 从ZONE_NORMAL分配size数量的字节,之所以对其为一个页面的大小是为了让所有的页面可以返回给调用者 */
#define alloc_bootmem_pages(x) \
__alloc_bootmem((x), PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) /* 将已分配位对齐到一页大小,这样满页将从DMA可用的最大地址开始分配 */
/* 从ZONE_NORMAL分配size数量的字节,之所以对齐为一个页面的大小是为了让所有的页面可以返回给调用者 */
#define alloc_bootmem_low_pages(x) \
__alloc_bootmem((x), PAGE_SIZE, 0) /*将已分配位对齐到一页大小,这样满页将从物理地址0开始分配 */
extern unsigned long __init free_all_bootmem (void);
extern unsigned long __init init_bootmem_node (pg_data_t *pgdat, unsigned long freepfn, unsigned long startpfn, unsigned long endpfn);
extern void __init reserve_bootmem_node (pg_data_t *pgdat, unsigned long physaddr, unsigned long size);
extern void __init free_bootmem_node (pg_data_t *pgdat, unsigned long addr, unsigned long size);
extern unsigned long __init free_all_bootmem_node (pg_data_t *pgdat);
extern void * __init __alloc_bootmem_node (pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal);
/* 在特定节点pgdat上从ZONE_NORMAL分配size数量的字节,这个分配的地方将对其L1硬件高速缓存,以最充分地
利用硬件高速缓存*/
#define alloc_bootmem_node(pgdat, x) \
__alloc_bootmem_node((pgdat), (x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS))
/* 在特定节点pgdat上从ZONE_DMA分配size数量的字节,以使所有的页面可以返回给调用者 */
#define alloc_bootmem_pages_node(pgdat, x) \
__alloc_bootmem_node((pgdat), (x), PAGE_SIZE, __pa(MAX_DMA_ADDRESS))
/* 在特定节点pgdat上从ZONE_DMA分配和对齐一个页面大小的size数量的字节,以使所有的页面可以返回给调用者 */
#define alloc_bootmem_low_pages_node(pgdat, x) \
__alloc_bootmem_node((pgdat), (x), PAGE_SIZE, 0)
#endif /* _LINUX_BOOTMEM_H */
/***************************************************************************************************************/
/* bootmem.c */
/*
* linux/mm/bootmem.c
*
* Copyright (C) 1999 Ingo Molnar
* Discontiguous memory support, Kanoj Sarcar, SGI, Nov 1999
*
* simple boot-time physical memory area allocator and
* free memory collector. It's used to deal with reserved
* system memory and memory holes as well.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
* Access to this subsystem has to be serialized externally. (this is
* true for the boot process anyway)
*/
unsigned long max_low_pfn;/* 以低端内存区域表示的最大物理内存页帧号(结束页帧号减去起始页帧号=最大低端
内存页帧号=0xd8400-0x1000=881664页) */
unsigned long min_low_pfn; /* 系统中可用的最小低端物理内存页帧号 */
unsigned long max_pfn; /* 系统中可以的最大物理内存页帧号 */
/* return the number of _pages_ that will be allocated for the boot bitmap */
/* 计算用于存储一个位图所需要的页面数量,以表明页面数量的分配状态
pages:一个节点内的页数,4096*/
unsigned long __init bootmem_bootmap_pages (unsigned long pages)
{
unsigned long mapsize;
mapsize = (pages+7)/8; /* =512 */
mapsize = (mapsize + ~PAGE_MASK) & PAGE_MASK; /* =4096 */
mapsize >>= PAGE_SHIFT; /* 位图占用的页数 =4*/
return mapsize;
}
/*
* Called once to set up the allocator itself.
*/
/* 初始化启动内存核心函数
pgdat:指向node_bootmem_data[i] 或者contig_bootmem_data
mapstart:预留的页帧数,这些用于存放启动内存位图
start:该节点的起始页帧号
end:该节点的结束页帧号
函数返回位图大小
*/
static unsigned long __init init_bootmem_core (pg_data_t *pgdat,
unsigned long mapstart, unsigned long start, unsigned long end)
{
bootmem_data_t *bdata = pgdat->bdata; /* 指向node_bootmem_data[i] 或者contig_bootmem_data */
unsigned long mapsize = ((end - start)+7)/8; /* 计算位图的大小(每一页需要一位来标识,所以所需映射图的大小是该节点
中页面数向上取整到最近的8的位数除以8所得到的字节数) */
/* 放入启动内存节点链表中 */
pgdat->node_next = pgdat_list;
pgdat_list = pgdat;
mapsize = (mapsize + (sizeof(long) - 1UL)) & ~(sizeof(long) - 1UL); /* 将位图向上取整到最近的字边界 */
bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT); /* 初始化位图的起始地址*/
bdata->node_boot_start = (start << PAGE_SHIFT); /* 将起始页帧号转换为一个物理地址存放起来 */
bdata->node_low_pfn = end;
/*
* Initially all pages are reserved - setup_arch() has to
* register free RAM areas explicitly.
*/
memset(bdata->node_bootmem_map, 0xff, mapsize); /* 设置位图全部为1====>表示此时内存不可用(由那些特点的体系结构代码
来标记可用页面) */
return mapsize;
}
/*
* Marks a particular physical memory range as unallocatable. Usable RAM
* might be used for boot-time allocations - or it might get added
* to the free page pool later on.
*/
/* 预留启动内存核心函数,预留内存的起始地址为addr,大小为size */
static void __init reserve_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size)
{
unsigned long i;
/*
* round up, partially reserved pages are considered
* fully reserved.
*/
unsigned long sidx = (addr - bdata->node_boot_start)/PAGE_SIZE; /* sidx为服务页的起始引索------addr距离该节点的起始物理地址的距离 */
unsigned long eidx = (addr + size - bdata->node_boot_start +
PAGE_SIZE-1)/PAGE_SIZE; /* 服务员的结束引索 */
unsigned long end = (addr + size + PAGE_SIZE-1)/PAGE_SIZE; /* 预留页的结束页帧号,向上页对齐 */
if (!size) BUG();
if (sidx < 0)
BUG();
if (eidx < 0)
BUG();
if (sidx >= eidx)
BUG();
if ((addr >> PAGE_SHIFT) >= bdata->node_low_pfn)
BUG();
if (end > bdata->node_low_pfn)
BUG();
for (i = sidx; i < eidx; i++) /* 遍历预留页面 */
if (test_and_set_bit(i, bdata->node_bootmem_map)) /* 设置对应的页面位图为1====>表示内存已经使用 */
printk("hm, page %08lx reserved twice.\n", i*PAGE_SIZE);
}
/*释放启动内存核心函数
释放启动内存(将要释放的页的位图清空即可) */
static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size)
{
unsigned long i;
unsigned long start;
/*
* round down end of usable mem, partially free pages are
* considered reserved.
*/
unsigned long sidx;
unsigned long eidx = (addr + size - bdata->node_boot_start)/PAGE_SIZE; /* 计算结束引索值 */
unsigned long end = (addr + size)/PAGE_SIZE; /* 获取结束页面帧号 */
if (!size) BUG();
if (end > bdata->node_low_pfn)
BUG();
/*
* Round up the beginning of the address.
*/
start = (addr + PAGE_SIZE-1) / PAGE_SIZE; /* 计算起始页面帧号*/
sidx = start - (bdata->node_boot_start/PAGE_SIZE); /* 计算起始引索值 */
for (i = sidx; i < eidx; i++) { /* 遍历分配的页面空间 */
if (!test_and_clear_bit(i, bdata->node_bootmem_map)) /*清除位图中对应的位即可 */
BUG();
}
}
/*
* We 'merge' subsequent allocations to save space. We might 'lose'
* some fraction of a page if allocations cannot be satisfied due to
* size constraints on boxes where there is physical RAM space
* fragmentation - in these cases * (mostly large memory boxes) this
* is not a problem.
*
* On low memory boxes we get it right in 100% of the cases.
*/
/*
* alignment has to be a power of 2 value.
*/
/* 分配内存核心函数
bdata:分配的内存的节点
size:分配的内存的大小
align:分配的内存的对齐方式
goal:分配的起始地址
函数意义描述:该函数实际就是根据参数goal确定扫描起始点,然后扫描从该起始点开始的内存位图,如果
有位图为0则说明有内存未被使用,然后在确定其后的参数size大小的内存是否被使用,如果没有被使用则
就分配这块内存,否则继续扫描位图寻找空闲页面,如果找到了将找的页面的起始地址转换为虚拟地址
返回否则返回NULL*/
static void * __init __alloc_bootmem_core (bootmem_data_t *bdata,
unsigned long size, unsigned long align, unsigned long goal)
{
unsigned long i, start = 0;
void *ret;
unsigned long offset, remaining_size;
unsigned long areasize, preferred, incr;
unsigned long eidx = bdata->node_low_pfn - (bdata->node_boot_start >>
PAGE_SHIFT); /* 获取该节点的内存页面数 */
if (!size) BUG();
if (align & (align-1)) /* 如果不是2的幂对齐则BUG() */
BUG();
offset = 0; /* 对齐的缺省偏移是0 */
if (align &&
(bdata->node_boot_start & (align - 1UL)) != 0) /* 如果该节点的起始地址没有按参数align设置的对齐方式对齐 */
offset = (align - (bdata->node_boot_start & (align - 1UL))); /* 要使用的偏移与起始地址低位标记的请求对齐,实际上,这里的
offset与align的一般值相似*/
offset >>= PAGE_SHIFT;
/*
* We try to allocate bootmem pages above 'goal'
* first, then we try to allocate lower pages.
*/
if (goal && (goal >= bdata->node_boot_start) &&
((goal >> PAGE_SHIFT) < bdata->node_low_pfn)) { /* 如果指定了分配起始地址且该地址在内存节点范围内 */
preferred = goal - bdata->node_boot_start; /* 计算开始的适当偏移 */
} else
preferred = 0; /* 否则为0 */
/* 考虑偏移,调整适当偏移的大小,这样该地址将可以正确对齐 */
preferred = ((preferred + align - 1) & ~(align - 1)) >> PAGE_SHIFT;
preferred += offset;
areasize = (size+PAGE_SIZE-1)/PAGE_SIZE; /* 大小对齐到页 */
incr = align >> PAGE_SHIFT ? : 1; /* 对齐到页 */
restart_scan:
for (i = preferred; i < eidx; i += incr) { /* 遍历在给定起始地址后面的页面 */
unsigned long j;
if (test_bit(i, bdata->node_bootmem_map)) /* 遍历位图,如果页面已经被分配出去则进入下次循环 */
continue;
for (j = i + 1; j < i + areasize; ++j) { /* 如果扫描到页面为空闲状态则遍历该页面后面指定的size大小的页面 */
if (j >= eidx)
goto fail_block;
if (test_bit (j, bdata->node_bootmem_map)) /* 如果其后的页面已经被分配出去了则跳转到 fail_block*/
goto fail_block;
}
start = i; /* 如果找到了则记录分配的位置 */
goto found;
fail_block:;
}
if (preferred) {
preferred = offset;
goto restart_scan; /*再次往后寻找 */
}
return NULL;
found:
if (start >= eidx) /* 如果超过了该节点的范围则BUG */
BUG();
/*
* Is the next page of the previous allocation-end the start
* of this allocation's buffer? If yes then we can 'merge'
* the previous partial page with this allocation.
*/
if (align <= PAGE_SIZE
&& bdata->last_offset && bdata->last_pos+1 == start) { /* 如果对齐小于一页并且 前面的页面在其中有空间并且前面是有的页
页相邻,则试着与前一次分配合并*/
offset = (bdata->last_offset+align-1) & ~(align-1); /* 更新用于对align请求正确分页的偏移 */
if (offset > PAGE_SIZE) /* 如果偏移超过了页面边界则BUG */
BUG();
remaining_size = PAGE_SIZE-offset; /* 以前用过的页面中处于空闲的空间 */
if (size < remaining_size) { /* 如果旧的页面中有足够的空间剩余,这里使用旧的页面,然后更新bootmem_data结构来
反应这点*/
areasize = 0;
// last_pos unchanged
bdata->last_offset = offset+size;
ret = phys_to_virt(bdata->last_pos*PAGE_SIZE + offset +
bdata->node_boot_start);
} else { /* 否则计算除了这一页还需要多少页面并更新bootmem_data */
remaining_size = size - remaining_size;
areasize = (remaining_size+PAGE_SIZE-1)/PAGE_SIZE;
ret = phys_to_virt(bdata->last_pos*PAGE_SIZE + offset +
bdata->node_boot_start);
bdata->last_pos = start+areasize-1;
bdata->last_offset = remaining_size;
}
bdata->last_offset &= ~PAGE_MASK;
} else {/* 如果没有则记录本次分配用到的页面和偏移,以便下次分配时的合并 */
bdata->last_pos = start + areasize - 1;
bdata->last_offset = size & ~PAGE_MASK;
ret = phys_to_virt(start * PAGE_SIZE + bdata->node_boot_start); /* 计算分配的页面起始地址并转换为虚拟地址 */
}
/*
* Reserve the area now:
*/
for (i = start; i < start+areasize; i++)
if (test_and_set_bit(i, bdata->node_bootmem_map)) /* 将对应的位图中相应的位设置为1---->表示已分配 */
BUG();
memset(ret, 0, size); /* 将内存设置为0 */
return ret;
}
/* 释放节点pgdat中的所有内存核心函数*/
static unsigned long __init free_all_bootmem_core(pg_data_t *pgdat)
{
struct page *page = pgdat->node_mem_map;
bootmem_data_t *bdata = pgdat->bdata;
unsigned long i, count, total = 0;
unsigned long idx;
if (!bdata->node_bootmem_map) BUG();
count = 0; /* 计数空闲页面数 */
idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT); /* 计算该节点的内存总页数 */
for (i = 0; i < idx; i++, page++) { /* 遍历所有的页面 */
if (!test_bit(i, bdata->node_bootmem_map)) { /* 测试 页面对应位图,如果该页标记为空闲*/
count++;
ClearPageReserved(page); /* 清除预留标志位 */
set_page_count(page, 1); /* 设置页面的计数为1 ,这样伙伴分配器将考虑这是页面最后一个用户,并将其放入
空闲链表中*/
__free_page(page); /* 调用伙伴分配器的释放函数来释放页面,这样页面被加入到空闲链表中*/
}
}
total += count;/* 计算页面总数 */
/*
* Now free the allocator bitmap itself, it's not
* needed anymore:
*/
page = virt_to_page(bdata->node_bootmem_map); /* 获取位图的页帧号 */
count = 0;
for (i = 0; i < ((bdata->node_low_pfn-(bdata->node_boot_start >> PAGE_SHIFT))/8 + PAGE_SIZE-1)/PAGE_SIZE; i++,page++) { /* 遍历位图占用的页面 */
count++;
ClearPageReserved(page); /* 清除预留标志位 */
set_page_count(page, 1);/* 设置页面的计数为1 ,这样伙伴分配器将考虑这是页面最后一个用户,并将其放入
空闲链表中*/
__free_page(page); /* 调用伙伴分配器的释放函数来释放页面,这样页面被加入到空闲链表中*/
}
total += count;/* 增加总页面计数 */
bdata->node_bootmem_map = NULL;
return total; /* 返回释放的总页面数(返回加入到伙伴分配器空闲链表的页面总数) */
}
/* 初始化启动内存节点(被bootmem_init()调用)
pgdat:指向discontig_node_data[i] 或者contig_node_data
freepfn:预留的页帧数,这些用于存放启动内存位图
startpfn:该节点的起始页帧号
endpfn:该节点的结束页帧号
*/
unsigned long __init init_bootmem_node (pg_data_t *pgdat, unsigned long freepfn, unsigned long startpfn, unsigned long endpfn)
{
return(init_bootmem_core(pgdat, freepfn, startpfn, endpfn));
}
/* 标记特定节点pgdat上地址addr和addr+size之间的页面保留,请求部分保留页面的每个部分将导致这个页面被保留 */
void __init reserve_bootmem_node (pg_data_t *pgdat, unsigned long physaddr, unsigned long size)
{
reserve_bootmem_core(pgdat->bdata, physaddr, size);
}
/* 标记特定节点pgdat上地址addr和addr+size之间的页面空闲 */
void __init free_bootmem_node (pg_data_t *pgdat, unsigned long physaddr, unsigned long size)
{
return(free_bootmem_core(pgdat->bdata, physaddr, size));
}
/* 该函数在引导分配器生命周期结束时使用,它遍历位图中的所有页面,对于每一个空闲的页面,其标志位
将被清除,且页面被释放到物理页面分配器中,因此运行时分配器可以建立自己的空闲链表*/
unsigned long __init free_all_bootmem_node (pg_data_t *pgdat)
{
return(free_all_bootmem_core(pgdat));
}
/* 初始化内存为0到PFN页面之间的值,可用内存的起始位置在PFN的起始处 */
unsigned long __init init_bootmem (unsigned long start, unsigned long pages)
{
max_low_pfn = pages;
min_low_pfn = start;
return(init_bootmem_core(&contig_page_data, start, 0, pages)); /* 初始化bootmem_data结构 */
}
/*标记页面在地址addr和预留的addr+size之间的请求部分,保留页面在某个部分将导致整个页面被保留 */
void __init reserve_bootmem (unsigned long addr, unsigned long size)
{
reserve_bootmem_core(contig_page_data.bdata, addr, size); /* 这里使用contig_page_data.bdata作为参数是由于在NUMA体系结构中,从
该节点静态分配内存*/
}
/* 标记在地址addr和addr+size之间的页面空闲 */
void __init free_bootmem (unsigned long addr, unsigned long size)
{
return(free_bootmem_core(contig_page_data.bdata, addr, size));
}
/* 该函数在引导分配器生命周期结束时使用,它遍历位图中的所有页面,对于每一个空闲的页面,其标志
位将清除,且页面被释放到物理页面分配器,因此运行时分配器可以建立自己的空闲链表*/
unsigned long __init free_all_bootmem (void)
{
return(free_all_bootmem_core(&contig_page_data));
}
/* UMA系统中使用该函数在引导阶段分配内存
pgdat被省略,该函数默认在contig_page_data节点上分配内存
size:所需要分配的空间大小
align:要求对齐的字节数,如果分配的空间比较小,就以SMP_CACHE_BYTES对齐
goal:最佳的分配的起始地址,底层函数一般从物理地址0开始其他函数开始于MAX_DMA_ADDRESS,它是这种结构
中DMA传输模式的最大地址*/
void * __init __alloc_bootmem (unsigned long size, unsigned long align, unsigned long goal)
{
pg_data_t *pgdat;
void *ptr;
for_each_pgdat(pgdat) /* 遍历pgdat_list内存节点链表 */
if ((ptr = __alloc_bootmem_core(pgdat->bdata, size,
align, goal))) /* 尝试从该节点分配内存 */
return(ptr);
/*
* Whoops, we cannot satisfy the allocation request.
*/
printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size);
panic("Out of memory");
return NULL;
}
/* NUMA系统中使用该函数在引导阶段分配内存
pgdat:要分配的节点
size:所需要分配的空间大小
align:要求对齐的字节数,如果分配的空间比较小,就以SMP_CACHE_BYTES对齐
goal:最佳的分配的起始地址,底层函数一般从物理地址0开始其他函数开始于MAX_DMA_ADDRESS,它是这种结构
中DMA传输模式的最大地址*/
void * __init __alloc_bootmem_node (pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal)
{
void *ptr;
ptr = __alloc_bootmem_core(pgdat->bdata, size, align, goal); /* 调用核心的分配函数 */
if (ptr)
return (ptr);
/*
* Whoops, we cannot satisfy the allocation request.
*/
printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size);
panic("Out of memory");
return NULL;
}
/***************************************************************************************************************/
/* init.c */
/*
* linux/arch/arm/mm/init.c
*
* Copyright (C) 1995-2002 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef CONFIG_DISCONTIGMEM
#define NR_NODES 1
#else
#define NR_NODES 4
#endif
#ifdef CONFIG_CPU_32
#define TABLE_OFFSET (PTRS_PER_PTE)
#else
#define TABLE_OFFSET 0
#endif
#define TABLE_SIZE ((TABLE_OFFSET + PTRS_PER_PTE) * sizeof(pte_t))
static unsigned long totalram_pages;
extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
extern char _stext, _text, _etext, _end, __init_begin, __init_end;
extern unsigned long phys_initrd_start; /* =0xd8000000 */
extern unsigned long phys_initrd_size; /* =3*1024*1024 */
/*
* The sole use of this is to pass memory configuration
* data from paging_init to mem_init.
*/
static struct meminfo meminfo = { 0, }; /* setup.c中meminfo的副本(在paging_init()中拷贝) */
/*
* empty_zero_page is a special page that is used for
* zero-initialized data and COW.
*/
struct page *empty_zero_page; /* 空的第0个页面,在 paging_init()中分配*/
#ifndef CONFIG_NO_PGT_CACHE
struct pgtable_cache_struct quicklists;
int do_check_pgt_cache(int low, int high)
{
int freed = 0;
if(pgtable_cache_size > high) {
do {
if(pgd_quicklist) {
free_pgd_slow(get_pgd_fast());
freed++;
}
if(pmd_quicklist) {
pmd_free_slow(pmd_alloc_one_fast(NULL, 0));
freed++;
}
if(pte_quicklist) {
pte_free_slow(pte_alloc_one_fast(NULL, 0));
freed++;
}
} while(pgtable_cache_size > low);
}
return freed;
}
#else
int do_check_pgt_cache(int low, int high)
{
return 0;
}
#endif
/* This is currently broken
* PG_skip is used on sparc/sparc64 architectures to "skip" certain
* parts of the address space.
*
* #define PG_skip 10
* #define PageSkip(page) (machine_is_riscpc() && test_bit(PG_skip, &(page)->flags))
* if (PageSkip(page)) {
* page = page->next_hash;
* if (page == NULL)
* break;
* }
*/
void show_mem(void)
{
int free = 0, total = 0, reserved = 0;
int shared = 0, cached = 0, slab = 0, node;
printk("Mem-info:\n");
show_free_areas();
printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
for (node = 0; node < numnodes; node++) {
struct page *page, *end;
page = NODE_MEM_MAP(node);
end = page + NODE_DATA(node)->node_size;
do {
total++;
if (PageReserved(page))
reserved++;
else if (PageSwapCache(page))
cached++;
else if (PageSlab(page))
slab++;
else if (!page_count(page))
free++;
else
shared += atomic_read(&page->count) - 1;
page++;
} while (page < end);
}
printk("%d pages of RAM\n", total);
printk("%d free pages\n", free);
printk("%d reserved pages\n", reserved);
printk("%d slab pages\n", slab);
printk("%d pages shared\n", shared);
printk("%d pages swap cached\n", cached);
#ifndef CONFIG_NO_PGT_CACHE
printk("%ld page tables cached\n", pgtable_cache_size);
#endif
show_buffers();
}
struct node_info { /* 节点信息描述结构 */
unsigned int start; /* 节点起始页帧号(页对齐) */
unsigned int end; /* 节点结束页帧号(页对齐) */
int bootmap_pages;/* 启动内存位图占用的位图 */
};
#define O_PFN_DOWN(x) ((x) >> PAGE_SHIFT)
#define V_PFN_DOWN(x) O_PFN_DOWN(__pa(x))
#define O_PFN_UP(x) (PAGE_ALIGN(x) >> PAGE_SHIFT)
#define V_PFN_UP(x) O_PFN_UP(__pa(x))
#define PFN_SIZE(x) ((x) >> PAGE_SHIFT)
#define PFN_RANGE(s,e) PFN_SIZE(PAGE_ALIGN((unsigned long)(e)) - \
(((unsigned long)(s)) & PAGE_MASK))
/*
* FIXME: We really want to avoid allocating the bootmap bitmap
* over the top of the initrd. Hopefully, this is located towards
* the start of a bank, so if we allocate the bootmap bitmap at
* the end, we won't clash.
*/
/* 在给定的节点上寻找一个可以容纳位图的起始页帧号
node:=0---->第一个节点上
bootmap_pages:启动内存位图占用页面数:=16页
mi指向setup.c中的全局变量meminfo
meminfo.nr_banks=4
meminfo.end=0xd8400000
meminfo.bank[0].start = 0xc0000000,
meminfo.bank[0].size = 4*1024*1024,
meminfo.bank0].node = (0xc0000000 - 0xc0000000) >> 27 =0
meminfo.bank[1].start = 0xc8000000,
meminfo.bank[1].size = 4*1024*1024,
meminfo.bank1].node = (0xc8000000 - 0xc0000000) >> 27 =1
meminfo.bank[2].start = 0xd0000000,
meminfo.bank[2].size = 4*1024*1024,
meminfo.bank2].node = (0xd0000000 - 0xc0000000) >> 27 =2
meminfo.bank[3].start = 0xd8000000,
meminfo.bank[3].size = 4*1024*1024,
meminfo.bank3].node = (0xd8000000 - 0xc0000000) >> 27 =3*/
static unsigned int __init
find_bootmap_pfn(int node, struct meminfo *mi, unsigned int bootmap_pages)
{
unsigned int start_pfn, bank, bootmap_pfn;
start_pfn = V_PFN_UP(&_end); /* 起始帧指向内核结束地址对齐到一页上并转换为对应的页帧号 */
bootmap_pfn = 0;
for (bank = 0; bank < mi->nr_banks; bank ++) { /* 遍历每个bank ,寻找与参数node相等的节点*/
unsigned int start, end;
if (mi->bank[bank].node != node)
continue;
start = O_PFN_UP(mi->bank[bank].start); /* 该节点的起始页帧号 */
end = O_PFN_DOWN(mi->bank[bank].size +
mi->bank[bank].start); /* 该节点的结束页帧号 */
if (end < start_pfn)
continue;
if (start < start_pfn)
start = start_pfn;
if (end <= start)
continue;
if (end - start >= bootmap_pages) {
bootmap_pfn = start;
break;
}
}
if (bootmap_pfn == 0)
BUG();
return bootmap_pfn;
}
/*
* Scan the memory info structure and pull out:
* - the end of memory
* - the number of nodes
* - the pfn range of each node
* - the number of bootmem bitmap pages
*/
/* 该函数被bootmem_init()调用,该函数用于寻找内存结束地址和节点(函数返回的是启动内存位图占用的总空间大小)
mi指向setup.c中的全局变量meminfo,该变量的值如下:
meminfo.nr_banks=4
meminfo.bank[0].start = 0xc0000000,
meminfo.bank[0].size = 4*1024*1024,
meminfo.bank0].node = (0xc0000000 - 0xc0000000) >> 27 =0
meminfo.bank[1].start = 0xc8000000,
meminfo.bank[1].size = 4*1024*1024,
meminfo.bank1].node = (0xc8000000 - 0xc0000000) >> 27 =1
meminfo.bank[2].start = 0xd0000000,
meminfo.bank[2].size = 4*1024*1024,
meminfo.bank2].node = (0xd0000000 - 0xc0000000) >> 27 =2
meminfo.bank[3].start = 0xd8000000,
meminfo.bank[3].size = 4*1024*1024,
meminfo.bank3].node = (0xd8000000 - 0xc0000000) >> 27 =3 */
static unsigned int __init
find_memend_and_nodes(struct meminfo *mi, struct node_info *np)
{
unsigned int i, bootmem_pages = 0, memend_pfn = 0;
for (i = 0; i < NR_NODES; i++) {
np[i].start = -1U;
np[i].end = 0;
np[i].bootmap_pages = 0;
}
for (i = 0; i < mi->nr_banks; i++) {
unsigned long start, end;
int node;
if (mi->bank[i].size == 0) {
/*
* Mark this bank with an invalid node number
*/
mi->bank[i].node = -1;
continue;
}
node = mi->bank[i].node; /* 获取bank所属的节点 */
if (node >= numnodes) {
numnodes = node + 1;
/*
* Make sure we haven't exceeded the maximum number
* of nodes that we have in this configuration. If
* we have, we're in trouble. (maybe we ought to
* limit, instead of bugging?)
*/
if (numnodes > NR_NODES)
BUG();
}
/*
* Get the start and end pfns for this bank
*/
start = O_PFN_UP(mi->bank[i].start); /* 获取bank的起始地址并对其到一页上后的页帧号 */
end = O_PFN_DOWN(mi->bank[i].start + mi->bank[i].size); /* 获取bank的结束地址并转换为页帧号:=0xd8400 */
if (np[node].start > start)
np[node].start = start; /* 保存节点起始地址 */
if (np[node].end < end) /* 保存节点结束页地址 */
np[node].end = end;
if (memend_pfn < end)
memend_pfn = end; /* 结束页帧号 */
}
/*
* Calculate the number of pages we require to
* store the bootmem bitmaps.
*/
for (i = 0; i < numnodes; i++) {
if (np[i].end == 0)
continue;
np[i].bootmap_pages = bootmem_bootmap_pages(np[i].end -
np[i].start); /* 计算用于存储一个位图所需要的页面数量,以表明页面数量的分配状态*/
bootmem_pages += np[i].bootmap_pages; /* 计算位图占用总的内存空间:=4*4=16页 */
}
/*
* This doesn't seem to be used by the Linux memory
* manager any more. If we can get rid of it, we
* also get rid of some of the stuff above as well.
*/
max_low_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET); /* 结束页帧号减去起始页帧号=最大低端内存页帧号=0xd8400-0x1000=881664页 */
// max_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET);
mi->end = memend_pfn << PAGE_SHIFT;
return bootmem_pages;
}
/* 检查ram disk
返回ram disk的地址所在的节点的节点号*/
static int __init check_initrd(struct meminfo *mi)
{
int initrd_node = -2;
#ifdef CONFIG_BLK_DEV_INITRD
unsigned long end = phys_initrd_start + phys_initrd_size; /* =0xd8300000 */
/*
* Make sure that the initrd is within a valid area of
* memory.
*/
if (phys_initrd_size) {
unsigned int i;
initrd_node = -1;
for (i = 0; i < mi->nr_banks; i++) { /* 遍历每个bank */
unsigned long bank_end;
bank_end = mi->bank[i].start + mi->bank[i].size; /* 获取bank的结束地址 */
if (mi->bank[i].start <= phys_initrd_start &&
end <= bank_end)
initrd_node = mi->bank[i].node; /* 保存ram disk的地址所在的节点的节点号 */
}
}
if (initrd_node == -1) {
printk(KERN_ERR "initrd (0x%08lx - 0x%08lx) extends beyond "
"physical memory - disabling initrd\n",
phys_initrd_start, end);
phys_initrd_start = phys_initrd_size = 0;
}
#endif
return initrd_node;
}
/*
* Reserve the various regions of node 0
*/
/* 预留节点上从bootmap_pfn开始大小为bootmap_pages页面,因为这些页面已经被位图使用 ,还有内核映像,页表等
占用的内存页面*/
static __init void reserve_node_zero(unsigned int bootmap_pfn, unsigned int bootmap_pages)
{
pg_data_t *pgdat = NODE_DATA(0);
/*
* Register the kernel text and data with bootmem.
* Note that this can only be in node 0.
*/
reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); /* 保留内核映像文件占用的内存页面 */
#ifdef CONFIG_CPU_32
/*
* Reserve the page tables. These are already in use,
* and can only be in node 0.
*/
reserve_bootmem_node(pgdat, __pa(swapper_pg_dir), /* 预留页表占用的内存页面 */
PTRS_PER_PGD * sizeof(pgd_t));
#endif
/*
* And don't forget to reserve the allocator bitmap,
* which will be freed later.
*/
reserve_bootmem_node(pgdat, bootmap_pfn << PAGE_SHIFT,
bootmap_pages << PAGE_SHIFT); /* 预留位图占用的内存页面 */
/*
* Hmm... This should go elsewhere, but we really really
* need to stop things allocating the low memory; we need
* a better implementation of GFP_DMA which does not assume
* that DMA-able memory starts at zero.
*/
if (machine_is_integrator())
reserve_bootmem_node(pgdat, 0, __pa(swapper_pg_dir));
/*
* These should likewise go elsewhere. They pre-reserve
* the screen memory region at the start of main system
* memory.
*/
if (machine_is_archimedes() || machine_is_a5k())
reserve_bootmem_node(pgdat, 0x02000000, 0x00080000);
if (machine_is_edb7211() || machine_is_fortunet())
reserve_bootmem_node(pgdat, 0xc0000000, 0x00020000);
if (machine_is_p720t())
reserve_bootmem_node(pgdat, PHYS_OFFSET, 0x00014000);
#ifdef CONFIG_SA1111
/*
* Because of the SA1111 DMA bug, we want to preserve
* our precious DMA-able memory...
*/
reserve_bootmem_node(pgdat, PHYS_OFFSET, __pa(swapper_pg_dir)-PHYS_OFFSET); /* 预留用于DMA传输的内存页面 */
#endif
}
/*
* Register all available RAM in this node with the bootmem allocator.
*/
/* 释放启动内存节点每个bank上的内存-----标记每个节点的内存可用 */
static inline void free_bootmem_node_bank(int node, struct meminfo *mi)
{
pg_data_t *pgdat = NODE_DATA(node); /* 获取内存节点对应的描述结构即discontig_node_data[i] 或者contig_page_data*/
int bank;
for (bank = 0; bank < mi->nr_banks; bank++) /* 遍历每个bank */
if (mi->bank[bank].node == node)
free_bootmem_node(pgdat, mi->bank[bank].start,
mi->bank[bank].size);
}
/*
* Initialise the bootmem allocator for all nodes. This is called
* early during the architecture specific initialisation.
*/
/*启动内存初始化,被setup_arch()函数调用
mi指向setup.c中的全局变量meminfo,该变量的值如下:
meminfo.nr_banks=4
meminfo.bank[0].start = 0xc0000000,
meminfo.bank[0].size = 4*1024*1024,
meminfo.bank0].node = (0xc0000000 - 0xc0000000) >> 27 =0
meminfo.bank[1].start = 0xc8000000,
meminfo.bank[1].size = 4*1024*1024,
meminfo.bank1].node = (0xc8000000 - 0xc0000000) >> 27 =1
meminfo.bank[2].start = 0xd0000000,
meminfo.bank[2].size = 4*1024*1024,
meminfo.bank2].node = (0xd0000000 - 0xc0000000) >> 27 =2
meminfo.bank[3].start = 0xd8000000,
meminfo.bank[3].size = 4*1024*1024,
meminfo.bank3].node = (0xd8000000 - 0xc0000000) >> 27 =3*/
void __init bootmem_init(struct meminfo *mi)
{
struct node_info node_info[NR_NODES], *np = node_info;
unsigned int bootmap_pages, bootmap_pfn, map_pg;
int node, initrd_node;
bootmap_pages = find_memend_and_nodes(mi, np); /* 查找内存结束地址和节点数,并返回启动内存位图占用的页数:=16页 */
bootmap_pfn = find_bootmap_pfn(0, mi, bootmap_pages); /*在给定的节点上寻找一个可以容纳位图的起始页帧号*/
initrd_node = check_initrd(mi); /* 获取ram disk的地址所在的节点的节点号 */
map_pg = bootmap_pfn;
/*
* Initialise the bootmem nodes.
*
* What we really want to do is:
*
* unmap_all_regions_except_kernel();
* for_each_node_in_reverse_order(node) {
* map_node(node);
* allocate_bootmem_map(node);
* init_bootmem_node(node);
* free_bootmem_node(node);
* }
*
* but this is a 2.5-type change. For now, we just set
* the nodes up in reverse order.
*
* (we could also do with rolling bootmem_init and paging_init
* into one generic "memory_init" type function).
*/
np += numnodes - 1;
for (node = numnodes - 1; node >= 0; node--, np--) { /* 遍历每个节点 */
/*
* If there are no pages in this node, ignore it.
* Note that node 0 must always have some pages.
*/
if (np->end == 0) {
if (node == 0)
BUG();
continue;
}
/*
* Initialise the bootmem allocator.
*/
init_bootmem_node(NODE_DATA(node), map_pg, np->start, np->end); /* 初始化每个节点 */
free_bootmem_node_bank(node, mi); /*标记每个节点的内存可用*/
map_pg += np->bootmap_pages;
/*
* If this is node 0, we need to reserve some areas ASAP -
* we may use bootmem on node 0 to setup the other nodes.
*/
if (node == 0) /* 如果遍历到了0节点 */
reserve_node_zero(bootmap_pfn, bootmap_pages); /* 预留节点上从bootmap_pfn开始大小为bootmap_pages页面,因为这些页面已经被位图使用 ,还有内核映像,页表等
占用的内存页面*/
}
#ifdef CONFIG_BLK_DEV_INITRD
if (phys_initrd_size && initrd_node >= 0) {
reserve_bootmem_node(NODE_DATA(initrd_node), phys_initrd_start,
phys_initrd_size); /* 预留ram disk占用的内存页面 */
initrd_start = __phys_to_virt(phys_initrd_start);
initrd_end = initrd_start + phys_initrd_size;
}
#endif
if (map_pg != bootmap_pfn + bootmap_pages)
BUG();
}
/*
* paging_init() sets up the page tables, initialises the zone memory
* maps, and sets up the zero page, bad page and bad page tables.
*/
/* 初始化页表,该函数用于完成页表收尾工作*/
void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
{
void *zero_page;
int node;
memcpy(&meminfo, mi, sizeof(meminfo));
/*
* allocate the zero page. Note that we count on this going ok.
*/
zero_page = alloc_bootmem_low_pages(PAGE_SIZE); /* 分配一页的内存 */
/*
* initialise the page tables.
*/
memtable_init(mi); /* 页表初始化, 该函数初始化对应于ZONE_DMA和ZONE_NORMAL的所有物理内存所必要的页表 */
if (mdesc->map_io)
mdesc->map_io(); /* 这里调用的是brutus_map_io()函数来映射IO内存 */
flush_cache_all(); /* 该宏刷新整个CPU高速缓存系统,该函数在内核页表发生变化时使用 */
flush_tlb_all(); /* 该函数刷新系统中所有处理器上的所有TLB ,在它完成以后,对应页表的变更在
全局可见,修改全局的内核页表后,如完成vfree()后,有必要调用该函数操作*/
/*
* initialise the zones within each node
*/
for (node = 0; node < numnodes; node++) {
unsigned long zone_size[MAX_NR_ZONES];
unsigned long zhole_size[MAX_NR_ZONES];
struct bootmem_data *bdata;
pg_data_t *pgdat;
int i;
/*
* Initialise the zone size information.
*/
for (i = 0; i < MAX_NR_ZONES; i++) {
zone_size[i] = 0;
zhole_size[i] = 0;
}
pgdat = NODE_DATA(node); /* 获取node节点对应的节点描述结构变量struct pglist_data,其实际就是discontig_node_data[nid]或者contig_page_data */
bdata = pgdat->bdata; /* 获取内存引导节点描述结构变量 */
/*
* The size of this node has already been determined.
* If we need to do anything fancy with the allocation
* of this memory to the zones, now is the time to do
* it.
*/
zone_size[0] = bdata->node_low_pfn -
(bdata->node_boot_start >> PAGE_SHIFT); /* 计算该节点的页面数 */
/*
* If this zone has zero size, skip it.
*/
if (!zone_size[0])
continue;
/*
* For each bank in this node, calculate the size of the
* holes. holes = node_size - sum(bank_sizes_in_node)
*/
zhole_size[0] = zone_size[0];
for (i = 0; i < mi->nr_banks; i++) { /* 遍历每个bank查找与节点匹配的bank */
if (mi->bank[i].node != node)
continue;
zhole_size[0] -= mi->bank[i].size >> PAGE_SHIFT; /* 计算空洞的页面数 */
}
/*
* Adjust the sizes according to any special
* requirements for this machine type.
*/
arch_adjust_zones(node, zone_size, zhole_size); /* 空函数 */
free_area_init_node(node, pgdat, 0, zone_size,
bdata->node_boot_start, zhole_size); /* 空闲区域初始化节点 */
}
/*
* finish off the bad pages once
* the mem_map is initialised
*/
memzero(zero_page, PAGE_SIZE);
empty_zero_page = virt_to_page(zero_page);
flush_dcache_page(empty_zero_page);
}
/* 释放addr-end之间的内存 */
static inline void free_area(unsigned long addr, unsigned long end, char *s)
{
unsigned int size = (end - addr) >> 10;
for (; addr < end; addr += PAGE_SIZE) {
struct page *page = virt_to_page(addr);
ClearPageReserved(page);
set_page_count(page, 1);
free_page(addr);
totalram_pages++;
}
if (size && s)
printk(KERN_INFO "Freeing %s memory: %dK\n", s, size);
}
/*
* mem_init() marks the free areas in the mem_map and tells us how much
* memory is free. This is done after various parts of the system have
* claimed their memory after the kernel image.
*/
/* 内存初始化,会被start_kernel()调用
在系统启动后,引导内存分配器就不再需要了,这些函数负责销毁不需要的引导内存分配器结构,并
将其余的页面传入到普通的物理页面分配器中*/
void __init mem_init(void)
{
unsigned int codepages, datapages, initpages;
int i, node;
codepages = &_etext - &_text;
datapages = &_end - &_etext;
initpages = &__init_end - &__init_begin;
high_memory = (void *)__va(meminfo.end); /* 高端内存设置为物理内存的结束地址:=0xd8400000 */
max_mapnr = virt_to_page(high_memory) - mem_map;
/*
* We may have non-contiguous memory.
*/
if (meminfo.nr_banks != 1) /* 如果不止一个bank则取消内存孔洞创建的映射 */
create_memmap_holes(&meminfo);
/* this will put all unused low memory onto the freelists */
for (node = 0; node < numnodes; node++) { /* 遍历每个节点 */
pg_data_t *pgdat = NODE_DATA(node); /* 获取每个节点对应的 管理结构*/
if (pgdat->node_size != 0)
totalram_pages += free_all_bootmem_node(pgdat); /* 标记所有的节点内存为可用 */
}
#ifdef CONFIG_SA1111
/* now that our DMA memory is actually so designated, we can free it */
free_area(PAGE_OFFSET, (unsigned long)swapper_pg_dir, NULL); /* 释放0xc0000000~swapper_pg_dir之间的内存 */
#endif
/*
* Since our memory may not be contiguous, calculate the
* real number of pages we have in this system
*/
printk(KERN_INFO "Memory:");
num_physpages = 0;
for (i = 0; i < meminfo.nr_banks; i++) { /* 遍历每个bank */
num_physpages += meminfo.bank[i].size >> PAGE_SHIFT; /* 计算内存总页面数 */
printk(" %ldMB", meminfo.bank[i].size >> 20);
}
printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT));
printk(KERN_NOTICE "Memory: %luKB available (%dK code, "
"%dK data, %dK init)\n",
(unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
codepages >> 10, datapages >> 10, initpages >> 10);
if (PAGE_SIZE >= 16384 && num_physpages <= 128) {
extern int sysctl_overcommit_memory;
/*
* On a machine this small we won't get
* anywhere without overcommit, so turn
* it on by default.
*/
sysctl_overcommit_memory = 1;
}
}
/* 释放所有标记为__init的数据结构占用的内存 */
void free_initmem(void)
{
if (!machine_is_integrator()) {
free_area((unsigned long)(&__init_begin),
(unsigned long)(&__init_end),
"init");
}
}
#ifdef CONFIG_BLK_DEV_INITRD
static int keep_initrd;
void free_initrd_mem(unsigned long start, unsigned long end)
{
if (!keep_initrd)
free_area(start, end, "initrd");
}
static int __init keepinitrd_setup(char *__unused)
{
keep_initrd = 1;
return 1;
}
__setup("keepinitrd", keepinitrd_setup);
#endif
void si_meminfo(struct sysinfo *val)
{
val->totalram = totalram_pages;
val->sharedram = 0;
val->freeram = nr_free_pages();
val->bufferram = atomic_read(&buffermem_pages);
val->totalhigh = 0;
val->freehigh = 0;
val->mem_unit = PAGE_SIZE;
}
阅读(2043) | 评论(0) | 转发(0) |