Речь пойдет о способе извлечения данных с неисправного SSD для случаев когда после попытки чтения любого сбойного сектора - SSD совсем перестает отдавать данные и помогает только отключение включение питания.

Представляю доработанную версию скрипта ddrescue-loop с поддержкой управления USB реле и uhubctl.

Для прерывания питания SSD задействовал простое и дешевое решение USB Relay Module LCUS-1 CH340 которые доступны на Aliexpress. И подключение через док станцию AgeStar 31CBNV1C на основе USB-NVMe моста JMicron JMS583.

Рассмотрим процесс восстановления на примере случая с неисправными M.2 NVMe SSD производства Kimtigo на контроллере Maxio MAP1202.

ddrescue-loop v0.2.1

ddrescue-loop v0.2.1

ddrescue-loop-v0.2.1.gz

#!/bin/sh
#ddrescue-loop script writen by gumanzoy <gumanzoy@gmail.com>

# Compatible only with Linux, not with other *nix!
# Depends on udev /dev and sysfs /sys kernel interfaces

# For SATA requires AHCI compatible motherboard
# For all Intel and modern AMD platforms (AM4 and newer), check the UEFI Setup
# SATA settings to ensure Port Hot Plug is enabled

# For USB requires lsusb from usbutils package
# And optional uhubctl for power off/on cycle
# Or hardware USB Relay Module LCUS-1 CH340

# [RU] forum thread. Обсуждение
# https://forum.ixbt.com/topic.cgi?id=11:47589-31

# /* This program is free software. It comes without any warranty, to
# * the extent permitted by applicable law. You can redistribute it
# * and/or modify it under the terms of the Do What The Fuck You Want
# * To Public License, Version 2, as published by Sam Hocevar. See
# * http://www.wtfpl.net/ for more details. */

VERSION=0.2.1

showhelp () {
echo "ddrescue-loop v""$VERSION"" перезапускает процесс ddrescue в случае его завершения"
echo "Внимание следует соблюдать очередность аргументов"
echo "Указывать ключи в произвольном порядке нельзя!"
echo "Числовые значения аргументов обязательно через пробел"
echo -n "\n"
echo "# ----- SATA ----- SATA ----- SATA ----- SATA ----- SATA -----"
echo "# Остановить/запустить диск на SATA порту:"
echo "-ata <n> -stop""		""остановить диск на SATA порту <n>"
echo "-ata <n> -scan""		""сканировать SATA порт <n>"
echo -n "\n"
echo "# Запустить восстановление c SATA:"
echo "ddrescue-loop -ata <n> [-loop <n>] [-pwc] [-wait <n>] [-act <n>] outfile mapfile [ddrescue options]"
echo -n "\n"
echo "# Укажите номер SATA порта к которому подключен диск источник:"
echo -n "-ata <n>""		""Номер SATA порта <n> цифра (смотрите вывод dmesg)"
echo -n "\n""			""#: "; ls /sys/class/ata_port
echo -n "\n"
echo "# Функция циклической остановки/перезапуска диска на SATA порту:"
echo "-loop <n>""		""<n> предельное число попыток"
echo -n "\n"
echo "# Таймер ожидания остановки/перезапуска диска:"
echo "-wait <n>""		""Время в секундах <n> [10]"
echo -n "\n"
echo "# Переопределить таймаут ожидания исполнения ATA команд:"
echo "-act <n>""		""Время в секундах <n> [30]"
echo -n "\n"
echo "# ------ USB ------ USB ------ USB ------ USB ------ USB -----"
echo "# Отключить/включить питание USB устройства <ID>, методом <hub/rle>:"
echo "-usb <ID> -pwc hub""	""Использовать uhubctl --search <ID>"
echo "-usb <ID> -pwc rle""	""Использовать USB реле LCUS-1 CH340 RLETTY=""$RLETTY"
echo -n "\n"
echo "# Запустить восстановление c USB:"
echo "ddrescue-loop -usb <ID> [-loop <n>] [-pwc <hub/rle>] [-wait <n>] outfile mapfile [ddrescue options]"
echo -n "\n"
echo "# Укажите Hex идентификаторы VID:PID USB устройства источника:"
echo "-usb <ID>""		""<VID:PID> через двоеточие (смотрите вывод lsusb)"
echo -n "\n"
echo "# Функция циклического перезапуска ddrescue:"
echo "-loop <n>""		""<n> предельное число попыток"
echo -n "\n"
echo "# Основные:"
echo "outfile""			""Устройство приемник данных / файл образа"
echo "mapfile""			""ddrescue map/log файл (обязательно)"
echo -n "\n"
echo "# В конце после mapfile можно указать опции запуска ddrescue через пробел"
echo "# Поддержка зависит от версии. Полный список опций в мануале. Важные:"
echo "-P [<n>]""		""Предпросмотр данных [число строк] по умолчанию 3"
echo "-b 4096""			""<bytes> размер сектора (физического блока) [default 512]"
echo "-c <n>""			""Размер кластера <n> секторов за раз [default 128]"
echo "-O"" #Рекомендую!		""После каждой ошибки заново открывать файл устройства"
echo "-J"" #Опционален		""При ошибке перечитать последний не сбойный сектор"
echo "-r <n> #ИЛИ -r -1""	""<n> число повторных проходов до перехода к trim"
echo "-m <domain.mapfile>""	""Ограничить область чтения доменом <file> ddru_ntfsbitmap"
}

