パーティションの取り扱い
Rev.1を表示中。最新版はこちら。
ブロックデバイスはそれぞれにstruct gendisk構造体で管理されています。しかしパーティションについてはstruct gendisk構造体を有していません。親となるstruct gendisk構造体のpartメンバーにその情報を設定しているにすぎません。実際hda/hda1/hda2/hda3・・・と同じメジャー番号です。そしてIO処理(ストラテジルーチン)も同じものがコールされます。その処理はキューの中の開始セクタ情報をそのままで処理しています。パーティションにかかる処理はありません。
ブロックデバイスからするとパーティションという概念はないのです。その全ボリュームに対して「先頭から何セクタか?」の操作に過ぎません。
しかしVFSではパーティションは独立した1デバイスとして認識します。そのパーティション内での先頭セクタを基準にし、そのセクタ位置でもって入出力を行います。そして実際IO命令を実行する段に、そのセクタ位置を、全ボリュームからの位置に変更して処理を行っています。
前者の処理を汎用ブロック層、後者の処理をIOスケジューラ層と言うそうで、IOスケジューラ層においてセクタの再配置が行われます。なお汎用ブロック層が介在するおかげで、LVM(論理ボリューム)RAIDなので、それを意識する必要がなくなるわけです。
汎用ブロック層ではstruct bio構造体を作成します。これには読み込みセクタ位置とかセクタサイズ等のIO処理にかかる情報が設定されています。ただしここでのセクタ位置はパーティション内のセクタ位置です。
このstruct bio構造体を引数とし__generic_make_request関数でIOスケジューラ層へわたっていき、パーティション内のセクタを、全ボリュームとしてのセクタ位置に変換します。そしてブロックデバイスが有している要求キューと、このセクタ再配置したbioとで、エレベータ関数(IO処理を効率化するためのロジック)と称する処理に委ねるようになっています。
__generic_make_request関数から呼ばれるblk_partition_remap関数で、セクタの再配置をおこないます。bioは読み書きするファイルのinodeをベースに作成され、従ってブロックデバイスファイルのメジャー番号とマイナー番号として、bio->bi_bdevにはそのブロックデバイスディスクリプタが設定されています。
if (bio_sectors(bio) && bdev != bdev->bd_contains)で再配置するかどうかのチェックを行います。bio_sectors(bio)で0セクタでないかチェックします。0セクタはMBRに相当するセクタで、パーティション内であってはならないセクタです。bdev->bd_containsには全ボリュームのブロックデバイスディスクリプタが、bdevは処理しているブロックデバイスディスクリプタが設定されています。それが異なるというのはパーティションということです。
そしてbio->bi_sectorのセクタ位置にp->start_sectのパーティションの開始セクタ位置を足し込んでいます。これで全体型のセクタ位置になりました。そすてbio->bi_bdev = bdev->bd_containsで、IO処理デバイスを、パーティションからデバイス全体のブロックデバイスに書き換えています。これでパーティションという枠がはずれ、デスク全体のIO操作となるわけです。
static inline void blk_partition_remap(struct bio *bio) { struct block_device *bdev = bio->bi_bdev; if (bio_sectors(bio) && bdev != bdev->bd_contains) { struct hd_struct *p = bdev->bd_part; bio->bi_sector += p->start_sect; bio->bi_bdev = bdev->bd_contains; blk_add_trace_remap(bdev_get_queue(bio->bi_bdev), bio, bdev->bd_dev, bio->bi_sector, bio->bi_sector - p->start_sect); } }