2011年7月19日作成

rewalk

ファイルサーバはdumpするとき、変更のあったブロックを上書きしません。 代わりに、新しいブロックを割り当てて、 そのアドレスでDir構造体(ファイルインデックスのようなもの)を更新します。 このとき、アドレス変更のあったブロックがopenされている場合、 オープンファイルに持っているアドレスは古いアドレスを指していますので、 新しいものに置き換えないと整合性が取れません。 この動きをdev/cw.cで定義されているrewalkで処理します。 このあたりはfsオープンファイルの管理も参照。

cfsdump(Filsys *fs)
	rewalk(Cw *cw)
		rewalk1(Cw *cw, Off addr, int slot, Wpath *up)

rewalk1ともうひとつrewalk2がありますが、 これはmainツリー(cw, /adm/users等)の場合にrewalk1が使われ、 dumpツリー(ro, /n/dump/2011/等)ではrewalk2が使われます。 違いは、ブロックに書き込む処理の有無みたい。

rewalk1 1

dumpしている最中、ファイルサーバコンソールに

rewalk1 1
rewalk1 1
rewalk1 1
...

と、大量のメッセージが書き込まれることが数回ありました。 これが起こると、以降のdumpでも必ず発生するようになり、 ファイルサーバをリブートすれば治まります。 このメッセージの出所はrewalk1で、具体的には、 オープンファイルからルートへ辿り、 新しいアドレスのルートから順にアドレスを解決していくとき、 ディレクトリがおかしい場合にプリントされているっぽい。 なにはともあれ、rewalk1のソースを一部引用。

// 親ディレクトリの新アドレスを解決
up->addr = rewalk1(cw, up->addr, up->slot, up->up);
// 親ディレクトリ(Dentry)を取得
p = getbuf(cw->dev, up->addr, Bread|Bmod);
d = getdir(p, up->slot);
if(!d || !(d->mode & DALLOC)) {
	print("rewalk1 1\n");
	if(p)
		putbuf(p);
	return addr;
}
p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);

ここで、cwは、dump対象となったcwデバイスです。 で、addrとslotは対象となったファイルの、upはその親ディレクトリ。 また、rewalk1はcfsdumpの最中に呼ばれます。 そのためWORM中の新しいアドレスはまだ空ですが、 cw.c:splitによりキャッシュには書き込まれています。

さてさて、この前提でソースを読むと、 どうやら新しい親ディレクトリから該当ファイルが辿れなかった場合、 または途中のどこかで途切れた場合にrewalk1 1が起こるみたい。 分かりにくいところでは以下の式

!(d->mode & DALLOC)

これは、Dir構造体が使われていないときに真となります。 詳しくはファイルサーバのディスクレイアウトに書きましたが、 ファイルを削除したときにDir配列を詰めないので、このようになっています。

問題はここからで、理由はなんとなく分かるものの、 それが起こる原因がいまいち分かりません。 splitによりキャッシュにある新しいブロックはCdumpになっているので キャッシュ落ちしないはずだし、 ファイルを開いた状態で削除したなら起こりえますが、 それだけで56行もrewalk1 1が出てくるのは理解できないし。 splitでの書き込み時にcwio()の戻り値をみていないので これが失敗したのかと予想を立ててみるものの、 エラーになった場合は

cwio: write induced dump error ...

こんなエラーが出るはずなのでたぶん違う。 split以外ではざっとみた限り戻り値みてるしなあ。。

DMEXCL(stat(2))ビットが立っているファイルは開いたまま削除できません。 立ってなければファイルを開いた状態で削除できますが、 それを読み書きするとエラーになります。fossil等は知らない。

/mail/grey/tmp/*が怪しいなあと思いましたが、 ふつうにOEXCL(open(2))が立ってた。 でもこれ、ファイルがすでにあれば弾くフラグなので関係ないか。