get_ata_host () {
until SCSIHOST=`readlink -f /sys/class/ata_port/ata"$1"/device/host?/scsi_host/host?/` \
&& test -d "$SCSIHOST"; do sleep 1; done
}

get_ata_target () {
until SYSFSTGT=`readlink -f /sys/class/ata_port/ata"$1"/device/host?/target?:?:?/?:?:?:?/` \
&& test -d "$SYSFSTGT"; do sleep 1; done
}

get_ata_dev () {
until INDEV=`readlink -f /dev/disk/by-path/pci-*-ata-"$1"` \
&& test -b "$INDEV"; do sleep 1; done
}

device_delete () {
while test -f "$SYSFSTGT"/delete; do echo 1 > "$SYSFSTGT"/delete; sleep 1; done
}

get_usb_dev_by_path () {
INDEV="/dev/"`basename "$1"`
SYSFSTGT="$1""/device/"
}

get_usb_dev_by_id () {
IDVID=`echo -n "$1" | cut -d ":" -f1`
IDPID=`echo -n "$1" | cut -d ":" -f2`

until get_usb_dev_by_path `udevadm trigger -v -n -s block \
-p ID_VENDOR_ID="$IDVID" -p ID_MODEL_ID="$IDPID"` \
&& test -b "$INDEV"; do sleep 1; done
}

power_cycle () {
if [ -n "$USBID" ] && [ "$PWRCTL" = hub ]; then
uhubctl --search "$USBID" --action cycle --delay "$LOOPWAIT"
elif [ "$PWRCTL" = rle ]; then /bin/echo -en "\xA0\x01\x01\xA2" > "$RLETTY" && \
sleep "$LOOPWAIT" && /bin/echo -en "\xA0\x01\x00\xA1" > "$RLETTY"
fi
}

if [ "$1" = "-h" -o "$1" = "--help" ]; then showhelp
exit; fi

if [ "`whoami`" != "root" ]; then
echo Exit. This script should be run as root !
exit 1; fi

if [ -z "$RLETTY" ] && test -c /dev/ttyUSB0; then RLETTY="/dev/ttyUSB0"
elif [ -n "$RLETTY" ] && ! test -c "$RLETTY"; then
echo "RLETTY=""$RLETTY"" control device not found"; exit 1; fi

if [ -n "$1" ] && [ "$1" = "-ata" ]; then
if [ -n "$2" ] && test -d /sys/class/ata_port/ata"$2"; then
SATAP="$2"; get_ata_host "$SATAP"; shift; shift
else echo -n "Please enter correct port number: "; ls /sys/class/ata_port; exit 1; fi
fi

if [ -n "$1" ] && [ "$1" = "-stop" ] && [ -n "$SATAP" ]; then
get_ata_target "$SATAP"; device_delete; exit; fi

if [ -n "$1" ] && [ "$1" = "-scan" ] && [ -n "$SATAP" ]; then
echo '0 0 0' > "$SCSIHOST"/scan; exit; fi

