メモリリーク

ここ10日間ほど悩みのタネだった、xattr on jffs2で発生していたメモリリーク問題にケリが付いた。

そもそもこの問題は、(1)JFFS2区画をマウント→(2)I/O負荷をかける→(3)アンマウント→(4)I/O負荷をかける→(5)アンマウント、した時にメモリリークが発生するというものである。
メモリリークが発生していたのはjffs2_xattr_ref構造体のスラブで、これはinodeのname/valueペアの対応関係を表現する役割を持つ。これが、2回目以降のアンマウント時にリークしていたのだった。

原因は、ファイルシステムのマウント時に既に消去されてしまったinodeとjffs2_xattr_refを関連付けてしまったこと。
本来、inodeが利用されなくなった時にjffs2_xattr_refはリリースされるが、最初から誰にも利用されていないinodeは、そもそもこの様なパスは通らない。NAND-Flashサポートが有効である時(Write-Bufferingが有効な時)、inodeやらxattrの消去は上書きで行わないために、I/O負荷をかけた後のメディアにはobsoleteなinodeの断片が転がっていることになる。
次回のマウント時には、これらobsoleteなinodeも含めてメディア全体を一旦スキャン(*)し、その後に使用されていないものを排除していくという手順で処理が行われている。
jffs2_xattr_refとinodeを関連付ける処理はこれよりも前に行われるために、関連付けの時点ではinodeが有効なのかタダの残骸なのかは判断できない。そのために、残骸に関連付けられたjffs2_xattr_refが存在してしまい、それらは一般的なinode破棄のパスを通らなかったためにメモリリークしてしまった。

とりあえずワークアラウンドとして Garbage Collector の処理の中に、以下の一行を埋め込んで対策。

 if (!ic->nlink) {
D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
ic->ino));
spin_unlock(&c->inocache_lock);

  1. jffs2_xattr_delete_inode(c, ic);

continue;
}

inodeが他にどういった状態を取り得るかを調べて、最終的にどういう形にするかを考えよう。嗚呼それにしても長かった。これで安心してW杯を見られる。

(*) ちなみに、EBS(Erase Block Summary)というのは、メディアのスキャンを遅延させることでマウント時間を短縮する技術