電源連動 USB HDD の切断・再接続を CLI で

さいきんの外付けハードディスクは電源連動機能とかついてたりしますね。んで,たとえば REGZA とかにつなぐと,テレビの電源を on にしたときだけハードディスクの電源が on になったりします。

同じようなことを Linux からもやってみたい。

といっても PC の電源を切ったらハードディスクの電源が切れるのは当たり前。PC の電源を入れっぱなし HDD 接続しっぱなしで,ソフトウェア的に HDD の電源を入れたり切ったりできたらいいなぁと思いやってみました。

もしうまくできれば,たとえば定時バックアップの際だけハードディスクの電源を入れて,バックアップが終わったら電源を切るとかできそう。かえって寿命が落ちるかもしんないけど。

調べてみたら,またたびりなっくす UbuntuでのUSBメディアの安全な取り外し とかその元ネタの Yan Li's Words: Safely remove an USB hard drive in Linux とかですでにやられてました。再接続までコミでやってるのはなかったので一応記事におこしておきます。


実験環境は下記の通り。

切断編

前提として,アンマウント済みになってるとする。

1. 下調べ

いくつか知っておく必要のある ID がある。

udevadm というコマンドを使うと,デバイスノードパスからこれらの情報を取得することができる。今回の例だと HDD が /dev/sdc にぶらさがっているとする。

$ sudo udevadm info --query=path --name=/dev/sdc

/devices/pci0000:00/0000:00:1d.7/usb1/1-8/1-8:1.0/host3/target3:0:0/3:0:0:0/block/sdc

出力結果のうち

  • 1-8 ってところが,USB のバス ID = 1, デバイス ID = 8 であることを示している
  • 3:0:0:0 ってところが SCSI のホスト ID = 3, チャネル = 0, デバイス ID = 0, LUN = 0 であることを示している


ちなみに,SCSIバイスとしての各種 ID を知りたいだけなら /proc/scsi/scsi の中身を見てもわかる。

$ cat /proc/scsi/scsi

Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
  Vendor: ATA      Model: WDC WD1600BEVT-7 Rev: 11.0
  Type:   Direct-Access                    ANSI  SCSI revision: 05
Host: scsi2 Channel: 00 Id: 00 Lun: 00
  Vendor: Generic- Model: Multi-Card       Rev: 1.00
  Type:   Direct-Access                    ANSI  SCSI revision: 00
Host: scsi3 Channel: 00 Id: 00 Lun: 00
  Vendor: I-O DATA Model: HDCR-U           Rev:     
  Type:   Direct-Access                    ANSI  SCSI revision: 02
2. HDD へのフラッシュ(強制書込)

OS 側がアンマウントしていても,HDD 内部のキャッシュ内容がフラッシュされていない可能性がある(らしい)ので強制的にフラッシュする。非標準コマンドの sdparm というコマンドを使うので apt-get などでインストールしておくこと。

$ sudo sdparm --command=sync /dev/sdc

    /dev/sdc: I-O DATA  HDCR-U            

んー。非同期で実行されている気もする。フラッシュが終了してるという確証はどうやったら得られるのだろう。

ちなみにたいていのディストリビューションで標準的にインストールされてる hdparm-f コマンドも同じようなことができそうだけど,man 読んだ感じだと,あれは OS 側のバッファキャッシュをフラッシュするためのコマンドのような気がする。


さらに,同じコマンドを使ってデバイス(HDD)のスピンダウンを行うこともできるらしい。

$ sudo sdparm --command=stop /dev/sdc

だけど,手持ちの IO-DATA の HDCR-U だと,一度スピンダウンするもののそのあと再びすぐにスピンアップしてしまい意味がなかった。逆に壊れるんじゃないかと怖くなった。

3. SCSI サブシステムから切り離し

んで,いままでのコマンドはデバイスノードパス対象のコマンドだったんだけど,もう必要ない,というかデバイスノード経由でアクセスできると危ないので,切り離す。

切り離す前。

$ ls /dev/sd*

/dev/sda  /dev/sda1  /dev/sda2  /dev/sda3  /dev/sdb  /dev/sdc  /dev/sdc1

いままでの実行例でわかるとおり,USB HDD のデバイス名は /dev/sdc*

さきほど調べた SCSIバイスとしてのホスト ID 等をパラメータとして指定しつつ /proc ファイルシステムに下記のようなテキストを書き込む。

$ echo 'scsi remove-single-device 3 0 0 0' | sudo tee /proc/scsi/scsi

あ,tee コマンドを使っているのは,今回の環境例が Ubuntu であり root 権限でコマンドを実行するには sudo 使うから。簡単に言うと,root 権限で

# echo 'scsi remove-single-device 3 0 0 0' > /proc/scsi/scsi

というのを実行しているのとほぼ同義。これをそのまま sudo でラップしてもリダイレクトしているのは一般ユーザ権限になってしまいうまくいかないので,tee コマンドを利用して root 権限でパイプ書込をしている。