if [ -n "$1" ] && [ "$1" = "-usb" ] && [ -z "$SATAP" ]; then
if [ -n "$2" ] && lsusb -d "$2"; then
USBID="$2"; get_usb_dev_by_id "$USBID"; shift; shift
else echo "Please enter correct USB Device ID:"
lsusb | cut -d ":" -f2,3 | grep -vi hub
exit 1; fi
fi

if [ -n "$1" ] && [ "$1" = "-loop" ]; then
if [ -n "$2" ] && [ "$2" -gt 0 ]; then
DDLOOP="$2"; shift; shift; fi
else DDLOOP=0
fi

if [ -n "$1" ] && [ "$1" = "-pwc" ]; then
if [ -n "$USBID" ] && [ -n "$2" ] && [ "$2" = "hub" -o "$2" = "rle" ]; then
PWRCTL="$2"; echo "PWRCTL=""$2"; shift; shift
elif [ -n "$RLETTY" ]; then
PWRCTL="rle"; echo "PWRCTL=rle"; shift; fi
fi

if [ -n "$1" ] && [ "$1" = "-wait" ]; then
if [ -n "$2" ] && [ "$2" -gt 0 ]; then
LOOPWAIT="$2"; shift; shift; fi
else LOOPWAIT=10
fi

if [ -n "$1" ] && [ "$1" = "-act" ]; then
if [ -n "$2" ] && [ "$2" -gt 0 ]; then
ATACMDT="$2"; shift; shift; fi
fi

if [ -n "$RLETTY" ] && [ "$PWRCTL" = rle ]; then
stty -F "$RLETTY" 9600 -echo && echo "RLETTY=""$RLETTY"; fi

if [ "$DDLOOP" = 0 ]; then
if [ -n "$USBID" ] && [ "$PWRCTL" = hub ]; then power_cycle; exit
elif [ -n "$RLETTY" ] && [ "$PWRCTL" = rle ]; then power_cycle; exit; fi
fi

if [ -z "$SATAP" ] && [ -z "$USBID" ]; then showhelp
exit; fi

OUTFILE="$1"; shift
MAPFILE="$1"; shift
DDOPTS="$@"

DONE=X
LOOPCOUNT=0

until [ "$DONE" = 0 ]; do

if [ -n "$SATAP" ]; then get_ata_target "$SATAP"; get_ata_dev "$SATAP"
elif [ "$LOOPCOUNT" -gt 0 ] && [ -n "$USBID" ]; then get_usb_dev_by_id "$USBID"
fi

if [ -n "$ATACMDT" ]; then echo "$ATACMDT" > "$SYSFSTGT"/timeout
fi

echo ddrescue "-fd" "$INDEV" "$OUTFILE" "$MAPFILE" "$DDOPTS"
ddrescue "-fd" "$INDEV" "$OUTFILE" "$MAPFILE" $DDOPTS
DONE="$?"

if [ "$DONE" != 0 ] && [ "$DDLOOP" -gt 0 ]; then

  device_delete &
  sleep "$LOOPWAIT"

  if [ -n "$PWRCTL" ]; then power_cycle
  elif [ -n "$SATAP" ]; then while test -d "$SYSFSTGT"; do
  sleep "$LOOPWAIT"; done; fi

  if [ -n "$SATAP" ]; then sleep "$LOOPWAIT"
  echo '0 0 0' > "$SCSIHOST"/scan; fi

  DDLOOP=$(($DDLOOP-1))
  LOOPCOUNT=$(($LOOPCOUNT+1))

  echo "\n\033[1mDDLOOP #""$LOOPCOUNT"; tput sgr0
  date; echo -n "\n"

  sleep "$LOOPWAIT"

else DONE=0
fi
done

This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2
This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2

Как пользоваться. Параметры запуска

Использование с SATA устройствами разобрано в моей первой статье. Если не читали, то рекомендую сначала ознакомится с ней.

Так как я не слишком искушен в sh скриптинге, и вообще не программист - с разбором параметров особо не мудрствовал. Поэтому есть некоторые важные ограничения!

