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

mpage_writepages()


1. 概要

WriteBack処理の中で呼び出されるmpage_writepages()のメモ。呼出元のWriteBack処理については「WriteBack処理」参照。

mpage_writepages()はページキャッシュからDirtyPageを取り出し、Write I/Oを開始する。

2. 処理の流れ

2.1 mpage_writepages()

mpage_writepages()はmappingで渡されたページキャッシュからDirtyページを取り出し、Write I/Oを発行していく。mpage_writepags()のプロトタイプは以下のとおり。

プロトタイプ: int mpage_writepages(struct address_space *mapping,
                struct writeback_control *wbc, get_block_t get_block)

mapping: WriteBack対象のページキャッシュ(mapping)
wbc: WriteBack処理管理情報
get_block: get_blockハンドラ

処理の流れは以下のとおり。

mpage_writepages()の処理概要
:
while (!done && (index <= end) &&
                  (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
                  PAGECACHE_TAG_DIRTY,
                  min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1))) {
    /*
     * ページキャッシュ(mapping)からDirtyページを少しずつ取得して処理していく
     */


    /* 取得したDirtyPageを順番にWriteBackしていく */
    for (i = 0; i < nr_pages; i++) {
        :
        if (writepage) {
            /* generic_writepages()マクロ経由で呼び出された場合
             *(writepagesハンドラが未定義のファイルシステムの場合)
             *
             * ファイルシステムのwritepageハンドラを呼び出して
             * Write I/O発行
             */
            ret = (*writepage)(page, wbc);
        } else {
            /* 連続ブロックをbioに格納。非連続ブロックなら内部でI/O発行 */
            bio = __mpage_writepage(bio, page, get_block,
                                       &last_block_in_bio, &ret, wbc,
                                       page->mapping->a_ops->writepage);
        }
        :
    }
}

/* __mpage_writepage()で作成した連続ブロックのI/Oを発行 */
if (bio)
    mpage_bio_submit(WRITE, bio)

mpage_writepages()はpagevec_lookup_tag()でページキャッシュ(mapping)からDirtyページを取得して、1ページずつ処理していく。

各ページの処理にはファイルシステムのwritepageハンドラ(mapping->a_ops->writepage)が呼び出される場合と、__mpage_writepage()が呼び出される場合がある。通常は、__mpage_writepage()が使用されるが、get_block引数がNULLで呼び出されていた場合(*1)はwritepageハンドラが呼び出されて、DirtyページのWrite I/Oが発行される。

__mpage_writepage()はWrite I/O用のbio(*2)を作成する。__mpage_writepage()は対象Dirtyページが図1のようにディスク上で連続ブロックになっていた場合は、これらのブロックに対するI/Oをbioに格納して返す。作成したbioは__mpage_writepage()の次回呼出し時にも引数として渡され、連続ブロックであれば次々とbioにWrite I/Oマージされていくことになる。作成されたbioは__mpage_writepage()の最後でまとめてI/O発行(mpage_bio_submit())される。このようにWriteBack対象のディスクブロックが連続していた場合はI/O処理が効率化される。


図1 連続ブロックのケース

一方、図2のようにディスクブロックが非連続になっている場合や、ページ内にDirtyでないBufferが見つかった場合は、I/Oをbioに返すのではなく__mpage_writepage()内でWrite I/Oを発行する(*3)。この場合、bioに連続ブロックI/Oが格納されていれば、bioのI/Oも発行する。


図2 非連続ブロックのケース

(*1) get_block引数がNULLで呼び出されるのは、__mpage_writepage()がgeneric_writepages()マクロ経由で呼び出される時で、これはwritepagesハンドラが未定義のファイルシステムにおけるWriteBack処理の場合となる。(「WriteBack処理」参照)
(*2)「ブロックI/O」参照
(*3) I/Oの発行にはファイルシステムのwritepageハンドラ(page->mapping->a_ops->writepage)が使用される。

2.2 __mpage_writepage

__mpage_writepage()は指定ページがディスク上で連続ブロックになっているかをチェックし、連続していた場合は、bioにI/O情報を格納して返す。返したbioはmpage_writepages()から繰り返し呼び出される際に、引数として再度渡されるようになっており、そこにI/Oをマージしてbioを返していくことで、連続ブロックのI/Oがひとまとまりになるようにする。

非連続ブロックを見つけた場合は、既に保持しているbioの連続ブロックI/Oを発行した後、非連続だったページのブロックI/Oを発行する(confuse:で処理)。


__mpage_writepage()の処理概要
if (page_has_buffers(page)) {
    /* pageにBufferが既にある場合 */
    /* 全Bufferをなめて、pageのディスクブロックが
     * 連続しているかチェックする
     * 非連続だった場合はconfuseに飛ばしてI/O発行
     */
    do {
        :
        if (!buffer_dirty(bh) || !buffer_uptodate(bh))
            goto confused;
        /* Blockの連続チェック */
        if (page_block) {
            if (bh->b_blocknr != blocks[page_block-1] + 1)
                goto confused;
        }
        :
    } while ((bh = bh->b_this_page) != head);
    /* ページのバッファが全てMapされていて、連続ブロックだった場合 */
    if (first_unmapped)
       goto page_is_mapped;

   goto confused;
}

/*
 * Bufferがない場合はこちら
 */
for (page_block = 0; page_block < blocks_per_page; ) {
    /* get_block()でBufferの割り当て(マップ) */
    :
    if (page_block) {
        /* block番号が非連続ならconfuseへ */
         if (map_bh.b_blocknr != blocks[page_block-1] + 1)
             goto confused;
    }
    :
}

page_is_mapped:
/* ページが連続ブロックにマップされている場合の処理 */
if (page->index >= end_index) {
    /* pageがファイルの最後のページならはみでている部分を0クリア */
}

if (bio && *last_block_in_bio != blocks[0] - 1)
    bio = mpage_bio_submit(WRITE, bio);

alloc_new:
if (bio == NULL) {
    /* 引数でbioが渡されていなければ割り当て */
    bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
    bio_get_nr_vecs(bdev), GFP_NOFS|__GFP_HIGH);
    if (bio == NULL)
        goto confused;
}

/* Unmap領域までをbioに追加 */
length = first_unmapped << blkbits;
if (bio_add_page(bio, page, length, 0) < length) {
    bio = mpage_bio_submit(WRITE, bio);
    goto alloc_new;
}

if (page_has_buffers(page)) {
    /* Bufferをclear状態に */
}

/* writebackフラグ設定(I/O完了時にクリアされる) */
set_page_writeback(page);

/* BoundaryBuffer等であれば、ここでI/O発行
 * そうでなければlast_block_in_bio更新
 */

goto out; /* bioを呼出元に返す */

confused:
/*
 * bioがあればmpage_bio_submit()でI/O発行した後、
 * writepage_fn()(writepageハンドラ)で非連続
 * ブロックだったページのI/Oを発行する。
 */

out:
    return bio;  /* 連続ブロックI/Oを格納したbioを返す */

関連ページ


最終更新 2008/01/14 01:59:41 - kztomita
(2008/01/13 18:41:21 作成)
添付ファイル
contiguas.png - kztomita
uncontiguas.png - kztomita


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