Ошибки ATA в Linux: Перевод в название устройства?

Вопрос или проблема

Когда система Linux получает ошибку ATA, она записывает ее в системный журнал с сообщением, идентифицирующим диск как “ata%d.00”. Как мне это перевести в имя устройства (например, /dev/sdb)? Мне кажется, это должно быть тривиально, но я не могу понять, как это сделать.

Питер вдохновил меня написать продвинутый скрипт (скриптлет), который может даже обнаруживать USB-накопители (вместо того чтобы выводить глупости вроде “ata0.00”). В отличие от скрипта Питера, вы также получите подномер (как в 4.01), если у вас есть более одного устройства на том же контроллере или канале. Вывод будет EXACTLY таким же, как вы его получаете в syslog. Испытано. Работает очень хорошо на моей Debian машине, хотя всегда есть много возможностей для улучшения (например, слишком громоздкие регулярные выражения). Но СТОП! Кажется, слишком большое количество экранированных символов, которое вы можете найти в моих регулярных выражениях, предназначено только для совместимости! Вы не можете предполагать, что все используют GNU sed, именно поэтому я сознательно обошелся без расширенных регулярных выражений.

ОБНОВЛЕНИЯ
(1) Больше не будет разбирать вывод ls. (упс!) Поскольку вы все знаете: Не разбирайте ls.
(2) Теперь также работает в средах только для чтения.
(3) Вдохновленный предложением из этого чата здесь, я снова сделал выражения sed намного менее сложными.

#!/bin/bash
# примечание: вдохновлено Питером
#
# *ОБНОВЛЕНИЕ 1* теперь мы больше не разбираем вывод ls
# *ОБНОВЛЕНИЕ 2* теперь мы используем массив вместо оператора <<<, который требует
# наличия записываемого каталога /tmp:
# ограниченные среды с доступом только для чтения часто не позволят вам этого сделать

# сохранить оригинальный IFS
OLDIFS="$IFS"

for i in /sys/block/sd*; do
 readlink $i |
 sed 's^\.\./devices^/sys/devices^ ;
      s^/host[0-9]\{1,2\}/target^ ^ ;
      s^/[0-9]\{1,2\}\(:[0-9]\)\{3\}/block/^ ^' \
 \
  |
  while IFS=' ' read Path HostFull ID
  do

     # СТАРАЯ строка: оставлена по причинам читабельности
     # IFS=: read HostMain HostMid HostSub <<< "$HostFull"

     # НОВЫЕ строки: теперь также будут работать без проблем в средах только для чтения
     IFS=: h=($HostFull)
     HostMain=${h[0]}; HostMid=${h[1]}; HostSub=${h[2]}

     if echo $Path | grep -q '/usb[0-9]*/'; then
       echo "(Устройство $ID не является устройством ATA, а является устройством USB [например, флеш-накопителем])"
     else
       echo $ID: ata$(< "$Path/host$HostMain/scsi_host/host$HostMain/unique_id").$HostMid$HostSub
     fi

  done

done

# восстановить оригинальный IFS
IFS="$OLDIFS"

Посмотрите на /proc/scsi/scsi, который будет выглядеть примерно так:

$ cat /proc/scsi/scsi
Прикрепленные устройства:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
  Поставщик: ATA       Модель: ST3250823AS       Рев: 3.03
  Тип:   Прямой доступ                       ANSI SCSI revision: 05
Host: scsi1 Channel: 00 Id: 00 Lun: 00
  Поставщик: ATA       Модель: ST3750528AS       Рев: CC44
  Тип:   Прямой доступ                       ANSI SCSI revision: 05
Host: scsi2 Channel: 00 Id: 00 Lun: 00
  Поставщик: ATA       Модель: ST3750330AS       Рев: SD1A
  Тип:   Прямой доступ                       ANSI SCSI revision: 05