Следует соблюдать очередность аргументов. Указывать ключи в произвольном порядке нельзя!
Числовые значения аргументов обязательно через пробел.

ddrescue-loop -usb <ID> [-loop N] [-pwc <hub/rle>] [-wait N] outfile mapfile [ddrescue options]

Укажите Hex идентификаторы VID:PID USB устройства источника:
-usb <ID> VID:PID через двоеточие (смотрите вывод lsusb)

Функция циклической остановки/перезапуска ddrescue:
-loop N Предельное число попыток N целое число. Указывать обязательно.

Функция прерывания питания устройства:
-pwc hub Использовать uhubctl --search <ID>
-pwc rle Использовать USB реле LCUS-1 CH340

Таймер ожидания остановки/перезапуска диска:
-wait N Время в секундах. 10 по умолчанию.

В конце после mapfile можно указать опции запуска ddrescue. Их обрабатывает уже сама ddrescue, можно указывать все как обычно.

Демонстрация работы. Записи вывода терминала

https://asciinema.org/a/628786Запись вывода терминала ddrescue-loop
https://asciinema.org/a/628786
Запись вывода терминала ddrescue-loop
https://asciinema.org/a/628787Запись вывода терминала dmesg -Wt
https://asciinema.org/a/628787
Запись вывода терминала dmesg -Wt

При ошибке чтения сектора и после сообщений в dmesg
uas_eh_device_reset_handler start
uas_eh_device_reset_handler success
Device offlined - not ready after error recovery
I/O error, dev sdd, sector 8985600

Устройство перестает отдавать данные, процесс ddrescue завершается
Can't reopen input file: No such device or address
Скрипт подает команду на включение реле, затем перезапускает ddrescue и чтение продолжается.

Как это работает

Так как имена устройств sda sdb sdc не постоянны и буква меняется в зависимости от очередности подключения к системе. Применил вот такое решение: скрипт принимает на вход ID VID:PID USB устройства источника. Их отображает lsusb. В скрипте код получает адрес блочного устройства /dev/sdX И делает это каждый раз после отключения питания диска перед перезапуском процесса ddrescue

USB Relay Module LCUS-1 CH340
USB Relay Module LCUS-1 CH340

USB Relay Module LCUS-1 CH340
Вариант с USB Type-A
aliexpress.com/item/4001216792789.html
aliexpress.com/item/1005001993993906.html
Вариант с USB Type-C
aliexpress.com/item/1005004323626598.html
aliexpress.com/item/1005004347242232.html

Использование USB реле. Подразумевается подключение питания на USB/SATA диск через контакты реле COM и NC Чтобы в выключенном состоянии питание проходило. А при подаче команды на включение реле - питание отключалось. Скрипт управляет реле с помощью команд:

echo -en "\xA0\x01\x01\xA2" > "$RLETTY"
sleep "$LOOPWAIT"
echo -en "\xA0\x01\x00\xA1" > "$RLETTY"

По умолчанию RLETTY=/dev/ttyUSB0 можно переопределить передав переменную окружения перед запуском скрипта:
RLETTY=/dev/ttyUSB1 ddrescue-loop -pwc rle

Также добавил поддержку uhubctl. Теоретически можно использовать вместо реле, но у меня нет подходящего USB хаба. Возможно будет полезно для восстановления с флешек.
ddrescue-loop -usb <ID> -pwc hub

uhubctl --search "$USBID" --action cycle --delay "$LOOPWAIT"

Подключение реле к док станции

В док станции AgeStar 31CBNV1C есть переключатель для отключения питания. Я подключил реле вместо него.

Док станция AgeStar 31CBNV1C
Док станция AgeStar 31CBNV1C

У переключателя пять контактов. Крайние для крепления к плате. Припаиваться нужно ко второму и третьему слева. На фото отметил красным.

Выпаял переключатель совсем, впаял два провода, залудил обратные концы и убрал в термоусадку. Собрал док станцию обратно в корпус. Вот что получилось.

Док станция + реле
Док станция + реле

Непосредственно процесс восстановления

Данный раздел пишу в том числе в расчете на тех кто далек от Linux. Не уверен что у меня получится понятно объяснить, но попробую. По большей части все примечания такого рода убрал под спойлеры.

