2015年2月28日土曜日

Intel Edison で opkg upgrade すると Windows に対して USB OTG で接続できなくなった

 前回,SparkFun Blocks for Intel Edison を重ねてみたと書いた。 その続きである。

 ここしばらく Intel Edison で遊んでいる。 Edison は小さいが十分な能力を持つコンピューターである。 購入した時点で yocto linux がインストールされているが,当然最新版ではないため,まずは firmware をアップデートしないといけない。 さらに,opkg というシステムで様々なアプリケーションをインストールしたり,それらのアプリケーションをアップデートしたりできる。

 今回,firmware のアップデートの後に opkg でのアップデートを行った際, Windows に接続した時に USB メモリのように見えるはずのものが,全然接続されなくなってしまった。 その顛末について書こう。


 結論を先に書いておこう。 原因は /boot/ partition のサイズが小さいため,opkg upgrade で kernel のアップグレードの際にエラーを起こしていた。 そのため,一部の kernel module がうまくインストールされずにトラブルになっていた。 特に USB gadget あたりに不都合が生じていたため,UBS OTG が不調になっていた。

これに対する使用時の対処法としては,下記のいずれとかなる。
 1. repo を使った upgrade を一斉行わない。repo は後からインストールしたいソフトのためのみに使うか,upgrade は個別に行う。
 2. repo として,src/gz edison http://repo.opkg.net/edison/repo/edison は使わない(kernel の情報が含まれる)
 3. /boot/ partition サイズを変更して,kernel もアップグレードしてしまう(多少作業が必要)

また,エラーから回復するには,
 1. update 用の firmware のファイル(edison-image-ww05-15 等)が Edison に残っていれば,
     # reboot ota
  で firmware のアップデートからやり直す。

 2. update 用の firmware のファイルがない場合は,
  opkg upgrade 前のバージョン用の bcm_bt_lpm.ko,usb_f_acm.ko,u_serial.ko,libcomposite.ko,g_multi.ko を
  適切な場所にコピーしてから再起動し,firmware のアップデートからやり直す。
  (この作業により,USB OTG が働くので firmware アップデートが可能になる)

 3. update 用の firmware のファイルもないし,opkg upgrade 前のバージョンの kernel modules のファイルがない場合
  どこかから opkg upgrade 前のバージョンの kernel modules のファイルを持ってきて,上記 2.,上記 1. の順に作業する。

追記):/boot full when opkg upgrade にリポジトリを作ってくれた AlexT_Intel 氏によるコメントがあった(2014/12/30 14:21 のコメント)。それによると,彼が作った repo に対しては opkg upgrade はしない方がいい,というのが彼の意見だった。でも,/boot のパーティションサイズをいじるのもいうほど難しくはないが…


 まずは作業環境について書いておこう。 私は Intel Edison を2個持っている。 1個は Intel 製の Arduino 拡張ボードとセットになっているものであり, Edison のシリアルモニター接続用と USB OTG 用に2個の micro USB 端子がついたものである。 ここで USB OTG は USB クライアント同士をつなげるようにしたもである。 これまで USB クライアント機器だったものが,ほかの USB 機器に対して親機のように振る舞える機能である。 言うなれば USB におけるサーバー的な機能をクライアントに持たせたもの,である。
 この Arduino ボードの場合,USB OTG 用の USB 端子は電源供給機能を併せ持っているが,シリアルモニター接続用は電源供給は行わない。

 もう一個の Edison は Edison 本体は Intel 製だが,拡張基板として SparkFun 社製の SparkFun Blocks for Intel Edison の中の Base Block をつけたもの,である。 この Base Block も Intel 製の Arduino 拡張基板と同様,シリアルモニター接続用と USB OTG 用に合わせて2個の micro USB 端子がついているが,Arduino 基板と違って,いずれの USB 端子も Edison に対して電源を供給できる。
 今回の現象は Arduino,SparkFun 両方のボードに載せた Edison でおこった(両方で起こった。すなわち拡張ボードの問題ではない)。

 次に経緯を書こう。 今回の発端は Intel Edison の firmware をバージョンアップしようと思った事に始まる。 Edison は昨年 (2014年) の夏の終わり頃に発売された(だったはず…)。 その後,何度もシステムソフトウェアのバージョンアップがなされているが, 昨年の年末から2月にかけてはあまり Edison をいじる機会がなかった。 今回,SparkFun 社製の Base Block がアメリカからやっとこさ届いたので(注文してから2週間近く待たされた), 久々に Edison で遊んでみようと思ったのだった。

 Edison のシステムソフト (firmware) をバージョンアップすると,/home 以外は基本的に入れなおし,になる。 つまりアップデートはクリーンインストールを意味する。 そのため,新しく使いはじめる時には,まずは firmware update をすることにしている。 Firmware は Intel の support site からダウンロードできる。 support site の中の Yocto complete image が Edison の firmware (yocto linux という少メモリ用の linux) である。 現在のバージョンは,ファイル名で言うと
   edison-image-ww05-15
