読者です 読者をやめる 読者になる 読者になる

linux:x86_64のpaging_init()を読み進める。

x86_64 kernel linux

今日は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()へ。