Использую ПК с GNU/Linux Debian 11 (еще не обновился до Debian 12 по причине лени).
Док станцию подключаю к USB3 порту. А к SATA подключено несколько 3.5" жестких дисков.

Сохраняю образ в файл, на диск с файловой системой Ext4. Файл создается разреженный (sparse file) таким образом место расходуется только под фактический объем скопированного, а не под весь объем неисправного SSD. При этом файл монтирую в /dev/loopN это позволяет работать с ним так же как с физическим диском.

Для создания/подключения/отключения образов удобно использовать графический интерфейс gnome-disk-utility

gnome-disk-utility используется не только в среде Gnome. Зависимостей относительно не много.

Создавать образ нужно такого же или большего объема. Для того чтобы не ошибиться можно скопировать и указать размер диска в байтах (отображается в gnome-disks в правой панели вверху при выборе соответствующего диска).

В меню «гамбургер» пункт New Disk Image..., в открывшемся окне выбрать размер в байтах, вставить скопированное число в поле и удалить запятые. Указать имя и путь для сохранения. Нажать Attach new image...

Созданный образ подключится в свободный /dev/loopN в режиме чтение/запись. При выборе и подключении существующего файла образа он по умолчанию подключается в режиме только чтение. Не забывайте снимать галочку Set up read-only device

Если таблица разделов на SSD читается. То прежде чем запускать копирование можно построить файл домена с помощью утилиты ddru_ntfsbitmap (из состава ddrutility). Это позволяет для разделов с файловой системой NTFS ограничить объем копирования только занятым пространством.

К сожалению в случае с SSD это не позволяет ускорить процесс, а только экономит место под образ. Так как нули из не занятых блоков копируются на полной скорости и без ошибок. Сбойные же сектора располагаются там где были какие то файлы и именно на обработку диском ошибок тратится основная часть времени.

Создание файла домена с помощью ddru_ntfsbitmap

В приведенных ниже командах в /dev/sdX вместо X подставить соответствующую устройству букву (можно посмотреть в том же gnome-disk-utility).

Нужно создавать файл домена в привязке ко всему диску, а не к отдельному разделу. Поэтому нужно указывать в командах именно устройство /dev/sdX а не раздел /dev/sdXN

Для ddru_ntfsbitmap нужно вычислить и указать значение input offset (partition offset) ключ --inputoffset или коротко -i

Сначала нужно запустить sudo fdisk -l /dev/sdX
Найти в таблице нужный раздел NTFS, и скопировать значение из столбца Start
Затем запустить команду, куда вместо START подставить значение

sudo ddru_ntfsbitmap -i $((START*512)) -m mftdomain.map /dev/sdX domain.map

Будут созданы искомые файлы domain.map и mftdomain.map

А также еще несколько файлов, имена которых начинаются на __ (двойное нижнее подчеркивание). Они не нужны, их можно удалить.

Запуск процесса копирования

Скрипт ddrescue-loop для удобства запуска можно скопировать в /usr/local/bin/

Скопировать и выдать права на исполнение

sudo zcat ddrescue-loop-v0.2.1.gz > /usr/local/bin/ddrescue-loop
sudo chmod +x /usr/local/bin/ddrescue-loop

Сначала в отдельном терминале запустить dmesg -Wt чтобы видеть что происходит с диском.

Запускать ddrescue-loop нужно с правами root. Также и dmesg, но только в дистрибутивах где по умолчанию включен kernel.dmesg_restrict=1 (Debian входит в их число). Для краткости команду sudo добавлять не буду, но она подразумевается.

ddrescue-loop -usb 152d:0583 -loop 9999 -pwc rle /dev/loopN mapfile.log -b 4096 -c 32 -O -J

