I/O スケジューラ (その7)
1. はじめに
前回は CFQ における elevator_merged_fn メソッドの実体と I/O ブロックレイヤにおける周辺処理をしらべて、こいつがどんな役割を担っているのかという事を調べ、リクエストオブジェクト同士におけるマージの後に呼ばれる処理で、I/O スケジューラではマージしたリクエストを内部キューから外し、必要に応じて内部キューにおけるマージされたリクエストのリスト位置を更新していた。
2. 概要
今回は I/O スケジューラの elevator_allow_merge_fn メソッドの役割と CFQ の実装、及び関連する I/O ブロックレイヤの処理についてみてゆく。
例によってまずは、カーネルドキュメント [1] から処理の概要について調べる。
elevator_allow_merge_fn called whenever the block layer determines
that a bio can be merged into an existing
request safely. The io scheduler may still
want to stop a merge at this point if it
results in some sort of conflict internally,
this hook allows it to do that.
ドキュメントには、bio オブジェクトをリクエストオブジェクトにマージする際に、マージ可能かどうかを判定するという事が書かれている。
3. 学習内容のまとめ
elevator_allow_merge_fn メソッドを含む I/O ブロックレイヤにおける bio オブジェクトの request オブジェクトへのマージ関連処理の一連の動作は次のようになっている。
I/O ブロックレイヤは bio オブジェクトを request オブジェクトにマージする前に elv_rq_merge_ok() 関数を実行し、bio オブジェクトを引数で渡した request オブジェクトにマージできるかどうかの確認を行う。
そして I/O ブロックレイヤで bio オブジェクトを request オブジェクトに対してマージ可能だと判断した後、I/O スケジューラ的にマージ可能かどうかを確かめるために、I/O ブロックレイヤは elevator_allow_merge_fn メソッドを呼び出してこれを確認する。
I/O スケジューラ側では、当該マージ要求に対して、マージされては困るケースについて elevator_allow_merge_fn メソッドの実体として記述する。
尚、I/O スケジューラは、elevator_allow_merge_fn メソッドに対する応答としてマージ可能ならば 1 を返し、マージしてほしくない場合には 0 を返す。
4. 内容詳細
それでは、実際に CFQ の elevator_allow_merge_fn メソッドの実態である cfq_allow_merge() について見て行く。以下がそのコード。
static int cfq_allow_merge(struct request_queue *q, struct request *rq,
struct bio *bio)
{
struct cfq_data *cfqd = q->elevator->elevator_data;
struct cfq_io_context *cic;
struct cfq_queue *cfqq;
/*
* Disallow merge of a sync bio into an async request.
*/
if (cfq_bio_sync(bio) && !rq_is_sync(rq))
return 0;
/*
* Lookup the cfqq that this bio will be queued with. Allow
* merge only if rq is queued there.
*/
cic = cfq_cic_rb_lookup(cfqd, current->io_context);
if (!cic)
return 0;
cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));
if (cfqq == RQ_CFQQ(rq))
return 1;
return 0;
}
引数で渡した bio オブジェクトとリクエストオブジェクトの I/O タイプが異なる場合、CFQ はマージ不可と判断する。I/O タイプについては前回の話を参照してもらいたい。
又、cfq_cic_rb_lookup() によって cfq_io_context が取得できないケースも同様にマージ不可と判断する。cfq_cic_rb_lookup() 関数については前々回に話をしたので、それを参照してもらいたい。
cfq_allow_merge() では、cfq_cic_rb_lookup() の呼び出しを行う際に、cfq_data オブジェクトとカレントプロセスの io_context オブジェクトをこれに渡している。
cfq_data オブジェクトは I/O ブロックレイヤが持つ elevator_queue オブジェクトの elevator_data メンバが参照するオブジェクトであり io_context オブジェクトはカレントプロセスが参照している。
つまり CFQ I/O スケジューラのデータ構造の設計では、プロセスのブロックデバイスの組合せによって cfq_io_context オブジェクトが一意に決定されるという事がわかる。また、cfq_cic_rb_lookup() から cfq_io_context オブジェクトと cfq_data オブジェクトが1対1に対応する事もわかる。
そして cfq_allow_merge() では最後に、取得した cfq_io_context オブジェクトが持つ2本の cfq_queue オブジェクトのうち、マージする bio オブジェクトの cfq_queue オブジェクトを取得し、これがマージされるリクエストオブジェクトが参照する CFQ の内部リクエストキューである場合にのみ、マージ可能であると判断する。
以上が CFQ における elevator_allow_merge_fn メソッドの実装である。
ここで elevator_allow_merge_fn メソッドを実行している I/O ブロックレイヤの処理を見てみる。次に示すのは後述する I/O ブロックレイヤの elv_rq_merge_ok() コードである。
/*
* can we safely merge with this request?
*/
inline int elv_rq_merge_ok(struct request *rq, struct bio *bio)
{
if (!rq_mergeable(rq))
return 0;
/*
* different data direction or already started, don't merge
*/
if (bio_data_dir(bio) != rq_data_dir(rq))
return 0;
/*
* must be same device and not a special request
*/
if (rq->rq_disk != bio->bi_bdev->bd_disk || rq->special)
return 0;
if (!elv_iosched_allow_merge(rq, bio))
return 0;
return 1;
}
I/O ブロックレイヤは elv_iosched_allow_merge() [block/elevator.c] から elevator_allow_merge_fn メソッドを呼び出しており、その呼び出し元は elv_rq_merge_ok() [block/elevator.c] である。
そして、elv_iosched_allow_merge() が 0 を返すと、無条件で elv_rq_merge_ok() は 0 を返す。
5. まとめ
今回学習した内容は次の3つ。
(1) CFQ I/O スケジューラにおける elevator_allow_merge_fn メソッドの実装
(2) I/O スケジューラ一般における elevator_allow_merge_fn メソッドの処理の役割
(3) I/O ブロックレイヤにおける bio オブジェクト併合許可の確認処理の中身
[1] Documentation/block/biodoc.txt
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/i-o-30b930b130e530fc30e9-305d306e7/tbping