Riallocazione di un settore illeggibile in una partizione LVM

linuxNelle più recenti distribuzioni Linux è presente smartd,una utility per il monitoraggio dei dischi ATA,IDE,SATA e SCSI.

SMART (Self-Monitoring,Analysis and Reporting Technology) è una tecnologia sviluppata per monitorare l’affidabilità degli hard disk, eseguire dei test di funzionamento e quindi di tentare di prevedere eventuali failures.

Talvolta ci sarà capitato di rilevare errori riportati dal kernel,dal demone smartd o tramite sistemi di altra natura,del tipo

Apr 17 16:20:31 seti kernel: hde: dma_intr:
       status=0x51 { DriveReady SeekComplete Error }

Apr 17 16:20:31 seti kernel: hde: dma_intr:
                   error=0x40 { UncorrectableError }.
                      LBAsect=6305975, sector=6305901

Questo genere di errori sono tipicamente corrispondenti ad errori presenti in lettura e scrittura sul nostro hard disk.Inoltre tali errori, in ambiente SMART,corrispondono a messaggi di log del tipo:

 Apr 17 16:20:32 seti smartd[2253]: Device: /dev/hde. 1 Currently unreadable (pending) sectors

che inequivocabilmente ci informano circa l’impossibilità di accedere in lettura o in scrittura ad un settore (danneggiato) del disco. A seconda della tipologia del settore illegibile il danno potrebbe essere di diversa entità:uno dei casi peggiori vede posizionato il settore all’interno di una partizione LVM,ovviamente sul disco singolo (mai in un RAID che darebbe maggiore salvaguardia al dato).

E’ presente in rete un ottimo Bad Block HOWTO che presenta ottimi suggerimenti che vedremo di riassumere brevemente. Cerchiamo, prima di tutto, di individuare l’errore: cominciamo con un semplice test eseguito sul disco sul quale sono presenti gli errori sopraesposti:

# smartctl -t log /dev/hde

In questo caso attraverso l’utility smartctl sarà eseguito un test approfondito, dalla durata di circa mezz’ora, sul device /dev/hde. Al termine dello stesso, sarà possibile recuperare i risultati del test appena terminato, in questo modo:

# smartctl -l selftest /dev/hde
# 1 Extended offline Completede: reade failure 40% 630597

Come si può notare dall’output restituito, il test appena eseguito si è interrotto al 400/o avendo individuato un errore nell’indirizzamento di blocco logico (Logica\ Block Addressing – LBA) corrispondente al settore numero 6305975 come evidenziato anche nel messaggio del kernel. Andiamo a controllare come è partizionato il nostro disco fisso:

#fdisk -lu /dev/hde

Device    Boot Start  End       Blocks   Id System 
/dev/hdel *    63     208844    104391   83 Linux 
/dev/hde2      208845 120101939 59946547+8e Linux LVM

La prima cosa che facilmente si nota è che l’indirizzo logico del blocco si riferisce ad un settore presente nella partizione Linux LVM /dev/hde2.
A questo punto, però, si deve fare attenzione: l’LBA dell’errore restituito in precedenza, 6305975, costituisce il valore assoluto di indirizzamento logico della memoria di massa complessiva, per individuare il volume logico servirà un offset relativo alla seconda partizione. Di fatto sottrarremo dal valore dell’indirizzo riportato in precedenza il valore dell’indirizzo dal quale inizia la partizione LVM interessata.
Quindi, nel nostro esempio, l’offset cercato sarà:

6305975 - 208845 = 6097130 (FS block offset)

In presenza di allocazione lineare, con i comando pvdisplay sarà possibile rilevare la dimensione dei Physical Extent (PE). nel nostro caso 32768. Considerando che il physical block size del sistema corrisponde a 512 bytes (cioè metà KB), si avrà che la dimensione di ogni PE sarà uguale a:

  32768 * 2 = 65536 (PE physical size)

Per determinare in quale PE si trovi il blocco logico rovinato, sarà sufficiente dividere l’FS block offset precedentemente calcolato per la dimensione di ogni PE appena ottenuta:

 6097130 / 65536 = 93.034 -> 93 (#PE)

Sappiamo perciò che il blocco logico in questione risiede sul 93-esimo PE. Di fatto, attraverso l’output del comando:

# lvdisplay --maps |egrep 'Physical|LV Name|Type'

LV Name /dev/VolGroup00/LogVol00 
   Type linear 
   Physical volume /dev/hde2 
   Physical extents 0 to 1800 
   
LV Name /dev/VolGroup00/LogVol0l 
   Type linear 
   Physical volume /dev/hde2 
   Physical extents 1801 to 1828

possiamo conseguentemente determinare che il PE conside-rato appartiene al primo Logical Volume (LV) chiamato /dev/VolGroup00/LogVol00 del Volume Group (VG) chiamato /dev/VolGroup00.
Visto che ora si andrà ad intervenire scendendo dal livello di partizione LVM a livello di LV, ci occorrerà anche sapere l’offset iniziale di quest’ultimo, ottenuto sommando il blocco logico di partenza del VG e tutti i blocchi logici relativi ai PE degli LV precedenti. Tramite il comando:

# egrep pe_start $(grep -l $part /etc/lvm/backup/*) 

pe_start = 384

sarà quindi prima possibile determinare il blocco logico di partenza del VG; poi, osservando che il nostro LV è di fatto il primo LV del VG, si determina che il primo PE di /dev/VolGroup00/LogVol00 è uguale a 0 (mentre se ad esempio avessimo dovuto lavorare sul secondo LV sarebbe stato uguale a 1801). Quindi risulterà:

384 +(0 * 65536) = 384 (LV offset)

Finalmente sarà possibile determinare il blocco fisico danneggiato del file-system secondo la formula: Finalmente sarà possibile determinare il blocco fisico danneggiato del file-system secondo la formula:

((FS block offset) - (LV offset)) / ((PE physical size) / (physical block size))

ovvero

((6097130 - 384) / (65536 / 512)) = (6096746 / 128) = 47630.828 -> 47630

Nel calcolo è da notare come il valore 128 corrisponda al numero di blocchi fisici presenti in ogni PE. Siamo finalmente arrivati!

Testiamo ora che effettivamente il blocco fisico #47630 sia danneggiato attraverso il comando dd e l’opzione skip che consente di leggere l’input saltando i primi 47630 blocchi
fisici (ovvero tentando di leggere esattamente il blocco fisico danneggiato):

# dd if=/dev/VolGroupOO/LogVol00 of=/~/block-47630 bs=65536 count=l skip=47630 

dd: reading '/dev/VolGroup00/LogVol00': Input/output errar

E’ di sicuro interesse conoscere quali file siano impattati dal o dai settori danneggiati: è possibile utilizzare, allo scopo, il comando debugfs, in un modo analogo al seguente

# debugfs /dev/VolGroup00/LogVol00 

debugfs: icheck 47630 
Block Inode number 
47630 11020 
debugfs: ncheck 11020 
Inode     Pathname 
11020 /var/www/test.php

In questo caso siamo stati fortunati e il danno è tutto sommato limitato (un semplice file di test sul server di sviluppo), ma ovviamente non si può contare sempre sulla buona sorte (Murphy è, come noto, in agguato).
Non ci resta altro da fare che formattare il blocco fisico danneggiato (scrivendo zeri per una lunghezza pari alla dimensione del PE physical size) attraverso il comando dd e l’opzione seek che consente di scrivere in output saltando i primi 47630 blocchi fisici (ovvero accedendo esattamente al blocco fisico danneggiato):

# dd if=/dev/zero of=/dev/VolGroup00/LogVol00 
count=l bs=65536 seek=47630 

Il settore verrà riallocato in maniera autonoma da disco, e andrà inserito nella cosiddetta glist (grown list), che contiene appunto i settori illeggibili che vengono riassegnati.
In alternativa, si possono utilizzare le utility del pacchetto sg3_utils, che permettono di interagire con i device SCSI (quando il dispositivo è SCSI, ovviamente): in particolare si dovranno utilizzare i tool sg_verify e sg_reassign, rispettivamente per controllare l’effettivo danneggiamento del settore e per forzare il riassegnamento. Seguirà, come visto in precedenza, la formattazione del settore incriminato.

# sg_verify --lba:6305975 /dev/sda 
# sg_reassign --address=6305975 /dev/sda