であり,インストール後に /etc/version を表示させると
   /etc/version ==> weekly-120
と書かれるバージョンである。 ちなみに uname -a コマンドで調べると
   Linux ultedison 3.10.17-poky-edison+ #1 SMP PREEMPT Fri Jan 30 14:16:35 CET 2015 i686 GNU/Linux
と表示される。

 このバージョンにアップデートした後に,アプリケーションのインストール用に opkg を最新にした。 やり方は別の所にも書いたが,/etc/opkg/ の中に設定ファイル(リストファイル)の読み込み先を記述して,
   # opkg update

   # opkg upgrade
を行えばよい。 「update」はリストファイルの更新作業であり,「upgrade」はインストールされているアプリケーション自体の更新作業になる。

 今回 firmware バージョン「weekly-120」に対して,この「upgrade」を行うと,トラブルが発生した。 (最初は opkg upgrade が原因とはわかってなかった。とりあえず気づいたらおかしかった。) 上記にもあるように,2本の USB ケーブルを使って Edison と Windows をつないだ時に, 本来はシリアルモニター接続と USB ホストとして Edison をつなぐことができるはずなのに, 今回は USB ホスト (OTG) が見えなくなってしまった(シリアルモニターは認識されていた。念のため)。 具体的には,USB OTG が働いていると windows から見ると USB メモリがつながれたと認識され,外部ストレージが増えた,と表示される。 また,デバイスドライバーで調べると「ポート (COM と LPT)」の中に3個(2個の場合もある…なぜ?)増えないといけないものが,1個しか見当たらない,という事態になった。 例えば,
   USB Serial Port (COM3)
   Intel Edison USB Composite Device (COM4):これがでない時があった…なぜ?
   Intel Edison Virtual Com Port (COM5)
と3つ見えるべきものが (COM 番号は環境によって異なる。USB Serial Port は COM3, COM6, COM9, COM19 に遭遇したことがある),
   USB Serial Port (COM3)
1個だけしか見えなくなってしまった。

 まず疑ったのは USB ケーブルだった。 micro USB 用のケーブルは,携帯電話などの充電用に使われるものも多く,気をつけないと充電はできるがデータ通信はできない,というケーブルもある。 そこで,ケーブルを入替えたりしてみたが,結果は同じだった。

 次は通信関係の設定を疑ってみた。 それは USB OTG で windows から見えなくなってから,Edison 内で ifconfig コマンドで調べると,
   localhost
   wlan0 (無線 LAN アダプター)
はリストされるが,
   usb0
が見えなくなっていたからだった。
 Edison は,基本的に無線 LAN でつないで linux サーバーとして使おうと思っているので,無線 LAN の IP アドレスを固定で使っている。 その辺りの設定のせいかと思っていろいろ調べたりいじったりしたが,直接の関係はなかった。
 他にも /etc/log/journal/ の下にログファイルがたまりすぎて領域を狭くしてしまう, という話もあったので,journal があふれたのが悪いのかもしれない,と思って, journal の設定をいじったりもしてみたが,効果はなかった。

 いろいろ見ているうちに,実は起動時にエラーが発生しているのに気づいた(ちゃんとメッセージは見ないと…)。
