WD_SOFTBADSECTオプション

不幸にして職場で作業に使用しているPCのハードディスクが不調となったのですが、NetBSDのATAなハードディスクのドライバーwd(4)のWD_SOFTBADSECTオプションが役立ったというか、動作を検証する経験をしたので書いてみます。

問題のハードディスクは不良セクターが40くらい発生していて、負荷を掛けて多少増えたかもしれない状況です。

WD_SOFTBADSECTの詳細

あまり知られていませんが、NetBSDのwd(4)にはWD_SOFTBADSECTというオプションがあって、発生した不良セクターをソフトウェア、つまりドライバー側で対処する機能があります。

同様な試みには bad144(8) というコマンドが古くからあって、DEC standard 144 bad sector という今時は誰も知らない(私も名前の由来は知らない)方法でで不良セクターをソフトウェア的に管理します。ところが、これはハードディスクの伝統的なシリンダー、ヘッド、(トラックあたりの)セクター数といった配置を前提としています。いわゆるCHSによるセクターのアドレッシングですが、bad144(8)はこれに依存していて論理セクターアドレスでないと足りない今時のハードディスクでは事実上使用できません。

bad144(8)は所定の形式で不良セクターの情報を物理的にハードディスクに記録しますが、対照的にWD_SOFTBADSECTオプションは名前のようにソフトウェア的に処理を行います。つまり、ドライバー内で不良セクターのリストを用意して、エラーが起きたセクターを記録して、さらなるアクセスを避けるといった目的に使用しています。(と、思います。)

dkctl(8)

発生した不良セクターのリストは、dkctl(8)というシステムコマンドで状況を確認できます。dkctl(8)はディスクのパーティションのドライバーdk(4)を制御するシステムコマンドですが、この他にハードディスクの内蔵キャッシュの制御なども行います。

WD_SOFTBADSECTオプションをカーネルで有効にしていると、不良セクターのリストの管理も可能です。サブコマンドのbadsectorでflush, list, retryを指定できます。

不良セクターの確認

今回、実際に不良セクターが発生した後で、その様子をbadsector listで確認すると次のようになります。

# dkctl wd1 badsector list
/dev/rwd1d: blocks 1903708656 - 1903708783 failed at Fri Mar 16 12:22:55 2012
/dev/rwd1d: blocks 1903707504 - 1903707631 failed at Fri Mar 16 12:22:33 2012
/dev/rwd1d: blocks 1903707627 - 1903707627 failed at Fri Mar 16 12:00:55 2012
/dev/rwd1d: blocks 1903707626 - 1903707626 failed at Fri Mar 16 12:00:36 2012
/dev/rwd1d: blocks 1903707625 - 1903707625 failed at Fri Mar 16 12:00:17 2012
/dev/rwd1d: blocks 1903708688 - 1903708688 failed at Fri Mar 16 11:59:59 2012

不良セクターの再確認

不良セクターのリストに含まれる内容をbadsector retryで再度確認してみます。

# dkctl wd1 badsector retry 
/dev/rwd1d: bad sector clusters 6 total sectors 260
/dev/rwd1d: bad sectors flushed
/dev/rwd1d: Retrying 1903708688 - 1903708688
/dev/rwd1d: block 1903708688 - failed
/dev/rwd1d: Retrying 1903707625 - 1903707625
/dev/rwd1d: block 1903707625 - failed
/dev/rwd1d: Retrying 1903707626 - 1903707626
/dev/rwd1d: block 1903707626 - failed
/dev/rwd1d: Retrying 1903707627 - 1903707627
/dev/rwd1d: block 1903707627 - failed
/dev/rwd1d: Retrying 1903707504 - 1903707631
/dev/rwd1d: block 1903707504 - ok
/dev/rwd1d: block 1903707505 - ok
... (中略) ...
/dev/rwd1d: block 1903708779 - ok
/dev/rwd1d: block 1903708780 - ok
/dev/rwd1d: block 1903708781 - ok
/dev/rwd1d: block 1903708782 - ok
/dev/rwd1d: block 1903708783 - ok
#

実際には読めたセクターがリストから除かれていますので、もう一度確認すると確かに減っていることがわかります。

# dkctl wd1 badsector list 
/dev/rwd1d: blocks 1903707627 - 1903707627 failed at Fri Mar 16 12:40:00 2012
/dev/rwd1d: blocks 1903707626 - 1903707626 failed at Fri Mar 16 12:39:41 2012
/dev/rwd1d: blocks 1903707625 - 1903707625 failed at Fri Mar 16 12:39:22 2012
/dev/rwd1d: blocks 1903708688 - 1903708688 failed at Fri Mar 16 12:39:03 2012

姑息な秘密兵器

ここで標準のdkctl(8)にはない秘密兵器を使ってみます。といってもエラーが起きたセクターに対して書き込みを行った上で、badsector retryを再度行う姑息(だけど役立つかもしれない)な技です。

# dkctl wd1 badsector rewrite
/dev/rwd1d: bad sector clusters 4 total sectors 4
/dev/rwd1d: bad sectors flushed
/dev/rwd1d: Retrying 1903708688 - 1903708688
/dev/rwd1d: block 1903708688 - ok
/dev/rwd1d: Retrying 1903707625 - 1903707625
/dev/rwd1d: block 1903707625 - ok
/dev/rwd1d: Retrying 1903707626 - 1903707626
/dev/rwd1d: block 1903707626 - ok
/dev/rwd1d: Retrying 1903707627 - 1903707627
/dev/rwd1d: block 1903707627 - ok
# dkctl wd1 badsector list
#

不良セクターに対して書き込みを行うと、ATAなハードディスクは代替セクターの割り当てを行う機能があります。ここでは、それが働いて見かけ上不良セクターがなくなったように見えます。

もっとも、今回のハードディスクは別途atactl(8)のsmart statusで確認すると、S.M.A.R.T.の代替セクターの数が増えていません。これは、やはり何か問題なので早々に交換しなければならないでしょう。

dkctl rewriteの修正

dkctl rewriteの修正は長らく手元に置いていたものでsend-prしたようなぁ、と思っていたらなかったかもしれません。