ロード トップ 参照元 逆検索 検索 ヘルプ

[logo] ジャーナリングファイルシステム/V7を改造してみる


ジャーナリングファイルシステム

ほんとうにできるのか... というと疑問ではあるが、とりあえず検討してみる。

改造するベースとする ソースは、 v7em-003.tgz とする。


トランザクションデータ とその管理

トランザクションデータ という ジャーナルに書かれるデータをまず考える。

ファイルを作ったり、書き込んだりする といった オペレーションを行うと いくつか の buf が汚れることになる。この更新イメージ を トランザクションデータとして書き出す。1 つのオペレーションが複数の トランザクションデータになることはないように制御する。

オペレーションを行おうとしたら、トランザクションデータにはいるかどうか まず計算する。入るのであれば、関係する buf を トランザクションデータを 管理するデータ (とりあえず struct trans と呼ぶ) に登録する。

struct trans は、トランザクションデータがすべて書かれても 残る。 すべての buf が 更新されるか 新たなトランザクションデータに引き継がれ 、登録された buf がなくなれば、開放される。

また、struct trans の数は、有限であるとする。空いている struct trans がなくなったり、ジャーナルがいっぱいで書けないような 状況になれば、まったく更新できなくなる。そのときは、先頭の struct trans に含まれる buf を積極的に 書き出すようにする。

struct trans の引き継ぎ

1つの buf を 複数の struct trans で管理する必要はない。

つぎの更新 が入った場合、disk に書き出されたのと同じ扱いをして、 つぎの struct trans に 登録する。

buf の状態管理

buf は、データが ないか clean か ダーティ かの区別があった。 基本的に ダーティ なものは、いつ書き出してもよかった。

ところが、メタデータの場合はそうではない。トランザクションデータが 書かれるまでは、Disk に書き出してはいけない。

struct trans へのポインタがあり、struct trans の状態が 未書き込みで あれば、Disk に書き出さない。Disk に書き出したら、 struct trans へのポインタがあれば、登録を外す処理が必要。

また、メタデータの場合、buf を、つぎに書き出す トランザクションデータ の struct trans に 登録するという 処理が必要。

また、この登録処理は、トランザクションデータを書き出している最中は またせないといけない。


ところで、普通は、1つ1つの更新プリミティブ も トランザクションと呼ぶ。 ここでは、それらを1つにまとめて ジャーナルを更新する単位を トランザクションと呼ぶことにする。

誤解を招くような気がするんで コメント。

1つ1つ更新の単位は、とりあえずオペレーションと呼ぶことにする。 これも普通の使いかたとちがうけども、--- トランザクションを 構成するもの という意味では同じか。

ここでは、

ということにする。

struct buf レベルでの 更新イメージのみを対象にする .. という 考えは、普通とは違う... たぶん。


struct buf の 変更

buf には、どういうデータが加わって、状態がどうなるのかを整理してみる。

追加するデータ

    * struct trans *b_trans         : トランザクションへのポインタ
    * struct buf *b_tforw,*b_tback  : トランザクションに属するリスト
    * unsigned short b_chs,b_che    : 変更箇所の 範囲
    * char b_flags                  : トランザクション関係の状態(たぶん不要)

追加する状態

    * B_TRANS    :  iodone で、trans_iodone を呼ぶかどうか
                     および、delayed write して良いかどうか。
                     ( B_TRANS なものは、delay write で書き出さない ) 

struct trans の 構造

struct trans

   * char t_stat                    ;  下記の状態 
   * struct trans *t_forw,*t_back   ;  同じ状態のもののリスト
   * daddr_t t_gen,t_off            ;  ジャーナル 上の位置
   * daddr_t t_size                 ;  ジャーナル上のサイズ
   * struct buf *t_buf              ;  buf のリスト
   * int t_bufnum                   :  上のリストの個数
   * struct buf *t_bufnext          :  次の 状態に使う buf のリスト
   * int t_bufnextnum               :  上のリストの個数

状態

    TS_FREE      0
    TS_CREATE    1
    TS_START     2
    TS_TRANS     3
    TS_END       4
    TS_ESTART    5

グローバル変数

  struct trans *tlist[6];

trans 自体の状態は、

     TS_FREE     使っていない
     TS_CRATE    トランザクション作成中
     TS_START    トランザクションデータ書き込み中 (trans_inodone で次に) 
     TS_TRANS    トランザクション完了待ち中       (trans_inodone で次に) 
     TS_END      トランザクション完了
     TS_ESTART   トランザクション完了処理中       (trans_inodone で次に) 

という状態を循環することにする。T_END と T_ESTART の2つに分けたのは、 完了マークを書くための buf を get してこないといけないため、 その処理は、iodone の延長(= 割り込み処理中)ではできない。

trans に属してる buf は、書き込み終ると、trans から開放 される。全部なくなれば、trans の状態遷移を行う。この部分は以下の情報 を対象にする。

t_buf の内容は、状態に応じて変わる。

TS_START の状態で トランザクションのために登録する buf は、 別のところ ... t_bufnext に置くことにする。 状態が遷移したら、t_bufnext を t_buf に移す。

T_END から T_ESTART の処理を誰が受け持つか あんまりちゃんと考えていないが。

