Lo hice y lo entendí

El blog de Vicente Navarro
01 nov

Hibernación en Linux con TuxOnIce. Notas sobre los initrd y sobre cpio.

Llevaba tiempo queriendo dedicarle tiempo a ver cómo está actualmente el panorama de la hibernación a disco en Linux. Ya lo había estado probando hace varios años, en tiempos del kernel 2.4, y la verdad es que estaba en pañales.

Yo sabía que existía el proyecto Suspend2, recientemente renombrado a TuxOnIce, y que parece ser que está dando muy buenos resultados. Pero fue cuando en la lista de novedades del kernel 2.6.20 leí que ya se incluían en el kernel grandes mejoras en la infraestructura necesaria para soportar la hibernación, que me decidí a retomar el tema.

Según leo en el artículo Suspend2 de la Wikipedia, parece que hay un tira y afloja entre los desarrolladores sobre si la parte que implementa Suspend2 debe de ir en el kernel o en el espacio de usuario. De momento, aún necesita parchear el kernel.

En cualquier caso, no hay que hacer muchas cosas para poder probar el TuxOnIce en una distribución que aún no lo soporte por defecto, y casi todas ellas vienen documentadas en el capítulo 2 del Software Suspend HOWTO. Resumiendo, hay que:

  1. Verificar que tenemos bastante espacio en la swap para almacenar todo el contenido de la memoria. Si no lo hay, se puede usar compresión o un fichero dedicado a esto.
  2. Parchear el kernel, compilarlo, instalarlo y arrancar con él.
  3. Hacer un pequeño cambio en los scripts del initrd.
  4. Añadir una opción de arranque al GRUB/LILO.
  5. Ejecutar el script hibernate.

Para hacer lo primero, tan sólo tenemos que descargar el parche tuxonice-3.0x apropiado para nuestro kernel. Ahora mismo están soportados los kernels 2.6.22.10 y 2.6.23. Si nos interesa un parche para una versión anterior, podemos buscar entre los parches anteriores de suspend2-2.2x.

Vamos a /usr/src/linux-2.6.22.10 (o a donde tengamos el kernel) y hacemos:

# bzcat /path/al/tuxonice-3.0-rc1-for-2.6.22.10.patch.bz2 | patch -p1

Entramos al “make menuconfig“, Power management options, habilitamos el Software Suspend (Hibernation) y el Enhanced Hibernation (TuxOnIce) y entramos en él:

TuxOnIce make menuconfig

Lo configuramos, salimos, guardamos y vemos que ya tenemos las opciones listas en el .config:

# grep TOI .config
CONFIG_TOI_CORE=y
CONFIG_TOI_FILE=y
CONFIG_TOI_SWAP=y
CONFIG_TOI_CRYPTO=y
CONFIG_TOI_USERUI=y
CONFIG_TOI_USERUI_DEFAULT_PATH="/usr/local/sbin/tuxonice_fbsplash"
# CONFIG_TOI_KEEP_IMAGE is not set
CONFIG_TOI_REPLACE_SWSUSP=y
CONFIG_TOI_CHECKSUM=y
CONFIG_TOI_DEFAULT_WAIT=25
CONFIG_TOI=y

En las versiones Suspend2 las opciones se llaman de otra forma:

CONFIG_SUSPEND2=y
CONFIG_SUSPEND2_FILE=y
CONFIG_SUSPEND2_SWAP=y

Cuando arrancamos el nuevo kernel con el soporte de TuxOnIce, no vemos nada especial en la salida del dmesg, a diferencia de lo que ocurría con el Suspend2, que mostraba lo siguiente:

Suspend2 Userspace Storage Manager support registered.
Suspend2 Basic User Interface support registered.
Suspend2 Compressor support registered.
Suspend2 Block I/O support registered.
Suspend2 Swap Allocator support registered.
Suspend2 File Allocator support registered.
Suspend2 Userspace UI support registered.

Ahora, según la documentación, tenemos que conseguir que los scripts del initrd ejecuten lo siguiente antes de montar el filesystem de root:

echo 1 > /sys/power/suspend2/do_resume

En realidad, aunque no esté documentado, en el TuxOnIce lo que hay que hacer es un:

