Долгое время не выдавалось интересных задачек, которые можно было бы освятить в заметке. Вчера всё-таки нашлась интересная с моей точки зрения.
В исходных данных имеем RAID1 массив, построенный на mdadm, оба диска в котором имеют ошибки на чтение в нескольких секторах. Система говорит о том, что в обоих из них Unrecovered read error — auto reallocate failed:
Aug 21 21:53:11 one kernel: [112350.663076] sd 2:0:0:0: [sda] Unhandled sense code
Aug 21 21:53:11 one kernel: [112350.663081] sd 2:0:0:0: [sda] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
Aug 21 21:53:11 one kernel: [112350.663089] sd 2:0:0:0: [sda] Sense Key : Medium Error [current] [descriptor]
Aug 21 21:53:11 one kernel: [112350.663133] sd 2:0:0:0: [sda] Add. Sense: Unrecovered read error - auto reallocate failed
Aug 21 21:53:11 one kernel: [112350.663144] sd 2:0:0:0: [sda] CDB: Read(10): 28 00 3a 38 53 b0 00 00 08 00
Aug 21 21:53:11 one kernel: [112350.663160] end_request: I/O error, dev sda, sector 976769972
Попытки пройти long тест в S.M.A.R.T не увенчиваются успехом. Вырезки из S.M.A.R.T
# smartctl -a /dev/sda ... Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error # 1 Extended offline Completed: read failure 90% 32520 976769972 ... # smartctl -a /dev/sdb ... Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error # 1 Extended offline Completed: read failure 90% 32519 55084971
Таким образом становиться ясно, что пока в массиве представлено 2-а жестких диска он «малость» работает исправно. Перекидывая неудачные попытки чтения с одного диска на другой диск. Это выражается в следующих сообщениях:
Aug 22 09:12:18 one kernel: [153097.501298] end_request: I/O error, dev sdb, sector 750558140 Aug 22 09:12:18 one kernel: [153097.508526] raid1: sdb4: rescheduling sector 695471160 Aug 22 09:12:18 one kernel: [153097.515736] raid1: sdb4: rescheduling sector 695471408 Aug 22 09:12:30 one kernel: [153109.603257] sd 4:0:0:0: [sdb] Unhandled sense code Aug 22 09:12:30 one kernel: [153109.603259] sd 4:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE Aug 22 09:12:30 one kernel: [153109.603262] sd 4:0:0:0: [sdb] Sense Key : Medium Error [current] [descriptor] Aug 22 09:12:30 one kernel: [153109.603277] sd 4:0:0:0: [sdb] Add. Sense: Unrecovered read error - auto reallocate failed Aug 22 09:12:30 one kernel: [153109.603281] sd 4:0:0:0: [sdb] CDB: Read(10): 28 00 2c bc 9b b5 00 00 08 00 Aug 22 09:12:30 one kernel: [153109.603287] end_request: I/O error, dev sdb, sector 750558140 Aug 22 09:12:30 one kernel: [153109.610682] raid1:md3: read error corrected (8 sectors at 695471248 on sdb4) Aug 22 09:12:30 one kernel: [153110.033913] raid1: sda4: redirecting sector 695471160 to another mirror Aug 22 09:12:31 one kernel: [153110.177597] raid1: sda4: redirecting sector 695471408 to another mirror
В случае окончательного выхода из строя одного из дисков массив никогда не сможет восстановиться, потому что чтение с любого диска заканчивается провалом.
# dd if=/dev/sda of=/dev/null bs=1M dd: reading `/dev/sda': Input/output error 476938+1 records in 476938+1 records out 500106223616 bytes (500 GB) copied, 5724.82 s, 87.4 MB/s
Разумеется, существует вариант сделать копию системы штатным tar архивом. После этого заменить жесткие диски и развернуть ранее сохраненную систему. Еще один из вариантов попытаться заменить один жесткий диск, создать на нем изначально RAID1 массив с одним missing диском, после этого попытаться синхронизировать систему с оставшегося диска с помощью tar или rsync.
Однако было замечено, что у дисков недоступные для чтения сектора находятся в различных местах. Таким образом возникла идея попытаться форсировать remapping испорченных секторов на одном из дисков и попытаться после этого восстановить данные в них с использованием другого полудохлого диска. В ходе этой операции мы получим один диск с полной копией наших данных, с которого можно эти данные полностью прочитать без ошибок. Останется заменить второй полудохлый диск новым, mdadm автоматически прочитает данные и синхронизирует их с новым диском. При желании после синхронизации можно заменить диск, на котором произвели remapping секторов.
Для того, чтобы форсировать жесткий диск произвести remapping секторов необходимо произвести запись в указанный сектор. С минимальной математической частью можно ознакомиться в статье Forcing a hard disk to reallocate bad sectors. В нашем случае эмпирическим путем было выяснено, что сектора с 976769972 по 976769979 имеют проблемы на жестком диске sda
# hdparm --read-sector 976769972 /dev/sda /dev/sda: reading sector 976769972: FAILED: Input/output error ... # hdparm --read-sector 976769979 /dev/sda /dev/sda: reading sector 976769979: FAILED: Input/output error #
Таким образом производим фиктивную запись в указанный диапазон секторов
for sector in $(seq 976769972 976769979); do hdparm --write-sector $sector --yes-i-know-what-i-am-doing /dev/sda; done
После этого можно убедиться, что данные сектора доступны для чтения (предыдущей командой командой мы их полностью обнулили)
for sector in $(seq 976769972 976769979); do hdparm --read-sector $sector /dev/sda; done
Теперь необходимо в указанные области записать информацию с соседнего диска из зеркала. Так как нам известно смещение 976769972 и что количество поврежденных секторов равно 8 копируем указанную область целым блоком. Обратите внимание копируем область с диска sdb, записываем на диск sda:
# dd if=/dev/sdb of=copy skip=976769972 count=8 8+0 records in 8+0 records out 4096 bytes (4.1 kB) copied, 9.3381e-05 s, 43.9 MB/s # dd if=copy of=/dev/sda seek=976769972 oflag=direct count=8 8+0 records in 8+0 records out 4096 bytes (4.1 kB) copied, 0.000874658 s, 4.7 MB/s
Обнуление через вызов hdparm
не является обязательным. Можно напрямую записать сектора требуемыми данными через dd
командой
for sector in $(seq 976769972 976769979); do dd if=/dev/sdb of=/dev/sda skip=$sector seek=$sector oflag=direct count=1 done
Осталось убедиться, что все данные с диска можно прочитать
dd if=/dev/sda of=/dev/null bs=1M 476940+1 records in 476940+1 records out 500107862016 bytes (500 GB) copied, 5622.88 s, 88.9 MB/s
Таким образом в ходе описанных манипуляций у нас есть условно рабочий жесткий диск sda, который мы оставляем в системе и меняем жесткий диск sdb. По завершении синхронизации на диск sdb производим замену sda. По завершении синхронизации имеем полностью исправный массив с целостными данными.
В заключении хочется отметить, что в поиске неисправных секторов незаменимой может оказаться команда
dd if=/dev/sda of=/dev/null bs=512 conv=sync,noerror
При желании можно перенаправить сохранение бинарной копии системы на отдельный носитель в этом случае ключ sync просто незаменим. В случае ошибок сбойные сектора на копии будут заполнены NUL (нулями). Таким образом полученная копия будет полностью соответствовать с точки зрения размера оригиналу.
Вторым незаменимым помощником может служить команда badblocks
. Важно помнить, что для ее вызова необходимо указать размер блока равный 512 байтам. По умолчанию программа используется значение 1024. Таким образом вызов будет выглядеть следующим образом
badblocks -v -b 512 /dev/sda
Программа будет считывать сектор за сектором и отображать информацию о секторах, которые не удалось прочитать. Например,
# badblocks -v -b 512 /dev/sda Checking blocks 0 to 1565565871 Checking for bad blocks (read-only test): 578915880 578915881 578915882 578915883 578915884 578915885 578915886 578915887 578915888 578915889 578915890 578915891 578915892 578915893 578915894 578915895
В этом случае остается сохранить вывод программы в файл и последовательно перезаписать полученные сектора с другого диска.
Интересные материалы для ознакомления:
Pingback: debugfs практическое руководство | Обитель UNIX