Host: scsi10 Channel: 00 Id: 00 Lun: 00
  Поставщик: WDC WD20 Модель: EARS-00MVWB0      Рев:     
  Тип:   Прямой доступ                       ANSI SCSI revision: 02

scsi0 id 0 это sda и ata1.00, scsi1 id 0 это sdb и ata2.00 и так далее.

Также посмотрите на /var/log/dmesg, который показывает информацию о загрузке драйвера ata и немного проясняет ситуацию. Ищите строку, начинающуюся с “libata”.

Я предпочитаю скриптлеты вместо длинных объяснений. Это работает на моей системе Ubuntu. Добавьте комментарии по вашему усмотрению:

# на Ubuntu получить идентификатор ata для блочных устройств sd*
ls -l /sys/block/sd* \
| sed -e 's^.*-> \.\.^/sys^' \
       -e 's^/host^ ^'        \
       -e 's^/target.*/^ ^'   \
| while read Path HostNum ID
  do
     echo ${ID}: $(cat $Path/host$HostNum/scsi_host/host$HostNum/unique_id)
  done

Попробуйте это:

# find -L /sys/bus/pci/devices/*/ata*/host*/target* -maxdepth 3 -name "sd*" 2>/dev/null | egrep block |egrep --colour '(ata[0-9]*)|(sd.*)'

Я никогда не понимал dmesg – некоторые строки касаются “ata4”, другие “scsi” или sdc, но никто не назначает “ata4 . . . sdc”. Команда, показанная выше, находит путь /sys/bus/, где оба ata4 и sdc указаны.

Только если ваша система udev его создаёт, вы можете просто ввести:

# udevadm info --query=all -n /dev/sd[a-z] |egrep ^P

и мы получим полный путь, включая пары ataX и sdY. Эта команда также показывает sdY, соответствующие USB и SAS (scsi) дисковым устройствам.

Следующая команда не дает правильного ответа, потому что она учитывает серию устройства ata-x для каждого драйвера sATA отдельно, и поэтому вы можете видеть ata-1 более одного раза.

# ls -l /dev/disk/by-path/ 
lrwxrwxrwx 1 root root 2020-06-17 12:01 pci-0000:00:1d.7-usb-0:3:1.0-scsi-0:0:0:0 -> ../../sdd 
lrwxrwxrwx 1 root root 2020-06-17 12:07 pci-0000:00:1f.2-ata-1 -> ../../sda 
lrwxrwxrwx 1 root root 2020-06-17 12:07 pci-0000:00:2f.2-ata-1 -> ../../sdb 
lrwxrwxrwx 1 root root 2020-06-17 12:07 pci-0000:00:2f.2-ata-2 -> ../../sdc

Это на самом деле довольно сложно. В то время как безопасно предположить, что “ID scsi” это “ID SATA минус один”, я предпочитаю быть действительно осторожным и проверять unique_id, который я предполагаю (основываясь на этой записи) является идентификатором SATA.

Моя ошибка была:

[6407990.328987] ata4.00: exception Emask 0x10 SAct 0x1 SErr 0x280100 action 0x6 frozen
[6407990.336824] ata4.00: irq_stat 0x08000000, interface fatal error
[6407990.343012] ata4: SError: { UnrecovData 10B8B BadCRC }
[6407990.348395] ata4.00: failed command: READ FPDMA QUEUED
[6407990.353819] ata4.00: cmd 60/20:00:28:c2:39/00:00:0c:00:00/40 tag 0 ncq 16384 in
[6407990.353820]          res 40/00:00:28:c2:39/00:00:0c:00:00/40 Emask 0x10 (ATA bus error)
[6407990.369618] ata4.00: status: { DRDY }
[6407990.373504] ata4: hard resetting link
[6407995.905574] ata4: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
[6407995.976946] ata4.00: configured for UDMA/133
[6407995.976961] ata4: EH complete

