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

linux:sparse_init()の前にalloc_usemap_and_memmap()を見ておく

x86_64 linux kernel

paging_init()の2行目で実行するsparse_init()を見る前にここで使っている関数を先に軽く見ておく。

alloc_usemap_and_memmap()はこのようなI/Fの関数。 引数の最後のdataはalloc_func()の一番目の引数として使う。

469 static void __init alloc_usemap_and_memmap(void (*alloc_func)
470                                         (void *, unsigned long, unsigned long,
471                                         unsigned long, int), void *data)

変数宣言部は特に目立ったことはなし。

473         unsigned long pnum;
474         unsigned long map_count;
475         int nodeid_begin = 0;
476         unsigned long pnum_begin = 0;
477 

0からNR_MEM_SECTIONSまでのループで何をするかというと、まずpnumはセクション番号として使っていて、このセクションが存在するかをチェックして存在しなければcontinue。 セクションがあれば__nr_to_section()でセクション番号からmem_section構造体のデータを取得。 セクションの開始番号がpnum_beginに入る。

478         for (pnum = 0; pnum < NR_MEM_SECTIONS; pnum++) {
479                 struct mem_section *ms;
480 
481                 if (!present_section_nr(pnum))
482                         continue;
483                 ms = __nr_to_section(pnum);
484                 nodeid_begin = sparse_early_nid(ms);
485                 pnum_begin = pnum;
486                 break;
487         }

次は先ほどのセクション開始番号の次からループを開始。最初のif文は先ほどのチェックと同じ。

488         map_count = 1;
489         for (pnum = pnum_begin + 1; pnum < NR_MEM_SECTIONS; pnum++) {
490                 struct mem_section *ms;
491                 int nodeid;
492 
493                 if (!present_section_nr(pnum))
494                         continue;

mem_section構造体からNUMAノードのIDを取得。

495                 ms = __nr_to_section(pnum);
496                 nodeid = sparse_early_nid(ms);

これは前回の記事の最後の方で↓のようなことをしているんだけど、これによりここでノードIDが取れる。

182                 if (!ms->section_mem_map)
183                         ms->section_mem_map = sparse_encode_early_nid(nid) |
184                                                         SECTION_MARKED_PRESENT;

ノードが同じならmap_countを1増やしてcontinue。map_countはalloc_func()で使用する。

497                 if (nodeid == nodeid_begin) {
498                         map_count++;
499                         continue;
500                 }

ここでalloc_func()を呼んでメモリ確保する。

501                 /* ok, we need to take cake of from pnum_begin to pnum - 1*/
502                 alloc_func(data, pnum_begin, pnum,
503                                                 map_count, nodeid_begin);

sparse_init()ではこのalloc_func()にsparse_early_usemaps_alloc_node()を渡しているんだけど、今はメモリを確保するってことだけわかれば先に進めるのでとりあえず(゚ε゚)キニシナイ!!

ここも見たままですな。

504                 /* new start, update count etc*/
505                 nodeid_begin = nodeid;
506                 pnum_begin = pnum;
507                 map_count = 1;
508         }

ループが終わったところでNR_MEM_SECTIONSまでのメモリチャンクを確保する。

509         /* ok, last chunk */
510         alloc_func(data, pnum_begin, NR_MEM_SECTIONS,
511                                                 map_count, nodeid_begin);
512 }

alloc_usemap_and_memmap()が終わったところでsparse_early_usemaps_alloc_node()を見てみる。 alloc_usemap_and_memmap()でノードが同じならmap_countをインクリメントしていたけど、それがsize * usemap_countで使われている。

342 static void __init sparse_early_usemaps_alloc_node(void *data,
343                                  unsigned long pnum_begin,
344                                  unsigned long pnum_end,
345                                  unsigned long usemap_count, int nodeid)
346 {
347         void *usemap;
348         unsigned long pnum;
349         unsigned long **usemap_map = (unsigned long **)data;
350         int size = usemap_size();
351 
352         usemap = sparse_early_usemaps_alloc_pgdat_section(NODE_DATA(nodeid),
353                                                           size * usemap_count);

sparse_early_usemaps_alloc_pgdat_section()は実際にメモリを確保する処理なんだけど、ここも実装は特に気にしなくて良いはずなので飛ばす。

エラー処理なので見るべきところはなし。

354         if (!usemap) {
355                 printk(KERN_WARNING "%s: allocation failed\n", __func__);
356                 return;
357         }
358 

セクション数のループで配列のusemap_mapに先ほどsparse_early_usemaps_alloc_pgdat_section()でメモリを確保した変数をセット。 最後のcheck_usemap_section_nr()はチェック用の関数なので設定した内容に問題が無い限りは無視。

359         for (pnum = pnum_begin; pnum < pnum_end; pnum++) {
360                 if (!present_section_nr(pnum))
361                         continue;
362                 usemap_map[pnum] = usemap;
363                 usemap += size;
364                 check_usemap_section_nr(nodeid, usemap_map[pnum]);
365         }
366 }

これでalloc_usemap_and_memmap()の処理は読み終わり。

ちなみに、今呼んでいるコードはpaging_init()から呼ばれる関数なのでページングの設定中な訳だけどこの時点でもページングは有効になってます。 これはstart_kernel()が呼ばれた段階で既にcpuはlong modeになっているんですが、long modeではページングの使用が必須なのでブート時に使うようの設定がされています。 この辺のコードはφ(.. )メモシテオコウ linuxのlong modeへの移行処理で読んでます。

Linuxカーネル解析入門 (I・O BOOKS)

Linuxカーネル解析入門 (I・O BOOKS)