2009年9月29日火曜日

set_zonelist_orderとdefault_zonelist_order

前回見たbuild_all_zonelists関数から呼び出している関数のうち、今回は最初に呼んでいる
「set_zonelist_order」を見てみます。
実体は同じくmm/page_alloc.cにあります。

========
2298 static void set_zonelist_order(void)
2299 {
2300 if (user_zonelist_order == ZONELIST_ORDER_DEFAULT)
2301 current_zonelist_order = default_zonelist_order();
2302 else
2303 current_zonelist_order = user_zonelist_order;
2304 }
========

となっているので「deaflt_zonelist_order」を見てみます。

========
2244 static int default_zonelist_order(void)
2245 {
2246 ~変数宣言~

2257 low_kmem_size = 0;
2258 total_size = 0;
/* 全ノードのゾーンリストをチェックする */
2259 for_each_online_node(nid) {
2260 for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) {
2261 z = &NODE_DATA(nid)->node_zones[zone_type];
2262 if (populated_zone(z)) {
/* ゾーンタイプがZON_DMAかZONE_DMA32ならlow_kmem_sizeに加算 */
2263 if (zone_type < ZONE_NORMAL)
2264 low_kmem_size += z->present_pages;
/* トータルのサイズを加算 */
2265 total_size += z->present_pages;
2266 }
2267 }
2268 }
/* DMAエリアが無かったり、巨大ならZONELIST_ORDER_NODEでリターン */
2269 if (!low_kmem_size || /* there are no DMA area. */
2270 low_kmem_size > total_size/2) /* DMA/DMA32 is big. */
2271 return ZONELIST_ORDER_NODE;

/* 平均サイズを算出 */
2277 average_size = total_size /
2278 (nodes_weight(node_states[N_HIGH_MEMORY]) + 1);
2279 for_each_online_node(nid) {
2280 low_kmem_size = 0;
2281 total_size = 0;
/* もう一度同じように計算する */
2282 for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) {
2283 z = &NODE_DATA(nid)->node_zones[zone_type];
2284 if (populated_zone(z)) {
2285 if (zone_type < ZONE_NORMAL)
2286 low_kmem_size += z->present_pages;
2287 total_size += z->present_pages;
2288 }
2289 }
/* 平均より大きく、全サイズの70%を占めるノードがあるならZONELIST_ORDER_NODEでリターン*/
2290 if (low_kmem_size &&
2291 total_size > average_size && /* ignore small node */
2292 low_kmem_size > total_size * 70/100)
2293 return ZONELIST_ORDER_NODE;
2294 }
/* ここまで来たらZONELIST_ORDER_ZONEでリターン */
2295 return ZONELIST_ORDER_ZONE;
2296 }

=========

ノードリスト中のノードサイズに従ってどのゾーンから空きページを探すかを決定している、、、
のだと思います。まだ自信なし。。
ここで返った値が、先述のset_zonelist_orderの中でcurrent_zonelist_orderに代入されます。

2009年9月25日金曜日

build_all_zonelists

今回見てみるのはbuild_all_zonelistsです。
実体はmm/page_alloc.cにあります。

========
/* コメントやprintkは省略してます */
void build_all_zonelists(void)
{
set_zonelist_order();

if (system_state == SYSTEM_BOOTING) {
/* すべてのノードのzonelistsを構築する */
__build_all_zonelists(NULL);
/* デバッグ用なので無視 */
mminit_verify_zonelist();
/* current->mems_allowedを初期化している。なんだ?? */
cpuset_init_current_mems_allowed();
} else {
/* ここに来たら異常。システムを停止させる */
stop_machine(__build_all_zonelists, NULL, NULL);
}
/* 全ノードの割り当て可能なページ数を取得 */
vm_total_pages = nr_free_pagecache_pages();

/* デバッグ用なので省略 */
}

========

要約するとゾーンリストを構築して、割り当て可能なページ数を「vm_total_pages」に格納する処理になります。
Linuxではページをゾーンという単位で管理しており、ゾーンリストというのはそれらゾーンをリストしたものです。

ゾーンリストについては下記を参考にしました。
Linux Kernel Hack Japan「build_all_zonelists()/linux2.6」


ここから呼び出している関数群についてはまた次回から見ていこうと思います。

2009年9月21日月曜日

preempt_disable

間があいてしまいました。
忘れないうちに今日はpreempt_disableを見てみましょう。

実体はinclude/linux/preempt.hにあります。

======
#define preempt_disable() \
do { \
inc_preempt_count(); \
barrier(); \
} while (0)
======

inc_preempt_countでプリエンプションのカウンタをインクリメントしています。preempt_enableのときは逆にデクリメントします。その後のbarrierは最適化しないでね、とコンパイラに指示するものです。
do-while文ですが条件が0固定なので1度しか実行されません。敢えてdo-whileにしているのでしょうが、なぜなのでしょう?

と思って調べてみたら解説がありました。
[ruby-dev:11748] Re: do while(0)


なるほど。。。

2009年9月7日月曜日

smp_prepare_boot_cp(パス)

次に来るsmp_prepare_boot_cpはSMP構成のみ有効になるコードなのでパスします。
start_kernelの次の部分を見てみましょう。

=====
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init();
=====

スケジューラの初期化です。
実体はkernel/sched.cにありますが、大きな関数なのでスケジューラ関係は
別の機会にまとめて見てみようと思います。
なのでその次。

=====
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
build_all_zonelists();
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s¥n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it¥n");
local_irq_disable();
}
sort_main_extable();
trap_init();
rcu_init();

=====

次回から1つずつ追っていこうと思います。

2009年9月6日日曜日

setup_nr_cpu_ids

今回はsetup_nr_cpu_idsを見てみます。
実体はinit/main.cにあります。

