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 修復用暫定版、

#!/bin/sh
#
# tar2disk: tar ball をディスクに展開
#
# usage: tar2disk sda
# ##### 2012/10/01 sfdiskをシリンダ指定から、セクタ指定へ変更。#####

mnt=./mnt
sddev=/dev/hdb   # ##### 初期化する環境に合わせて変更してください #####

PATH=$PATH:/sbin

#
# 16進->10進変換
#
hex2dec() {
    expr 1 + `printf "%d" $1`
}

#
# モデルごとの制限セクタ数
#
sector160=`hex2dec 0x12A05FBF`
sector250=`hex2dec 0x1D1A9C7F`
sector300=`hex2dec 0x22ECB57F`
sector320=`hex2dec 0x2540BF7F`
sector400=`hex2dec 0x2E90F73F`
sector500=`hex2dec 0x3A353900`
sector750=`hex2dec 0x574FCD9F`
sector1000=`hex2dec 0x74706DAF`

#cyl_end=379   # sda5 の終了セクタ
cyl_end=6088636   # ##### シリンダ値をセクタ値に変更 #####


#
# ディスク名からモデルで丸められたセクタ数を得る
# ##### 2012/10/01 セクタ数を返すように修正 #####
disk2cyl() {
    local disk=$1
#local sector=`cat /sys/block/$disk/size`
local sector=976566529  # ##### 修復のため、HDDサイズを500GBに固定。 #####
    if [ $sector -lt $sector160 ]; then
 echo -n error
 exit 1
    elif [ $sector -lt $sector250 ]; then
 expr \( \( $sector160 / 255 / 63 \) \* 16065 \) - $cyl_end
    elif [ $sector -lt $sector300 ]; then
 expr \( \( $sector250 / 255 / 63 \) \* 16065 \) - $cyl_end
    elif [ $sector -lt $sector320 ]; then
 expr \( \( $sector300 / 255 / 63 \) \* 16065 \) - $cyl_end
    elif [ $sector -lt $sector400 ]; then
 expr \( \( $sector320 / 255 / 63 \) \* 16065 \) - $cyl_end
    elif [ $sector -lt $sector500 ]; then
 expr \( \( $sector400 / 255 / 63 \) \* 16065 \) - $cyl_end
    elif [ $sector -lt $sector750 ]; then
 expr \( \( $sector500 / 255 / 63 \) \* 16065 \) - $cyl_end
    elif [ $sector -lt $sector1000 ]; then
 expr \( \( $sector750 / 255 / 63 \) \* 16065 \) - $cyl_end
    else
 expr \( \( $sector1000 / 255 / 63 \) \* 16065 \) - $cyl_end
    fi
    return 0
}


#
# パーティションを切る
# ##### 2012/10/01 シリンダ指定からセクタ指定へ変更 sda6の開始セクタは暫定値 #####
partition() {
 local disk=$1
 local c5=417689               # ##### 26*255*63-1 #####
 local cyl=`disk2cyl $disk`
 local ext=`expr $cyl + $c5 + 4`

 dd if=/dev/zero of=/dev/$disk bs=512 count=2 || exit 1
 sfdisk -f -u S /dev/$disk <<EOF || exit 1
2048,626535,L
628584,833327,L
1461912,4209030,S
5670943,$ext,E
5670944,$c5,L
6088636,$cyl,L
EOF
}


#
# format
# ##### 2012/10/01 ブロックサイズを4096バイトに変更。#####
format() {
 local i=$1

 if [ $i -eq 1 ]; then
  [ $flag_reset -eq 1 ] && return 0
  mke2fs -b 4096 -j -m1 $sddev$i || return 1
 elif [ $i -eq 2 ]; then
  mke2fs -b 4096 -j -m1 $sddev$i || return 1
 elif [ $i -eq 5 ]; then
  mke2fs -b 4096 -j -N 100000 \
   -O dir_index,filetype,sparse_super $sddev$i || return 1
 else
  [ ! -f /sbin/mkfs.xfs ] && cp -a ${SCRIPT_PATH}/mkfs.xfs /sbin/
  mkfs.xfs -f $sddev$i || return 1
 fi
 return 0
}


#
# fw_install
#
fw_install() {
 touch verify_on_boot
 cp -a ../sd?.tgz ../tar2disk ./.landisk/
 mkdir ./.landisk/mnt
}


#
# 初期化後updateの為にflag fileをtouchする
#
set_reset_flag() {
 touch ./.landisk/reset_ok
}


#
# ディスクを 0 クリアする
#
sanitize_disk() {
 local i=$1
 echo "--- sanitize disk"
 dd if=/dev/zero of=$sddev$i bs=1M || return 1
}


#
# install_logを 作成する
#
set_install_log() {
 local end_time
 local j
 local mac_addr=`/sbin/ifconfig eth0 | sed -n 's/^.*HWaddr \([0-9A-F:]*\)[ ]*$/\1/p'`
 local log_dir="$mnt/factory_log"
 local install_log="$log_dir/install.log"

 mount ${sddev}5 $mnt || return 1
 if [ ! -d $log_dir ]; then
  mkdir $log_dir || return 1
 fi
 end_time=`date`
 echo "Mac Address: $mac_addr" > $install_log
 echo "Install Start: $START_TIME" >> $install_log
 echo "Install End: $end_time" >> $install_log
 for j in 1 2 3 4 5 6 7 8 9 10; do
  sync
  umount $mnt && break || sleep 1
 done
}