Так что моя процедура для определения того, что такое ata4:

  1. найдите PCI Id контроллера SATA

    # lspci | grep -i sata
    00:1f.2 SATA controller: Intel Corporation 631xESB/632xESB SATA AHCI Controller (rev 09)
    
  2. найдите соответствующий уникальный идентификатор:

    # grep 4 /sys/devices/pci0000:00/0000:00:1f.2/*/*/*/unique_id
    /sys/devices/pci0000:00/0000:00:1f.2/host3/scsi_host/host3/unique_id:4
    
  3. значит это на scsi_host/host3, что мы можем перевести в 3:x:x:x, что мы можем greppить в dmesg, чтобы узнать больше:

    # dmesg | grep '3:.:.:.'
    [    2.140616] scsi 3:0:0:0: Direct-Access     ATA      ST3250310NS      SN06 PQ: 0 ANSI: 5
    [    2.152477] sd 3:0:0:0: [sdd] 488397168 512-byte logical blocks: (250 GB/232 GiB)
    [    2.152551] sd 3:0:0:0: [sdd] Write Protect is off
    [    2.152554] sd 3:0:0:0: [sdd] Mode Sense: 00 3a 00 00
    [    2.152576] sd 3:0:0:0: [sdd] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
    [    2.157004] sd 3:0:0:0: [sdd] Attached SCSI disk
    [    2.186897] sd 3:0:0:0: Attached scsi generic sg3 type 0
    
  4. вот наше устройство, мы можем (по желанию) найти серийный номер, чтобы убрать это устройство (или проверить кабель и так далее), прежде чем наш RAID массив полностью выйдет из строя:

    # hdparm -i /dev/sdd | grep Serial
     Model=ST3250310NS, FwRev=SN06, SerialNo=9SF19GYA
    

И вы закончили!

У меня была такая же проблема, и я смог идентифицировать диски, проверив dmesg. Там вы можете увидеть идентификатор контроллера (правильный термин??) и модель диска. Затем используйте ls -l /dev/disk/by-id, чтобы сопоставить номер модели с /dev/sda (или чем-то другим). Кроме того, мне нравится использовать Disk Utility для этой информации. Примечание: это работает только в том случае, если ваши диски имеют разные номера моделей, в противном случае вы не сможете различить их.

>dmesg |grep ata
...
[   19.178040] ata2.00: ATA-8: WDC WD2500BEVT-00A23T0, 01.01A01, max UDMA/133
[   19.178043] ata2.00: 488397168 sectors, multi 16: LBA48 NCQ (depth 31/32), AA
[   19.179376] ata2.00: configured for UDMA/133
[   19.264152] ata3.00: ATA-8: WDC WD3200BEVT-00ZCT0, 11.01A11, max UDMA/133
[   19.264154] ata3.00: 625142448 sectors, multi 16: LBA48 NCQ (depth 31/32), AA
[   19.266767] ata3.00: configured for UDMA/133
...

>ls -l /dev/disk/by-id
lrwxrwxrwx 1 root root  9 Feb 18 12:17 ata-WDC_WD2500BEVT-00A23T0_WD-WXE1A7131446 -> ../../sda
lrwxrwxrwx 1 root root 10 Feb 18 11:48 ata-WDC_WD2500BEVT-00A23T0_WD-WXE1A7131446-part1 -> ../../sda1
lrwxrwxrwx 1 root root  9 Feb 18 12:17 ata-WDC_WD3200BEVT-00ZCT0_WD-WXHZ08045183 -> ../../sdb
lrwxrwxrwx 1 root root 10 Feb 18 11:48 ata-WDC_WD3200BEVT-00ZCT0_WD-WXHZ08045183-part1 -> ../../sdb1

Самый простой способ – просмотреть журналы ядра с запуска, так как имена устройств диска смешиваются из различных источников (например, USB-накопителей) или назначаются в зависимости от типа устройства (например, cdrom может быть вместо этого scdX, и всё имеет sgX). На практике, если у вас не смешаны разные типы шин (например, SATA+USB), то устройство ata с самым низким номером, скорее всего, будет sda, если это не устройство cdrom.

