/home の SD 移行


できれば / を SD に移行したほうが全体の構造としてはシンプルだ。 /home だけ ... などという中途半端なことをすると、元の dirty な構造も生き残ってしまう。 とはいえ、SD ドライバが module として提供されているために / 自身の移行は容易なことではない。

SD ドライバが module になっている以上、どうしても最初は cramfs 上に / が置かれる。 boot 時の / と実使用時の / を変えるのは ext-root-mini-HOWTO にあるように initrd を使うのが普通だし、SL-A300 の boot loader も initrd を扱えるようだが、 initrd.bin (cramfs) とは排他に見えるし、そもそも RAM を無駄使いする initrd は使いたくない(笑)。

SL-A300 の bootstrap 手順

  1. ROM を / に mount する。
  2. /etc/inittab (実体は /root/etc/inittab) をみて /etc/rc.d/rc.sysinit (同 /root/etc/rc.d/rc.sysinit) 起動。
  3. Network 周りの初期設定などを読み込む。
  4. /etc/rc.d/rc.rofilesys (/root/etc/rc.d/rc.rofilesys) 起動、ディレクトリツリーを構成。 /etc の実体が /root/etc から /home/etc に切り替わる。
  5. /root/etc/rc.d/rc.sysinit は depmod -a してから devinfo.o をロード。
  6. さらに mount -a -t nonfs する。
  7. runlevel 3 へ移動と同時に /etc/rc.d/rc (/home/etc/rc.d/rc) 起動。
  8. /etc/rc.d/rc3.d/* 起動。SD ドライバ (sharp_mmcsd_m.o) はこの段階でロードされる。

ディレクトリの構成を触るなら /root/etc/rc.d/rc.rofilesys が中心になる。 ... と思って油断すると mount -a -t nonfs するときに参照する /etc/fstab (/home/etc/fstab) を直すのを忘れる。つーか、忘れた(泣)。

ディレクトリ構成

SD 移行するといっても /tmp, /var など書き変え頻度が高いものは SD に置きたくないし、 /var は出来ればリセットを跨いで保護したい。 もしかするとリセットを押さなければならなくなった理由の一端が /var/log/ に残っているかもしれないからだ。 あんまり役に立ったことないけど ...
ということで、こんな感じ:

/dev/mtdblock0  /            cramfs   ro               1  1 
/dev/mtdblock1  /var         ext2     defaults         0  2
/dev/mmcda2     /home        ext2     defaults         0  3
/dev/shm        /tmp         tmpfs    defaults         0  0

none            /proc        proc     defaults
none            /dev/pts     devpts   gid=5,mode=620   0  0
/dev/hda1       /mnt/cf      auto     noauto,owner     0  0
/dev/mmcda1     /mnt/card    auto     noauto,owner     0  0

このままだと writable disk に置かなければならない /etc, /dev が cramfs に残る。 それぞれ /home/etc, /var/dev から bind にしてある。

# mount -o bind /var/dev  /dev
# mount -o bind /home/etc /etc
"mount -o bind " は obsolete で、"mount --bind " と書くべきだが、 busybox ではまだ前者の構文が使われている。 古い構文にしても bind mount がちゃんと busybox で使えることが実は驚きだったり。
mount --bind vs symlink

ディレクトリの差し替えにはふつー symlink を使うのだろうが、 これだと symlink 先を変えたい時に symlink を書き換える必要がある。 Read only device からの link は変えられない。
このため SL-A300 では symlink を ROMの中 → RAM の中 → ターゲットと 2 hop させ、 中間の link を置き換えたり、あるいは mount で link を潰したりして書き換えている。

同じことが mount --bind (mount -o bind) によっても出来る。 こちらは read only だろうとなんだろうとおかまいなしであり、 0 hop で出来る。
ならばいっそ /mnt/card を / に bind してしまえ ── という考え方もないでもないし、 実は出来るんだが、 「bind する時に潰されるディレクトリを使っていたアプリはそのままもとのディレクトリを使う」 という当然の仕様により、そのまんま全てのプログラムが旧 / を参照し続ける。ということで没になった。

ぶっちゃけて言えば、symlink がとびかうと cd の挙動や ls の表示が dirty になるのが嫌だ。 mount --bind の場合、かわりに /proc/mounts が少々 dirty になる。 あんまり見にいかないし、それなら許せる。本質的にはそういうことだ。

/dev

/dev は cramfs に置き続けることができない。Writable disk の上に置かなければならない。 ... ええと、アプリが /dev/tty*/dev/console などを握った時に device file の owner を書き換えるなどといった動作があるためだと思ったが、 これ、原則 root で動いてる SL-A300 で問題になるのか? とはいえ、 わざわざ /dev/ram の上に /dev を作り直してるから、 最初のうち ROM の上に置きっぱなしにしていて問題になったに違いない ...

