x86_64のページングを設定している部分を読もうと思って読み始めて前回の続きになるsparse_memory_present_with_active_regions()を読もうと思ったんだけど、この関数で使う変数(struct memblockのメンバ変数のmemory(型はstruct memblock_type))を調べたほうが良さそうだったので、先にこっちを読む。
struct memblockはこのようになっていてstruct memblock_type memoryがメンバ変数にある。 この構造体はざっくりと言ってしまうとメモリのブロックをmemoryに登録して管理している。
37 struct memblock { 38 bool bottom_up; /* is bottom up direction? */ 39 phys_addr_t current_limit; 40 struct memblock_type memory; 41 struct memblock_type reserved; 42 };
で、メモリブロックを登録するのはstruct memblock_typeでこちらはでは領域の情報は保存せず、領域が何個あるとか、最大数とかの情報が置かれる。メモリの範囲が設定されるのはregionsのほう。
30 struct memblock_type { 31 unsigned long cnt; /* number of regions */ 32 unsigned long max; /* size of the allocated array */ 33 phys_addr_t total_size; /* size of all regions */ 34 struct memblock_region *regions; 35 };
struct memblock_regionはこのような変数でメモリの開始アドレスとサイズが必ずある。
22 struct memblock_region { 23 phys_addr_t base; 24 phys_addr_t size; 25 #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP 26 int nid; 27 #endif 28 }; 29
そして、sparse_memory_present_with_active_regions()の段階ではこのstruct membloc.memoryを手繰る処理があるのでこの関数が呼ばれる以前で設定されているということ。
まず、struct memblockだけど、これはmm/memblock.cにあってmemblock_memory_init_regionsという構造体がセットされている。
28 struct memblock memblock __initdata_memblock = { 29 .memory.regions = memblock_memory_init_regions,
この構造体もmm/memblock.cにあり、以下のようになっている。
25 static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
そして、これにデータを設定している部分なんだけど、まずはe820のメモリマップが必要になるのでそちらを読んでみる。 これはarch/x86/kernel/setup.cのarch_setup()内でsetup_memory_map()を呼び出しで実行。
922 x86_init.oem.arch_setup(); 923 924 iomem_resource.end = (1ULL << boot_cpu_data.x86_phys_bits) - 1; 925 setup_memory_map(); 926 parse_setup_data();
setup_memory_map()はarch/x86/kernel/e820.cにあって、これは割と短い関数。
1062 void __init setup_memory_map(void) 1063 { 1064 char *who; 1065 1066 who = x86_init.resources.memory_setup(); 1067 memcpy(&e820_saved, &e820, sizeof(struct e820map)); 1068 printk(KERN_INFO "e820: BIOS-provided physical RAM map:\n"); 1069 e820_print_map(who); 1070 }
まあ、実際の処理はx86_init.resources.memory_setup()なのでそこを見る。x86_initは前回も出てきたx86の初期化用の関数が登録されている構造体。memory_setup()はdefault_machine_specific_memory_setup()がセットされていて、これもarch/x86/kernel/e820.cにある。
1024 char *__init default_machine_specific_memory_setup(void) 1025 { 1026 char *who = "BIOS-e820"; 1027 u32 new_nr; 〜中略〜 1058 /* In case someone cares... */ 1059 return who; 1060 }
ここの中身は飛ばしてsetup_memory_map()に戻ると最後にe820_print_map()でe820のメモリマップを表示する部分があり、これはdmesgで確認できる。 dmesgのこの部分がe820_print_map()で表示している内容。
[ 0.000000] e820: BIOS-provided physical RAM map: [ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009d7ff] usable [ 0.000000] BIOS-e820: [mem 0x000000000009d800-0x000000000009ffff] reserved 〜略〜 [ 0.000000] BIOS-e820: [mem 0x0000000078ef8000-0x0000000078ef8fff] ACPI data [ 0.000000] BIOS-e820: [mem 0x0000000078ef9000-0x00000000797a7fff] ACPI NVS [ 0.000000] BIOS-e820: [mem 0x00000000797a8000-0x000000007a30dfff] reserved [ 0.000000] BIOS-e820: [mem 0x000000007a30e000-0x000000007a30efff] usable [ 0.000000] BIOS-e820: [mem 0x000000007a30f000-0x000000007a351fff] ACPI NVS [ 0.000000] BIOS-e820: [mem 0x000000007a352000-0x000000007aca1fff] usable [ 0.000000] BIOS-e820: [mem 0x000000007aca2000-0x000000007afd6fff] reserved [ 0.000000] BIOS-e820: [mem 0x000000007afd7000-0x000000007affffff] usable 〜略〜 [ 0.000000] BIOS-e820: [mem 0x00000000fee00000-0x00000000fee00fff] reserved [ 0.000000] BIOS-e820: [mem 0x00000000ff000000-0x00000000ffffffff] reserved [ 0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000087f5fffff] usable
ここまででe820のメモリマップが取得できたことがわかったので、次はmemblockを調べるほうに戻る。 arch_setup()ではmemblockのreservedにメモリ領域の予約をしている部分もあるけどここは飛ばす。
975 /* after early param, so could get panic from serial */ 976 memblock_x86_reserve_range_setup_data();
そしてsetup_arch()をまた読み進めて行ってあたるのがここでmemblock_x86_fill()が探していたもの。
1071 memblock_set_current_limit(ISA_END_ADDRESS); 1072 memblock_x86_fill();
memblock_set_current_limit()は物理アドレスISA_END_ADDRESS以降は触らないようにって設定だと思う。
ではmemblock_x86_fill()を見てみとこれは比較的シンプル!
1072 void __init memblock_x86_fill(void) 1073 { 1074 int i; 1075 u64 end; 1076 1077 /* 1078 * EFI may have more than 128 entries 1079 * We are safe to enable resizing, beause memblock_x86_fill() 1080 * is rather later for x86 1081 */ 1082 memblock_allow_resize();
memblock_allow_resize()はmemblock_can_resizeを1にするだけ。使用方法は名前の通りですね。リサイズ可能かのチェックはmemblock_double_array()で唯一行ってた。
ここからはe820のメモリマップを見ていく。
1083 1084 for (i = 0; i < e820.nr_map; i++) { 1085 struct e820entry *ei = &e820.map[i]; 1086 1087 end = ei->addr + ei->size;
メモリの範囲を計算して、
1088 if (end != (resource_size_t)end) 1089 continue; 1090
アドレスの範囲チェック。
1091 if (ei->type != E820_RAM && ei->type != E820_RESERVED_KERN) 1092 continue; 1093
領域がe820のramじゃないとかカーネル予約済みかチェック
1094 memblock_add(ei->addr, ei->size);
そして、ついにmemblockにデータを登録するmemblock_add()さんの登場。 とりあえず、これはひとまず置いておいて続きを見てしまう。
1095 } 1096 1097 /* throw away partial pages */ 1098 memblock_trim_memory(PAGE_SIZE); 1099
ループを抜けたらmemblock_trim_memory()の呼び出しがあり、これはe820の各メモリマップの開始・終了アドレスをページ境界に揃えている。
1100 memblock_dump_all(); 1101 } 1102
最後はmemblock_dump_all()でメモリマップの表示だけど、これはカーネルのコマンドラインパラメータmemblock=debugがセットされている場合だけ実行。 memblock_x86_fill()の処理は以上なので次はmemblock_add()を見てみる。
これはmemblock_add_region()のラッパーなのでさくっと。
543 int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size) 544 { 545 return memblock_add_region(&memblock.memory, base, size, MAX_NUMNODES); 546 }
memblock_add_region()は今まで出てきた奴らよりはちょい長めの関数。 関数冒頭にコメントもあり良い感じ。
447 /** 448 * memblock_add_region - add new memblock region 449 * @type: memblock type to add new region into 450 * @base: base address of the new region 451 * @size: size of the new region 452 * @nid: nid of the new region 453 * 454 * Add new memblock region [@base,@base+@size) into @type. The new region 455 * is allowed to overlap with existing ones - overlaps don't affect already 456 * existing regions. @type is guaranteed to be minimal (all neighbouring 457 * compatible regions are merged) after the addition. 458 * 459 * RETURNS: 460 * 0 on success, -errno on failure. 461 */ 462 static int __init_memblock memblock_add_region(struct memblock_type *type, 463 phys_addr_t base, phys_addr_t size, int nid) 464 {
変数宣言をしつつ、メモリの開始アドレス、終了アドレスの設定。
465 bool insert = false; 466 phys_addr_t obase = base; 467 phys_addr_t end = base + memblock_cap_size(base, &size); 468 int i, nr_new;
memblock_cap_size()はこのような関数。
58 /* adjust *@size so that (@base + *@size) doesn't overflow, return new size */ 59 static inline phys_addr_t memblock_cap_size(phys_addr_t base, phys_addr_t *size) 60 { 61 return *size = min(*size, (phys_addr_t)ULLONG_MAX - base); 62 }
サイズのチェック。コメントからすると特殊ケースっぽい。
473 /* special case for empty array */ 474 if (type->regions[0].size == 0) { 475 WARN_ON(type->cnt != 1 || type->total_size); 476 type->regions[0].base = base; 477 type->regions[0].size = size; 478 memblock_set_region_node(&type->regions[0], nid); 479 type->total_size = size; 480 return 0; 481 }
このへんからが本番。 memblock.memoryに登録されているデータ数分のループ。
483 /* 484 * The following is executed twice. Once with %false @insert and 485 * then with %true. The first counts the number of regions needed 486 * to accomodate the new area. The second actually inserts them. 487 */ 488 base = obase; 489 nr_new = 0; 490 491 for (i = 0; i < type->cnt; i++) {
ループの最初は変数定義から。
492 struct memblock_region *rgn = &type->regions[i]; 493 phys_addr_t rbase = rgn->base; 494 phys_addr_t rend = rbase + rgn->size; 495
resions[i]に登録されている(e820の)アドレスチェック
496 if (rbase >= end) 497 break; 498 if (rend <= base) 499 continue;
regionsに登録されているメモリ開始位置(rbase)がendを超えたらループ終了。
開始アドレスに問題がなければmemblock_insert_region()でデータ登録。
500 /* 501 * @rgn overlaps. If it separates the lower part of new 502 * area, insert that portion. 503 */ 504 if (rbase > base) { 505 nr_new++; 506 if (insert) 507 memblock_insert_region(type, i++, base, 508 rbase - base, nid); 509 }
baseアドレスを更新してループ冒頭に。
510 /* area below @rend is dealt with, forget about it */ 511 base = min(rend, end); 512 }
ループを抜けてからの処理。 ループを抜けた状態でbaseがendを超えていなければそれも登録する。
514 /* insert the remaining portion */ 515 if (base < end) { 516 nr_new++; 517 if (insert) 518 memblock_insert_region(type, i, base, end - base, nid); 519 } 520
最後にregionのリサイズもしくはマージで終了。ここはとりあえず読まない。
521 /* 522 * If this was the first round, resize array and repeat for actual 523 * insertions; otherwise, merge and return. 524 */ 525 if (!insert) { 526 while (type->cnt + nr_new > type->max) 527 if (memblock_double_array(type, obase, size) < 0) 528 return -ENOMEM; 529 insert = true; 530 goto repeat; 531 } else { 532 memblock_merge_regions(type); 533 return 0; 534 } 535 }
memblock_insert_region()はデータの登録だけなのでシンプル。
421 /** 422 * memblock_insert_region - insert new memblock region 423 * @type: memblock type to insert into 424 * @idx: index for the insertion point 425 * @base: base address of the new region 426 * @size: size of the new region 427 * @nid: node id of the new region 428 * 429 * Insert new memblock region [@base,@base+@size) into @type at @idx. 430 * @type must already have extra room to accomodate the new region. 431 */ 432 static void __init_memblock memblock_insert_region(struct memblock_type *type, 433 int idx, phys_addr_t base, 434 phys_addr_t size, int nid) 435 { 436 struct memblock_region *rgn = &type->regions[idx]; 437 438 BUG_ON(type->cnt >= type->max); 439 memmove(rgn + 1, rgn, (type->cnt - idx) * sizeof(*rgn)); 440 rgn->base = base; 441 rgn->size = size; 442 memblock_set_region_node(rgn, nid); 443 type->cnt++; 444 type->total_size += size; 445 }
ということで、setup_memory_map()でe820のメモリマップを取得し、その中から使える領域をmemblock_x86_fill()で調べつつmemblock_add()でmemblock構造体のmemoryにあるregionsに登録していくというのが 今回見た範囲。
( ´ー`)フゥー...
- 作者: Daniel P. Bovet,Marco Cesati,高橋浩和,杉田由美子,清水正明,高杉昌督,平松雅巳,安井隆宏
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2007/02/26
- メディア: 大型本
- 購入: 9人 クリック: 269回
- この商品を含むブログ (68件) を見る