Разберем приведенные параметры:

  • -usb 152d:0583 это ID VID:PID док станции (а точнее контроллера JMicron JMS583 USB-NVMe)
    Посмотреть список подключенных устройств можно запустив ddrescue-loop -usb

  • -loop 9999 -pwc rle предельное число попыток перезапуска в цикле и использовать USB реле.

  • /dev/loopN Где N заменить на соответствующую цифру. Предполагается что файл приемник создан и смонтирован (посмотреть можно в gnome-disk-utility)
    Вместо этого можете просто указать куда сохранить файл образ, тогда ddrescue создаст его сама.

  • mapfile.log имя ddrescue map/log файла указываем обязательно.

  • -b 4096 обязательно указываем реальный размер сектора (физического блока)
    По умолчанию 512 и это не соответствует современным накопителям как SSD так и HDD

  • -c 32 ограничиваем размер кластера (сколько секторов ddrescue будет пытаться читать за раз в обычном режиме до перехода к trimming). По умолчанию 128, а так как мы увеличили размер сектора то это уже чересчур.

  • -O обязательно указываем. Чтобы ddrescue после каждой ошибки пыталась заново открыть файл устройства. Это необходимо для того чтобы в случае невозможности дальнейшего чтения - процесс ddrescue завершался с ошибкой и скрипт задействовал метод остановки/перезапуска диска.

  • -J тоже указываем, это дополнительная проверка - при ошибке перечитывать последний не сбойный сектор.

Если создавали файл домена то первый проход запускаем с добавлением в конец строки запуска -m mftdomain.map
Затем когда вычитали весь MFT то читаем все остальные задействованные файловой системой сектора -m domain.map

Тонкая подстройка в процессе

Если останавливали ddrescue по Ctrl+C для изменения параметров то перед перезапуском можно задействовать реле или uhubctl (если используем хаб вместо реле)
ddrescue-loop -pwc rle или ddrescue-loop -usb <ID> -pwc hub

На начальном этапе можно (но не обязательно) стараться вычитывать в первую очередь крупные беспроблемные участки. Для этого можно перепрыгивать скопления бэдов, добавляя в конце опцию -i Например -i 30G если чтение в прямом направлении. И можно читать задом наперед, для этого указывать -R и -s Например -R -s 40G

ddrescue-loop -usb 152d:0583 -loop 9999 -pwc rle -wait 4 -act 23 /dev/loopN mapfile.log -b 4096 -c 32 -O -J -m domain.map

Здесь добавлены -wait 4 то есть уменьшен таймер ожидания с 10 до 4
-act 23 таймаут ожидания исполнения ATA команд уменьшен с 30 до 23

Эти параметры подбирал экспериментально для описываемого случая для того чтобы постараться уменьшить время ожидания пере-подключения при ошибках SSD.

Значение -act подбирал с оглядкой на кол-во сообщений uas_eh_device_reset_handler start uas_eh_device_reset_handler success в dmesg после каждого сбойного сектора. При 30 ядро успевало делать две попытки reset'a. При 22 уже три reset'a - а это выходит дольше. Оптимальным оказалось значение 23.

Когда основные стадии процесса вычитки завершены, то в режиме scraping для ускорения можно увеличить размер блока, при этом качество пострадает.
Например указать -b 16Ki -c 1 или -b 32Ki -c 1 вместо -b 4096 -c 32

Когда процесс близится к завершению

В данном случае вычитка одного SSD включая trimming заняла около 14 суток. Но scraping еще не завершен.

Промежуточный / на 98.51% финальный результат
Промежуточный / на 98.51% финальный результат

Файловые системы с разделов получившегося образа можно монтировать средствами ядра Linux сразу из /dev/loopNpP, где P - номер раздела. Это можно делать с помощью gnome-disk-utility. При этом обязательно только в режиме чтения. Надежнее отключить образ и пере-подключить в режиме только чтение.

Но для улучшения результатов, поисков фрагментов MFT и файлов по сигнатурам лучше использовать специализированный софт. Из свободных TestDisk/PhotoRec.

Я уже давно пользуюсь Linux версией DMDE. К сожалению исходники закрыты, это платное ПО. Однако в бесплатной версии (только для личного не коммерческого использования) ограничено только кол-во одновременно восстанавливаемых файлов, но не их размер. Это отлично подходит для оценки возможности восстановления нужных файлов.

Использование такого ПО и разбор разных нюансов с этим связанных точно не поместится в данную статью. Да и мои знания об устройстве файловых систем совсем поверхностные.

Заключение

