ZFS ソースツアー

2012/09/07
BigAdmin の日本語訳が、Sunと共に消滅してしまったので、サルベージしたもをちょっとアレンジして再現してみました。そのうちまじめにソースを追ってみたいと思います。

Source Tour (Community Group zfs.source)
Oracle Solaris の管理: ZFS ファイルシステム

ZFS は、ZPL (ZFS POSIX Layer)、DMU (Data Management Unit)、および SPA (Storage Pool Allocator) の 3 つのコンポーネントから構成されている。

ファイルシステムコンシューマ

POSIX ファイルシステム API を介して ZFS と対話する基本アプリケーション。要するに、ZFS を使うすべてのアプリケーションがこのカテゴリに分類されるということになる。システムコールが、汎用的な OpenSolaris VFS レイヤーを経由して ZPL に渡される。

デバイスコンシューマ

ZFS は、「エミュレートされるボリューム」を作成できる。このボリュームはストレージプール内のストレージに基づいているが、通常のデバイスとして /devの下にある。これらのデバイスと直接対話するアプリケーションはいくつかあるが、もっとも頻繁に使用するコンシューマは、デバイスの上位層のターゲットドライバとカーネルファイルシステムである。

管理コンシューマ

管理コンシューマになるアプリケーションとは、ZFS のファイルシステムやストレージプールを操作するアプリケーションで、この操作にはプロパティーやデータセット階層の調査が含まれる。主なアプリケーションは zpool(1M) と zfs(1M) の 2 つ。

このコマンドは ZFS ストレージプールを作成および管理するのに使用される。このコマンドはコマンドの構文の解析し libzfs 呼び出しに渡す。また途中で発生するすべてのエラー処理を行う。このコマンドのソースは usr/src/cmd/zpool にある。

zpool_main.c コマンドの本体。すべての引数およびサブコマンドを処理する
zpool_vdev.c 一連の vdev を libzfs 用の nvlist 表現に変換するコード
zpool_iter.c プールに対する繰り返し処理を簡単に行えるようにするコード
zpool_util.c そのほかのユーティリティー関数

このコマンドは ZFS ファイルシステムの作成および管理に使用される。zpool(1M) と同様に、コマンド行の引数を解析して処理結果を libzfs に渡す。このコマンドのソースは usr/src/cmd/ZFS にある。

zfs_main.c コマンドの本体。すべての引数およびサブコマンドを処理する
zfs_iter.c データセットに対する繰り返し処理を行うコード

libzfs

管理アプリケーションが ZFS カーネルモジュールと対話するための主要インタフェース。このライブラリは、ストレージプールおよびファイルシステムへのアクセスおよび操作のための、統合されたオブジェクトベースの機構を提供する。カーネルとの通信を行う背後の機構として、ioctl(2) による /dev/zfs の呼び出しが使用される。このライブラリのソースは usr/src/lib/libzfs にある。

libzfs_dataset.c データセットを操作する主要インタフェース
libzfs_pool.c プールを操作する主要インタフェース
libzfs_changelist.c プロパティーの変更をすべての子に伝播するユーティリティールーチン
libzfs_config.c プール構成情報を読み取りと操作
libzfs_graph.c データセットの依存リストを作成する
libzfs_import.c プールを発見してインポートする
libzfs_mount.c データセットをマウントおよびマウント解除と、データセットの共有
libzfs_status.c プール状態に基づいて FMA のナレッジベースの記事にリンクする
libzfs_util.c その他のルーチン

ZPL (ZFS POSIX Layer)

ZFS ファイルシステムと対話するための主要インタフェース。DMU の上位に位置する比較的薄い層で、ファイルおよびディレクトリからなる抽象化したファイルシステムを提供する。ZPL は OpenSolaris VFS インタフェースと背後の DMU インタフェースの間のギャップを埋める。また、アクセス制御リスト (Access Control List、ACL) 規則や同期 (O_DSYNC) セマンティクスを適用する。

zfs_vfsops.c OpenSolaris VFS インタフェース
zfs_vnops.c OpenSolaris vnode インタフェース
zfs_znode.c 各 vnode は背後の znode に対応する
zfs_dir.c ディレクトリ操作
zfs_acl.c ACL の適用
zfs_ctldir.c 疑似ファイルシステムの実装 .ZFS/snapshot
zfs_log.c 授受特性ログエントリを記録する ZPL インタフェース
zfs_replay.c 授受特性ログエントリを再実行する ZPL インタフェース
zfs_byteswap.c ZPL データ構造のバイトスワップルーチン

ZVOL (ZFS エミュレートされるボリューム)

ZFS には、ストレージプール空間を背後に持つ raw デバイスを提供する機能が含まれている。これらのデバイスはソースコード内では 'zvols' と呼ばれる。
zvol.c ボリュームエミュレーションインタフェース

/dev/zfs

このデバイスは libzfs のメイン制御ポイントである。コンシューマは ioctl(2) インタフェースを直接利用できるが、libzfs と密接に絡み合っているため、公開インタフェースではない(libzfs も)。ioctl() のパラメータの妥当性検査を行い、要求を ZFS 内の該当する場所に転送している。
zfs_ioctl.c /dev/ZFS と ioctl() の間のインタフェース
ZFS.h カーネルとユーザーランドの間で共有されるインタフェース