適当にいれることにする。kernel_thread があると楽なのだが ... そういうものは、期待しない。


トランザクションデータの 登録 ...

よくわからないが ...とりあえずはこんなものを考える。

   struct trans *trans_get()
   trans_add(struct trans *tp,struct buf *bp,int type,int from,int to)
   trans_put(struct trans *tp)

ひょっとしたら、リソースの予約という概念が必要かも知れない。 必要なら、trans_get のパラメータに加えることにする。

trans_add では、buf および 変更を範囲を登録。

type は、更新 と 開放(= データブロックとして使われるかも知れない) だけで良いと思うが、一応、OP 毎に名前を付けて管理することにする。

たとえば、

といった感じか。(TOP じゃなくて、TRANS_OP とか長くすべきかちょっと悩むけど とりあえず TOP )


どこから手を付けるべきか ...

について表示してみる。

まずはそこまで。れっつとらい。

とりあえず、bmap を 修正して考えてみた。

bmap

iupdat

alloc ==

== free ===

free では、新たに block を freelist の 書くから そこで、 trans_add する。ここでは、bwrite しているが、 トランザクションデータを書くまで、disk に書いてはいけないので、 bdwrite に変更。

ここで、トランザクションに関係あるのは、bdwrite だけであることに気が付いた。 そして、grep してみると 上記の更新 でだいたい網羅してしまっている。

あとは、TOP_SUPER をどうするか ... これは実際には、T_START になる 直前で f_mod 見て 処理すれば良い。 ... なんとなく、できそうな気が ... してくる。本当か?


ちょっと試す。

まずは mkfs

./v7-mkfs
file sys size: 64000
file system: rootdisk
isize = 20480
m/n = 3 500

とかパラメータを入れると、32MB の rootdisk というファイルができる。 これに、v7-tar で展開。

   gunzip < v7.tar.gz | ./v7-tar -xvf - 2>&1 |tee log

なんか出て来た。

     :
TOP_ILIST: bno = 3448 8 - 12
TOP_USED: bno = 3460 0 - 0
TOP_ILIST: bno = 3448 12 - 16
TOP_USED: bno = 3463 0 - 0
TOP_ILIST: bno = 3448 16 - 20
TOP_INODE: bno = 6 320 - 384
TOP_INODE: bno = 6 320 - 384
TOP_INODE: bno = 14 320 - 384
TOP_USED: bno = 3466 0 - 0
TOP_INODE: bno = 6 256 - 320
TOP_USED: bno = 3469 0 - 0
TOP_INODE: bno = 6 256 - 320
    :

... ここで、mkdir とか rmdir での ジャーナリングをすっかり忘れていた ことに気が付く。

V7 は、ディレクトリ のファイルの更新を userland にまかせていたのであった。 これをどうするか検討しなくては ...

とりあえずここまでは、 v7jfs-000.txt に パッチ形式でおいておく。


mkdir rmdir をどうすべきか ..

mkdir は、

という一連の処理がある。

rmdir は、

これを まとめてトランザクションにできるようにする ... ... となると、trans_get を かぶせるような ことが出来れば良い。
それは、u.u_ap に入っているかどうかで判断できるから OK。

あとは、通常ファイルの更新に対してもジャーナルを作るというオプションを 作れば良い。この オプションは、writei に対するもの。

具体的には、

の2つのパスで、u にモードを設定できるようにする。

mkdir は、こんな感じか。

TOP_USED: 99 1 bno = 19594 0 - 0
TOP_INODE: 99 2 bno = 257 128 - 192
TOP_FDATA: 99 3 bno = 19594 0 - 16
TOP_INODE: 99 4 bno = 257 128 - 192
TOP_FDATA: 99 5 bno = 19594 16 - 32
TOP_INODE: 99 6 bno = 257 128 - 192
TOP_INODE: 99 7 bno = 246 256 - 320

なんかそれっぽい。mkdir というトランザクションに 7 つの オペレーションが はいっている。で、2 回 link しているから、bno = 19594 に 2 つのデータを 書き込む。... ええ感じやん。

INODE は、257 に対して 3 回変更。246 に対して、1 回変更。 USED は、あたらしく、データを取って来て USER データとして使うから、 ちょっと、Disk に書くのをまてや ... つうことだが、この場合は出さなくて良いか。


ところで、組み込みTiki で こいつの位置付けが だいぶはっきりしてきた。

ということだ。で、Host OS を通して ちゃんと Disk に書くことまで 保証できるのだろうか? そうでないと ジャーナリングしても意味がない。

という2 通りの方法が一応ある。ただし、fsync/fdatasync は、そのファイル 全体に対して.. ということになる。これしか使えないとなると、 FS とジャーナル のファイルを別々にして ... ジャーナルを書く/ FS を書く というのを交互にやることになりそうだ。

まあ、それでも良いんだが ... mmap している場合は、msync というのも 使える。これは BSD なんかの マニュアルを見ると、(ページ単位で)好きなところを 書き出せるように見える。

Linux でも fsync はちゃんと Disk に書いてくれるようなので、 Linux なら fsync によるコントロール。BSD なら msync によるコントロール という風に 使い分けすることになるかも。


(最終更新 Thu Mar 30 19:19:41 2006)