HDD のバッドセクタ異常を解消させる方法(smartctl/badblocks/e2fsck/hdparm)

先日、家庭内サーバーとして使っていたPCが壊れたため、パーツ買い替え等をしていたのですが、接続していたHDDに不良セクタが出ていたため、修復を行っていました。

本記事は、その修復の手順のメモです。修復の流れは以下の通りです

以下、それぞれの詳細です。

smartctl で状況確認

私は普段は munin で smart を監視させているのですが、そこで異常が検知されました。

まずは何が起こっているかを、smartctl で全情報を表示して確認。

$ sudo smartctl -a /dev/sda; echo "exit code: $?"

主なエラー要因は exit code で表現されています。詳しくは、manpage に書かれていますが、exit code はビットフラグになっていて、64 ならエラーログ有り、128 ならセルフテストでのエラー検知を表しています。(両方なら 192になります。)

exit code が 64 (のビットを含んでいる)なら、以下のようなログが表示されているかと思います。下記の例は 34869129 番セクターの READ でエラーが起きているようです。

$ sudo smartctl -l error /dev/sda
smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.15.0-175-generic] (local build)
Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
SMART Error Log Version: 1
Warning: ATA error count 863 inconsistent with error log pointer 5

ATA Error Count: 863 (device log contains only the most recent five errors)
    CR = Command Register [HEX]
    FR = Features Register [HEX]
    SC = Sector Count Register [HEX]
    SN = Sector Number Register [HEX]
    CL = Cylinder Low Register [HEX]
    CH = Cylinder High Register [HEX]
    DH = Device/Head Register [HEX]
    DC = Device Command Register [HEX]
    ER = Error register [HEX]
    ST = Status register [HEX]
Powered_Up_Time is measured from power on, and printed as
DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,
SS=sec, and sss=millisec. It "wraps" after 49.710 days.

Error 863 occurred at disk power-on lifetime: 32098 hours (1337 days + 10 hours)
  When the command that caused the error occurred, the device was active or idle.

  After command completion occurred, registers were:
  ER ST SC SN CL CH DH
  -- -- -- -- -- -- --
  40 51 01 89 0f 14 e2  Error: UNC at LBA = 0x02140f89 = 34869129

  Commands leading to the command that caused the error were:
  CR FR SC SN CL CH DH DC   Powered_Up_Time  Command/Feature_Name
  -- -- -- -- -- -- -- --  ----------------  --------------------
  20 00 01 89 0f 14 e2 08      06:31:15.851  READ SECTOR(S)
  b0 d5 01 09 4f c2 00 08      06:30:46.223  SMART READ LOG
  b0 d5 01 06 4f c2 00 08      06:30:46.222  SMART READ LOG
  b0 d5 01 01 4f c2 00 08      06:30:46.221  SMART READ LOG
  b0 d5 01 00 4f c2 00 08      06:30:46.221  SMART READ LOG
(以下略)

smartctl でセルフテストを実行

smartに対応した多くの HDD は、通常使用中でも実行可能なセルフテスト機能を持っていたりします。このセルフテストは

$ sudo smartctl -t short /dev/sda

で実行を指示することができます。セルフテストは short の他に long なども指定可能です。 longのほうがより多くのエラーを検知できますが、実行にかかる時間が長くなります。(手元の個体だと short で 2 分、long で 4時間ほどでした。サポートしているテストの種類や実行にかかる予定時間は -a で表示される属性に含まれています。)

上記のコマンドでセルフテストの実行を初めたら、その時に表示された終了予定時刻になるぐらいまで待ったうえで、

$ sudo smartctl -l selftest /dev/sda

とすると、実行結果を見ることができます。

例えば、以下のような出力だった場合、「(先頭からテストして) 34869129 番のセクタで読み込み不可のセクタが見つかった」という意味になります。

SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Short offline       Completed: read failure       90%     32095         34869129

破損したセクタ上にあったファイルの修復と、ファイルシステムの不整合の修復

参考ページには、セクタ番号をもとに、そこに書かれていたのがどのファイルかを特定する方法が書かれていますので、必要であればそちらをご参照ください。

今回私はファイルシステム上の修復だけで良かったので、fsck と badblocks だけで手抜きをしました。

badblocks を使う場合、まずは badblocks を使って不良なブロックを検出したうえで、e2fsck で書き込む、という2段階の手順を紹介されていることが多いようですが、e2fsck の -c オプションを使えば、裏で badblocks を自動で使って一発でやってくれるので楽です。-cで読み込みのみのテスト、-cc とすると非破壊の読み書きテストを行い、不良ブロックを検出することができます。

$ sudo e2fsck -vcck /dev/sda1