В зависимости от вашей системы, это может быть выяснено, блуждая по sysfs. На моей системе ls -l /sys/dev/block показывает, что 8:0 (major:minor из записи в /dev) указывает на /sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda. Аналогично, ls -l /sys/class/ata_port показывает, что ata1 указывает на /sys/devices/pci0000:00/0000:00:1f.2/ata1/ata_port/ata1, который находится на том же подустройстве PCI.

Поскольку я использую SATA, и на каждом порту находится только один диск, я могу сделать вывод, что ata1.00 = sda. Все мои диски имеют .00, я подозреваю, что если бы я использовал мультиплексор портов, моим дискам были бы даны номера .01, .02, .03 и т.д. Смотрев на журналы других людей, контроллеры PATA используют .00 и .01 для master и slave, и на основе их журналов, если у вас есть ataX.01, то .01 должно сопоставляться с “ID” в папке host:channel:ID:LUN из списка /sys/dev/block/. Если у вас есть несколько папок ataX/ и hostY/ в одной и той же папке PCI-устройства, я подозреваю, что папка с наименьшим номером ataX соответствует папке с наименьшим номером hostY.

В /sys/class/ata_port/ata${n}/device/ вы можете увидеть папку host${x}. Например, на моей машине:

gibby ~ # ls /sys/class/ata_port/ata1/device/
ata_port  host0  link1  power  uevent
gibby ~ # ls /sys/class/ata_port/ata2/device/
ata_port  host1  link2  power  uevent
gibby ~ # lsscsi
[0:0:0:0]    диск    ATA      WDC WD1002FAEX-0 1D05  /dev/sda
[1:0:0:0]    диск    ATA      WDC WD2001FFSX-6 0A81  /dev/sdb
[2:0:0:0]    диск    ATA      WDC WD1002FAEX-0 1D05  /dev/sdc
[3:0:0:0]    диск    ATA      WDC WD2001FFSX-6 0A81  /dev/sdd
[5:0:0:0]    диск    ATA      SAMSUNG MZ7TD256 2L5Q  /dev/sde

В host${x} ${x} относится к первому числу в [0:0:0:0]. Так что для меня ata1 относится к host0, который также может быть представлен в форме SCSI как 0:*:

gibby ~ # lsscsi 0:\*
[0:0:0:0]    диск    ATA      WDC WD1002FAEX-0 1D05  /dev/sda

Скрипт ниже даст вам хороший обзор, как это:

sda [  180.0 ГБ] INTEL SSDSC2BW180A4, BTDA4052066D1802GN pci0000:00/0000:00:11.0/ata1/host0/target0:0:0/0:0:0:0/block/sda
sdb [ 1000.2 ГБ] WDC WD1000DHTZ-04N21V1, WD-WXM1E83CNTX5 pci0000:00/0000:00:11.0/ata3/host2/target2:0:0/2:0:0:0/block/sdc
sdc [ ------ ГБ] -- pci0000:00/0000:00:12.2/usb1/1-5/1-5:1.0/host6/target6:0:0/6:0:0:0/block/sdf

Таким образом, в одной строке на диск вы имеете sdX имя устройства, размер, модель, s/n и номера pci и ata. Вывод sdc выше соответствует USB-считывателю карт SD без вставленной карты. Поэтому —- вместо реальной информации.

#!/bin/bash
BLKDEVS=`ls -l /sys/block/sd*|sed -e 's/^.* -> //' -e 's/^...devices.//'`
echo $BLKDEVS|tr \  \\n |sort| \
while read DISK ; do
    SD=`echo $DISK|sed -e 's/^.*\///'`
    INFO=`hdparm -i /dev/$SD 2>/dev/null|grep Model=|sed -e 's/Model=//' -e 's/FwRev=[^ ]*//' -e 's/SerialNo=//'`
    ! [[ $INFO ]] && INFO='--'
    SIZE=`fdisk -l /dev/$SD 2>/dev/null|grep '^Disk .* bytes'|sed -e 's/^[^,]*, \([0-9]*\) bytes$/\1/'`
    if [[ $SIZE ]] ; then
        SIZE=`echo $SIZE|awk '{printf "[%7.1f GB]" , $1/1000/1000/1000}'|tr \  _`
    else
        SIZE='[ ------ ГБ]'
    fi
    echo $SD $SIZE $INFO $DISK
