13 de julio de 2010

OpenSolaris lanza un ultimatum a Oracle

Según nos hemos enterado por el blog de un tal Ben Rockwood, el OpenSolaris Governing Board (OGB), o sea, la máxima entidad administrativa de la comunidad OpenSolaris, ha lanzado un ultimatum a Oracle: O envían un representante de la compañía para tratar el futuro de la comunidad y del desarrollo de OpenSolaris antes del 16 de Agosto, o la OGB se autodisolverá y devolverá la gestión de la comunidad a Oracle, algo que vendría a ser equivalente a certificar su muerte.

Esta medida drástica sin duda ha sido provocada por la clamorosa ausencia de actualizaciones de OpenSolaris. Se suponía que su próxima versión debería haber visto la luz en Marzo (tras haber sido retrasada un mes). En Marzo no apareció nada, y se dijo a la comunidad que la nueva versión se retrasaría hasta Junio. Estamos a principios de Julio, y ni rastro de nuevas versiones. Si hay motivos razonables que justifiquen tal retraso, Oracle no se los ha comunicado a la comunidad, no ha dado ni una sola explicación.

En un post de hace dos días, relacionado con este asunto, un tal Giovanni Tirloni revela como el número de casos PSARC (un sistema burocrático para hacer propuestas de nuevas características de Solaris) que no son revelados a la comunidad ha aumentado drásticamente hasta un 30% del total. Dicho de otra manera, que Oracle está planeando las nuevas características de Solaris en secreto, al margen de la comunidad. Desde luego todas estas cosas no son señales esperanzadoras. Esta situación es la que empuja a la OGB a tomar la medida drástica del ultimatum, a forzar a Oracle a aclarar sus planes de software libre, o a sincerarse y acabar con la farsa.

¿Qué hará Oracle? Difícil adivinarlo. Por las declaraciones hechas hasta hoy, no parece que tengan intención alguna de hacer que Solaris vuelva a ser un SO completamente propietario. Lo revelado hasta hoy apunta más bien a que quieren hacer una mezcla de partes privativas y libres con mayor presencia -mayor de la que ya de por si hacía Sun- de partes privativas. De ir por ese camino, es dudoso que la comunidad acepte alegremente tales reglas, y aun aceptándolas OpenSolaris no dejaría de ser una farsa que jamás desarrollaría una verdadera comunidad. Algunos en la comunidad hablan ya de volver a Linux/FreeBSD.

De matar Oracle la utopía de un Solaris verdaderamente libre, la única esperanza estaría en algo como Nexenta, que es una compañía análoga a Red Hat, con su distro de OpenSolaris propia, con la que es capaz de sacar algún dinero con el que contratar a programadores propios e impulsar el desarrollo del SO libre ellos solos. Claro que también cabe la posibilidad de que Oracle espabile y arregle todo este absurdo meollo de OpenSolaris en el que ellos mismos han decidido introducirse. ¿Pero acaso no han tenido ya tiempo de sobra para arreglar las cosas y no lo han hecho?

2 de julio de 2010

Linux y la fragmentación: el asignador de bloques de Ext4

(Nota: Este post es el cuarto y último de una serie de varios artículos sobre la fragmentación. Se recomienda leer la serie completa: 1, 2 y 3).

Continua la serie de artículos sobre la fragmentación en sistemas de archivo Linux. Este nuevo post trata sobre las novedades que incorporó Ext4 a su asignador de bloques. La información está sacada de este documento, y se puede también encontrar en un gran comentario al principio del archivo fs/ext4/mballoc.c.

El sistema de asignación de bloques es en gran medida el corazón de todo sistema de archivos, y los algoritmos utilizados tienen una gran importancia. Se trata de uno de esos puntos clave cuyos mecanismos siempre deben estar bien aceitados. No solo se les exige evitar la fragmentación, temática de esta serie de posts, también se les pide ser endiabladamente rápidos y muy escalables.


Una de las técnicas de organización del espacio que la mayoría de sistemas de archivos utilizan es subdividir el espacio disponible en partes de varios cientos de MB, de ese modo el sistema de archivos puede operar en varias subdivisiones a la vez casi independientemente. En el caso de Ext4, esa subdivisión se llama "grupo de bloques", y tiene un tamaño de 128 MB. Cada grupo de bloques tiene una serie de bloques dedicados a tareas especiales: hay unos en los que se almacenan los inodos (y cuya cantidad es estática y determinada por mkfs, razón por la que Ext4 no puede variar el número de inodos dinámicamente); también hay un bloque que se emplea en almacenar un mapa de bits que indica los bloques libres y los utilizados, y otro mapa de bits utilizado para indicar qué inodos están siendo utilizados. Cuando se va a iniciar una operación en un archivo, la decisión de asignación de bloques comienza, en principio, en el grupo de bloques al que pertenece el inodo del archivo, el cual suele asignarse en el grupo de bloques en el que está el inodo del directorio.

Ext4 dispone de dos espacios de preasignación principales a partir de los cuales se asignan bloques en un grupo de bloques: preasignación de CPU y preasignación de inodo. Las preasignaciones son rangos de bloques que no están siendo utilizados, pero están reservados y, en principio, no serán utilizados por nadie más. Si el archivo va a tener un tamaño menor a 16 bloques (64 KB), se utiliza el primer espacio de preasignación, si no, el segundo. De no poder atender la petición de asignación del espacio de preasignación, se procede a buscar espacio libre en un sistema de asignación "buddy" que contiene el resto del espacio que no está u ocupado o en las preasignaciones.

