Lo hice y lo entendí

El blog de Vicente Navarro
19 nov

Solucionando el error “attempt to access beyond end of device” con reglas de udev, hal y/o un parche del kernel

Recientemente actualicé mi Debian Lenny/Testing como suelo hacer de vez en cuando. No sé exactamente qué paquetes se actualizarían pero empezó a ocurrir algo que no me gustaba: Tres veces durante el arranque se me llenaba la pantalla de cientos de mensajes como el siguiente:

attempt to access beyond end of device
sdb: rw=0, want=3636361172, limit=398297088
attempt to access beyond end of device
sdb: rw=0, want=3636361176, limit=398297088

Y uno de los problemas que me generaban estos errores es que me llenaban el buffer circular de mensajes del kernel y no podía ver los mensajes de arranque del kernel con dmesg.

Yo tengo dos discos Maxtor SATA iguales en RAID 0 usando los servicios de la placa con el chipset NVidia NForce 4. El RAID se activa automáticamente durante el arranque gracias a que el initrd ejecuta el comando dmraid durante el arranque (ya salió esto en Hibernación en Linux con TuxOnIce. Notas sobre los initrd y sobre cpio.). Tras ejecutar el dmraid, unos nuevos ficheros de dispositivo que representan al disco lógico formado por los dos discos físicos son creados en /dev/mapper/ :

# ll /dev/mapper/
total 0
drwxr-xr-x  2 root root     180 2007-11-18 22:03 ./
drwxr-xr-x 14 root root    5880 2007-11-18 21:04 ../
crw-rw----  1 root root  10, 63 2007-11-18 22:03 control
brw-rw----  1 root disk 254,  0 2007-11-18 22:03 nvidia_bdehcbaa
brw-rw----  1 root disk 254,  1 2007-11-18 22:03 nvidia_bdehcbaa1
brw-rw----  1 root disk 254,  2 2007-11-18 22:03 nvidia_bdehcbaa2
brw-rw----  1 root disk 254,  3 2007-11-18 21:03 nvidia_bdehcbaa3
brw-rw----  1 root disk 254,  4 2007-11-18 22:03 nvidia_bdehcbaa5
brw-rw----  1 root disk 254,  5 2007-11-18 22:03 nvidia_bdehcbaa6

Los ficheros de dispositivo de los discos físicos (en mi caso /dev/sdb y /dev/sdc) no tienen ninguna utilidad desde ese momento.

Sin embargo, en el primer sector del primero de los de los discos del RAID, en /dev/sdb sí hay una tabla de particiones, lo que pasa es que no tiene sentido aplicada a ese disco, y sí aplicada a todo el RAID:

# fdisk /dev/mapper/nvidia_bdehcbaa

Command (m for help): p

Disk /dev/mapper/nvidia_bdehcbaa: 407.8 GB, 407856087040 bytes
255 heads, 63 sectors/track, 49585 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0xffffffff

                      Device Boot      Start         End      Blocks   Id  System
/dev/mapper/nvidia_bdehcbaa1   *           1        4079    32764536    7  HPFS/NTFS
/dev/mapper/nvidia_bdehcbaa2            4080        8158    32764567+   c  W95 FAT32 (LBA)
/dev/mapper/nvidia_bdehcbaa3            8159        9375     9775552+  83  Linux
/dev/mapper/nvidia_bdehcbaa4            9376       49585   322986825    f  W95 Ext'd (LBA)
/dev/mapper/nvidia_bdehcbaa5            9376        9862     3911796   82  Linux swap / Solaris
/dev/mapper/nvidia_bdehcbaa6            9863       49585   319074966   83  Linux

# fdisk /dev/sdb

Command (m for help): p

Disk /dev/sdb: 203.9 GB, 203928109056 bytes
255 heads, 63 sectors/track, 24792 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00100058

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1   *       92547      133645   330121216   ec  Unknown
Partition 1 does not end on cylinder boundary.
/dev/sdb2          158380      304655  1174951567+   0  Empty
Partition 2 does not end on cylinder boundary.
/dev/sdb3          226354      301364   602522742   83  Linux
Partition 3 does not end on cylinder boundary.
/dev/sdb4               4           4           0    0  Empty
Partition 4 does not end on cylinder boundary.

Por tanto, cualquier acceso a esas particioness (sdbn) tendrá como resultado el error del que hablábamos al principio.