echo 1 > /sys/power/tuxonice/do_resume

Si consultamos el HOWTO, veremos que hacer esto causa que el kernel mire si hay alguna imagen de hibernación disponible para restaurar y la restaure:

do_resume

Echo anything into this to start resuming.

Por tanto, lo que hacemos es pedirle al kernel que cargue la imagen anterior del estado del sistema antes de montar ningún sistema de ficheros. Dejamos el detalle de cómo hacer esto para la segunda parte de la entrada.

Para que el kernel sepa dónde encontrar la imagen, tenemos que pasarle como parámetro el fichero de dispositivo que contiene la swap:

resume=swap:/dev/hda5

Por ejemplo, podríamos poner algo así en el /boot/grub/menu.lst:

title  linux
root   (hd0,1)
kernel /boot/vmlinuz root=/dev/hda1 resume=swap:/dev/hda5 vga=791
initrd /boot/initrd.img

El último paso consiste en instalar el script hibernate (en el paquete del mismo nombre en Debian) y ejecutarlo para hibernar el sistema. El paquete contine una lista de módulos del kernel que se sabe que hacen fallar la hibernación si están activos en el fichero /etc/hibernate/blacklisted-modules. Hay módulos tan conocidos como el driver binario de NVidia: nvidia, los de USB: ehci_hcd, uhci_hcd, ohci_hcd, el del driver WiFi de Ralink: rt2500, el de Atheros: ath_pci, los de red y AGP de VIA: via_rhine, via_agp y el famoso ndiswrapper.

El script intenta descargar los módulos problemáticos, pero, por ejemplo, no puede con el nvidia si las X están corriendo, lo que nos obliga a salir al modo consola y parar las X para lanzar la hibernación.

# hibernate
Some modules failed to unload: nvidia
hibernate: Aborting suspend due to errors in ModulesUnloadBlacklist (use --force to override).

Una vez paradas las X, si ejecutamos el hibernate, la hibernación tiene este aspecto desde el punto de vista del dmesg:

TuxOnIce 3.0-rc1, with support for checksumming, usm, compression, swap storage, file storage, userui.
Initiating a hibernation cycle.
Failed to launch userspace program '/usr/local/sbin/tuxonice_fbsplash': Error -2
Launch userspace program failed.
Freezing processes & syncing filesystems.
Stopping filesystems.
Fuse filesystem done.
Fuse filesystem done.
Fuse filesystem done.
Stopping tasks ... done.
Preparing Image. Try 1.
Stopping filesystems.
Stopping tasks ... done.
Starting to save the image..
Writing caches...
20%...40%...60%...80%...100%...done.
Waited for i/o due to synchronous I/O 3 times.
Doing atomic copy.
Suspending console(s)
[...]
Disable nonboot cpus.
Disabling non-boot CPUs ...
Cannot set affinity for irq 0
CPU 1 is now offline
SMP alternatives: switching to UP code
CPU1 is down

El PC se apaga y si volvemos a arrancarlo, vuelve a la imagen y nos da la siguiente información adicional en el dmesg:

[...]
Reading caches...
20%...20%...60%...60%...100%...100%...done.
Waited for i/o due to readahead not ready 39105 times.
Cleaning up...
Restarting filesystems ...
Restarting tasks ... 
TuxOnIce debugging info:
- TuxOnIce core  : 3.0-rc1
- Kernel Version : 2.6.22.10-VIC
- Compiler vers. : 4.2
- Attempt number : 1
- Parameters     : 0 573440 0 7 0 0
- Overall expected compression percentage: 0.
- Checksum method is 'md4'.
  0 pages resaved in atomic copy.
- Compressor is 'lzf'.
  Compressed 271077376 bytes into 159378408 (41 percent compression).
- SwapAllocator active.
  Swap available for image: 977700 pages.
- FileAllocator inactive.
- I/O speed: Write 73 MB/s, Read 124 MB/s.
- Extra pages    : -228 used/500.

Por cierto, si arrancamos el sistema sin haber hibernado, al escribir en el fichero /sys/power/tuxonice/do_resume el kernel no encontrará una imagen en la swap y nos dará la siguiente información en el dmesg:

TuxOnIce 3.0-rc1, with support for checksumming, usm, compression, swap storage, file storage, userui.
TuxOnIce: Normal swapspace found.

Tras las pruebas que he hecho, pienso que el TuxOnIce aún no va fino del todo. Además del inconveniente de tener que quitar el módulo nvidia y no poder suspender con las X arriba, la parada de los módulos USB causa que los sistemas de ficheros montados en unidades USB estén en mal estado al recuperar la hibernación. ¡Esperemos que poco a poco todos los módulos conflictivos se vayan modificando para que acepten suspenderse sin dar problemas!

initrd

Bueno, nos hemos dejado para el final el ver cómo añadir a los scripts del fichero initrd el comando:

echo 1 > /sys/power/tuxonice/do_resume

Los initrd, o initial ramdisk son pequeños sistemas de ficheros que Linux puede cargar durante el arranque en la memoria para cargar módulos de él y ejecutar scripts y comandos que nos ayuden a encontrar y montar el filesystem root. Haciéndolo así, el kernel no necesita tener todos los módulos integrados y nos permite tener disponibles utilidades de espacio de usuario, como el dmraid o el udev, que facilitan la tarea de montar el filesystem root.

El dmraid, en concreto, sirve para activar los RAID integrados en casi todas las placas bases modernas. Como yo tengo dos discos en RAID 0, me hace mucha falta ejecutar el dmraid antes de intentar montar el sistema de ficheros. Cuando el dmraid aún no se incluía en muchas distribuciones por defecto, a mí me tocaba andar editando a mano los scripts del disco initrd para incluir los binarios del dmraid y que se lanzaran en el momento adecuado. Una pesadilla que ya finalizó cuando Debian empezó a incluir el dmraid en sus initrd estándar.

Pues bien, hace algún tiempo los initrd eran simplemente sistemas de ficheros ext3 o cramfs en loopback y comprimidos con gzip. En Debian se generaban con el comando mkinitrd y los scripts adicionales incluidos en el mismo paquete, el initrd-tools. Tales initrd era fáciles de montar y editar. Se descomprimían y con un “mount -o loop initrd /mnt/punto_montaje” ya podías ponerte a trabajar.

Sin embargo, aquellos initrd requerían toda la gestión propia de un sistema de ficheros, por lo que se popularizó el formato del initramfs, que son ficheros cpio comprimidos con gzip, mucho más sencillos de cargar para el kernel. En la documentación del kernel (Documentation/initrd.txt) encontramos detalles sobre este tipo de initrd y sobre cómo usarlos:

Compressed cpio images
----------------------

Recent kernels have support for populating a ramdisk from a compressed cpio
archive, on such systems, the creation of a ramdisk image doesn't need to
involve special block devices or loopbacks, you merely create a directory on
disk with the desired initrd content, cd to that directory, and run (as an
example):

find . | cpio --quiet -c -o | gzip -9 -n > /boot/imagefile.img

Examining the contents of an existing image file is just as simple:

mkdir /tmp/imagefile
cd /tmp/imagefile
gzip -cd /boot/imagefile.img | cpio -imd --quiet

En Debian, el comando para gestionarlos es el mkinitramfs y el paquete el initramfs-tools.

Pues bien, este paquete nos permite añadir nuestros scripts en el directorio /etc/initramfs-tools/scripts/ en función de cuándo queremos que se ejecute:

# ll /etc/initramfs-tools/scripts/
total 44
drwxr-xr-x 11 root root 4096 2006-09-25 08:00 ./
drwxr-xr-x  5 root root 4096 2007-08-26 14:32 ../
drwxr-xr-x  2 root root 4096 2006-09-19 16:36 init-bottom/
drwxr-xr-x  2 root root 4096 2007-11-01 08:55 init-premount/
drwxr-xr-x  2 root root 4096 2006-09-19 16:36 init-top/
drwxr-xr-x  2 root root 4096 2006-09-19 16:36 local-bottom/
drwxr-xr-x  2 root root 4096 2006-09-19 16:36 local-premount/
drwxr-xr-x  2 root root 4096 2007-11-01 13:05 local-top/
drwxr-xr-x  2 root root 4096 2006-09-19 16:36 nfs-bottom/
drwxr-xr-x  2 root root 4096 2006-09-19 16:36 nfs-premount/
drwxr-xr-x  2 root root 4096 2006-09-19 16:36 nfs-top/