読み書きテストを行うとかなり時間がかかるので、-yオプション(「フリーブロック数が間違っているようなので修復するか?」などの質問に自動で yes と答える)をつけるかどうかは好みでどうぞ。

不良なセクタを HDD ドライブレベルで使用不可にする

ファイルシステム側で不良セクタを避けて使ってくれていれば、基本的には問題は無いはずですが、HDD側でも不良セクタは修復しておいたほうが無難なので、そちらも対応しました。

エラーログやセルフテストの結果から、不良セクタの場所はわかっています。(下記は、いろいろいじったあとなのでテスト回数が2回増えてますが気にしないでください。)

SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed: read failure       90%     32098         34869129
# 2  Short offline       Completed without error       00%     32097         -
# 3  Short offline       Completed: read failure       90%     32095         34869129

まず、確認のため、該当セクタを直接読んでみます。

$ sudo hdparm --read-sector 34869129 /dev/sda

/dev/sda:
reading sector 34869129: SG_IO: bad/missing sense data, sb[]:  70 00 03 00 00 00 00 0a 40 51 e2 01 11 04 00 00 00 89 00 00 00 00 00 00 00 00 00 00 00 00 00 00
succeeded
0000 0000 0000 0000 0000 0000 0000 0000
(以下略)

たしかにエラーが出ているようです。(succeeded と出ているのがミスリーディングではありますが。)

そこで、hdparmでここに書き込みを行います。なお、これは危険な操作で、もしこのセクタをまだファイルシステムが使用していた場合、ファイルシステムが破壊されるかもしれません。(以下のコマンドライン例からは、最終確認のためのオプションを削っています。実行したときに出る説明をよく読んでからオプションを追加して再実行してください。)

$ sudo hdparm --write-sector 34869129 /dev/sda

/dev/sda:
re-writing sector 34869129: succeeded

この書き込みによって、この不良セクタに予備のセクタが割り当て直されることになります。うまく修復されていれば、このセクタを再度読み込みしてもエラーがでなくなります。

$ sudo hdparm --read-sector 34869129 /dev/sda

/dev/sda:
reading sector 34869129: succeeded
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
(以下略)

ここで、再度セルフテストして、他のセクタに異常が無いかを確認します。

$ sudo smartctl -t short /dev/sda
(略)
(数分待ってから)
$ sudo smartctl -l selftest /dev/sda
smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.15.0-175-generic] (local build)
Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Short offline       Completed without error       00%     32098         -
# 2  Extended offline    Completed: read failure       90%     32098         34869129
# 3  Short offline       Completed without error       00%     32097         -
# 4  Short offline       Completed: read failure       90%     32095         34869129

$ sudo smartctl -t long /dev/sda
(略)
(数時間待ってから)
$ sudo smartctl -l selftest /dev/sda
smartctl 6.6 2016-05-31 r4324 [x86_64-linux-4.15.0-175-generic] (local build)
Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF READ SMART DATA SECTION ===
SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed without error       00%     32102         -
# 2  Short offline       Completed without error       00%     32098         -
# 3  Extended offline    Completed: read failure       90%     32098         34869129
# 4  Short offline       Completed without error       00%     32097         -
# 5  Short offline       Completed: read failure       90%     32095         34869129
2 of 2 failed self-tests are outdated by newer successful extended offline self-test # 1

今回の場合、short, long ともにエラー無しになりました。もし他のセクタでエラーが出た場合は hdparm による書き込みで一つ一つ潰していくことになります。

badblocks 指定の解除

HDD 側で不良セクタの改修ができたなら、ファイルシステム側で避ける必要は無いので、badblocks 指定は解除しても問題無いと思います。-kオプション無しで再度 e2fsck -cc してやれば、不良ブロックが無いことを確認したうえで指定解除ができます。

$ sudo e2fsck -vcc /dev/sda1

解除するぐらいなら初めから設定しなければ良いのでは、という向きもあるかもしれませんが、少なくともファイルシステム上の修復は必要なので、少なくとも1度は fsck をかけるべきでしょうし、一度避けておけば修復も幾分気が楽なのでこの形をとりました。(もちろんフォーマットして使うなら不要です。)

おまけ: munin の smart plugin の警告を消す

HDD のエラーログはずっと残ってしまい、多分消す方法が無いので、一度エラーが発生すると smartctl の exit code には 64 が残ってしまいます。

munin の smart plugin はデフォルトでは exit code が非 0 だと警告を出して鬱陶しいので、以下の内容で /etc/munin/plugin-conf.d/smart_sda を作成しました。

[smart_sda]
env.ignoreexit 64

こうしておけば、このプラグインは smartctl の exit code で返された値から 64 のビットはマスクして扱うようになり、HDD にエラーログが記録されていても警告はでなくなります。セルフテストの失敗など、他のエラーコードは無視したりはしないので安心です。


参考: