FreeBSD
ZRouter

Flashのupgrade

11月のFreeBSD Workshopで話した内容をブラッシュアップしてみました。

まずZRouterなFreeBSDの起動を説明します。

FLASHに焼かれた圧縮されたkernelをu-bootがRAMに展開して実行を移します。

1.png

kernelはFLASHの圧縮されたiso9660なrootfsをgeom_uzipとgeom_map/geom_flashmapを使って認識してmountします。

2.png

mountされたrootfsのrcが実行されて/tmpとunionfsな/etcで使うmdを作成してnewfsしてmountします。

3.png

# df
Filesystem                   1K-blocks  Used Avail Capacity  Mounted on
/dev/flash/spi0s.rootfs.uzip     16602 16602     0   100%    /
devfs                                1     1     0   100%    /dev
/dev/md0                          7543   762  6178    11%    /tmp
<above>:/tmp/etc                 24145 17364  6178    74%    /etc

ここまでが通常の起動状態になります。

FLASHをupgradeするためにはFLASHのrootfsをumountする必要があります。当初rootfsの最小限必要なshなどのバイナリをmdのufsにコピーしてrerootしていました。ufsは圧縮されていないので6M近くになり、16MしかRAMが無いターゲットなどでは厳し状態でした。先日のWorkshopで話した事はここまででした。

ちなみにrerootとはFreeBSD 10.3くらいから入った機能で、reboot -rでrootfsのmount時点からの再起動が出来る機能です。kenvで次のmountデバイスが指定できます。

課題で話した内容に、たまにrerootが失敗する事があり調べてみました。tmpfsの空きメモリのチェックがあり、これが4MByteとメモリが少ないターゲットでは厳しい事が分かったので、カーネルオプションで設定できるようにしてレビュー出してみました。

その後、最小構成のファイルシステムのiso/uzipでの再構成なども検討してみたのですが、コマンドを増やすとイメージが大きくなる事やワークスペースが必要になるため現実的ではありませんでした。

ふとFLASHの圧縮されたrootfsをそのままmdにコピーすれば良いではないかと思いつきました。(後から思えば至極当然の事なんですが、結構いろいろ考えました)

4.png

自分以外のプロセスをkillして/etcと/tmpをumountしてddでrootfsをFLASHからmd0にコピーしてこれにrerootするようにしました。

自分以外とはinit(pid=1)から起動したlogin以外のプロセスになります。psコマンドから引っ張る事もできますが、sedもawkも無いので/var/runの下のpidファイルを元にしました。

dd if=/dev/flash/spi0s.rootfs of=/dev/md0
kenv vfs.root.mountfrom=cd9660:md0.uzip
reboot -r

5.png

rerootで起動した事はkenvのvfs.root.mountfromで分かるので、この場合はmdを小さめで作るようにしました。4MのFLASHの場合md0を3Mで作ってmd1を1Mくらいで作るのが良いと思われます。これであれば16MのRAMのターゲットでも十分に実用になります。

これで完全にRAM上で起動している状態なので、FLASHの焼き直しが安全におこなえます。

具体的にはtftpとpipeとddを使ってFLASHのupgradeデータを流し込む方法などがあります。

mkfifo /tmp/flashpipe
dd if=/tmp/flashpipe of=/dev/flash/spi0s.upgrade obs=64k conv=osync &
echo "bin
get Fon_FON2305E_FDT.zimag /tmp/flashpipe
quit" | tftp tftpserver
kill -INT 1

ZRouterを作ったrayからはhttpのuploadとかスイッチなどで対応できるようにしてほしいと言われているのですが、この方法であれば応用で出来そうな気がします。

いろいろプロセスが上がっているとtmpfsのメモリ確保に失敗してrerootが失敗します。tmpfsのメモリ最低確保を変更できるようなパッチつくってみたのですが、できるだけrcでの起動しないようにして、リブート直後にupgradeを行うのが現実的です。

upgradeのパーティションを間違えて設定してしまいu-bootをつぶしてしまうと文鎮になってしまうので注意が必要です。

4Mや8MのFLASHの場合はこの方法が良いと考えられますが、16M以上の場合は、やはり最小限のbinをmdにコピーしてrerootするほうが現実的だと思います。

16M以上をサポートするためにinit,shや必要なlibをmdのtmpにコピーする方法も対応してみました。ファイルの合計が4MちょっとくらいなのでFLASHが8Mの場合もこの方法で良いような気もします。

途中でtftpが止まった場合などはブート出来なくなるので、シリアルコンソールでもう一度焼きなおす必要があります。

開発が続いているCURRENTではときどき立ち上がらなくなったりする事もあります。リモートでのupgradeはシリアルコンソールでのupgradeの代わりになるものではありません。