root 以外の uid で使うことを考え、もちろん writable disk の上に置くが、 そのために device を一つ用意するのは美しくない。 かといって /dev を symlink にすることはできない。 boot 時に /dev/* が存在していなければならないから。symlink で 2 hop 飛ばしてもいいのだが、 /var/dev に dev/* を置いて /dev へ bind した。このほうが綺麗。

... ていうか、SL-A300 が元々やってたように /dev に空の /dev/ram を mount して、 それから tar xf .dev_default.tar して device file を作るってこええよ。 tar が途中でコケたら device file ボロボロの状態で止まるんかい。 /dev の移行は atomic 操作にしてくれ。

/var

/dev/mtdblock1 を /var にしか使わんのに 28MB も割り当てるのは論外だ、というわけで今は 2MB になっている。 SL-A300 の script にはかつて /var に 1MB 弱の /dev/ram を割り当てていたフシがあるので、 2MB あれば十分だろう ... つか、syslog 動いてないのに 1MB も要らんだろ。

再起動やリセットで /dev/mtdblock1 を正しく再利用するためには 再起動やリセット前に /dev/mtdblock1 を umount するか、少なくとも mount -o ro,remount しなければならないが、いまんとこそんなことはしていない。 ソフトリセットすると e2fsck が大量に文句言うのが見える :-)

/tmp

/var と同じく SD に置きたくないディレクトリ。 こちらはリセットで保護する必要ないってことで /dev/shm に (保護すべきだ、という流儀があることは知っている)。 /var の中に置いてもいいのだけど ──

/var の中に置いてないのは、/var が今や 2MB しかないからだ。 gcc は /tmp に大きな中間ファイルを置く。必要とあれば大きな領域となり、普段は小さい領域としたいのなら、 /dev/shm 以外に選択肢はない。

/mnt

/var/mnt に symlink され、さらに /usr/mnt.rom/ に symlink されている。 2 hop しているが、2 hop しているからといって特に意味があるわけではないし、 最後の /usr/mnt.rom/ は ROM の中なので実行時に mount point を新たに付け加えることもできない。 無意味である。

というわけで、実体を /mnt 自身とし、念のため /usr/mnt.rom は /mnt への symlink とした。

/etc/fstab

/etc/rc.d/init.d/rc.rofilesys (/root/etc/rc.d/init.d/rc.rofilesys) でディレクトリツリーを構成している。 したがって、この時点で /, /home, /var, /dev などはきちんと出来上がる。

ところが、その直後 rc.sysinit 中で mount -a -t nonfs していて、 /etc/fstab にはちゃんと /home などのエントリも書かれているため、 /home などは二重に mount してしまっている。

rc.roflesys 内だけいじってディレクトリを構成したら、 /etc/fstab 由来のディレクトリで上書きされて /dev や /var の中身が消え、 /sbin/init がお亡くなりになってしまっていた ...

/home 等 rc.rofilesys 中で構成してしまうディレクトリの項は /etc/fstab ではコメントアウトすべきだろう。

Rescue cramfs

SD に /home を移すと、 SL-A300 を使用中は SD が外せなくなる。 これは予定のうちだからいいとして、SD カードの upgrade の方法がなくなることが個人的に大問題だった。 というのはつまり、SD カードが扱えるマシンを SL-A300 しか持ってなかったから。 SD カードを SL-A300 に入れたままでは別の SD カードに /usr とか作ったりできない。そもそも SD に fsck かけることもできない。
ここで過去形になっているのは、まさに rescue cramfs を調整してるさなかに SL-A300 が boot しなくなってしまい、(動くことがわかっている) 旧システムに戻すことすらできなくなって、 やむをえず SD アダプタ買ってきたからだ ...
Filesystem 触るときの「起動しなかったらどうしよう」てなビクビク感は無くなったけど、 rescue cramfs の存在意義のあたり、ふと省みてしまった。

で。SD カードを外した状態での boot と入れた状態での boot でシステムの構造が切り替わるようにしておく。 SD カードを外した状態での boot したときには、nfs と konsole が使えるくらいでいい。 そういう意味では、すぐに作れるようなシステムだが、 /home を SD に移した本来のシステムとの絡みで様相がかなり変わる。

/dev/mtdblock0  /            cramfs   ro               1  1 
/dev/mtdblock1  /var         ext2     defaults         0  2
/dev/shm        /home        ext2     defaults         0  3

none            /proc        proc     defaults
none            /dev/pts     devpts   gid=5,mode=620   0  0
/dev/hda1       /mnt/cf      auto     noauto,owner     0  0
/dev/mmcda1     /mnt/card    auto     noauto,owner     0  0

/var を両者で共有し、SD が無い場合には /home を /dev/shm で構成している。 可変サイズ ramdisk が使えるのに /dev/ram 使う意味はない。 リセットの前後で保存する必要もない。どーせ rescue disk 扱いである。

/etc, /dev は上と同じく /var, /home の下に bind してある他、/tmp も /home/tmp に bind した。

SD のマウント時期

SD のドライバ (sharp_mmcsd_m.o) は /root に入っているが、カードが mount されるのは /home を作ったあとの /etc/rc.d/init.d/sd の実行時点だ。 /home を SD に置きたいのにこれでは遅すぎる。rc.rofilesys の実行時点まで前倒しした: /dev/mmcda2 を mount できるかどうかで SD の存在判定しているが ... ま、しょうがなかろ。
SD が無かった場合はドライバを一旦 rmmod し、 のちのち /etc/rc.d/init.d/sd が再 load するのに任せた。 たんに気分の問題で、たぶん rmmod しなくてもいいんじゃないかとは思う。

/usr/lib/ipkg/

Ipkg の管理ディレクトリ。2 種類のシステムで共有するわけにはいかないが、 実体は /home/root/usr/lib/ipkg/ なので、/home が分離されていれば分離される。

Install したさい、pkg の install 先を「本体」に選ぶと install 先は /home 下になる。 定義によって SL-A300 は /home 下しか書き込めないことになっているからだ。 /home ごと切替えるならちゃんと整合するわけである。

mount --bind と sysinfo

SL-A300 で使ってる kernel 2.4.13 の時代は mount -o bind で /proc/mounts が
/dev/mtdblock1 on /var type ext2 (rw,sync)
/dev/mtdblock1 on /dev type ext2 (rw,sync)
のような形になる。2.4.18 くらいになると
/tmp/A on /tmp/B type none (rw,bind)
となって、デバイスでなくディレクトリ間の関係であることが明示されるのだけど。

このため、sysinfo の storage のページで /dev/mtdblock1 について 2 度 report するようになる。 この重複レポート対策で 1 行パッチ。

CONFIG_COTULLA_LOGO_SCREEN と Startup_screen.bmp

Kernel config で CONFIG_COTULLA_LOGO_SCREEN=y になっていると boot と同時くらいにスクリーンに絵 (drivers/video/LogoScreen.c) がはりつく。 そして boot しおわって Qt/Embedded なアプリが準備に入ったころに、 こんどは /root/usr/QtPalmtop.rom/pics/Startup_screen.bmp が画面にはりついてメッセージが読めなくなる。

システム起動中のめんどうみてんのに、メッセージが読めないのはひどいってんで両者とも外した kernel (and system) を使っているが ... 標準では SL-A300 ってば、すげー寡黙だ。 標準では最下行に出る 2 行分くらいの文章しか読めないわけだが、 実は CONFIG_COTULLA_LOGO_SCREEN を外しても読める文はその 2 行だけである (笑)。 kernel も init もほとんど何のレポートもしてくれない。 fbcon の初期化が遅いこともあるのだろうが、それだけでは説明がつかんのだよな ...

Qt/Embedded のほうも、Startup_screen.bmp がない場合、 では何をするかというと白一色の「絵」をはりつける(笑)。 わりと早めに segmentation fault などでとらぶってる場合にのみ、メッセージが見える。 もちろん見えても ^C^S できるわけじゃないので 高速スクロールするエラーメッセージを指くわえて眺めてるだけだけど。

HOME キーとか CANCEL キーを ^C, ^S, ^Q あたりに bind しておくというようなことをしてくれてもバチはあたらんと思うのだがな ... fsck でとらぶった時に y/n 訊いてくるのに、いまのままではそれに答えることすらできん。

広大な空間

Memory 62MB Disk 2MB

やー、さすがに広いわ ...。43MB の空きメモリ。ユーザーエリアは上から /var, /home, /mnt/card. /var に 2MB も要らんなとは思いつつ。

62MB 化するときは 15日の実験とは違い、mm.c のほうもいじった。 /dev/mtdblock1 の領域だけを 0xf9000000 にはりつけるようにしている。

SD の partitioning

書くの忘れたけど、SD カードは partition を切ってある。 VFAT partition の /dev/mmcda1 と ext2 fs partition の /dev/mmcda2 だ。

前者がないと SD 経由の kernel/intird.bin upgrade が出来ないし、 後者がないと /home を移す気しない。... 2 枚カード使えばすむ話ではあるが、 入れ換えも手間だし、といちおう言ってみる。

なお、partiton を切っても kernel/initrd.bin の upgrade は正しく動いた。 /dev/mmcda1 を参照してるのか VFAT partition を参照してるのかは知らないけど (こういう時、おおむね最初の FAT partition を参照するらしいが、SL-A300 がどうかまでは確認してない)。

追記

コンソールが寡黙な件。
カーネルパラメータから "console=null" を外すことでカーネルメッセージが読めるようになった (59414d41 さんによる。Thanks!)。
[日記へ] [目次へ]