ext2_get_block()
1. 概要
ext2fsのget_blockハンドラであるext2_get_block()の実装まとめ。
2. 処理概要
2.1 ext2_get_block()
int ext2_get_block(*inode, iblock, *bh_result, create)ext2_get_block()はinodeで示すオブジェクト(ファイル等)のiblock番目のブロックの位置(ブロックデバイスとブロック番号)を取得する。この位置は、bh_resultに設定されて返される。get_blockハンドラに関する一般的な説明は「get_block()ハンドラ」参照。
ext2_get_block()の処理概要は以下のとおり。
/* * inodeが示すファイルのiblock番目のブロックにアクセスするための * パス(offsets)を取得する。 * 2.2節参照 */ int depth = ext2_block_to_path(inode, iblock, offsets, &boundary); /* * offsetsをたどり、必要なブロックが全てディスク上に * 存在しているか確かめる。 * 2.3節参照 */ partial = ext2_get_branch(inode, depth, offsets, chain, &err); if (!partial) { /* ブロックがディスク上に存在する場合 */ got_it: /* 判明したディスクブロックをbh_resultに設定する */ map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); if (boundary) set_buffer_boundary(bh_result); /* Clean up and exit */ partial = chain+depth-1; /* the whole chain */ goto cleanup; } /* ディスク上に未割り当てのブロックがある場合 */ if (!create || err == -EIO) { /* create = 0なら新規割り当てはせず終了 */ cleanup: while (partial > chain) { brelse(partial->bh); partial--; } out: return err; } if (err == -EAGAIN) goto changed; /* * 未割り当てブロックの新規割り当て処理 */ /* ブロックの割り当て場所(goal)を決める */ goal = 0; if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0) goto changed; /* ブロックを割り当てる * partial以降のブロックが対象 */ left = (chain + depth) - partial; err = ext2_alloc_branch(inode, left, goal, offsets+(partial-chain), partial); if (err) goto cleanup; if (ext2_use_xip(inode->i_sb)) { /* * we need to clear the block */ err = ext2_clear_xip_target (inode, le32_to_cpu(chain[depth-1].key)); if (err) goto cleanup; } /* i-nodeオブジェクトのi_next_alloc_blockと * i_next_alloc_goalを更新する。 * 2.4節参照 */ if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0) goto changed; /* 新しく割り当てられたブロックのBufferにはBH_newをセットする */ set_buffer_new(bh_result); goto got_it; changed: while (partial > chain) { brelse(partial->bh); partial--; } goto reread;
2.2 ext2_block_to_path()
int ext2_block_to_path(*inode, i_block, offsets[4], *boundary)ext2fsではi-node内にファイル内のブロックがディスク上のどこに存在しているのかブロック番号を格納したテーブルがある。ただしこのテーブルはファイル内の#0〜11番目のブロックのみしか格納しておらず、それ以降のブロックについてブロック番号を取得しようとすると、間接参照用のテーブルをたどっていく必要がある(「Ext2 FS」参照)。
ext2_block_to_path()はinodeで指定されたファイル内のi_block番目のブロックのブロック番号を取得するにあたって、間接参照用のテーブルのどのエントリを参照していけばよいのかをoffsetsに格納して返す。
例えばi_block=14のブロック番号を取得しようとした場合は、offsetsは図2.1のようになる。これは、まずi-node内の#12エントリから間接参照用テーブルのディスクブロック番号を読み出して、2段間接参照用テーブルを読み込んだ後、2段間接参照用テーブルの#2のエントリからi_block=14のブロックのブロック番号を取得できることを意味する(図2.2)。
図2.1 offsetとchain
図2.2 ブロックの間接アクセス
2.3 ext2_get_branch()
Indirect *ext2_get_branch(*inode, depth, *offsets, chain[4], *err)ext2_get_branch()はext2_block_to_path()によって返されたoffsetsを受け取り、offsetsで指定されたパスのブロックがディスク上に割り当てられているか調べていく。
図2.1のケースだと、i-node内のブロックテーブルの#12エントリからブロック番号を取得して、そのブロックを読み込む(このブロックには間接参照テーブルが格納されている)。次に読み込んだ間接参照テーブルの#2エントリからi_block=14ブロックのブロック番号を取得する。最後までブロック番号を取得できれば、offsetsで指定されたパスのブロックは全てディスク上に存在していることになる。この場合、NULLが返る。
一方、エントリからブロック番号を取得した結果、0だった場合、そのブロックはまだディスク上には存在していないことになる。
offsetsをたどってブロック番号を取得した結果は、図2.1に示すようにchainに格納して返されるが、ブロック番号が未割り当てだった場合は.key=0となり、そのchain要素のアドレスが返される。この値は呼出し元で未割り当てのブロックを割り当てていくのに使われる。
2.4 i_next_alloc_blockとi_next_alloc_goal
Ext2FSのi-nodeのメモリオブジェクトであるstruct ext2_inode_infoにはi_next_alloc_blockとi_next_alloc_goalというデータが存在する。これらは、ディスクブロックの割り当てをできるだけ連続にして、アクセス時の効率を上げるために使用される。
i_next_alloc_blockはこのファイルに直近で割り当てたブロック番号(ファイル内のブロック番号)を保持し、ファイル内のブロックの連続割り当て要求(linearly ascending allocation requests)を検出するのに使われる。
i_next_alloc_goalはこのファイルに対して直近で割り当てた物理ブロック番号を保持し、i_next_alloc_blockで連続ブロックの割り当てを検出した時に、次の割り当て位置を指定するのに使われる。
連続割り当ての検出はext2_find_goal()で行われる。
ext2_find_goal()での連続割り当て検出処理
/* * block - 割り当てるブロックの番号(ファイル内でのオフセット) * i_next_alloc_block - 前回割り当てたブロックの番号(ファイル内でのオフセット) */ if ((block == ei->i_next_alloc_block + 1) && ei->i_next_alloc_goal) { /* ファイル内での連続ブロック割り当て */ ei->i_next_alloc_block++; ei->i_next_alloc_goal++; } if (verify_chain(chain, partial)) { /* 連続ブロック割り当てなら、前回割り当てた * ディスクブロックの次のブロック(i_next_alloc_goal) * を割り当て対象に選択する。 * (i_next_alloc_goalは物理ブロック番号) */ if (block == ei->i_next_alloc_block) *goal = ei->i_next_alloc_goal; /* 連続割り当てでなければ他から探す */ if (!*goal) *goal = ext2_find_near(inode, partial); return 0; }