Linux Kernel(2.6)の実装に関するメモ書き

block_read_full_page()


Rev.9を表示中。最新版はこちら

概要

block_read_full_page()はPageCacheへのデータの読み込みを行う汎用ルーチン。

この関数は指定したページに対応するディスクブロックからファイルデータを読み込みページにデータを格納する(図1)。


図1 データの読み込み

この関数は、ファイルリードの過程でディスクからPageCacheへファイルデータを読み込むのに使用する。address_space_operationsのreadpage,readpagesに登録されているPageCacheへのデータ読み込みハンドラ(もしくはその延長の処理)から呼び出される(図2)。


図2 block_read_full_page()の位置づけ


mpageとの関係

同じようなPageCacheへのデータ読み込み関数としてmpageモジュールのmpage_readpage()などがある。

mpage_readpage()ではページに対応するディスクブロックが全て連続している場合に特化された処理となっている。ディスクブロックが非連続となっていてmapgeで処理できない場合は、より汎用的なblock_read_full_page()を呼び出すようになっている。

実装

block_read_full_page(struct page *page, get_block_t *get_block)
pageで指定されるPageCacheにディスクからデータを読み込むRead I/Oを開始する。
get_blockには「ファイル上のブロック番号」→「ディスクブロック番号」に変換するルーチンが渡される。これはファイルシステム毎に異なる。例えばExt2ならext2_get_block()。

編集中
block_read_full_page()の処理概要
:
/* Bufferがなければ作成する */
if (!page_has_buffers(page))
    create_empty_buffers(page, blocksize, 0);

/* Pageに対応するBuffer(ディスクブロックのデータ)
 * を全てチェック
 */
do {
    /* Uptodate状態ならI/O不要なので飛ばす */
    if (buffer_uptodate(bh))
        continue;

    if (!buffer_mapped(bh)) {
        /* Bufferがディスクブロックに対応付けられていない場合は
         * get_block()でディスク上の対応するブロック番号を
         * もとめてBufferをmapする。
         */
        if (iblock < lblock) {
            err = get_block(inode, iblock, bh, 0);
            if (err)
                SetPageError(page);
        }
        if (!buffer_mapped(bh)) {
            /* get_block()でmapできなかった。
             * ファイル終端(EOF)を越えている場合はここ。
             */
             /* pageのbh対応部分を0で初期化する */
             :
             continue;
        }
        if (buffer_uptodate(bh))
            continue;
    }
    /* I/Oが必要なBufferを配列に保存しておく */
    arr[nr++] = bh;
} while (i, iblock, (bh = bh->b_this_page) != head);

/* Pageに対応するBufferが全てディスクにマップ
 * されていたら、Pageもマップ状態にする
 */
if (fully_mapped)
    SetPageMappedToDisk(page);

/* I/OすべきBufferがなかったのなら
 * ページをUptodate状態にして終了
 */
if (!nr) {
    if (!PageError(page))
        SetPageUptodate(page);
    unlock_page(page);
    return 0;
}

/* I/Oの準備
 * ・Bufferのロック
 * ・I/O完了時のハンドラ(end_buffer_async_read)設定
 * ・Bufferに非同期Read中であることを示すBH_Async_Readフラグをセット
 */
for (i = 0; i < nr; i) {
    bh = arr[i];
    lock_buffer(bh);
    mark_buffer_async_read(bh);
}

/* I/O開始 */
for (i = 0; i < nr; i) {
    bh = arr[i];
    /* 他のプロセスのRead処理などにより、
     * すでにUptodate状態になっていた場合は
     * ここから直接I/O完了時のハンドラを呼び出す */
    if (buffer_uptodate(bh))
        end_buffer_async_read(bh, 1);
    else
        submit_bh(READ, bh);
}
return 0;


end_buffer_async_read()
Read I/O 完了時の処理

end_buffer_async_read()の処理概要
page = bh->b_page;
if (uptodate) {
    set_buffer_uptodate(bh);
} else {
    clear_buffer_uptodate(bh);
    if (printk_ratelimit())
        buffer_io_error(bh);
    SetPageError(page);
}

first = page_buffers(page);
local_irq_save(flags);
bit_spin_lock(BH_Uptodate_Lock, &first->b_state);
/* I/Oが完了してBufferについて
 * ・BH_Async_Readフラグを落とす
 * ・ロックを解除
 */
clear_buffer_async_read(bh);
unlock_buffer(bh);
/* page内のBufferが全てUptodate状態なら
 * pageもUptodate状態にする
 */
tmp = bh;
do {
    if (!buffer_uptodate(tmp))
        page_uptodate = 0;
    if (buffer_async_read(tmp)) {
        BUG_ON(!buffer_locked(tmp));
        goto still_busy;
    }
    tmp = tmp->b_this_page;
} while (tmp != bh);

if (page_uptodate && !PageError(page))
    SetPageUptodate(page);
unlock_page(page);

return;

still_busy:
:


関連ページ

mpage


最終更新 2007/03/03 15:23:05 - kztomita
(2007/03/02 10:10:08 作成)
添付ファイル
block_read_full_page.png - kztomita
read_op.png - kztomita
page-buffer.png - kztomita
io_completion.png - kztomita


リンク
最近更新したページ
検索