Los directorios por defecto están vacíos, porque los scripts que los paquetes de Debian necesitan introducir en el initrd los ponen en otro directorio, el /usr/share/initramfs-tools/scripts/. Por tanto, tenemos el directorio para los scripts del sistema en /usr/share y el directorio para nuestros scripts personalizados en /etc.

En mi caso, como la swap la tengo en una partición del RAID, la /dev/mapper/nvidia_bdehcbaa3, necesito ejecutar el dmraid antes de pedirle al kernel que recupere la imagen escrita en la swap. El script que ejecuta el dmraid está en el directorio local-top, junto con el del udev y el de lvm:

# ll /usr/share/initramfs-tools/scripts/local-top/
total 20
drwxr-xr-x 2 root root 4096 2007-11-01 13:05 ./
drwxr-xr-x 8 root root 4096 2007-08-26 14:32 ../
-rwxr-xr-x 1 root root  230 2007-04-28 19:26 dmraid*
-rwxr-xr-x 1 root root 1061 2007-05-29 00:00 lvm*
-rwxr-xr-x 1 root root  357 2007-05-29 00:00 udev_helper*

Por tanto, para asegurarme de que se ejecute el último de todos ellos, yo lo creo con un nombre que sea alfabéticamente el último: zz_tuxonice

# cat /etc/initramfs-tools/scripts/local-top/zz_tuxonice
#!/bin/sh
echo 1 > /sys/power/tuxonice/do_resume

Lanzo el mkinitrd:

# mkinitramfs -o /boot/initrd.img-2.6.22.10-VIC 2.6.22.10-VIC

y ya tenemos el comando que necesita el TuxOnIce en nuestro initrd.

Si el TuxOnIce no encontrara la partición de swap que le indicamos, lo intentará varias veces, quejándose en cada una de ellas que no lo encuentre:

TuxOnIce: Can't translate "/dev/mapper/nvidia_bdehcbaa5" into a device id yet.
Compression Driver: Argh! Nothing follows me in the pipeline!
compression didn't initialise okay.
TuxOnIce: Initialise modules failed!

Como el dmraid y comandos similares a veces necesitan algo de tiempo para acabar, puede ser que querarmos poner algún sleep dentro del zz_tuxonice.

cpio

El cpio nació para archivar a cinta en los UNIX System V, igual que el tar, que era el archivador de BSD. Sin embargo, para archivar a un fichero en disco, se ha popularizado mucho más tar que cpio. Es por ello que el cpio no se usa mucho hoy en día. Al menos no visiblemente, porque se usa mucho en los ficheros .rpm de paquetes de Red Hat, que normalmente (pero no siempre) tienen los ficheros almacenados en un archivo cpio comprimido con gzip.

En el tutoria de cpio leemos cómo crear un fichero cpio con los ficheros de un directorio:

$ find . -print -depth | cpio -ov > tree.cpio

o cómo recuperarlos:

$ cpio -idv < tree.cpio

Pero sin duda alguna, el uso más frecuente que aún se le suele dar es el de clonar dos directorios:

 $ find . -depth -print0 | cpio --null -pvd new-dir

algo que cada vez más se va haciendo con rsync en sistemas que cuentan con esta utilidad.

Por cierto, el comando ar también sirve para agrupar ficheros en uno sólo y es la base de los paquetes de Debian, los .deb.

:wq

Entradas relacionadas