=====
static void __init setup_nr_cpu_ids(void)
{
/* 有効なプロセッサIDを取得 */
nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1;
}

=====

これだけ!

2009年9月5日土曜日

setup_per_cpu_areas

今回はsetup_per_cpu_areasを見てみます。
実体はinit/main.cにあります。

=====
static void __init setup_per_cpu_areas(void)
{
unsigned long size, i;
char *ptr;

/* CPUの数を取得 */
unsigned long nr_possible_cpus = num_possible_cpus();

/* コピーに必要なサイズをとってくる */
size = ALIGN(PERCPU_ENOUGH_ROOM, PAGE_SIZE);
/* メモリを確保 */
ptr = alloc_bootmem_pages(size * nr_possible_cpus);

/* CPUの数だけ繰り返す */
for_each_possible_cpu(i) {
__per_cpu_offset[i] = ptr - __per_cpu_start;
/* 確保した領域に__per_cpu_startのセクションをコピーする */
memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
ptr += size;
}
}

=====

__per_cpu_startセクションはarch/x86/kernel/vmlinux_32.lds.Sにあります。

2009年9月3日木曜日

setup_command_line

今日はsetup_command_lineを見てみましょう。
実体はstart_kernelと同じinit/main.cにあります。

=====
static void __init setup_command_line(char *command_line)
{
saved_command_line = alloc_bootmem(strlen (boot_command_line)+1);
static_command_line = alloc_bootmem(strlen (command_line)+1);
strcpy (saved_command_line, boot_command_line);
strcpy (static_command_line, command_line);
}

=====

これはブート時のコマンドライン引数を保存しているだけですね。

2009年9月1日火曜日

mm_init_owner

今日はsetup_archを見てみましょう。
と、思ったのだけど非常に大規模なソースなので、ちょっとパス。start_kernelが見終わったら改めて見てみようと思います。実体はarch/x86/kernel/setup.cにあって、BIOSからとってきた情報をいろいろとセットしているみたい。

というわけで、ちょっとスキップして今回はmm_init_ownerを見てみます。
実体はkernel/fork.cにあります。
======
void mm_init_owner(struct mm_struct *mm, struct task_struct *p)
{
mm->owner = p;
}
======

これだけ。
start_kernel内では
mm_init_owner(&init_mm, &init_task);
としているので、init_mm->ownerにinit_taskのアドレスを格納しています。
で、この2つはどこから来たのでしょう。

それぞれarch/x86/kernel/init_task.cに以下のようにあります。
=====
struct mm_struct init_mm = INIT_MM(init_mm);
~(略)~
struct task_struct init_task = INIT_TASK(init_task);
=====

INIT_MM、INIT_TASKはinclude/linux/init_task.hにあります。
=====
#define INIT_MM(name) \
{ \
.mm_rb = RB_ROOT,
.pgd = swapper_pg_dir, \
.mm_users = ATOMIC_INIT(2), \
.mm_count = ATOMIC_INIT(1), \
.mmap_sem = __RWSEM_INITIALIZER(name.mmap_sem), \
.page_table_lock = __SPIN_LOCK_UNLOCKED(name.page_table_lock), \
.mmlist = LIST_HEAD_INIT(name.mmlist), \
.cpu_vm_mask = CPU_MASK_ALL, \
}

~(略)~

#define INIT_TASK(tsk) \
{ \
.state = 0, \
.stack = &init_thread_info, \
.usage = ATOMIC_INIT(2), \
.flags = PF_KTHREAD, \
.lock_depth = -1, \
.prio = MAX_PRIO-20, \
.static_prio = MAX_PRIO-20, \
.normal_prio = MAX_PRIO-20, \
.policy = SCHED_NORMAL, \
.cpus_allowed = CPU_MASK_ALL, \
.mm = NULL, \
.active_mm = &init_mm, \
.se = { \
.group_node = LIST_HEAD_INIT(tsk.se.group_node), \
}, \
.rt = { \
.run_list = LIST_HEAD_INIT(tsk.rt.run_list), \
.time_slice = HZ, \
.nr_cpus_allowed = NR_CPUS, \
}, \
.tasks = LIST_HEAD_INIT(tsk.tasks), \
.ptraced = LIST_HEAD_INIT(tsk.ptraced), \
.ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), \
.real_parent = &tsk, \
.parent = &tsk, \
.children = LIST_HEAD_INIT(tsk.children), \
.sibling = LIST_HEAD_INIT(tsk.sibling), \
.group_leader = &tsk, \
.real_cred = &init_cred, \
.cred = &init_cred, \
.cred_exec_mutex = \
__MUTEX_INITIALIZER(tsk.cred_exec_mutex), \
.comm = "swapper", \
.thread = INIT_THREAD, \
.fs = &init_fs, \
.files = &init_files, \
.signal = &init_signals, \
.sighand = &init_sighand, \
.nsproxy = &init_nsproxy, \
.pending = { \
.list = LIST_HEAD_INIT(tsk.pending.list), \
.signal = {{0}}}, \
.blocked = {{0}}, \
.alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), \
.journal_info = NULL, \
.cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \
.fs_excl = ATOMIC_INIT(0), \
.pi_lock = __SPIN_LOCK_UNLOCKED(tsk.pi_lock), \
.timer_slack_ns = 50000, /* 50 usec default slack */ \
.pids = { \
[PIDTYPE_PID] = INIT_PID_LINK(PIDTYPE_PID), \
[PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID), \
[PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \
}, \
.dirties = INIT_PROP_LOCAL_SINGLE(dirties), \
INIT_IDS \
INIT_TRACE_IRQFLAGS \
INIT_LOCKDEP \
}

=====

とinit_mm、init_taskの一部メンバを初期化しています。