ともかく,このようにすると,

$ ls /dev/sd*

/dev/sda  /dev/sda1  /dev/sda2  /dev/sda3  /dev/sdb

見事 /dev/sdc* のデバイスノードが消えた。

4. USB パワーを落とす

いよいよ USB デバイスのパワーを落とす。

$ echo 'suspend' | sudo tee /sys/bus/usb/devices/1-8/power/level

これを実行したしばらくのち((IO-DATA の USB HDD の場合,だからかと思ったんだけど,ひょっとすると /sys/bus/usb/devices/*/power/autosuspend のデフォルト値 2 が影響したのかもしれない。ちょっと未検証でわからん。see Manual driver binding and unbinding [LWN.net]))に実際にパワーが切れた。感動。

一番最初にあげた文献の例だとこれを行うまえに次で例示してある「USB サブシステムからの切り離し」をおこなっているんだけど,実際にはこの順序にしないと,後述する再接続ができなかった。


あと,この suspend というコマンドトークンで USB 電源を切るのは,カーネルバージョン 2.6.32(つまり Ubuntu Lucid のカーネルバージョンだ)以下でしか使えないらしい。

2.6.33 以上だと /sys/bus/usb/devices/*/remove というノードが新設されたのかな*1。ちょっとよくわからんです。

5. USB サブシステムから切り離し

OS の USB サブシステムから切り離す(ってこういう表現で合っているかもわかんないけど)。

$ echo -n '1-8' | sudo tee /sys/bus/usb/drivers/usb/unbind

再接続編

以上のようにして切断した USB HDD をつないだまま再接続(パワー on)するには,いままでの過程の逆をおこなえばよい。

まず USB サブシステムへの再接続(再登録)を行う。

$ echo -n '1-8' | sudo tee /sys/bus/usb/drivers/usb/bind

このとき以下のようなカーネルメッセージを吐くことがある。

usb 1-8: configuration #1 chosen from 1 choice
usb 1-8: can't set config #1, error -1

対処方法がわかんないので無視する。IO-DATA の USB HDD だから出たってこともないみたい。たしか USB メモリスティックでも出力された気がする。

次に USB パワーを上げる。

$ echo 'on' | sudo tee /sys/bus/usb/devices/1-8/power/level

なおいままでのシーケンスの順序(USB サブシステムからの切断とパワーオフの順序とか)がよくないと下記のようなカーネルメッセージを吐いてうまくいかないことがある。

/sys/bus/usb/devices/1-8/power/level: Transport endpoint is not connected

そんなときは物理的に USB から外して再接続してやり直してみること。


SCSI サブシステムへ再接続する。

$ echo 'scsi add-single-device 3 0 0 0' | sudo tee /proc/scsi/scsi

すると(通常 HDD を USB で接続したときのように)下記のようなカーネルメッセージが表示される。

scsi 3:0:0:0: Direct-Access     I-O DATA HDCR-U                PQ: 0 ANSI: 2 CCS
sd 3:0:0:0: Attached scsi generic sg2 type 0
sd 3:0:0:0: [sdc] 1953525168 512-byte logical blocks: (1.00 TB/931 GiB)
sd 3:0:0:0: [sdc] Write Protect is off
sd 3:0:0:0: [sdc] Mode Sense: 3c 00 00 00
sd 3:0:0:0: [sdc] Assuming drive cache: write through
sd 3:0:0:0: [sdc] Assuming drive cache: write through
 sdc: sdc1
sd 3:0:0:0: [sdc] Assuming drive cache: write through
sd 3:0:0:0: [sdc] Attached SCSI disk

で,実際にデバイスノードも生成される。

$ ls /dev/sd*
/dev/sda  /dev/sda1  /dev/sda2  /dev/sda3  /dev/sdb  /dev/sdc  /dev/sdc1

あとはマウントするなりなんなり。めでたしめでたし。

課題

再接続編を見ればわかるとおり,再接続時においてもともと接続されていたときの SCSI サブシステム上のバス ID や USB サブシステム上のバス ID とかがわかっていることが前提となっている。これらをなんとか sysfs や procfs 経由で取得できればなぁと思う。


今回使った USB HDD。ファンレスなのはうれしいけど,電源投入後スピンアップするまでがちょっと遅いのとうるさい(ゴゴゴゴっていう)気がする。採用ドライブの個性で個体差ありそうだけど。

本文と関係ないけど,センチュリーの裸族のお立ち台って電源連動機能なかったなぁ(実際にはクーリングファンつきの eSATA プラスには電源連動機能がついてる),電源連動機能つきのああいうのがあれば REGZA 用に使いたいなぁ,と思ってたら,玄人志向のやつは電源連動があるっぽい。レビューがほとんどないのが不安。あと,HDD にアダプタつける形になるのかな?よくわからん。