14 Comentarios a “Hibernación en Linux con TuxOnIce. Notas sobre los initrd y sobre cpio.”

  • Sagman dice:

    Pues como dices a ver si arreglan el tema de los modulos conflictivos jejeje, aunque yo nunca he usado la hibernación :)

  • @Sagman Pues sí, es bastante necesario que módulos tan importantes sean compatibles con la hibernación para que todo vaya un poco mejor.

  • RuBiCK dice:

    Buen artículo!

    Pues a mi en ubuntu creo recordar que me funciona de maravilla, eso sí, donde tengo problemas es algunas veces en la suspensión :)

  • @RuBiCK ¡Gracias! ¿No te desmonta y vuelve a montar las memorias y discos USB? ¿Usas un procesador gráfico de NVidia o uno de ATI o Intel?

  • Iván dice:

    Muy buen artículo!. Todavía me queda mucho por aprender, tendré que mirar más a fondo lo del initrd porque me ha picado el gusanillo!.

    Saludos, Iván.

  • @Iván ¡Muchas gracias!

  • Fernando dice:

    Hola, alguien sabe pq si hiberno linux, luego no me sale el GRUB para elegir con que sistema operativo quiero iniciar? Esto no pasa en Windows, es decir, si hiberno windows, luego sí me sale el GRUB al iniciar, para poder elegir entre Windows o Linux al arrancar. Lo digo pq quiero poder tener los dos hibernados siempre y arrancar con el que quiera. Creo que ahí hay un fallo, pq antes de coger la imagen de la hibernación de linux, te debería poder elegir si realmente quieres iniciar con linux y no con otro sistema operativo.

    Muchas gracias.

  • @Fernando Debe ser algo específico de tu distribución, que lo haga para asegurarse de que no arranques con otras opciones y no pierdas datos. ¿Cuál usas?

  • Lucas dice:

    Hola Super Coco, ya me voy acontumbrando a esto de escribir en el lugar correcto :).
    Tengo una duda, Ubuntu no viene con el kernel, o sea,tengo q bajarlo.
    Este es mi kernel : 2.6.22-14-generic; el unico sitio que encontre de descargas es kernel.org pero aqui no figura en la lista, el nombre de mi kernel.

    Me podrias decir cual es el que debo bajar?

    Saludos, y gracias!

  • @Lucas Los kernels que te puedes bajar de kernel.org son los llamados vanilla. Las distribuciones parten de esos kernels y les aplican parches propios para adaptarlos a su distribución. Por eso, aunque en lo principal una 2.6.22-14-generic de Ubuntu sea prácticamente igual a la 2.6.22.14 estándar, normalmente llevará algunos parches adicionales.

    En mi caso, por ejemplo, cuando me compilo un kernel, siempre me compilo el vanilla con algunos parches que me gustan, aunque el estándar de Debian añada cosas específicas al kernel. Si quisiera partir del que pone Debian por defecto, bajaría el paquete: linux-source-2.6.22, que me copia las fuentes de Debian a: /usr/src/linux-source-2.6.22.tar.bz2.

    El -generic es la coletilla que le pones al kernel para identificarlo y distinguirlo de otros kernels que puedas haber compilado para otros propósios. Yo suelo ponerle -VIC a los que me compilo yo, por ejemplo.

    Por tanto, si te bajas el 2.6.22.14 de kernel.org, estarás bajándote el mismo que tienes ahora pero versión vanilla. Si te instalas el paquete de fuentes del kernel de Ubuntu, estarás usando la versión que ellos usen ligeramente modificada sobre la original.

  • car dice:

    Disculpen tengo un 1GB de ram para Swap, creen que con esto esta bien para hibernar????????
    La compu tiene 2 GB.

  • @car Si quieres hibernar en la partición de swap, no es suficiente.

  • jose dice:

    Hola seguí tu guia hasta antes de la seccion “initrd” le di #hibernate y me aparencen esos mensajes y la compu se apaga, pero a la hora de encenderla pues aparece esta opcion en el grub: Warning Linux mikernel is suspended via software suspend, le doy a esa opcion y me aparece este mensaje: filename must be either an absolute pathname or blocklist y presionar cualquier tecla; entonces me regresa al grub, y pues si entre con la opcion de siempre y la compu pues parece que no hizo nada.¿cual puede ser mi error?
    Este es mi grub:
    kernel /boot/vmlinuz root=/dev/sda1 resume=swap:/dev/sda7 vga=791

  • @jose Si no haces la parte del initrd no te funcionará la vuelta al sistema hibernado. Aparte de eso, comprueba que /boot/vmlinuz realmente existe.

Tema LHYLE09, creado por Vicente Navarro