関連記事
- Part 1: 環境セットアップ
- Part 2: System call Interface
- Part 3: VFS
- Part 4: ext2 (1) write_iter
- Part 5: ext2 (2) write_begin
- Part 6: ext2 (3) get_block
- Part 7: ext2 (4) write_end
- Part 8: writeback (1) work Queue
- Part 9: writeback (2) wb_writeback
- Part 10: writeback (3) writepages
- Part 11: writeback (4) write_inode
- Part 12: block (1) submit_bio
- Part 13: block (2) blk_mq
- Part 14: I/O scheduler (1) mq-deadline
- Part 15: I/O scheduler (2) insert_request
- Part 16: I/O scheduler (3) dispatch_request
- Part 17: block (3) blk_mq_run_work_fn
- Part 18: block (4) block: blk_mq_do_dispatch_sched
- Part 19: MMC (1) initialization
- Part 20: PL181 (1) mmci_probe
- Part 21: MMC (2) mmc_start_host
- Part 22: MMC (3) mmc_rescan
- 概要
- はじめに
- writeback kthreadの概要
- writeback用のワーカスレッドの作成
- writebackワークキューにキューを追加
- writebackワークキューからキューを取り出す
- おわりに
- 変更履歴
- 参考
概要
QEMUの vexpress-a9 (arm) で Linux 5.15を起動させながら、ファイル書き込みのカーネル処理を確認していく。
本章では、writeback
ワークキューの作成から、ワーカーの追加・取り出しに関係する次の関数を確認した。
wb_wakeup_delayed
関数wb_wakeup
関数wb_queue_work
関数
はじめに
ユーザプロセスはファイルシステムという機構によって記憶装置上のデータをファイルという形式で書き込み・読み込みすることができる。
本調査では、ユーザプロセスがファイルに書き込み要求を実行したときにLinuxカーネルではどのような処理が実行されるかを読み解いていく。
調査対象や環境などはPart 1: 環境セットアップを参照。
本記事では、writebackカーネルスレッドが起床するために用いる機構work Queueについて確認する。
writeback kthreadの概要
メインメモリから記憶装置に書き込む手法は大きく分けて「write back」と「write through」の二通り存在する。
PCのようなシステムでは、記憶装置の書き込み上限や速度の観点から「write back」方式を利用することが多い。
ここでは、write back方式におけるデータの書き込み方法を確認する。
write back方式では、ユーザプロセスからファイル書き込みをすると、該当するページキャッシュやinodeキャッシュに対して、Dirtyのフラグを立て処理を終了する。
その後、write back用のカーネルスレッドがDirtyになっているキャッシュの記憶装置への書き込みを実施する。
Linuxでは、主に次のようなタイミングでライトバック処理が実行される。
sync(1)
やfsync(2)
が実行されたタイミング- ページ回収のタイミング
- ファイル操作などによる遅延実行のタイミング
writeback用のワーカスレッドの作成
Linux v5.15では、ライトバックを実現するためにWork Queueと呼ばれる機構を用いている。
Work Queueでは、指定した処理を指定した時間経過後に呼び出すことのできる仕組みとなっている。(詳細な説明は下記を参照)
writeback用のWork Queueは、Linuxカーネルの起動時に下記の関数によって生成される。
// 234: static int __init default_bdi_init(void) { int err; bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_UNBOUND | WQ_SYSFS, 0); if (!bdi_wq) return -ENOMEM; err = bdi_init(&noop_backing_dev_info); return err; } subsys_initcall(default_bdi_init);
ここで、write back用のWork Queue(bdi_wq
)はグローバル変数である。
Work Queueの生成には、alloc_workqueue
関数を利用する。
writeback用のWork Queueには下記のフラグを指定している。
WQ_MEM_RECLAIM
: ページフレームの回収に利用されることがあるWQ_UNBOUND
: Work Queueを一つのCPUに割り付けないWQ_SYSFS
: sysfs (devices/virtual/workqueue/writeback
)を生成する
その後、NFSといったblock deviceの実態が存在しないファイルシステムのためにbdi_init
関数によって、noop_backing_bdi_init
を初期化する。
noop_backing_bdi_init
は、backing_dev_info
型の変数であり、SDカードなど含めた周辺機器に対する情報を保持する。
default_bdi_init
関数で作成されたwriteback用のWork Queueは、下記の3つの関数にて使用される。
wb_wakeup_delayed
:dirty_writeback_centisecs
で指定されたセンチ秒後に、bdi_writeback
に紐づいている関数を実行する。wb_wakeup
:bdi_writeback
に紐づいている関数を実行する。wb_queue_work
: Writeback用のキューのリストを末尾に追加して、bdi_writeback
に紐づいている関数を実行する。
それぞれの関数は共通して、bdi_writeback
型の変数を引数としている。
bdi_writeback
型は、それぞれのブロックデバイスにおけるwritebackに関連するパラメータを保持した構造体となっている。
今回の環境では、対象デバイスがSDカードであるのでカーネルがSDカードを認識したタイミング(mmc_rescan
)でSDカード用のbdi_writeback
型のデータを生成する。
wb_init
関数が、**指定時間経過後にwb_workfn
関数を呼び出すようにbdi_writeback
を初期化する。
// 287: static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi, gfp_t gfp) { int i, err; memset(wb, 0, sizeof(*wb)); if (wb != &bdi->wb) bdi_get(bdi); wb->bdi = bdi; wb->last_old_flush = jiffies; INIT_LIST_HEAD(&wb->b_dirty); INIT_LIST_HEAD(&wb->b_io); INIT_LIST_HEAD(&wb->b_more_io); INIT_LIST_HEAD(&wb->b_dirty_time); spin_lock_init(&wb->list_lock); atomic_set(&wb->writeback_inodes, 0); wb->bw_time_stamp = jiffies; wb->balanced_dirty_ratelimit = INIT_BW; wb->dirty_ratelimit = INIT_BW; wb->write_bandwidth = INIT_BW; wb->avg_write_bandwidth = INIT_BW; spin_lock_init(&wb->work_lock); INIT_LIST_HEAD(&wb->work_list); INIT_DELAYED_WORK(&wb->dwork, wb_workfn); INIT_DELAYED_WORK(&wb->bw_dwork, wb_update_bandwidth_workfn); wb->dirty_sleep = jiffies; err = fprop_local_init_percpu(&wb->completions, gfp); if (err) goto out_put_bdi; for (i = 0; i < NR_WB_STAT_ITEMS; i++) { err = percpu_counter_init(&wb->stat[i], 0, gfp); if (err) goto out_destroy_stat; } return 0; out_destroy_stat: while (i--) percpu_counter_destroy(&wb->stat[i]); fprop_local_destroy_percpu(&wb->completions); out_put_bdi: if (wb != &bdi->wb) bdi_put(bdi); return err; }
writebackワークキューにキューを追加
writeback用のWork Queueは以下の3つの関数で使用される。
wb_wakeup_delayed
wb_wakeup
wb_queue_work
wb_wakeup_delayed
関数
wb_workfn
:dirty_writeback_interval
が経過した場合に、呼び出す。__mark_inode_dirty
: 対象のbdi_writeback
型のデータに対して、初めての書き込みの場合のみ呼び出す。
wb_wakeup
関数
inode_switch_wbs_work_fn
関数wb_start_writeback
関数wakeup_flusher_threads_bdi
関数: 下記の関数から呼ばれるlaptop_mode_timer_fn
関数: Laptop Modeによるタイマのコールバック関数。
wakeup_flusher_threads
下記の3つの関数から呼ばれる。ksys_sync
関数:sync
システムコールなどから呼ばれる。dirty_writeback_centisecs_handler
関数:/proc/sys/vm/dirty_writeback_centisecs
を書き換えた場合かつ、経過時間が過ぎている場合に呼ばれる。shrink_inactive_list
: 今回は調査を省略。
wb_start_background_writeback
関数wb_workfn
関数: 後述するwb_queue_work
で追加されたキューがある場合に呼び出す。wakeup_dirtytime_writeback
関数:dirty_expire_centisecs
ミリ秒経過毎に呼び出される。
wb_queue_work
関数
bdi_split_work_to_wbs
関数:backing_dev_info
のノードをキューに分割する。cgroup_writeback_by_id
関数: 今回は調査を省略。
writebackワークキューからキューを取り出す
ext2ファイルシステムのwrite処理内の__mark_inode_dirty
では、前述で紹介したwb_wakeup_delayed
関数を呼び出す。
// 263: void wb_wakeup_delayed(struct bdi_writeback *wb) { unsigned long timeout; timeout = msecs_to_jiffies(dirty_writeback_interval * 10); spin_lock_bh(&wb->work_lock); if (test_bit(WB_registered, &wb->state)) queue_delayed_work(bdi_wq, &wb->dwork, timeout); spin_unlock_bh(&wb->work_lock); }
おわりに
本記事では、Linux v5.15におけるwriteback用のWorkqueueを解説した。
変更履歴
- 2022/1/1: 記事公開
- 2022/09/19: カーネルバージョンを5.15に変更
参考
- アプリがディスクに書き込むまでの動きまとめ - フラミナル
- 書き込み時の動きについて解説されている。
- hiboma/ファイルシステムの実装/30-backing_dev_info.md at master · hiboma/hiboma · GitHub
- tmpfsのソースを参考にv2.6.32のファイルシステムの実装を確認した内容が記載されている
- Linuxのdirty page関連パラメータからコードを読む #Linux - Qiita
- dirty pageの仕組みやその後のシーケンスまで細かく記載されている
- http://baotiao.github.io/2017/03/02/data-to-disk/
- 中国語の記事となっているが、構造体の相関図が記載されている。
- VFS基础学习笔记 - 7. page cache回写 - コードワールド
- 中国語の記事となっているが、writeback処理について記載されている。
- https://www.kernel.org/doc/Documentation/laptops/laptop-mode.txt
- Laptop Mode