------------------------------------------------------------
[ OK ] Started Apply Kernel Variables.
[ OK ] Started Remount Root and Kernel File Systems.
[FAILED] Failed to start Load Kernel Modules.
See 'systemctl status systemd-modules-load.service' for details.
[ OK ] Started udev Coldplug all Devices.
     Mounting FUSE Control File System...
     Mounting Configuration File System...
     Starting Load/Save Random Seed...
     Starting Create Static Device Nodes in /dev...
[ OK ] Mounted Configuration File System.
[ OK ] Mounted FUSE Control File System.
[ OK ] Started Load/Save Random Seed.
------------------------------------------------------------
 エラーに気づいた当初は kernel module の読み込みエラーということなので,/etc/modules-load.d/ や /etc/modprobe.d/,/var/lib/opkg/info,/lib/modules/ の中を調べたりしてみたが,いまいち原因は不明だった(これで意外と時間をくった)。

 仕方ないので, USB OTG がちゃんと働いていた最初の状態に戻そうと思って, もう一度 firmware のアップデートからやり直してみた(Firmware update 用のファイルが残っていたのでこの作業ができた)。 まずは
   # reboot ota
で firmware を 最新の(いろんな設定をする前の)firmware に戻し,
   # ifconfig
でデバイスの状態を見てみた。 すると,ちゃんと lo0, usb0, wlan0 の3つが動いていた。

 その後,順に1個設定を行っては ifconfig で確認する,というのを繰り返していった。 そして,最終的に opkg upgrade を実行した後に USB OTG が働かなくなる症状がでるという事がわかった。

 しかし,わかったのは opkg upgrade の後にエラーが出る,というだけで,そもそもの原因は不明だった。 そこで起動時のエラーメッセージに従って,
   # systemctl status systemd-modules-load.service -l
としてみた。 すると,
-------------------------------------
? systemd-modules-load.service - Load Kernel Modules
Loaded: loaded (/lib/systemd/system/systemd-modules-load.service; static)
Active: failed (Result: exit-code) since Mon 2015-03-02 03:04:59 UTC; 52min ago
Docs: man:systemd-modules-load.service(8)
man:modules-load.d(5)
Process: 110 ExecStart=/lib/systemd/systemd-modules-load (code=exited, status=1/FAILURE)
Main PID: 110 (code=exited, status=1/FAILURE)

Mar 02 03:04:58 ultedison systemd[1]: Starting Load Kernel Modules...
Mar 02 03:04:59 ultedison systemd-modules-load[111]: Inserted module 'bcm4334x'
Mar 02 03:04:59 ultedison systemd-modules-load[111]: Failed to insert 'bcm_bt_lpm': No such file or directory
Mar 02 03:04:59 ultedison systemd[1]: systemd-modules-load.service: main process exited, code=exited, status=1/FAILURE
Mar 02 03:04:59 ultedison systemd[1]: Failed to start Load Kernel Modules.
Mar 02 03:04:59 ultedison systemd[1]: Unit systemd-modules-load.service entered failed state.
-------------------------------------
というのが出てきた。 これを見てもイマイチわからないが,とにかく bcm4334xbcm_bt_lpm という複数の kernel module が関係してそうというのはわかった。

 そこでいろいろとあっちこっち web site を見て調べたのだが,やっとの事で https://wiki.archlinux.org/index.php/systemd というサイトにたどり着いた(どうやったたどり着いたのかは忘却の彼方…)。そこの Troubleshootingを見ると以下のように書いてあった。
