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); : :