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

block_write_full_page()


1.概要

block_write_full_page()はPageCacheのページデータをディスクへWriteBackする汎用ルーチン。blocka_read_full_page()のWrite版。

pdflushによりDirtyページをディスクへWriteBackする際に、ファイルシステムのページ書込みルーチン(例えばext2_writepage()等)から使われる。

2. 処理概要

block_write_full_page()はWriteBack対象のDirtyページを引数で受け取る。 block_write_full_page()はページのBufferのうちDirtyなものについてのみ、Write I/Oを行う(図1)。

I/O発行時にBufferはBH_Dirtyフラグが落とされ、代わりにBH_Async_Writeをセットする。そして、ページにはPG_writebackがセットされる。PG_writeback,BH_Async_Writeそれぞれのフラグにより、Page,BufferがWriteBack中かどうかを判断できる。

Write I/Oが完了するとend_buffer_async_write()が呼び出され、I/O完了処理が行われる。ここでは、BufferにBH_UptodateをセットしBH_Async_Writeをクリアする。また、ページはPG_writebackをクリアする。最後にWriteBack完了を待ってブロックしている処理があればWakeupする。


図1 処理概要


3.実装

プロトタイプ: block_write_full_page(struct page *page, get_block_t *get_block, struct writeback_control *wbc)

  • page: WriteBackするPageCache上のページ
  • get_block: ディスクブロックを取得するget_blockハンドラ(ファイルシステムに依存)
  • wbc: WriteBack制御用のデータ

書込み処理のメインは__block_write_full_page()を呼び出して処理する。

__block_write_full_page()の処理概要

/* 対象ファイル(inode)の最終ブロック番号 */
last_block = (i_size_read(inode) - 1) >> inode->i_blkbits;

/* ページにBufferが無ければここで作成 */
if (!page_has_buffers(page)) {
    create_empty_buffers(page, 1 << inode->i_blkbits,
                           (1 << BH_Dirty)|(1 << BH_Uptodate));
}

/* ページのBufferのうちDirtyでディスクに
 * マップされていないものがあればマップする
 */
do {
    if (block > last_block) {
        /* このBufferはファイルの終端を越えている場合 */
        /* Dirtyフラグを落としてUptodate状態にする */
    } else if (!buffer_mapped(bh) && buffer_dirty(bh)) {
    /* Dirtyなのにまだ、ディスクにマップされていない */

        /* Bufferのデータをディスクに書き込むのに、
         * ディスクブロックを確定させておく必要があるので
         * get_blockハンドラを呼び出してディスクブロックに
         * マップする。
         */
        err = get_block(inode, block, bh, 1);
          :
    }

    /* Next Buffer */
    bh = bh->b_this_page;
    block++;
} while (bh != head);

/* Bufferのロックを取り、DirtyBufferならBH_Async_Writeをセット
 */
do {
    if (!buffer_mapped(bh))
        continue;
    if (wbc->sync_mode != WB_SYNC_NONE || !wbc->nonblocking) {
        /* Blockしてもいい場合 - lock_bufferでロックを取る */
        lock_buffer(bh);
    } else if (test_set_buffer_locked(bh)) { /* Block不可のWrite */
        /* test_setでブロックしないようにロックを取ろうとしたが取れなかった */
        redirty_page_for_writepage(wbc, page);
        continue;
    }
    if (test_clear_buffer_dirty(bh)) {
        /* BufferがDirtyだった */
        /* BH_Async_Writeをセットして非同期Writeの準備 */
        mark_buffer_async_write(bh);
    } else {
        /* DirtyでないのでWrite不要 */
        unlock_buffer(bh);
    }
} while ((bh = bh->b_this_page) != head);

/* I/Oを発行する */
do {
    struct buffer_head *next = bh->b_this_page;
    if (buffer_async_write(bh)) {
        submit_bh(WRITE, bh);
        nr_underway++;
    }
    bh = next;
} while (bh != head);
   :
   :


関連ページ


最終更新 2007/12/09 15:02:30 - kztomita
(2007/12/06 00:54:57 作成)
添付ファイル
block_write_full_page.png - kztomita


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