#
# factorylogの退避
#
bk_factory_log() {
 local factory_tgz=$topdir/factory_log.tar.gz
 if [ ! -f $factory_tgz ]; then
  mount ${sddev}5 $mnt || return 1
  tar cpzf $factory_tgz -C $mnt factory_log
  umount $mnt
 fi
}

#
# factorylogの書き戻し
#
restore_factory_log() {
 local factory_tgz=$topdir/factory_log.tar.gz
 if [ -f $factory_tgz ]; then
  tar xpzf $factory_tgz
  echo `LANG=C date`: reset_script: >> factory_log/powerlog
  rm -f $factory_tgz
 fi
}


#
# smartチェックを行う
#
smart_check() {
 local disk=$1
 local RESULT_SMARTCTL=0
 local RESULT_SMARTCTL_BIT0=0
 local RESULT_SMARTCTL_BIT1=0
 local RESULT_SMARTCTL_BIT2=0
 local RESULT_SMARTCTL_BIT3=0
 [ ! -f /usr/sbin/smartctl ] && cp -a ${SCRIPT_PATH}/smartctl /usr/sbin/

 /usr/sbin/smartctl --smart=on --offlineauto=on --saveauto=on \
     /dev/${disk}
 /usr/sbin/smartctl -a /dev/${disk}
 RESULT_SMARTCTL=$?
 RESULT_SMARTCTL_BIT0=$((${RESULT_SMARTCTL} & 1))
 RESULT_SMARTCTL_BIT1=$((${RESULT_SMARTCTL} & 2))
 RESULT_SMARTCTL_BIT2=$((${RESULT_SMARTCTL} & 4))
 RESULT_SMARTCTL_BIT3=$((${RESULT_SMARTCTL} & 8))

 if [ ${RESULT_SMARTCTL_BIT0} -ne 0 ]; then
     echo "*** FAILED TO ENABLE S.M.A.R.T. DISK=${disk} ***"
     echo "*** COMMAND LINE PARSE ERROR ***"
     return 1
 fi
 if [ ${RESULT_SMARTCTL_BIT1} -ne 0 ]; then
     echo "*** FAILED TO ENABLE S.M.A.R.T. DISK=${disk} ***"
     echo "*** DEVICE OPEN FAILED ***"
     return 1
 fi
 if [ ${RESULT_SMARTCTL_BIT2} -ne 0 ]; then
     echo "*** FAILED TO ENABLE S.M.A.R.T. DISK=${disk} ***"
     echo "*** S.M.A.R.T. COMMAND FAILED ***"
     return 1
 fi
 if [ ${RESULT_SMARTCTL_BIT3} -ne 0 ]; then
     echo "*** S.M.A.R.T ERROR DETECTED DISK=${disk} ***"
     echo "*** STATUS: DISK FAILING ***"
     return 1
 fi
 return 0
}


#
# メイン
#
disk=$1
mode=$2
topdir=`pwd`

if [ "$disk" = "" ]; then
 echo usage: tar2disk sda 1>&2
 exit -1
fi

if [ "x$mode" == "xreset" ]; then
 flag_install=0
 flag_reset=1
 flag_reset_full=0
elif [ "x$mode" == "xreset_full" ]; then
 flag_install=0
 flag_reset=1
 flag_reset_full=1
else
 flag_install=1
 flag_reset=0
 flag_reset_full=0
fi

for i in 1 2 5 6; do
 umount $sddev$i
done

if [ $flag_install -eq 1 ]; then
 smart_check $disk || exit 1
 partition $disk
fi

mkswap /dev/${disk}3
swapon /dev/${disk}3
trap "swapoff /dev/${disk}3" 0

for i in 1 2 5 6; do
 cd $topdir
    
 echo --- partitioning $i
 [ $i -eq 6 -a $flag_reset_full -eq 1 ] && sanitize_disk $i
 [ $i -eq 5 -a $flag_reset -eq 1 ] && bk_factory_log
 echo --- format partition
 format $i || exit 1
 mount $sddev$i $mnt || exit 1

 echo --- extracting tar ball
 cd $mnt || exit 1
 tar zxf ../sd$i.tgz || exit 1
 [ $i -eq 1 -a $flag_install -eq 1 ] && fw_install
 [ $i -eq 1 -a $flag_reset -eq 1 ] && set_reset_flag
 [ $i -eq 5 -a $flag_reset -eq 1 ] && restore_factory_log
 cd $topdir || exit 1
    
 echo --- unmounting
 for j in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
  sync
  umount $mnt && break || sleep 1
 done
 [ "$?" -ne 0 ] && exit 1
done

[ $flag_install -eq 1 ] && set_install_log
exit 0

修正したシェルスクリプトを使って初期化した後のパーティション構成です。拡張領域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 件のコメント :