Восстановление в данном случае считаю успешным. Пользовательские файлы извлечены и читаются. Справедливости ради нужно заметить что не все конечно, но дальше пытаться выцарапывать вряд ли имеет смысл. Так как остались только проблемные области общим объемом 1.54GB и вполне возможно 90% из них не читаемые.

Надеюсь мой опыт и скрипт кому нибудь еще пригодятся. Думаю что потребности в восстановлении данных с SSD меньше не становится. Не забывайте напоминать пользователям о бэкапах.

Благодарю за внимание! И удачных экспериментов!

Комментарии (11)


  1. sdy
    09.01.2024 22:55

    Правильно ли я понял, что отключение-включение нужно после каждой неудачи чтения битого блока, что приводит к тому, что данные могут вычитываться очень долго? Как вариант 14 суток чтение заняло в вашем случае

    Почему бы просто не использовать #PERST ? Посмотрел спеку на всякий случай, потому что не всегда все сигналы испльзуются, в данном случае сброс вроде у всех поддерживается


    1. gumanzoy Автор
      09.01.2024 22:55

      Правильно ли я понял, что отключение-включение нужно после каждой
      неудачи чтения битого блока, что приводит к тому, что данные могут
      вычитываться очень долго?

      Долго это ждать пока ddrescue поймет что не читается. Опция --timeout=0 не помогает.

      А передергивание по питанию быстро происходит. Тут проблема в том что передернуть не дожидаясь ddrescue можно, но тогда в map файле этот сектор как битый не будет отмечен и позиция не поменяется.

      Почему бы просто не использовать #PERST ?

      А как именно его использовать ?

      Линукс делает reset но это не помогает. Возможно какой то другой reset.

      sd 6:0:0:0: [sdd] tag#5 uas_eh_abort_handler 0 uas-tag 1 inflight: CMD IN
      sd 6:0:0:0: [sdd] tag#5 CDB: Read(10) 28 00 00 89 1c 00 00 01 00 00
      scsi host6: uas_eh_device_reset_handler start
      usb 4-3: reset SuperSpeed Gen 1 USB device number 108 using xhci_hcd
      scsi host6: uas_eh_device_reset_handler success
      sd 6:0:0:0: [sdd] tag#5 uas_eh_abort_handler 0 uas-tag 1 inflight: CMD
      sd 6:0:0:0: [sdd] tag#5 CDB: Test Unit Ready 00 00 00 00 00 00
      scsi host6: uas_eh_device_reset_handler start
      usb 4-3: reset SuperSpeed Gen 1 USB device number 108 using xhci_hcd
      scsi host6: uas_eh_device_reset_handler success
      sd 6:0:0:0: Device offlined - not ready after error recovery
      sd 6:0:0:0: [sdd] tag#5 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_TIMEOUT cmd_age=41s                                                                    sd 6:0:0:0: [sdd] tag#5 CDB: Read(10) 28 00 00 89 1c 00 00 01 00 00
      blk_update_request: I/O error, dev sdd, sector 8985600 op 0x0:(READ) flags 0x0 phys_seg 32 prio class 0


      1. sdy
        09.01.2024 22:55

        Можете сфоткать что на том выводе сейчас висит? Можно глянуть мультиметром есть ли сброс на том выводе когда Линукс как вы говорите его сбрасывает. Хотя лучше осциллом, если там импульс

        Похоже Линукс сбрасывает чип адаптера, а сам чип может и не дёргает perst

        Самое простое туда подать ноль самому и проверить сработает ли это также как и питанием подвергать. Но надо глянуть что там сейчас на выводе. Возможно просто подтяжка, а самого управления нет, а может есть. Просто может сброс быстрее будет отрабатываться чем питанием дергать. Тогда и время можно сократить будет.


        1. gumanzoy Автор
          09.01.2024 22:55
          +1

          Сфотографировал. Слишком мелкие контакты. Щупами туда лезть я не буду.

          Фото

          По сути для ускорения процесса копирования, даже если удастся как то задействовать PERST#, и это сработает - это ничего не даст. Так как большую часть времени занимает ожидание обработки ошибки прошивкой SSD, ядром Linux и процессом ddrescue

          В плане софта возможно еще есть потенциал куда дорабатывать.

          Единственное правильное решение это расковырять прошивку SSD и отключить контроль ошибок. Но это решение не универсальное.


          1. VelocidadAbsurda
            09.01.2024 22:55

            А без этого JMicron-адаптера, напрямую в PCIe слоте как оно себя ведёт? В таком виде должно быть куда больше возможностей настройки, к примеру, можно попробовать уменьшить /sys/module/nvme_core/parameters/io_timeout, да и reset какой-нибудь там же поискать (временно нахожусь вдали от машин с NVMe, не на чем посмотреть подробнее).


            1. gumanzoy Автор
              09.01.2024 22:55
              +1

              Именно эти диски не проверял. Так как их прислали отдельно от моноблоков где они стояли.

              Но другие вели себя так: после ошибки пропадает из системы и до отключения включения компа не работает.

              Вот например такой Kingston RBUSNS8154P3128GJ3 Выдавал в dmesg вот это. И после этого ничего не сделать до отключения включения всего ноутбука.

              nvme nvme0: I/O 773 QID 4 timeout, aborting
              nvme nvme0: I/O 0 QID 0 timeout, reset controller

              То есть нужно тогда делать аппаратное отключение питания для M.2 слота. Можно наверно через какой нибудь райзер сделать. Но из за глюков по шине PCI-E комп может вообще зависнуть. Некоторые SSD вешали систему.


              1. equand
                09.01.2024 22:55

                Можно просто удалять PCIe девайс и делать рескан, диск под новым номером проявится.

                Это проще делается в BSD, диск проще в BSD копируется (т.к. утилита камконтрол удобнее). Однако в Линуксе это тоже возможно. Что помогает уменьшить потребление сломанного диска, это позволяет уменьшить скорость и копировать медленно, другой вариант через USB.

                Чаще всего у диска проблема с полной шиной (Привет Кингстон), если у кингстона срезать скорость даже на один уровень (с 4гигабайт до 300мегабайт) то он работает более менее стабильно на копирование. Другой вариант впаять его в PCIe3 контроллер, тоже будет работать.


                1. gumanzoy Автор
                  09.01.2024 22:55
                  +1

                  Можно просто удалять PCIe девайс и делать рескан, диск под новым номером проявится.

                  Это не работает для неисправных дисков. Сегодня еще раз проверил. Затем комп вообще зависает.

                  Вот вчера принесли комп HP с SSD M.2 NVMe Kioxia. На бэдах отваливается, при этом пропадает содержимое /sys/class/nvme* и даже выгрузка и повторная подгрузка модулей nvme nvme_core не помогает. Но при этом комп продолжает работать (кроме nvme подсистемы), а не зависает намертво как с Kimtigo.

                  При этом при подключении через USB3 мост обрабатывает ошибку и читает дальше.

                  Скрины

                  Чаще всего у диска проблема с полной шиной (Привет Кингстон)

                  Не встречал такого. Все ссд и SATA и PCI-E отваливались строго на определенных секторах. Если попытаться прочитать именно битый сектор - сразу отвалится. То есть дело в том что прошивка диска зависает вместо обработки ошибки.


            1. gumanzoy Автор
              09.01.2024 22:55
              +4

              Проверил. Вставил в M.2. Один из этих Kimtigo.

              Загрузил Linux с флешки. Запустил dmesg -Wt и ddrescue

              Сначала ddrescue зависла. В dmesg одно сообщение

              nvme nvme1: I/O 385 QID 2 timeout, aborting

              Через минуту завис комп полностью.

              Думаю что бесперспективно пытаться через PCI-E глючные SSD читать.


  1. olegkrutov
    09.01.2024 22:55

    Удобней ориентироваться не на /dev/sdX, а на что-то типа /dev/disk/by-id, оно стабильно привязано к данному накопителю.


    1. gumanzoy Автор
      09.01.2024 22:55

      Можно. Но для надежного перезапуска ddrescue в цикле это не подходит. Так как неисправные HDD/SSD могут в процессе подглючивания при пере-подключении в очередной раз определиться как другая модель и с другим SN. И соответственно путь в /dev/disk/by-id тоже изменится.

      Поэтому реализовал в скрипте другой метод. Для SATA устройств - номер порта. Для USB - ID VID:PID