done

(только протестировано на ubuntu 12.04/14.04 и CentOS 6)

Ответ или решение

Для переводов сообщений об ошибках ATA в Linux, которые обозначаются как "ata%d.00" в логах системного журнала, необходимо понять, как эти ошибки соответствуют конкретным именам устройств, например, /dev/sdb. Этот процесс требует внимательного анализа различных системных интерфейсов и файловой системы.

Этап 1: Понимание структуры имен устройств

В Linux устройства хранения представляются в виде файлов в каталоге /dev, где каждый диск имеет соответствующее ему имя, например, /dev/sda, /dev/sdb и т.д. В то же время, системная информация об этих устройствах доступна в файлах, находящихся в /sys, в частности, в /sys/block.

Ошибки ATA, такие как ataX.00, обозначают конкретный контроллер и устройство на этом контроллере. Здесь X соответствует номеру ATA-контроллера, а 00 – номер устройства. Чтобы перевести это в файл устройства, можно использовать информацию из системных файлов и утилит, таких как ls, dmesg, и udevadm.

Этап 2: Использование dmesg для диагностики

Команда dmesg показывает сообщения ядра, включая информацию о подключенных устройствах. Изучая вывод этой команды, можно найти информацию о подключенных ATA-накопителях:

dmesg | grep ata

Этот вывод будет содержать сведения о каждом подключенном ATA-устройстве, включая его идентификатор и другую полезную информацию, такую как размер и модель диска.

Этап 3: Сопоставление с именами устройств

Чтобы сопоставить ataX с именем устройства /dev/sdY, можно использовать следующую команду:

ls -l /dev/disk/by-id

Эта команда показывает символические ссылки, которые связывают физические устройства с их представлением в /dev. В выводе вы сможете увидеть информацию о том, какое устройство (например, sda, sdb) связано с ata.

Этап 4: Скрипт для автоматизации процесса

Чтобы упростить процесс сопоставления, можно использовать следующий Bash-скрипт, который автоматически извлекает необходимые данные:

#!/bin/bash

for i in /sys/block/sd*; do
    readlink $i | sed 's%^/devices/%/sys/devices/%; s%/host[0-9]*/target% %; s%:[0-9]:block/%/%' |
    while IFS=' ' read Path HostFull ID; do
        IFS=: h=($HostFull)
        HostMain=${h[0]}
        HostMid=${h[1]}
        HostSub=${h[2]}

        if echo $Path | grep -q '/usb[0-9]*/'; then
            echo "(Устройство $ID не является ATA, а представляет собой USB-устройство)"
        else
            echo $ID: ata$(< "$Path/host$HostMain/scsi_host/host$HostMain/unique_id").$HostMid$HostSub
        fi
    done
done

Этот скрипт обходит все блоковые устройства в системе, определяет их уникальные идентификаторы и сопоставляет с соответствующими именами устройств. Он полезен для диагностики и устранения ошибок, которые могут возникнуть в процессе работы с ATA-устройствами.

Заключение

Перевод ошибок ATA, упоминаемых в журналах Linux, в имена устройств, такие как /dev/sdb, требует глубокого понимания структуры файловой системы и способа, которым операционная система обозначает устройства хранения. Используя системные команды и предоставленный скрипт, можно эффективно диагностировать и решать проблемы с ATA-накопителями на вашем сервере или рабочей станции.

Оцените материал
Добавить комментарий

Капча загружается...