-------------------------------------
1. systemctl --state=failed で失敗した service を調べる
2. systemctl status systemd-modules-load -l のようにして,失敗した service の status を見る
  もし Process ID が表示されていなければ,失敗した service を systemctl コマンド使って再読み込みさせてみる
  If the Process ID is not listed, just restart the failed service with systemctl restart systemd-modules-load
3. Process ID が表示されたら journalctl _PID=110 のようにして,より詳しく見てみる。
4. 設定に不備がある場合は /etc/modules-load.d/ の下を見てみる。
-------------------------------------

 そこで,journalctl _PID=110 を実行してみると,以下の様なものが出てきた。
-------------------------------------
Mar 02 05:14:07 ultedison systemd-modules-load[110]: Inserted module 'bcm4334x'
Mar 02 05:14:07 ultedison systemd-modules-load[110]: ctx=0xb82b9008 path=/lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/libcomposite.ko error=No such file or directory
Mar 02 05:14:07 ultedison systemd-modules-load[110]: ctx=0xb82b9008 path=/lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/libcomposite.ko error=No such file or directory
Mar 02 05:14:07 ultedison systemd-modules-load[110]: Failed to insert 'g_multi': No such file or directory
-------------------------------------

 これを見ると,g_multi の読み込み時に libcomposite.ko がない,と言っているみたいだった。 そこで,ほんとにないかどうかリストコマンドで見てみた。 すると,/lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/ というパス自体がなかった。 しかし,/lib/modules/3.10.17-yocto-standard/kernel/drivers/usb/gadget/ というパスは存在し,その中に
   usb_f_acm.ko
   u_serial.ko
   libcomposite.ko
   g_multi.ko
という4つのファイルがあった。

 systemctl status systemd-modules-load.service -l で得られたメッセージに出てきた bcm_bt_lpm.ko も /lib/modules/3.10.17-poky-edison+/kernel/drivers/ の下にはなくて,/lib/modules/3.10.17-yocto-standard/kernel/drivers/misc/bcm-lpm/ にあった。 どうやら,消されてしまった kernel-module ファイルを読み込もうとして,エラーになっているみたいだった。

 しかし,なぜ 3.10.17-poky-edison+ の下のファイルは消されたのだろう?,なぜ消されたファイルを読み込もうとしているのだろう?と考えた。 その原因を調べるために,再び Firmware update から始め,もう一度 opkg upgrade をしてみた。 すると,どうやら opkg upgrade は新しいエントリーをインストールする前に,古いファイルを消しているようだった。 さらに,最後の方で,
-------------------------------------
Configuring nodejs.
Configuring nodejs-dev.
Configuring upm.
Collected errors:
* copy_file_chunk: write: No space left on device.
-------------------------------------
と出てきた。 この最後の2行がどうも原因みたいだった(ホントは最後の行だけが問題)。 「デバイスにスペースがない」と言われている。 この時点ではなんのことだか不明だった。

 とりあえず,このエラーに陥った際のリカバリー方法を見つけることにした。 それを知らないと再び「やってしまった」時に困ると思ったからである。

 まず,上記にもあるように,firmware update 用のファイルが Edison 内に残っていれば,
   # reboot ota
として,firmware update からやり直せば,Edison は partition size まで含めて元に戻せる。

 次に,firmware update のファイルがない場合にどうすればいいのかを試してみた。 そこで最初に取った作戦は,消されてしまった以前のバージョンの kernel module を復活させることだった。 まずは,Firmware update に戻り,opkg upgrade する前の状態に戻した。 そこで,/lib/module/ 以下にある kernel modules をすべて /home/root/backup/ 以下にコピーしておいた。 (ディレクトリは適当に作った) それから opkg upgrade をし,エラーが発生する状態にした。 その後に,手動で /lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget//lib/modules/3.10.17-poky-edison+/kernel/drivers/misc/bcm-lpm/ を作り,必要な kernel module を手コピーしてみた。必要だったファイルは以下の5個:
    /lib/modules/3.10.17-poky-edison+/kernel/drivers/misc/bcm-lpm/bcm_bt_lpm.ko
    /lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/usb_f_acm.ko
    /lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/u_serial.ko
    /lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/libcomposite.ko
    /lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/g_multi.ko
 これらの旧ファイルのコピー後に再起動すると,なんと,うまく USB OTG が復活して,windows が新しいストレージが接続されました,と言ってきた。 これでちょっと一安心。

 それにしても,何が悪くてこんな事になるんやろ?と悩んでしまった。

 そこで,opkg update の後に,opkg upgrade をせずに,opkg list-upgradable としてみた。 これによって,更新対象のアプリケーションのリストが得られる。 そして,そのリストにあるソフトウェアを順に,opkg upgrade XXX として個別にアップグレードしてみた。

 すると,どうやら kernel module 関連のもののうちのどれか一個をアップグレードしようとすると,