DMU (Data Management Unit)

DMU は SPA によって提供されるフラットアドレス空間に構築されたトランザクションオブジェクトモデルを提供する。コンシューマは DMU と対話するためにオブジェクトセット、オブジェクト、およびトランザクションを使用する。オブジェクトセットはオブジェクトの集合で、各オブジェクトは SPA からのストレージの任意の部分である。各トランザクションは、グループとしてディスクにコミットしなければならない一連の操作であり、ZFSのディスク上の一貫性の中心となる。

dmu.c DMU の主要な外部インタフェース
dmu_objset.c オブジェクトセットの開閉と操作を行うための外部インタフェース
dmu_object.c オブジェクトの割り当を解放するためのインタフェース
txg.c トランザクションモデル制御スレッド
dmu_tx.c トランザクションの作成と操作を行うインタフェースです
dnode.c オープンコンテキストのオブジェクトレベルの操作関数
dnode_sync.c 同期コンテキストのオブジェクトレベルの操作関数
dbuf.c バッファー管理関数
dmu_zfetch.c データストリーム先読みロジック
refcount.c 一般参照カウントインタフェース
dmu_send.c スナップショットの送信および受信関数

DSL (Dataset and Snapshot Layer)

DSL は、継承したプロパティーおよび割り当て制限と予約の適用と共に、DMU オブジェクトセットを階層的名前空間の中に統合する。また、オブジェクトセットのスナップショットおよびクローンの管理も行う。
スナップショットの実装方法については、Matt のブログエントリを参照。

dsl_dir.c 名前空間を管理する関数
dsl_dataset.c スナップショット、ロールバック、クローンをサポートするインタフェース
dsl_pool.c プールレベルをサポートするインタフェース
dsl_prop.c プロパティーを操作する関数
unique.c 一意のオブジェクトセット ID をサポートする関数

ZAP (ZFS Attribute Processor)

ZAP は DMU の上位に構築され、スケーラブルなハッシュアルゴリズムを使用してオブジェクトセット内にさまざまな名前とオブジェクトの関連付けを作成する。ZAP は ZPL 内にディレクトリを実装するのにもっともよく使用されるが、拡張して DSL 全体で使用されたり、プール全体にわたるプロパティーを格納する方法としても使用される。ZAP には、非常に異なった 2 つのアルゴリズムがあり、それぞれ異なる種類のディレクトリ用にデザインされている。「マイクロ ZAP」は、エントリ数が比較的少なく各エントリがかなり短い場合に使用される。「ファット ZAP」はそれより大きめのディレクトリや非常に長い名前のディレクトリに使用される。

zap.c ファット ZAP のインタフェース
zap_leaf.c 低レベルサポート関数
zap_micro.c Micro ZAP のインタフェース

ZIL (ZFS Intent Log)

ZFS は常に一貫性のあるデータを提供するが、データの大半がディスクに即座に書き込まれないという、従来からのセマンティクスを採用している。このセマン ティクスを採用しないと、パフォーマンスがひどく低下するためである。しかし、アプリケーションによっては、より厳格なセマンティクス、すなわち read(2) や write(2) 呼び出しが戻った時点でデータがディスク上にあることが保証されるセマンティクスが要求されることがある。このような動作 (O_DSYNC で指定されるもの) を要求するアプリケーションのために、ZIL ではクラッシュの場合に再実行できる効率的なデータセット単位のランザクションログを使用して、必要とされるセマンティクスを提供する。
ZIL 実装については、Neil のブログエントリを参照。

zil.c 授受特性ログ

トラバース

トラバースは、ライブプール内の全てのデータを調べるための、安全かつ効率的で再スタート可能な方法を提供する。これは再同期化および消し込みの基盤になっている。トラバースは、すべてのメタデータを調べて、一定の期間内に変更されたブロックを検索する。その際、ZFS には書き込み時コピー特性があるため、機能停止時間に変更されなかった大規模なサブツリーが検索されないで済むという利点がある。トラバースは基本的に SPA の機能だが、スナップショット、クローン、およびディスク上のフォーマットの特定の特性を扱うために、いくつかの DMU 構造と親密な関係を持っている。

dmu_traverse.c トラバースのコード

ARC (Adaptive Replacement Cache)

ZFS は 1 次キャッシュのために ARC の修正バージョンを使用している。このキャッシュは DMU と SPA の間の層にあり、仮想ブロックレベルで動作している。これにより、ファイルシステムがキャッシュしたデータを、そのスナップショットやクローンと共有できる。

arc.c Adaptive Replacement Cache の実装

プール構成 (SPA)

プールレイヤー全体が SPA (Storage Pool Allocator) としばしば呼ばれるが、構成部分が本当の公開インタフェースである。SPA が、ZIO と vdev のレイヤーを張り合わせて、一貫性のあるプールオブジェクトにしている。SPA には、プールの作成や構成情報の破棄を行うルーチンと、データを vdev に定期的に同期化するルーチンが含まれている。

