今日はx86_64のpaging_init()から呼ばれるsparse_memory_present_with_active_regions()とそこで使われるmemory_present()のあたりを読む。 ディレクトリ的にはarch/x86/以下ではなくてmm/になる。
まずはsparse_memory_present_with_active_regions()を。 見た目は単純なので、for_each_mem_pfn_range()のループがどんなものなのかを見る。ところで、Linuxのループに関する関数っぽいものは大概マクロですね。
4372 /** 4373 * sparse_memory_present_with_active_regions - Call memory_present for each active range 4374 * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used. 4375 * 4376 * If an architecture guarantees that all ranges registered with 4377 * add_active_ranges() contain no holes and may be freed, this 4378 * function may be used instead of calling memory_present() manually. 4379 */ 4380 void __init sparse_memory_present_with_active_regions(int nid) 4381 { 4382 unsigned long start_pfn, end_pfn; 4383 int i, this_nid; 4384 4385 for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, &this_nid) 4386 memory_present(this_nid, start_pfn, end_pfn); 4387 }
実際にfor_each_mem_pfn_rangeはマクロでこんな処理をしている。
79 #define for_each_mem_pfn_range(i, nid, p_start, p_end, p_nid) \ 80 for (i = -1, __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid); \ 81 i >= 0; __next_mem_pfn_range(&i, nid, p_start, p_end, p_nid))
内容としては__next_mem_pfn_range()がポイントなのでそちらも見てみる。 では上から順番に見ていくと・・・
memblock.memoryは前回で調べたところでe820のメモリマップがリージョンごとに入っている。
808 void __init_memblock __next_mem_pfn_range(int *idx, int nid, 809 unsigned long *out_start_pfn, 810 unsigned long *out_end_pfn, int *out_nid) 811 { 812 struct memblock_type *type = &memblock.memory;
type->cntはregionsの数。idxは-1が最初に渡されるので0から始まってregions個分のループになる。
813 struct memblock_region *r; 814 815 while (++*idx < type->cnt) { 816 r = &type->regions[*idx]; 817
PFN_UPとPFN_DOWNはinclude/linux/pfn.hにあるマクロでアドレスからページフレーム番号を計算する。 ここだとリージョンの開始アドレスのページフレーム番号がリージョンの終了アドレスのページフレーム番号を超えているかチエック。
818 if (PFN_UP(r->base) >= PFN_DOWN(r->base + r->size)) 819 continue;
paging_init()から来る場合、nidはMAX_NUMNODESなので上のチェックに引っかからなければここでループ終了。
820 if (nid == MAX_NUMNODES || nid == r->nid) 821 break; 822 }
有効なリージョンが見つからなかった場合は*idxを-1にして終了。for_each_mem_pfn_rangeマクロのループの条件はidx >= 0なのでこれでループ終了する仕組み。
823 if (*idx >= type->cnt) { 824 *idx = -1; 825 return; 826 } 827
残りは引数で渡されたポインタがNULLじゃなければそれぞれのポインタに値をセットする。 out_start_pfnとout_end_pfnにはページブレーム番号、out_nidには見つかったリージョンのnidをセット。 nidはNUMAのNode IDの略だと思うんだけど、検索するときは全ノードを対象にして見つかった時はそのノードをout_nidにセットという感じ。
828 if (out_start_pfn) 829 *out_start_pfn = PFN_UP(r->base); 830 if (out_end_pfn) 831 *out_end_pfn = PFN_DOWN(r->base + r->size); 832 if (out_nid) 833 *out_nid = r->nid; 834 }
これでfor_each_mem_pfn_rangeが何をやっているかわかったのでsparse_memory_present_with_active_regions()に戻ってmemory_present()を見ていく。
4385 for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, &this_nid) 4386 memory_present(this_nid, start_pfn, end_pfn);
これも見た目はシンプル。startとendはページフレーム番号の開始と最後。nidはこれらのページフレームがあるノードの番号。
167 /* Record a memory area against a node. */ 168 void __init memory_present(int nid, unsigned long start, unsigned long end) 169 { 170 unsigned long pfn; 171 172 start &= PAGE_SECTION_MASK;
mminit_validate_memmodel_limitsは名前の通りなのでそんなに調べずに次に。
173 mminit_validate_memmodel_limits(&start, &end);
pfn_to_section_nr()はマクロでページフレーム番号をビットシフトしているセクション番号に変換。
174 for (pfn = start; pfn < end; pfn += PAGES_PER_SECTION) { 175 unsigned long section = pfn_to_section_nr(pfn); 176 struct mem_section *ms; 177
sparse_index_init()は対象セクション用にメモリを確保してグローバル変数のmem_section[]に保存するといったことをする関数。 set_section_nid()はセクション番号とノード番号の対応付けを管理している配列の変数に値をセットする。
178 sparse_index_init(section, nid); 179 set_section_nid(section, nid); 180
__nr_to_section()は見たままだけど、セクション番号からmem_sction構造体のポインタを返す。ここで返しているポインタはsparse_index_init()で設定されたもの。
181 ms = __nr_to_section(section);
sparse_index_init()が終わった段階ではsection_mem_mapは0なはず。 sparse_encode_early_nid()がやっているのは(nid << SECTION_NID_SHIFT)だけ。
182 if (!ms->section_mem_map) 183 ms->section_mem_map = sparse_encode_early_nid(nid) | 184 SECTION_MARKED_PRESENT; 185 } 186 }
ここまででsparse_memory_present_with_active_regions()の処理が終わってmemblock、mem_sectionといったところの設定も第一段階が終了した模様。そして次はsparse_init()へ。