-------------------------------------
* copy_file_chunk: write: No space left on device.
-------------------------------------
というエラーが出ることがわかった。 それはすなわち,kernel 本体をアップグレードしようとした時にエラーが出る,ということを示していた。 (kernel module の更新をしようとすると依存関係のために,まずは kernel を更新しようとする)。

 そこで,ネットでいろいろ検索していると,インテルの Edison や Galileo のユーザーコミュニティサイトで /boot full when opkg upgrade というのに辿り着いた。 (他にも似たようなエントリーがたくさんあって迷ってしまったが…) そこには,そもそも Edison 用の opkg リポジトリを作ってくれた AlexT_Intel というハンドルネームをもつ人がコメントしていた。

 AlexT_Intel 氏によると,/boot partition が小さいため,AlexT_Intel 氏の作った repo に対して,不用意に opkg upgrade しない方がいい,ということだった。 どうやらこのことが根本的な原因みたいだった。 欲しいアプリケーションをインストールするためだけに repo を使うのがいいというのが彼の意見だった。 しかし,/boot partition を増やして kernel を更新するやり方へのリンクも載せてあった。 そこでAlexT_Intel 氏のやり方で /boot パーティションのサイズを大きくしてから kernel の更新作業をしてみると,新しい kernel をインストールすることができた。これでいいような気がする。 しかし,面倒だと思う人は,AlexT_Intel 氏の作った repo のうち,kernel 関連がリストされている src/gz edison http://repo.opkg.net/edison/repo/edison をリストからはずせば,実用上は問題はないと思われる。 あるいは,opkg list-upgradable で出てくるアプリケーションに対して,個別に opkg upgrade XXX を行うというのもひとつの手だと思う。
 ちなみに repo 経由で kernel のインストールをすると,uname -a の結果は
     Linux ultamedi2 3.10.17-yocto-standard #6 SMP PREEMPT Sun Mar 1 18:33:18 CET 2015 i686 GNU/Linux
となった (2015/3/1 現在)。

