2012年10月30日火曜日

壊れたHDDを、Advanced FormatのHDDに交換。

前ブログ、 HDL-GS500が故障の続きです。

壊れたHDL-GS500のHDDを交換するために、新しいHDDを買いました。そのときの店員さんの話では、今販売しているHDDのほとんどがAdvanced Formatということでした。

今後、修理や交換のために入手できるHDDは、Advanced Formatの選択肢しか残らないようですね。

今回買ったのは、HITACHI HDS721010DLE630 1.0TB Advanced Formatです。


Advanced Format のHDDとは

近年に登場した4096バイト・セクターのHDDには、内部のファームウェアで、4096バイトの物理セクターを、512バイト単位の8個の論理セクターに分割するしくみ (512バイト・セクター・エミュレーション)をもったものがあります。これらのHDDは4kセクターHDDや、Advanced FormatのHDDと呼ばれています。

セクターサイズを512バイトから、4096バイトへ拡大する理由や課題について、以下に簡単に纏めてみました。詳細については後述する参考Webサイトを参照ください。

  • HDDの大容量化に伴って、512バイト・セクターの抱える課題が顕著になってきた。
    • ディスクの総容量に対するセクター・サイズとの比率が大きくなることで、エラー修正が煩雑になり効率が低下してしまう。
    • 面密度の増加に伴って、1セクターあたりの表面積が縮小していくことになり、メディア欠損時のエラー修正がより困難になってきた。
    • セクター・サイズの拡大による利点として、エラー修正機能の改善、フォーマット効率の向上が挙げられている。
  • 512バイト・セクター・エミュレーション。
    • 従来の512バイト・セクターを対象にした機器との互換を保ちながら、4096バイト・セクターへの移行をスムーズに進めるためのしくみ。
    • HDD内部のファームウェアにより、4096バイトの物理セクターを、512バイト単位の8個の論理セクターに分割してデータ処理を行う。
    • このしくみを使うことでPCから見たHDDは、今まで同様に512バイト・セクターのHDDように振る舞う。
  • ユーザー側で回避すべき重要な課題
    • 512バイト・セクター・エミュレーションが、パフォーマンスに与える影響を最小限にするため、HDDへの書き込み時の、読み出し・修正・書き込みプロセスの発生頻度を最小限に抑えることが重要。
    • ファイルシステムのデータ構造と、パーティションのアライメント・マッチングが必須。
    • 適切なブロックサイズの選択が重要。
  • 適切なパーティショニング・ツールの選択。
    • 各パーティションの開始位置を、アライメント0の状態に設定できるツールを使用する。
    • アライメント0の位置とは、セクター位置が8の倍数になる位置のことを言う。
      (4096 バイト/セクター = 512 バイト/セクター ×
    • 従来のパーティショニング・ツールでは、シリンダ境界をパーティション境界に設定するようになっているので、そのまま使用するとアライメントがズレてしまうことになる。
      この場合、デフォルトの操作単位を、シリンダーからセクターに変更する機能があれば、アライメント0のパーティションを作成できる場合がある。
    • HDDの開始セクターの位置は、クロスプラットフォームの観点から見た場合は2048が無難。

Linuxでの、Advanced Formatの使用については、以下のWebサイトが参考になりました。

IBM developerWorks 4KB セクター・ディスクで Linux を使用する: 実用的なアドバイス
Seagate アドバンスド・フォーマット4Kセクター・ハードディスク・ドライブへの移行


HDL-GS500修復用HDDの、パーティショニングの考察

今回の修復作業におけるLinux使用時の注意点は、以下の内容が考えられます。

  • HDDの開始セクター位置を 2048 に設定する。
  • 各パーティションの開始位置を、アライメント0の状態にする。
  • ただし、sda6のアライメントは調整しない。
  • ファイルシステムのブロックサイズを、4096バイトにする。

このあたりを考慮しながら、進めていくことにしました。

先日確認したときの、壊れたHDDのパーティション構成は以下のようになっていました。

~# fdisk -l /dev/sda

Disk /dev/sda: 500.1 GB, 500107862016 bytes
255 heads, 63 sectors/track, 60801 cylinders, total 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xee4739c6

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1           16065      642599      313267+  83  Linux
/dev/sda2          642600     1461914      409657+  83  Linux
/dev/sda3         1461915     5670944     2104515   82  Linux swap / Solaris
/dev/sda4         5670945   976559219   485444137+   5  Extended
/dev/sda5         5670946     6088634      208844+  83  Linux
/dev/sda6         6088636   976559219   485235292   83  Linux
それぞれのパーティション先頭セクターの値を、8の倍数に調整した結果、各パーティションの開始セクターの位置を以下の値に決めました。
  • sda1の開始セクタ 2048、
  • sda2の開始セクタ 628584、
  • sda3の開始セクタ 1461912、
  • sda5の開始セクタ 5670944、

拡張領域sda4の開始セクターはsda5の開始セクター-1としました。いまのところ、この領域の開始セクター位置も、アライメント0にする必要があるのかは良くわかっていません。入れ子になっている論理領域sda5やsda6の外側の領域なので、あまり影響が無いと考えています。

sda6の開始セクターは、今回のデータの修復のために、パーティション全体をコピーするので、あえて変更しないことにします。ここを変更するとセクター位置がズレてしまい、xfsファイルシステムにアクセスできなくなる心配があります。そのため、この部分のアライメントのズレはやむおえません。 データ修復後に改めて、sda6のアライメント調整をすることにしました。


初期化用シェルスクリプト tar2disk を利用する

先日USBメモリに保存することができた初期化シェルスクリプト、tar2diskの9行目の内容を、こちら のWebサイトの情報を元にして、

sddev=/dev/hdb を sddev=/dev/sda に。
修正してtar2disk実行したところ、この新しいHDDのサイズを正常に1.0GBと認識して、初期化することができました。ただし、この時点では、まだアライメント調整はできていません。

次に、tar2diskで初期化した1.0TB HDDのマスターブートレコード(MBR)と、拡張パーティションブートレコード(Extended Partition Boot Record)を 調べてみました。

オリジナルの、HDDのパーティションと比べたところ、各パーティション開始セクターの位置が、両方とも同じでした。

さらに、初期化用シェルスクリプトtar2diskを調べてみると、各パーティションの先頭位置は、あらかじめ決められた値に固定されており。

cat /sys/block/$disk/size
で、取得したHDDの総セクター数を元にして、拡張領域sda4のサイズと、論理領域sda5,sda6のサイズを計算しているようだ、ということがわかりました。

そこで、このシェルスクリプトを少し修正してみることにしました。修正点はパーティショニングと、ext3のブロックサイズの変更です。

disk2cyl( )を、HDDから取得した総セクター数を元にして、データ保存領域に使用するシリンダ数を計算している部分を、セクター数で返すようにに変更しました。

partition( )は、sfdiskコマンドを使い、一括してパーティショニングを行っています。この部分のシリンダ単位で指定している部分を、セクター単位で指定するように変更しました。

format( )は、 ext3のブロックサイズを4096バイトに修正しました。

現時点では、壊れた500GBのHDD修復が目的なので、sda6の開始セクター位置のアライメントを調整していません。これは、データの修復のためにパーティション全体をコピーする必要があるからです。 また、新しいHDDには1.0TBを使用していますが、修復の都合上HDDサイズを500GBで初期化するようにしています。

シェルスクリプト中の下に示す行は、初期化する環境に合わせて変更が必要です。

sddev=/dev/hdb   # ##### 初期化する環境に合わせて変更してください #####
例えば、初期化するHDDが/dev/sdaなら、sddev=/dev/sdaに変更します。

HDDサイズは500GBに固定、アライメント調整適用済みtar2disk 修復用暫定版、

  1. #!/bin/sh
  2. #
  3. # tar2disk: tar ball をディスクに展開
  4. #
  5. # usage: tar2disk sda
  6. # ##### 2012/10/01 sfdiskをシリンダ指定から、セクタ指定へ変更。#####
  7.  
  8. mnt=./mnt
  9. sddev=/dev/hdb # ##### 初期化する環境に合わせて変更してください #####
  10.  
  11. PATH=$PATH:/sbin
  12.  
  13. #
  14. # 16進->10進変換
  15. #
  16. hex2dec() {
  17. expr 1 + `printf "%d" $1`
  18. }
  19.  
  20. #
  21. # モデルごとの制限セクタ数
  22. #
  23. sector160=`hex2dec 0x12A05FBF`
  24. sector250=`hex2dec 0x1D1A9C7F`
  25. sector300=`hex2dec 0x22ECB57F`
  26. sector320=`hex2dec 0x2540BF7F`
  27. sector400=`hex2dec 0x2E90F73F`
  28. sector500=`hex2dec 0x3A353900`
  29. sector750=`hex2dec 0x574FCD9F`
  30. sector1000=`hex2dec 0x74706DAF`
  31.  
  32. #cyl_end=379 # sda5 の終了セクタ
  33. cyl_end=6088636 # ##### シリンダ値をセクタ値に変更 #####
  34.  
  35.  
  36. #
  37. # ディスク名からモデルで丸められたセクタ数を得る
  38. # ##### 2012/10/01 セクタ数を返すように修正 #####
  39. disk2cyl() {
  40. local disk=$1
  41. #local sector=`cat /sys/block/$disk/size`
  42. local sector=976566529 # ##### 修復のため、HDDサイズを500GBに固定。 #####
  43. if [ $sector -lt $sector160 ]; then
  44. echo -n error
  45. exit 1
  46. elif [ $sector -lt $sector250 ]; then
  47. expr \( \( $sector160 / 255 / 63 \) \* 16065 \) - $cyl_end
  48. elif [ $sector -lt $sector300 ]; then
  49. expr \( \( $sector250 / 255 / 63 \) \* 16065 \) - $cyl_end
  50. elif [ $sector -lt $sector320 ]; then
  51. expr \( \( $sector300 / 255 / 63 \) \* 16065 \) - $cyl_end
  52. elif [ $sector -lt $sector400 ]; then
  53. expr \( \( $sector320 / 255 / 63 \) \* 16065 \) - $cyl_end
  54. elif [ $sector -lt $sector500 ]; then
  55. expr \( \( $sector400 / 255 / 63 \) \* 16065 \) - $cyl_end
  56. elif [ $sector -lt $sector750 ]; then
  57. expr \( \( $sector500 / 255 / 63 \) \* 16065 \) - $cyl_end
  58. elif [ $sector -lt $sector1000 ]; then
  59. expr \( \( $sector750 / 255 / 63 \) \* 16065 \) - $cyl_end
  60. else
  61. expr \( \( $sector1000 / 255 / 63 \) \* 16065 \) - $cyl_end
  62. fi
  63. return 0
  64. }
  65.  
  66.  
  67. #
  68. # パーティションを切る
  69. # ##### 2012/10/01 シリンダ指定からセクタ指定へ変更 sda6の開始セクタは暫定値 #####
  70. partition() {
  71. local disk=$1
  72. local c5=417689 # ##### 26*255*63-1 #####
  73. local cyl=`disk2cyl $disk`
  74. local ext=`expr $cyl + $c5 + 4`
  75.  
  76. dd if=/dev/zero of=/dev/$disk bs=512 count=2 || exit 1
  77. sfdisk -f -u S /dev/$disk <<EOF || exit 1
  78. 2048,626535,L
  79. 628584,833327,L
  80. 1461912,4209030,S
  81. 5670943,$ext,E
  82. 5670944,$c5,L
  83. 6088636,$cyl,L
  84. EOF
  85. }
  86.  
  87.  
  88. #
  89. # format
  90. # ##### 2012/10/01 ブロックサイズを4096バイトに変更。#####
  91. format() {
  92. local i=$1
  93.  
  94. if [ $i -eq 1 ]; then
  95. [ $flag_reset -eq 1 ] && return 0
  96. mke2fs -b 4096 -j -m1 $sddev$i || return 1
  97. elif [ $i -eq 2 ]; then
  98. mke2fs -b 4096 -j -m1 $sddev$i || return 1
  99. elif [ $i -eq 5 ]; then
  100. mke2fs -b 4096 -j -N 100000 \
  101. -O dir_index,filetype,sparse_super $sddev$i || return 1
  102. else
  103. [ ! -f /sbin/mkfs.xfs ] && cp -a ${SCRIPT_PATH}/mkfs.xfs /sbin/
  104. mkfs.xfs -f $sddev$i || return 1
  105. fi
  106. return 0
  107. }
  108.  
  109.  
  110. #
  111. # fw_install
  112. #
  113. fw_install() {
  114. touch verify_on_boot
  115. cp -a ../sd?.tgz ../tar2disk ./.landisk/
  116. mkdir ./.landisk/mnt
  117. }
  118.  
  119.  
  120. #
  121. # 初期化後updateの為にflag fileをtouchする
  122. #
  123. set_reset_flag() {
  124. touch ./.landisk/reset_ok
  125. }
  126.  
  127.  
  128. #
  129. # ディスクを 0 クリアする
  130. #
  131. sanitize_disk() {
  132. local i=$1
  133. echo "--- sanitize disk"
  134. dd if=/dev/zero of=$sddev$i bs=1M || return 1
  135. }
  136.  
  137.  
  138. #
  139. # install_logを 作成する
  140. #
  141. set_install_log() {
  142. local end_time
  143. local j
  144. local mac_addr=`/sbin/ifconfig eth0 | sed -n 's/^.*HWaddr \([0-9A-F:]*\)[ ]*$/\1/p'`
  145. local log_dir="$mnt/factory_log"
  146. local install_log="$log_dir/install.log"
  147.  
  148. mount ${sddev}5 $mnt || return 1
  149. if [ ! -d $log_dir ]; then
  150. mkdir $log_dir || return 1
  151. fi
  152. end_time=`date`
  153. echo "Mac Address: $mac_addr" > $install_log
  154. echo "Install Start: $START_TIME" >> $install_log
  155. echo "Install End: $end_time" >> $install_log
  156. for j in 1 2 3 4 5 6 7 8 9 10; do
  157. sync
  158. umount $mnt && break || sleep 1
  159. done
  160. }
  161.  
  162. #
  163. # factorylogの退避
  164. #
  165. bk_factory_log() {
  166. local factory_tgz=$topdir/factory_log.tar.gz
  167. if [ ! -f $factory_tgz ]; then
  168. mount ${sddev}5 $mnt || return 1
  169. tar cpzf $factory_tgz -C $mnt factory_log
  170. umount $mnt
  171. fi
  172. }
  173.  
  174. #
  175. # factorylogの書き戻し
  176. #
  177. restore_factory_log() {
  178. local factory_tgz=$topdir/factory_log.tar.gz
  179. if [ -f $factory_tgz ]; then
  180. tar xpzf $factory_tgz
  181. echo `LANG=C date`: reset_script: >> factory_log/powerlog
  182. rm -f $factory_tgz
  183. fi
  184. }
  185.  
  186.  
  187. #
  188. # smartチェックを行う
  189. #
  190. smart_check() {
  191. local disk=$1
  192. local RESULT_SMARTCTL=0
  193. local RESULT_SMARTCTL_BIT0=0
  194. local RESULT_SMARTCTL_BIT1=0
  195. local RESULT_SMARTCTL_BIT2=0
  196. local RESULT_SMARTCTL_BIT3=0
  197. [ ! -f /usr/sbin/smartctl ] && cp -a ${SCRIPT_PATH}/smartctl /usr/sbin/
  198.  
  199. /usr/sbin/smartctl --smart=on --offlineauto=on --saveauto=on \
  200. /dev/${disk}
  201. /usr/sbin/smartctl -a /dev/${disk}
  202. RESULT_SMARTCTL=$?
  203. RESULT_SMARTCTL_BIT0=$((${RESULT_SMARTCTL} & 1))
  204. RESULT_SMARTCTL_BIT1=$((${RESULT_SMARTCTL} & 2))
  205. RESULT_SMARTCTL_BIT2=$((${RESULT_SMARTCTL} & 4))
  206. RESULT_SMARTCTL_BIT3=$((${RESULT_SMARTCTL} & 8))
  207.  
  208. if [ ${RESULT_SMARTCTL_BIT0} -ne 0 ]; then
  209. echo "*** FAILED TO ENABLE S.M.A.R.T. DISK=${disk} ***"
  210. echo "*** COMMAND LINE PARSE ERROR ***"
  211. return 1
  212. fi
  213. if [ ${RESULT_SMARTCTL_BIT1} -ne 0 ]; then
  214. echo "*** FAILED TO ENABLE S.M.A.R.T. DISK=${disk} ***"
  215. echo "*** DEVICE OPEN FAILED ***"
  216. return 1
  217. fi
  218. if [ ${RESULT_SMARTCTL_BIT2} -ne 0 ]; then
  219. echo "*** FAILED TO ENABLE S.M.A.R.T. DISK=${disk} ***"
  220. echo "*** S.M.A.R.T. COMMAND FAILED ***"
  221. return 1
  222. fi
  223. if [ ${RESULT_SMARTCTL_BIT3} -ne 0 ]; then
  224. echo "*** S.M.A.R.T ERROR DETECTED DISK=${disk} ***"
  225. echo "*** STATUS: DISK FAILING ***"
  226. return 1
  227. fi
  228. return 0
  229. }
  230.  
  231.  
  232. #
  233. # メイン
  234. #
  235. disk=$1
  236. mode=$2
  237. topdir=`pwd`
  238.  
  239. if [ "$disk" = "" ]; then
  240. echo usage: tar2disk sda 1>&2
  241. exit -1
  242. fi
  243.  
  244. if [ "x$mode" == "xreset" ]; then
  245. flag_install=0
  246. flag_reset=1
  247. flag_reset_full=0
  248. elif [ "x$mode" == "xreset_full" ]; then
  249. flag_install=0
  250. flag_reset=1
  251. flag_reset_full=1
  252. else
  253. flag_install=1
  254. flag_reset=0
  255. flag_reset_full=0
  256. fi
  257.  
  258. for i in 1 2 5 6; do
  259. umount $sddev$i
  260. done
  261.  
  262. if [ $flag_install -eq 1 ]; then
  263. smart_check $disk || exit 1
  264. partition $disk
  265. fi
  266.  
  267. mkswap /dev/${disk}3
  268. swapon /dev/${disk}3
  269. trap "swapoff /dev/${disk}3" 0
  270.  
  271. for i in 1 2 5 6; do
  272. cd $topdir
  273. echo --- partitioning $i
  274. [ $i -eq 6 -a $flag_reset_full -eq 1 ] && sanitize_disk $i
  275. [ $i -eq 5 -a $flag_reset -eq 1 ] && bk_factory_log
  276. echo --- format partition
  277. format $i || exit 1
  278. mount $sddev$i $mnt || exit 1
  279.  
  280. echo --- extracting tar ball
  281. cd $mnt || exit 1
  282. tar zxf ../sd$i.tgz || exit 1
  283. [ $i -eq 1 -a $flag_install -eq 1 ] && fw_install
  284. [ $i -eq 1 -a $flag_reset -eq 1 ] && set_reset_flag
  285. [ $i -eq 5 -a $flag_reset -eq 1 ] && restore_factory_log
  286. cd $topdir || exit 1
  287. echo --- unmounting
  288. for j in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
  289. sync
  290. umount $mnt && break || sleep 1
  291. done
  292. [ "$?" -ne 0 ] && exit 1
  293. done
  294.  
  295. [ $flag_install -eq 1 ] && set_install_log
  296. exit 0
  297.  

修正したシェルスクリプトを使って初期化した後のパーティション構成です。拡張領域sda4の、開始セクター位置のアライメントがズレているとメッセージが出ています。やっぱりここの位置合わせも行ったほうがいいのでしょうか?

root@Microknoppix:~# fdisk -l /dev/sda

Disk /dev/sda: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders, total 1953525168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk identifier: 0x00000000

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1            2048      628582      313267+  83  Linux
/dev/sda2          628584     1461910      416663+  83  Linux
/dev/sda3         1461912     5670941     2104515   82  Linux swap / Solaris
/dev/sda4         5670943   976559219   485444138+   5  Extended
Partition 4 does not start on physical sector boundary.
/dev/sda5         5670944     6088632      208844+  83  Linux
/dev/sda6         6088636   976559219   485235292   83  Linux
Partition 6 does not start on physical sector boundary.

さらに念のため、この1.0TB HDDのマスターブートレコード(MBR)と、拡張パーティションブートレコード(Extended Partition Boot Record)を調べてみました。

sda6の開始セクター位置を計算すると 5670943 + 417690 + 3 = 6088636 になるので、たぶんこれで大丈夫そうです。

この後、このHDDでHDL-GS500を組み立てなおし、正常に動作できることが確認できました。さらに一歩前進できました。


この後の計画

いろいろと試行錯誤しながらでしたが、前回計画したstep5まで、なんとか進めることができました。

この後、計画step6から、
GNU ddrescueを使って、壊れたHDDからデーターを救出。
へ続きます。

  1. 新HDDを取出し、旧HDDと共にPCへSATA接続。
  2. 旧HDDのsda6を、新HDDのsda6にコピーする。
  3. 新HDDで再組立て。
  4. HDL-GS500を起動して、保存しているデータのバックアップを取る。

0 件のコメント :