最近暑くなってきましたね。私もサーバマシンみたいにキンキンにエアコンの効いた部屋で一日中過ごせればいいのにといつも思います。まあ嘘ですけど。
これから数回に分けて、ARM において Linux の kexec がどのように動くかについて簡単にまとめてみます。
kexec とは¶
まずは kexec とは何かですが、ネーミングからしてシステムコールの exec からのアナロジーだと思われるので、こちらから類推するとわかりやすいでしょう。Unix で exec を実行すると、動作中のプロセスが exec する新しいプログラムに置き換えられます。つまり、プロセス番号を有するプロセスの実体は引き継いだまま、メモリ空間に配置されるプログラムが新しいものに置き換えられた上で、そのプログラムの先頭から実行されます。
kexec はこの exec のアプローチをカーネルに当てはめたものだと考えれば、ハードウェア状態は引き継いだまま、カーネルのイメージを新しいものに差し替えて、その先頭から実行する、とでもなるでしょうか。
kexec における大きなポイントはハードウェアの状態を引き継ぐ(ハードウェア的なリセットがかからない)というところで、この特性を利用すると以下のようなことが可能になります。
ブートローダとしての利用¶
ハードウェアが NOR Flash, NAND Flash を持っていて、ブートローダは NOR Flash 上に入っているとしましょう。しかしこのブートローダがショボくて NAND Flash からの読み出しができないとします。しかし、NOR Flash の容量が小さく、ファームウェアとして使用したい Linux イメージが入り切りません。
このとき、NAND Flash の読み出しができる程度に機能を絞り込んで、NOR Flash に配置できる程度に小さくしたブート専用の Linux を用意すると (Boot Linux)、以下のようなことができます。
- ブートローダから NOR Flash 上の Boot Linux を起動する
- Boot Linux から NAND Flash 上に用意された Linux イメージをロードする
- Boot Linux から新しい Linux イメージで kexec する
こうすれば起動したかった Linux が起動できますね。ハードウェアリセットがかかってしまうと、最初のブートローダから Boot Linux を起動するところに戻って無限ループしてしまいます。
似たようなケースとして、開発中に Linux をネットワークからロードしたいんだけどブートローダに NIC のドライバがない、なんて場合にも使えます。
ファームウェアアップデートの際に利用¶
ファームウェアアップデートをする際に、新しい Linux で再起動する必要があるとします。ここで素直にハードウェアリセットから再起動してもいいのですが、可能な限りダウンタイムを短くしたいというニーズがあるとしましょう。
ハードウェアリセットから起動をやり直すと、最初にブートローダが立ち上がって主記憶(DDR コントローラ)の初期化をして、その他もろもろデバイスの初期化をして Linux をロードして…、とそれなりに時間がかかります。kexec を使用するのであれば、新しい Linux をロード状態に持っていくまでは古い Linux がサービスを行っている状態のままできますし、DDR コントローラなどの再初期化もいらないので、その分だけ速く起動できます。
ARM における kexec¶
ARM における kexec サポートは Linux v3.9.7 時点においても Kconfig 上は EXPERIMENTAL という扱いになっているようです。ただ、弊社においては少し古いバージョンに関連修正をバックポートした状態で SMP 環境でも kexec が正常に行えることが確認できていますし、EXPERIMENTAL はそろそろ外れてもいいんじゃないかという感想は持っています。
kexec するに場合には、ブートローダから起動する場合と最低限の環境を合わせてやった上で、新しい Linux イメージの entry point にジャンプしてやることになります。ブートローダから起動する場合と合わせるべき最低限の環境には以下を含みます。
- SMP の動作状態
- MMU の動作状態
- キャッシュの動作状態
ブートローダの設定にもよりますが、ブートローダでは 1 つの CPU のみが MMU およびキャッシュ無効の状態で動作することが多いと思います。であれば、kexec する手順の中で、SMP で動作している場合はセカンダリ以下の CPU を止めて、MMU を無効にして、キャッシュを無効にして [1]、Linux イメージの先頭にジャンプすればよい、ということになります。
次回はこのあたりの詳細について個別に考えてみます。
[1] | 実際的にはキャッシュ全域が invalidate されていれば十分かもしれません。 |