エラーから回復する方法として,現時点では以下の方法が考えられる。
 1. update 用の firmware のファイル(edison-image-ww05-15 等)が Edison に残っていれば,
     # reboot ota
  で firmware のアップデートからやり直す。

 2. update 用の firmware のファイルがない場合は,
  opkg upgrade 前のバージョン用の bcm_bt_lpm.ko,usb_f_acm.ko,u_serial.ko,libcomposite.ko,g_multi.ko を
  以下の場所にコピーしてから再起動し,firmware のアップデートからやり直す。
     ・bcm_bt_lpm.ko:/lib/modules/3.10.17-poky-edison+/kernel/drivers/misc/bcm-lpm/ の下(ディレクトリは必要なら作る)
     ・他の4つ:/lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/ の下(ディレクトリは必要なら作る)
  (この作業により,USB OTG が働くので firmware アップデートが可能になる)

 3. update 用の firmware のファイルもないし,opkg upgrade 前のバージョンの kernel modules のファイルがない場合
  どこかから opkg upgrade 前のバージョンの kernel modules のファイルを持ってきて,上記 2.,上記 1. の順に作業する。

 しかし,3. の方法はどこかに opkg upgrade 前の kernel modules のファイルがないといけない。 私の場合は複数の Edison があるが,1個を除いて firmware update のファイルを残していたので苦労せずに済んだ。 最後の1個は firmware update のファイルを消してしまっていたが, 他の Edison で opkg upgrade 前の kernel modules のファイルをコピーして使うことができた。 もし opkg upgrade 実行の前に firmware update のファイルを消してしまい,opkg upgrade 前の kernel modules のファイルもない場合はどうすればいいんだろう?

 そこで,3. の状態で,kernel module をコピーしないで opkg upgrade 後の新しいバージョンの kernel modules のシンボリックリンクを使ってみた。 しかし,この作戦はうまくはいかなかった。kernel と kernel module のバージョンが違うから当然と言えば当然の結果だと思う。

 次に,3. の状態で AlexT_Intel 氏の方法で /boot パーティションのサイズを変更して,強引に kernel の update をし直す作戦もやってみた。 しかし,この方法もうまくいかなかった。 opkg upgrade しても「既にすべてのエントリーは最新です」みたいなメッセージがでるし,強引に reinstall させても,ちゃんとインストールできなかった。 そのため再起動しても起動できずに,何度も勝手に再起動を繰り返したりしていた。 やはり 3. の方法を行うために kernel module のバックアップが必要なのかもしれない。 (誰かいい方法を教えて下さい〜〜)

 ということで,以下が今回の結論である。
-----------------------------------
原因は /boot/ partition のサイズが小さいため,opkg upgrade で kernel のアップグレードの際にエラーを起こしていた。 そのため,一部の kernel module がうまくインストールされずにトラブルになっていた。 特に USB gadget あたりに不都合が生じていたため,UBS OTG が不調になっていた。

これに対する対処法としては,下記のいずれとかなる。
 1. repo を使った upgrade を一斉行わない。repo は後からインストールしたいソフトのためのみに使うか,upgrade は個別に行う。
 2. repo として,src/gz edison http://repo.opkg.net/edison/repo/edison は使わない(kernel の情報が含まれる)
 3. /boot/ partition サイズを変更して,kernel もアップグレードしてしまう(多少作業が必要)

Intel Edison の無線 LAN の IP アドレスを固定したいに続く)

2 件のコメント:

Rikuo Hasegawa さんのコメント...

今現在この状態です・・・upgrade前のものをとってくる部分を詳細に説明していただけると助かります・・・昔の /lib/modules/3.10..yocto-.../kernel/devices/usb/gadgets/* などから pokyへcpしてみましたがなかなか上手くいきません

まつぴ さんのコメント...

Hasegawa 様

 えーっと,上に書いたことぐらいしかないのですが…。
Hasegawa さんの Edison の場合がどういう症状なのかわからないのであまり詳しいことは言えませんが,
私の場合は,本文にもあるように,
----------------------------
 2. update 用の firmware のファイルがない場合は,
  opkg upgrade 前のバージョン用の bcm_bt_lpm.ko,usb_f_acm.ko,u_serial.ko,libcomposite.ko,g_multi.ko を
  以下の場所にコピーしてから再起動し,firmware のアップデートからやり直す。
     ・bcm_bt_lpm.ko:/lib/modules/3.10.17-poky-edison+/kernel/drivers/misc/bcm-lpm/ の下(ディレクトリは必要なら作る)
     ・他の4つ:/lib/modules/3.10.17-poky-edison+/kernel/drivers/usb/gadget/ の下(ディレクトリは必要なら作る)
  (この作業により,USB OTG が働くので firmware アップデートが可能になる)
----------------------------
でうまくいきました。
ポイントは /lib/modules/3.10.17-poky-editon+/ を作って,その下に入れないといけません。現存の/lib/modules/3.10.17-yocto-standard/ の下だとうまくいきません。
いまのところ,それぐらいしか思いつきません…。