La razón por la que existen dos espacios de preasignación es para evitar que los archivos creados en una presignación no se entrometa en las del otro. Los archivos pequeños en un mismo directorio (por ejemplo, /etc/*) se benefician de estar ubicados uno a continuación del otro, los archivos grandes de asignaciones grandes y continuas. El sistema de preasignación de inodos (archivos grandes) también tiene, aparte de la preasignación, un sistema de "reservas" para casos en los que múltiples archivos se escribien simultáneamente.


Existe una forma muy curiosa de probar este sistema de preasignaciones, un ejemplo que curiosamente apareció en el segundo post de esta serie: la prueba del archivo al que se le añadía un bloque (4KB) en 200 ocasiones consecutivas, y que resultaba tener una fragmentación notable:


----------------------------------------------------------------
["oflag=append conv=notrunc" son ambos necesarios para que se vayan añadiendo datos al archivo, "conv=fsync" sincroniza el archivo a disco para asegurarse de que no se queda en RAM]
# for i in `seq 200`; do dd if=/dev/zero of=prueba bs=4K count=1 oflag=append conv=notrunc,fsync; done
[Un montón de datos de salida de dd]
----------------------------------------------------------------

Este archivo esta bastante fragmentado a pesar de ser un test simple y estar el sistema de archivos de pruebas completamente vacío. Echando un ojo a la salida detallada de filefrag, se puede intentar adivinar las causas de este comportamiento:

----------------------------------------------------------------
# filefrag -v prueba
Filesystem type is: ef53
File size of prueba is 819200 (200 blocks, blocksize 4096)
 ext logical physical expected length flags
   0       0    34304               3
   1       3    34816    34306      1
   2       4    34307    34816      3
   3       7    34817    34309      1
   4       8    34310    34817      2
   5      10    34818    34311      1
   6      11    34312    34818      1
   7      12    34819    34312      1
   8      13    34313    34819      2
   9      15    34820    34314      1
  10      16    33808    34820    184 eof
prueba: 11 extents found
----------------------------------------------------------------

2 pequeños extents de 3 bloques, 2 de 2 bloques, 6 de 1 bloque...y luego uno grande de 184 bloques. ¿Por qué esa distribución tan absurda de los bloques? Si sumamos todos los extents pequeños -todos menos el de 184 bloques- obtenemos exactamente 16 bloques (64 KB). Precisamente el límite mencionado entre el espacio de preasignación para archivos pequeños y el de archivos grandes. ¿Que ha ocurrido? Parece ser que mientras el archivo era menor de 16 bloques, Ext4 le asignó espacio de la preasignación para archivos pequeños; una vez que creció más allá de ese tamaño se le asignaron bloques de espacio de preasignación para archivos grandes, que colocó los 184 bloques restantes contiguos, uno detrás de otro...


¿Por qué, sin embargo, se han creado tantos fragmentos pequeños en la preasignación de CPU, optimizada para archivos pequeños? El problema es que la preasignación de CPU, llamada "per-CPU" en su idioma original, contiene en si mismo varias preasignaciones, una para cada CPU. Esta técnica per-CPU es muy común en el kernel Linux, se tratan de arrays de variables, una para cada procesador del sistema, para que cada CPU pueda operar en su variable sin interferir con las demás. En el caso de la preasignación de CPU de Ext4, más que por cuestiones de escalabilidad el objetivo es que las asignaciones de un mismo proceso -que se ejecuta en una CPU determinada- estén físicamente juntas. El comando tar, por ejemplo, podría desempaquetar un conjunto de archivos pequeños, y al ejecutarse tar en una misma CPU, los desempaquetaría en el espacio de preasignación de esa CPU.

¿Qué ha ocurrido entonces en el ejemplo anterior del archivo? Pues que mi sistema tiene 2 CPUs, y el proceso dd se ha ejecutado cada vez en una de las CPUs, de modo que en cada añadidura de 4KB se utilizaba el espacio de preasignación de cada CPU. Cuando, por casualidad, dd se ha ejecutado en la misma CPU dos o tres veces seguidas, ha logrado crear pequeños extents de 2 ó 3 bloques, pero cuando había alternancia entre las CPUs utilizadas era necesario empezar un nuevo extent discontiguo respecto al anterior. Y hay un modo muy simple de confirmar esta teoría, que es forzar la ejecución de dd en una CPU determinada, utilizando taskset(1):

----------------------------------------------------------------
# for i in `seq 200`; do taskset -c 1 dd if=/dev/zero of=prueba bs=4K count=1 oflag=append conv=notrunc,fsync; done
[Un montón de datos de salida de dd]
# filefrag -v prueba
Filesystem type is: ef53
File size of prueba is 819200 (200 blocks, blocksize 4096)
 ext logical physical expected length flags
   0       0    34304              16
   1      16    33808    34319    184 eof
prueba: 2 extents found
----------------------------------------------------------------

Esta vez dd ha accedido en todas las ocasiones al mismo espacio de preasignación de CPU, y por consiguiente ha ubicado los 16 primeros bloques contiguos.

Para terminar el post, ¿por qué, podría preguntarse alguien, hay un límite entre ambas preasignaciones para archivos pequeños y grandes que está fijado 16 bloques, por qué ese número? Ese valor es, en realidad, configurable, y se puede cambiar en el archivo /sys/fs/ext4/DISPOSITIVO/mb_stream_req. Y con esto se descubre ese desconocido directorio, que como se puede comprobar, contiene otros archivos de configuración, varios de ellos parámetros para tunear el comportamiento del asignador de bloques (y documentados en Documentation/ABI/testing/sysfs-fs-ext4). La disponibilidad de tales parámetros demuestra que la asignación de bloques es un tema muy complejo, que puede necesitar atención en casos especiales o susceptible a optimizaciones para una carga determinada. Espero que todo esto demuestre que el tópico de que Linux no sufre fragmentación es muy opinable y depende de multitud de factores (y eso que ni siquiera se ha mencionado la necesidad actual de que los diferentes archivos de una misma aplicación estén contiguos), de ahí que la disponibilidad de e4defrag es necesaria y bienvenida.

27 de junio de 2010

Linux y la fragmentación: "delayed allocation"

(Nota: Este post es el tercero de una serie de varios artículos sobre la fragmentación. Se recomienda leer la serie completa: 1, 2 y 4).

El anterior post tenía ejemplos de fragmentación de archivos en Ext4. Hay que recalcar que eran ejemplos muy muy simples, que en el mundo real ocurren cosas muchas más complejos como que haya una actualización de seguridad mientras algún programa hace otra cosa. Había afirmado que este post detallaría los algoritmos de asignación de bloques de Ext4, pero en su lugar tratará sobre la técnica "delayed allocation" (asignación retardada), que es quien se encarga de llamar a esos algoritmos y por tanto condiciona su funcionamiento.


En los sistemas de archivos sin asignación retardada, cuando una aplicación llama a write(), como parte de esa llamada se asignan los bloques para los datos. Aunque los datos no se vayan a escribir al disco en ese preciso momento y vayan a permanecer hasta decenas de segundos en el cache, aunque durante ese tiempo los bloques asignados no sean escritos, se asignarán igualmente. Este sistema puede provocar fragmentación, dado que en muchos casos se necesitan múltiples llamadas a write() para escribir enteramente un archivo. Se llamará, por tanto, al asignador de bloques en todas esas escrituras. Y, ¿cómo puede el asignador predecir todas las escrituras que se van a hacer, cómo predecir que un archivo va a llegar a los 2 GB cuando recibe el encargo de asignar los bloques para del primer mega? Simplemente no puede.

La técnica de asignación retardada, utilizada en sorprendentemente pocos sistemas de archivos (concretamente: en Ext4, XFS, ZFS, Btrfs y HFS+; su escaso uso se debe a haber sido considerada como peligrosa durante mucho tiempo), retrasa la asignación de los bloques hasta el momento en el que el caché se sincroniza al disco. Las llamadas a write() no provocan ninguna asignación de bloques, quienes las provocan son las rutinas de la VM cuando decide que es hora de sincronizar el caché. Este sistema permite que la escritura al disco de ese archivo se haga de una sola vez. El asignador de bloques sabe el tamaño total a asignar, tiene más información, por lo tanto puede tomar decisiones de asignación más inteligentes. En Ext4 existe una forma muy gráfica de ilustrar el funcionamiento de este mecanismo con ayuda, cómo no, del comando filefrag:

----------------------------------------------------------------
$ # Creamos un archivo de 4KB sin sincronizarlo inmediatamente al disco
$ dd if=/dev/zero of=prueba bs=1K count=4
[salida de dd]
$ filefrag -v prueba
Filesystem type is: ef53
File size of prueba is 4096 (1 block, blocksize 4096) 
 ext logical physical expected length flags
   0      0        0              1 unknown,delalloc,eof
prueba: 1 extent found
----------------------------------------------------------------


Como vemos, filefrag no muestra la ubicación lógica ni física del archivo, la muestra como "unknow, delalloc". Es decir, que no se sabe, y que está en estado de asignación retardada: La ubicación simplemente no existe aun, está aun pendiente de realizarse. Sin embargo, si ordenamos sincronizar inmediatamente después el caché con el disco...

----------------------------------------------------------------
$ sync
$ filefrag -v prueba
Filesystem type is: ef53
File size of prueba is 4096 (1 block, blocksize 4096)
ext logical physical expected length flags
  0      0    36681               1  eof
prueba: 1 extent found
----------------------------------------------------------------

Como se ve, ya se ha asignado su bloque (igualmente se hubiera escrito al disco si dejamos pasar el tiempo).


Este sistema es muy bonito. Sin embargo, sería un error afirmar que es una receta para la infalibidad contra la fragmentación, ya que la sincronización del caché al disco se hace cuando a la VM le de la gana...y el comportamiento de la VM es muy, muy variable. En situaciones de poca memoria, una de las primeras cosas que hace es solicitar la sincronización del caché con el disco. Dada una situación extrema de escasez de memoria, se dará el caso de que nada más enviar una llamada write() la VM ordenará la sincronización del caché y provocará una asignación de bloques casi inmediatamente, aproximándose al comportamiento de sistemas de archivos sin asignación retardada.

Una prueba empírica: en el primer ejemplo del post anterior se escribían dos archivos de 10GB en paralelo, los parámetros de dd eran "bs=512M count=20", lo cual hacía que cada dd ocupara en memoria 512MB. El resultado fue de 164 y 155 extents en cada archivo. Repitiendo la prueba pero con parámetros "bs=1G count=10" hace que los dos procesos dd ocupen 2GB, lo cual aumenta la presión de memoria en mi sistema y provoca mayores llamadas de sincronización de la VM. Resultado, 305-311 extents por archivo, con mayor entrecruce de ambos archivos en el disco.

Por lo tanto, se observa como la fragmentación del sistema de archivos puede variar con algo tan trivial como la presión de memoria del sistema (y noten que muy a menudo se hacen benchmarks sin intentar reproducir este tipo de condiciones), y cómo es poco prudente afirmar la ausencia de fragmentación en Linux. Todo esto viene a girar alrededor de un concepto ya afirmado en el primer post: Todos los archivos se enfrentan a GBs y GBs de espacio libre, y la decisión de colocar un archivo en un lado u otro depende muy en gran medida de la implementación.

El próximo post tratará -esta vez si- sobre los algoritmos de asignación de bloques de Ext4.