spa.c プールのオープン、インポート、エクスポート、および破棄ルーチン
spa_misc.c ロック実行など、その他の SPA ルーチン
spa_config.c プール構成データの解析および更新
spa_errlog.c プール全体にわたる持続データエラーのログ
spa_history.c プール状態の変更に成功したコマンドの持続的な循環バッファー
zfs_fm.c FMA の使用に関するレポートを送信するルーチン

ZIO (ZFS I/O Pipeline)

ZIO パイプラインは、ディスクに送受信されるすべてのデータが通過する。これは DVA (Device Virtual Address) を vdev 上の論理的位置に変換するだけでなく、必要に応じてデータのチェックサムの計算や、データの圧縮を行う。ZIO は複数ステージのパイプラインとして実装され、各 I/O に対して、どのステージを実行するかを制御するビットマスクを持っている。パイプラインはきわめて複雑なため一言では説明できないが、次の図で全体像を示す。

I/O タイプ ZIO 状態 圧縮 チェックサム ギャングブロック DVA 管理 Vdev I/O
RWFCI オープン
RWFCI 子の準備完を待機
-W--- 圧縮書き込み
-W--- チェックサムの生成
-WFC- ギャングのパイプライン設定
-WFC- ギャングヘッダーの取得
-W--- ギャングヘッダーの書き換え
--F-- ギャングメンバーの解放
---C- ギャングメンバーの取り込み
-W--- DVA の割り当て
--F-- DVA の解放
---C- DVA の取り込み
-W--- ギャングのチェックサムの生成
RWFCI 準備完了
RW--I I/O 開始
RW--I I/O 完了
RW--I I/O 測定
RWFCI 子の準備完を待機
R---- チェックサム確認
R---- ギャングメンバーの読み取り
R---- 非圧縮読み取り
RWFCI 完了
I/O タイプ
I/O パイプラインの各フェーズは特定タイプの I/O に適用される。各文字は読み取り (Read、R)、書き込み (Write、W)、解放 (Free、F)、取り込み (Claim、C)、および入出力制御 (Ioctl、I) を意味する。
ZIO 状態
これらは I/O の同期をとるための内部状態である。たとえば、子 I/O を持つ I/O は、全ての子 I/O のブロックポインタの割り当て準備完了を待つ必要があり、すべての子 I/O の完了まで終了できない。
圧縮
このフェーズは、適用可能な場合に任意の圧縮アルゴリズムを適用する。
チェックサム
このフェーズは、適用可能な場合に任意のチェックサムアルゴリズムを適用する。
ギャングブロック
ブロック全体を書き込めるだけの連続した十分な領域がない場合に、ZIO パイプラインは I/O を小さな「ギャングブロック」に分割し、あとで透過的にアセンブルして完全なブロックにする。
DVA 管理
各 I/O には、プール内の vdev の特定部分に対応する DVA (Device Virtual Address) を割り当てる必要がある。このフェーズは、metaslab および領域マップを使用して、必要とされるアドレスの割り当てを行う。
Vdev I/O
プール内に含まれる vdev への I/O を実際に実行するフェーズ。
zio.c ギャングブロック変換を含むメインの ZIO ステージ
zio_checksum.c チェックサム機能の中央インタフェース
fletcher.c チェックサムアルゴリズム(Fletcher2, fletcher4)
sha256.c チェックサムアルゴリズム(SHA256)
zio_compress.c 圧縮機能の中央インタフェース
lzjb.c 圧縮アルゴリズム(LZJB)
uberblock.c 基本的な uberblock (ルートブロック) ルーチン
bplist.c 一連のブロックポインタを追跡する
metaslab.c DVA 変換の本体
space_map.c DVA 変換時に、使用する空き領域を追跡する
szio_inject.c データおよびデバイスの持続エラーを挿入するフレームワーク

VDEV (Virtual Device)

仮想デバイス·サブシステムはデバイスを配置し、アクセスするための統一された手段を提供する。仮想デバイスは、単一のルート vdev と、複数の内部 vdev(ミラーおよびRAID-Z)およびリーフ(ディスクおよびファイル)vdevsでツリーを形成する。各 vdev は利用可能な領域を表すとともに、物理ディスク上のブロックをレイアウトする。

vdev.c vdev の汎用ルーチン
vdev_disk.c ディスクの仮想デバイス
vdev_file.c ファイルの仮想デバイス
vdev_mirror.c 多重のミラー化
vdev_raidz.c RAID-Z のグループ化 (Jeff のブログを参照)
vdev_root.c 最上位の疑似 vdev
vdev_missing.c インポート専用デバイス
vdev_label.c 識別ラベルの読み取りと書き込みルーチン
vdev_cache.c 読み取りのためのシンプルなデバイスレベルのキャッシュ機能
vdev_queue.c vdev の入出力のスケジューリングアルゴリズム

LDI (Layered Driver Interface)

ZFS はスタックの最下部で、LDI および VFS インタフェース (ファイルの処理時) を使用して、背後の物理デバイスと対話している。

0 件のコメント:

コメントを投稿