		ディレクトリエントリキャッシュ 実装仕様書
						2014.3.31 数理技研

はじめに
	本ドキュメントは、Gfarm ファイルシステムのlinux カーネルドライバで
	ユーザーのreaddir 要求に過不足のない応答を返すための実装仕様を
	記述するものである。

1. 実装概要
	Gfarm ライブラリのreaddir の実装はopendir 毎にキャッシュを持ち、
	gfmd と通信するものであるため、巨大なディレクトリ読み込み中に
	エントリの生成/削除があってもさして齟齬を来さないものであった。

	しかし、カーネルドライバはエントリをinode と結びついたページ
	ページキャッシュに格納し、複数のプロセスでの参照を可能とするため、
	エントリのオフセット管理が必須となる。

	Gfarm ではエントリーのオフセットをプロトコル上返していない。
	gfmd ではエントリーは名前のツリーで管理されているため、これを
	オフセットに援用することは難しい。

	本実装では、gfmd に別途エントリー順序インデクスを設け、これを
	オフセットとし、プロトコルにもエントリーオフセットを付加することにした。

	カーネルドライバではページに含まれるオフセットを管理し、ユーザにも
	エントリのオフセットで開始位置を指定できるようにした。

2. プロトコル
	従来のディレクトリエントリ取得プロトコルにオフセットつきの
	プロトコルを追加した。
		GFM_PROTO_GETDIRENTS2,
		GFM_PROTO_GETDIRENTSPLUS2,
		GFM_PROTO_GETDIRENTSPLUSXATTR2,

	オフセットは符号付き64ビットインテジャである。

	いずれも、要求は個数に加えて開始オフセットを指定する。
	オフセット０は先頭を意味する。
	応答は各エントリ情報にオフセットも加える。

3. gfmd
	gfmd では inode から指されるDir 即ち struct gfarm_dirに
	エントリ管理構造を持たせる。

	struct dir_doff {
		DirEntry        *d_data;
		gfarm_doff_t    d_size; /* # d_data[] */
		gfarm_doff_t    d_freei; /* free index */
		gfarm_doff_t    d_nfree; /* # DirEntry */
	}
	d_data はDirEntryの配列でエントリの増加にしたがってrealloc される。
	エントリのオフセットとはこの配列のインデクスである。

	エントリは自身のオフセット情報を持っている。

	エントリが削除されても配列内の要素が移動することはない。
	このため、空のエントリーが増えるとreadir でサーチ時間がかかったり、
	create で挿入する場所のサーチに時間がかかる。
	空のエントリは現在はd_freei で管理されるのみであるが、
	ビットマップリストを導入するとか、
	有効リストとフリーリストをつくるとかの工夫が必要である。	【課題】

4. db
	db のディレクトリエントリレコードには名前とinunmber の他に
	doff を持たせる。
	これはfailover のためである。

	但し、db 変更は従来の稼働中のシステムに影響を与えるため、
	活かしていない。						【課題】

5. gfarm
	新規プロトコルを発行するようにした。
	seekdir はgfmdに出さず、保持している情報を変更するだけにした。

6. カーネルドライバ
	カーネルドライバではページキャッシュを復活させたが、
	gfmd へのreaddir の回数が増えているので、
	ページキャッシュを使うか否かをマウントオプションで指定できるようにした。


6.1. データ構造
	inode がディレクトリの場合、ページに含まれる最大オフセットを管理する
	データを持つようにした。

	struct gfsk_dirinfo {
		gfarm_doff_t    d_last;
		pgoff_t d_ninfo;        /* current */
		pgoff_t d_tinfo;        /* total */
		gfarm_doff_t    d_off[1];
	};

	d_off はページインデクスでアクセスされるオフセット情報で当該ページの
	最大オフセットを記録する。

	ページはstruct gfs_dirent のデータが詰められるがレコード長は名前の
	長さに従ってデータアラインを合わせた実質の長さである。

6.2. 処理
6.2.1. readdir
	1. ユーザーのreaddir 要求で、当該inode のgfsk_dirinfo を調べる。
	2. struct file のカレントオフセットが示すオフセットを含むページを
	   d_off から探す。
	3. ページを vm に要求して、ページ内のオフセットに等しいか大きい
	   エントリを 探す。
	4. 要求に満ちるまでページ内のエントリをfiller に渡す。
	5. 次のエントリが空でページに十分な空きがあればgfarm からエントリを
	   ページに詰める。
	6. vmからのページがENOENTであればeof と見做す。
	7. file のfpos は最後のエントリのオフセットに1足したものである。

6.2.2. readpage
	要求されたページからEOFまでのエントリを詰める。
	1. 要求されたページにd_last の次のオフセットからの情報を詰める。
	2. ページをmapし、gfarm から得たエントリ情報をレコード長で詰める。
	3. gfarm からNULL エントリが戻ってきたらEOFと見なし戻る。
	4. ページがいっぱいになったらこのページは開放し、
	   dirinfo 情報を更新、次のページを得て map する。

     【問題】
	EOF情報はないので、最後のページでは常にgfmd に問い合わせがいく。

6.3.マウントオプション
	関連する以下のオプションを追加した。

	dirpage={0|1}
		readdir でページキャッシュを有効にするか否か。
		デフォルトは無効(0)

	d_delete={0|1}
		ネガティブキャッシュを無効にするか否か。
		デフォルトは無効(1)