# dd if=/dev/sdb1 of=/dev/null count=1 bs=512 2>&1
attempt to access beyond end of device
sdb: rw=0, want=1486754274, limit=398297088
Buffer I/O error on device sdb1, logical block 0
attempt to access beyond end of device
sdb: rw=0, want=1486754282, limit=398297088
Buffer I/O error on device sdb1, logical block 1
attempt to access beyond end of device
sdb: rw=0, want=1486754290, limit=398297088
Buffer I/O error on device sdb1, logical block 2
attempt to access beyond end of device
sdb: rw=0, want=1486754298, limit=398297088
Buffer I/O error on device sdb1, logical block 3
attempt to access beyond end of device
sdb: rw=0, want=1486754274, limit=398297088
Buffer I/O error on device sdb1, logical block 0
dd: reading `/dev/sdb1': Input/output error
0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.129062 seconds, 0.0 kB/s

Entonces, ¿por qué salen tres bloques con cientos de errores durante el arranque? ¿Qué hay durante el arranque que trate de acceder a dichas particiones?

Tras muchas pruebas, conseguí determinar que el demonio udevd que se ejecuta desde el initrd, el que se ejecuta de nuevo una vez montado el sistema de ficheros raíz y el demonio hald eran los tres procesos que causaban la avalancha de errores.

Por tanto, toca repasar la entrada Creación de reglas udev para personalizar el nombre de los ficheros de dispositivo de las memorias USB y el documento Writing udev rules para ver cómo podemos evitar que el udevd trate de escanear dichas particiones inexistentes.

Y es en la opción ignore_device donde encontramos una solución al problema. ¡Pero ojo! Si creamos una regla como esta:

BUS=="scsi", ATTRS{model}=="Maxtor 6B200M0  ", OPTIONS+="ignore_device"

los ficheros de los discos sdb y sdc no se crearán, el dmraid no podrá formar el RAID y no conseguiremos arrancar el sistema (el kernel nos dará un panic, de hecho). Es mucho mejor decirle al udev que simplemente no nos cree los ficheros de dispositivo de las particiones:

KERNEL=="sdb1", OPTIONS+="ignore_device"
KERNEL=="sdb2", OPTIONS+="ignore_device"
KERNEL=="sdb3", OPTIONS+="ignore_device"
KERNEL=="sdb4", OPTIONS+="ignore_device"

Si reiniciamos tras crear la regla, no tendremos los errores de uno de los udevd pero sí del que se ejecuta desde el initrd. Para evitarlo, no tenemos que olvidar hacer un mkinitramfs para que las reglas se actualicen también en el initrd.

Comprobamos que ya no se crean los ficheros de dispositivo de las particiones:

# ll /dev/sdb*
brw-rw---- 1 root disk 8, 16  2007-11-18 23:02 sdb

Tras modificar las reglas, como ya no hay ficheros de dispositivo, el hald ya no genera errores tampoco, pero en mis pruebas he encontrado la forma de que no dé errores aunque los ficheros de dispositivo existieran. Usando como ejemplo las reglas existentes para ejecutar antes de probar los dispositivos en /usr/share/hal/fdi/preprobe/, creo el fichero etc/hal/fdi/preprobe/10-vicente.fdi con el siguiente contenido:

<?xml version="1.0" encoding="UTF-8"?>

<deviceinfo version="0.2">
   <device>
      <match key="scsi.model" string="Maxtor 6B200M0">
         <merge key="info.ignore" type="bool">true</merge>
      </match>
   </device>
</deviceinfo>

Con esta regla, estoy diciéndole al hald que ignore los dispositivos con el scsi.model que a mí me interesa. La información necesaria para construir las reglas la podemos obtener del comando lshal. Precisamente, tras crear la regla y hacer un /etc/init.d/hald restart vemos que en la salida del lshal ya nos aparece el dato de que el dispositivo va a ser ignorado (info.ignore = true (bool)):

udi = '/org/freedesktop/Hal/devices/temp/30'
  info.bus = 'scsi'  (string)
  info.ignore = true  (bool)
  info.linux.driver = 'sd'  (string)
  info.parent = '/org/freedesktop/Hal/devices/pci_10de_55_scsi_host_0'  (string)
  info.product = 'Ignored Device'  (string)
  info.subsystem = 'scsi'  (string)
  info.udi = '/org/freedesktop/Hal/devices/ignored-device'  (string)
  linux.hotplug_type = 2  (0x2)  (int)
  linux.subsystem = 'scsi'  (string)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:08.0/host4/target4:0:0/4:0:0:0'  (string)
  scsi.bus = 0  (0x0)  (int)
  scsi.host = 4  (0x4)  (int)
  scsi.lun = 0  (0x0)  (int)
  scsi.model = 'Maxtor 6B200M0'  (string)
  scsi.target = 0  (0x0)  (int)
  scsi.type = 'disk'  (string)
  scsi.vendor = 'ATA'  (string)

Esto podemos usarlo también si no nos interesa por algún motivo que al introducir ciertas memorias USB no nos salga el típico diálogo de posibles acciones a realizar con el dispositivo introducido. Y también podemos configurar el montaje automático de las unidades extraíbles con reglas del hald. Podemos echarle un vistazo a las reglas comentadas de /etc/hal/fdi/policy/preferences.fdi para entender un poco cómo funciona.

Pero aún tenemos una alternativa mejor que crear reglas de udev o hal. En avoid creation of partitions beyond actual capacity (patch for 2.6.15), Gerte Hoogewerf propone un parche para que el kernel evite las particiones que supuestamente van más allá de la capacidad del disco. Se trata únicamente de una pequeña modificación en el fichero fs/partitions/check.c. Como el parche no se aplica sobre el kernel 2.6.22.10 lo he adaptado y lo dejo aquí a disposición del que lo quiera usar: partition_check_2.6.22.10.patch

Para aplicarlo hacemos:

# cd /usr/src/linux
# patch -p1 < /path_al_parche/partition_check_2.6.22.10.patch
patching file fs/partitions/check.c

Y si queremos quitarlo hacemos lo mismo pero con la opción -R:

# patch -p1 -R < /path_al_parche/partition_check_2.6.22.10.patch
patching file fs/partitions/check.c

El parche debería de servir para otras versiones del kernel recientes. Podemos probar a aplicarlo en otras versiones. Si no es aplicable, simplemente no lo aplicará:

# patch -p1 < /path_al_parche/partition_check_2.6.22.10.patch  
patching file fs/partitions/check.c
Hunk #1 FAILED at 182.
1 out of 1 hunk FAILED -- saving rejects to file fs/partitions/check.c.rej

Tras aplicar el parche, recompilar el kernel y reiniciar con el nuevo kernel parcheado, vemos que las particiones erróneas son descartadas:

sd 3:0:0:0: [sdb] 398297088 512-byte hardware sectors (203928 MB)
sd 3:0:0:0: [sdb] Write Protect is off
sd 3:0:0:0: [sdb] Mode Sense: 00 3a 00 00
sd 3:0:0:0: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
sd 3:0:0:0: [sdb] 398297088 512-byte hardware sectors (203928 MB)
sd 3:0:0:0: [sdb] Write Protect is off
sd 3:0:0:0: [sdb] Mode Sense: 00 3a 00 00
sd 3:0:0:0: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
 sdb: sdb1 sdb2 sdb3
 sdb: partition sdb1 beyond device capacity
 sdb: partition sdb2 beyond device capacity
 sdb: partition sdb3 beyond device capacity
sd 3:0:0:0: [sdb] Attached SCSI disk

¡Así que ya está el problema resuelto por este otro camino! ¿No os parece que este parche se debería de incluir en el kernel estándar?

Anexo sobre cómo crear un parche del kernel

Para crear un parche del kernel, nos preparamos dos directorios iguales con las fuentes del kernel totalmente limpias. Podemos descomprimir las fuentes del kernel y renombrar el directorio donde se han descomprimido o hacer un make mrproper al un kernel que ya hemos configurado y compilado, pero perderemos el .config y habrá que recompilar todo desde cero.

Por ejemplo, podemos hacer (cuidado con no reemplazar las fuentes que solemos usar si descomprimimos en /usr/src):

# tar xvfz linux-2.6.XX.YY.tar.bz2
# mv linux-2.6.XX.YY linux-2.6.XX.YY-vanila
# cp -rp linux-2.6.XX.YY-vanila linux-2.6.XX.YY-new

Hacemos las modificaciones en el kernel del directorio linux-2.6.XX.YY-new y obtenemos el parche ejecutando, desde fuera de ambos directorios:

# diff -uNr linux-2.6.XX.YY-vanila linux-2.6.XX.YY-new > foobar.patch

Actualización 9/12/07: El parche parece que funciona bien con el kernel 2.6.23.9.

:wq

Entradas relacionadas

10 Comentarios a “Solucionando el error “attempt to access beyond end of device” con reglas de udev, hal y/o un parche del kernel”

  • Iván dice:

    Muy interesante la entrada!, siempre aprendo algo nuevo con tus posts. Sigo echando de menos ese artículo sobre las distintas maneras de compilar el kernel ;-) .

    Saludos, Iván.

  • @Iván ¡Gracias! Te prometo que está en la cola ;-)

  • Ringmaster dice:

    ¡Uauh!! Ya me gustaría a mi dominar linux tan a fondo como tú. Por otro lado, estaría bien que hubiera un apartado “Sobre mí”, donde hicieras un resumen sobre cómo has cogido estos conocimientos, nada personal, simplemente para saber qué hay que hacer para llegar a este nivel y dejar de pensar que eres de otra galaxia ;-), sino que es posible con trabajo y constancia.
    De verdad, con sinceridad, a los lectores como yo les pica la curiosidad sobre la trayectoria profesional de los que nos sorprenden por su competencia.

  • @Ringmaster Desde luego, realmente te has empeñado en sonrojarme :P .

    Hace unos meses ya escribí una especie de “Sobre mí y Linux”: Los Linux de mi vida

    Así que es sólo eso, la experiencia y leer documentación y más documentación :-) .

  • Ringmaster dice:

    Gracias por el enlace a tan nostálgico anuncio, ¡Super Coco! ¿Para cuándo un listado de tus entradas más leídas? (Y así no se me pasarían entradas tan interesantes como esa, ya sé que no sería una solución perfecta, pero bueno)…

  • @Ringmaster Aunque la hubiera, ya te digo yo que precisamente esa no estaría entre ellas :D

  • RuBiCK dice:

    Muy buen artículo, interesante y bien explicado ;)

  • NeoPolus dice:

    Hola Super Coco, tengo una duda sobre ‘preferencias’: ¿dmraid o mdadm?

    Me he fijado en que usas dmraid y, ‘sabiendo que sabes’ más que yo, me he preguntado ‘qué sabe él que yo (que nunca he usado dmraid, sólo mdadm) no sé’ :)

    Por ahora a mi me ha ido muy bien con mdadm, concretamente me ha sorprendido la flexibilidad:
    Comparto dos discos (/dev/hdb y /dev/sda; ¡funcionando sobre controladoras diferentes!: uno IDE, el otro SATA) con una partición RAID 0 (juegos), una partición RAID 1 (mis documentos / home) y una partición linear/extendida (para aprovechar el resto de espacio, que no es el mismo en ambos discos, para ‘descargas’) entre un Windows XP* y Ubuntu (y hay mas particiones en esos dos discos).

    Por lo que tengo entendido a diferencia de mdadm (que es ‘softwareRAID’) dmraid es para usar con ‘fakeRAID’. Esto supongo que limitará su uso a placas base con controladoras que soporten RAID en la BIOS (mi controladora SATA lo soporta, la IDE no). Pero en cualquier caso el ‘fakeRAID’ (a diferencia del ‘RAID hardware’ de verdad) acaba realizándose a nivel software (aunque inicialmente este software sea la BIOS), con lo que no deberían existir diferencias de rendimiento significativas.

    Así que volviendo al principio: ¿qué sabes tú que yo no sé :)? ¿por qué en tu caso usas dmraid?, en resumen: ¿donde tiene ventaja dmraid sobre mdadm?

    Y ya que estamos, otra pregunta: ¿alguien ha probado a montar desde mdadm discos antes montados con dmraid o viceversa?

    * Por si alguien no lo sabe: Windows XP sólo soporta oficialmente RAID 0 por software**, pero se puede parchear para que soporte RAID 1 (y RAID 5) por software; ver http://www.tomshardware.com/2004/11/19/using_windowsxp_to_make_raid_5_happen/ o un resumen en español en http://wiki.mundodisco.net/index.php/RAID-1_y_RAID-5_en_Windows_XP
    ** Windows soporta RAID software para todos los discos menos el del sistema: no puede arrancar desde RAID software; pero si soporta arrancar desde fakeRAID. (Linux no tiene esta restricción.)

  • @NeoPolus Has explicado perfectamente la diferencia entre mdadm y dmraid. Yo uso el RAID de la BIOS porque me permite tener dos discos iguales SATA en RAID0 de forma transparente independientemente de si estoy en Windows o en Linux. Probablemente, para un sistema sólo con Linux sea mucho más flexible usar mdadm, pero así puedo usar las mismas particiones en RAID0 desde ambos sistemas operativos.

Tema LHYLE09, creado por Vicente Navarro