Usando un SSD
Mi portátil, una Thinkpad X220, venía con un disco de estado sólido (SSD) además de un disco SATA, por cosas de experimentos el disco SSD lo removí, pero encontré otro y lo agregué a mi equipo, ahora bien, ¿cómo migrar satisfactoriamente mi root system ya instalado a este nuevo SSD?.
![SSD]()
He aquí mi breve experiencia compartida.
Instalación física del SSD
![]()
La Thinkpad viene con 2 bahías mini-PCIe, en una ya viene la tarjeta inalámbrica y trae otra vacía (lista para un modem 3G) dónde he colocado mi disco SSD, ahora bien, como es una Thinkpad tuve que “flashear” un BIOS mod (basado en 8duj25us_8DET69WW) para “eliminar” esa mala costumbre de Lenovo te poner una “lista blanca” de dispositivos que puedes meter a tu portátil (y que para colmo son más caros).
Al retirar los tornillos, se desprende la tapa que contiene el touchpad y el teclado y debajo encontrarán las bahías.
![original.jpg]()
Para Thinkpad un disco SSD en esa bahía es tomado como secundario (sdb) y no como primario, no es ningún problema ya que en mi disco SATA /boot está separado de raíz y no pensaba migrar el arranque de allí.
Particionando el disco
Hay 2 trucos básicos para particionar un disco de estado sólido y aprovechar al máximo su potencial:
Alineación de Particiones
Alinear las particiones es una de las actividades más importantes y menos tomada en cuenta cuando se diseña el layout de particiones, yo destiné 10GB (y realmente no necesito más para root) y para diseñarla usé parted:
parted /dev/sdb
mklabel gpt
unit s
mkpart primary 2048s 10G
align-check optimal 1
Dejamos 2048 sectores (1MB) al principio del disco, esto permite una mejor alineación del disco con el tamaño de los sectores (512Bytes):
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Number Start End Size Filesystem Name Flags
17,4kB 1049kB 1031kB Free Space
1 1049kB 10,0GB 9999MB ext4 primary
Dejar Holgura
Deja al menos un 10% libre al final del disco, este margen de holgura le permite al disco un “respiro” en su operación.
Además, orientadas a Linux hay otras consideraciones
NO usar SWAP
con 8GB de RAM es casi imposible que mi equipo se vaya a SWAP con la más alta demanda (viendo videos con un par de máquinas virtuales KVM en el fondo, navegando con el gran consumidor de RAM Chromium mientras chateo por Telegram), sin embargo, la ventaja de la X220 es que viene con un disco SATA, así que la SWAP se quedará en dicho disco (sólo de ser necesario).
Usar GPT
No usaré Windows ni ningún otro sistema operativo no-UNIX en este equipo, por lo que usar una geometría de particiones basada en GPT (GUID Partition Table), no solamente me permite múltiples particiones primarias, GPT almacena su tabla de particiones al principio y al final (una copia) lo que da una protección adicional para recuperación de particiones con herramientas como Testdisk.
Luego de particionado, procedemos a formatear.
Formato ext4
Aunque “mkfs.ext4 /dev/sdb1″ serviría para la mayoría de las opciones, en nuestro caso necesitamos activar una serie de opciones como el TRIM, con TRIM (discard: según ext4) los bloques de datos borrados o que dejan de estar en uso por el sistema operativo, el sistema puede notificarle al disco SSD que “limpie” dichos bloques inmediatamente para evitar la ralentización del mismo en operaciones de escritura futuras; además de TRIM, EXT4 no inicializa al ejecutar el comando mkfs ni la tabla de inodos ni el journal del mismo (característica conocida como “lazy initialization”) haciendo más lentas las operaciones iniciales de montaje y primera copia; por último, deseamos activar el modo WRITEBACK, con “data=writeback” los datos son escritos “después” que se ha escrito la metadata de la misma en el journal, esto tiene grandes ventajas en el performance (la operación de escritura de datos se hace en segundo plano) pero conlleva que un fallo eléctrico luego de ejecutado el commit de la metadata pero antes de finalizar la escritura de datos creará un sistema inconsistente; como me encuentro en un sistema protegido por batería (portátil), esto para mí no es un problema serio.
Sin embargo, recordemos las sabias palabras de Linus al respecto de data=writeback:
If your data gets written out long after the metadata hit the disk, you are going to hit all kinds of bad issues if the machine ever goes down. Linus
Insisto, habilitar “data=writeback” solo si tienes una garantía ABSOLUTA de que no habrán fallos del equipo durante un proceso de escritura en disco.
Para determinar si mi disco SSD soporta TRIM ejecuto:
hdparm -I /dev/sdb | grep 'TRIM'
* Data Set Management TRIM supported (limit 2 blocks)
Luego, ejecuto entonces el formato de la partición sdb1:
mkfs.ext4 -E lazy_itable_init=0,lazy_journal_init=0 -b 4096 -m 0 -O extents,dir_index /dev/sdb1
Y tune2fs:
tune2fs -o journal_data_writeback,discard -c 5 -i 5 /dev/sdb1
Adicionalmente, bajo a 5 (del por defecto 2o) la cantidad de veces que el sistema de archivos debe ser chequeado al arranque, esto porque es un equipo que constantemente se suspende|hiberna, necesito que cada vez que arranque sea chequeado, para evitar largas esperas y que espere mucho tiempo antes que un chequeo se haga demasiado tarde y el sistema simplemente no arranque (arranque en initramfs).
Al finalizar, chequeamos:
e2fsck /dev/sdb1
Y verificamos características:
tune2fs -l /dev/sdb1 | grep discard
Default mount options: journal_data_writeback user_xattr acl discard
Ahora procedemos al montaje.
Montaje de la partición
Creamos un directorio temporal y realizamos el montaje:
mkdir /ssd
Y
mount -t ext4 /dev/sdb1 /ssd -o discard,noatime,nodiratime,nodelalloc,data=writeback,i_version,barrier=0,user_xattr,inode_readahead_blks=64,commit=100
Entre las caraterísticas del montaje están:
- discard: habilita el TRIM de la partición
- i_version: habilita el uso de una versión “extendida” de 64-bit de los inodos de ext4
- barrier: deshabilita los write barriers.
- noatime,nodiratime: no escribir las fechas de acceso en archivos y directorios, información poco útil en /
- commit: permite definir la cantidad de segundos que la metadata en disco es escrita y sincronizada, 100 segundos es mucho tiempo para garantizar confiabilidad en los datos, pero aumenta drásticamente el performance.
- inode_readahead_blks: ext4 cuenta con un algoritmo de pre-lectura (read-ahead), dicho algoritmo “pre-lee” los inodos a los lados de un inodo leído, para “optimizar” la extracción de datos, el valor por defecto es de 32 bloques, se incrementa a 64.
- nodelalloc: ext4 ejecuta una “delayed allocation” de los bloques y solo los reserva cuando ya va a escribir a disco, esto es potencialmente peligroso combinado con data=writeback, la totalidad de los bloques que ocupan un archivo son reservados apenas la data es copiada desde userspace a la caché del dispositivo, así, cuando la metadata se esté escribiendo en el disco, ya los bloques para almacenar dicha data estarán reservados.
Sincronizando mi raíz
Para sincronizar mi partición raíz utilizo el siguiente comando rsync:
rsync -aAvPSXx --numeric-ids --exclude="/dev/*" --exclude="/proc/*" --exclude="/sys/*" --exclude="/tmp/*" --exclude="/home/*" / /ssd/
En general excluyo todo archivo que no pertenezca a la partición raíz y los puntos de montaje de sistema (/dev, /sys).
Al finalizar la sincronización, fuerzo un trim “manual” a la partición:
fstrim -v /ssd
/ssd: 3395391488 bytes were trimmed
Cambios al FSTAB
Los cambios más importantes al FSTAB (archivo: /ssd/etc/fstab) tienen que ver con el montaje “especial” de raíz, y con el montaje en RAMFS de las particiones temporales básicas del sistema:
# tmp
tmpfs /tmp tmpfs noatime,nosuid 0 0
tmpfs /var/run tmpfs nosuid,mode=0755 0 0
tmpfs /var/lock tmpfs noexec,nosuid,nodev 0 0
Y raíz:
UUID=970518df-7db4-44af-979c-7c9fd86a7488 / ext4 discard,noatime,nodelalloc,data=writeback,barrier=0,i_version,commit=100,inode_readahead_blks=64,errors=remount-ro 0 1
el UUID de la partición /dev/sdb1 lo obtienen fácilmente con el comando “blkid”
Actualizando GRUB
En el archivo /ssd/etc/default/grub cambiamos el elevador a “deadline“:
GRUB_CMDLINE_LINUX=”acpi_osi=Linux acpi=ht elevator=deadline”
Un I/O scheduler, implica la forma como Linux envía los bloques de datos a los discos, en general mi equipo es una estación de trabajo, tengo bases de datos, directorios, máquinas virtuales y “deadline” es la opción más idónea, manteniendo las colas con tiempos “deadline”, para que algunas muy retrasadas “expiren”, evitando que nos quedemos sin recursos, además, en deadline, las lecturas se priorizan por sobre las escrituras, mejorando el performance de largas operaciones de lectura sobre los discos.
CFQ (el elevador por defecto en Debian), es incluso más lento que “deadline” en operaciones con bases de datos o cargas multihilos.
Este siempre es mi elevador por defecto, sin embargo, para los discos SSD hay un elevador mucho más simple, NOOP.
Con NOOP, las operaciones van sin orden (y unidas) en una única cola de escritura que es entregada al dispositivo, su ordenamiento, escritura dependerán del dispositivo.
Hay que asumir el riesgo de usar uno u otro scheduler, en mi caso uso “deadline” por lo importante de priorizar las lecturas sobre las escrituras (útil en bases de datos y directorios LDAP), sin embargo, usar CFQ permitirá a un usuario garantizar un ancho de banda más consistente para ciertas aplicaciones (ejemplo: desktops).
También el scheduler puede ser cambiado por disco, algo que veremos en el próximo apartado.
Hacemos una “jaula” para poder actualizar el GRUB desde el nuevo disco SSD (montado en /ssd):
- Monto el actual /boot dentro de /ssd
mount /dev/sda1 /ssd/boot
- Monto los sistemas especiales con la opción “-o bind”:
mount /dev /ssd/dev -o bind
mount /sys -o bind /ssd/sys
mount /proc -o bind /ssd/proc
Y creo la jaula con:
chroot /ssd
Ya dentro de la jaula, la ejecución de “update-grub” causará que se use /dev/sdb1 como nuevo “root”.
update-grub
actualicen además el initramfs:
update-initramfs -t -u
Y nos salimos de la jaula.
exit
Reiniciamos el equipo.
Optimización del disco
Ahora vamos a crear un archivo especial de optimización del disco (comentado anteriormente).
archivo: /sbin/optimicedisk.sh
#!/bin/bash
# script para optimizar el acceso a disco de sistemas con noop o deadline
# autor: Jesus Lara <jesuslarag@gmail.com>
disks=( sdb )
# parameters
SCHEDULER="deadline"
QUEUEDEPTH=256
BLOCKDEV=16384
NCQ=32
for DISK in "${disks[@]}"
do
echo "Change I/O elevator to $SCHEDULER"
echo $SCHEDULER > /sys/block/$DISK/queue/scheduler
echo "Optimice server disk block size to $BLOCKDEV"
/sbin/blockdev --setra $BLOCKDEV /dev/$DISK
echo "Set queue depth to $QUEUEDEPTH"
/bin/echo $QUEUEDEPTH > /sys/block/$DISK/queue/nr_requests
echo "Enabling NCQ on $DISK"
echo $NCQ > /sys/block/$DISK/device/queue_depth
# read ahead and sectors read by disk
/bin/echo "8192" > /sys/block/$DISK/queue/read_ahead_kb
/bin/echo "4096" > /sys/block/$DISK/queue/max_sectors_kb
if [ "$SCHEDULER" == 'deadline' ]; then
# fifo batch reduce seeks
/bin/echo 32 > /sys/block/$DISK/queue/iosched/fifo_batch
fi
# enable write-caching, readahead and disable noise-reduction (performance boost)
/sbin/hdparm -a512 -A1 -W1 /dev/$DISK
done
En este archivo hacemos varias cosas pero 3 son muy importantes:
- definimos la cantidad de bloques que son pre-leídos en las lecturas secuenciales (read-ahead) a 16384 (blockdev –setra)
- Hemos indicado al sistema que este disco no es rotacional (es un SSD, sin parte móviles como platos o discos que causen latencia)
- Habilitamos NCQ (Native Command Queuing), con NCQ se garantiza una paralelización hasta el máximo permitido (31 según LIBATA) de posibles operaciones paralelas de lectura/escritura (NOTA: debe estar habilitado AHCI en el BIOS).
![original]()
Con esta serie de características garantizamos un equilibrio entre performance y uso.
Luego, colocamos este archivo en el rc.local:
/bin/bash /sbin/optimicedisk.sh
Y lo hacemos ejecutable:
chmod +x /sbin/optimicedisk.sh
Es importante además, definir si el disco es o no un dispositivo rotacional, esto afectará mucho el comportamiento de NCQ:
archivo: /etc/udev/rules.d/66-ssd.rules
# system default : set deadline scheduler for rotating disks
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="deadline"
# SSD specific : set noop scheduler for non-rotating disks
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="noop"
# set a larger readahead size
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/read_ahead_kb}="2048"
Con esta regla UDEV, nos garantizamos que para todo disco detectado como no-rotacional, entonces se seleccione el scheduler noop.
NOTA: se usa el índice 66 ya que las reglas UDEV para almacenamientos persistentes se ejecutan en la regla 60 (persistent-storage.rules).
Al finalizar el archivo de reglas lo probamos con:
udevadm test --action=add /sys/block/sdb
Y veremos entre la larga lista de acciones de udevadme algo como esto:
…
udev_rules_apply_to_event: ATTR '/sys/devices/pci0000:00/0000:00:1f.2/host2/target2:0:0/2:0:0:0/block/sdb/queue/scheduler' writing 'noop' /etc/udev/rules.d/66-ssd.rules:5
…
demostrando que la regla se ha aplicado correctamente a nuestro disco SSD.
Otras consideraciones
- Re-organizar mi disco duro para que /var (y por sobre todo /var/cache y /var/log, además de /var/spool) queden sobre el disco SATA
- Actualizar el firmware del disco, OCZ trae un comando para hacerlo desde Linux.
- Realizar algunos cambios adicionales en sysctl como por ejemplo:
# VM
vm.dirty_ratio = 20
vm.dirty_background_ratio = 20
vm.dirty_bytes = 67108864
vm.dirty_background_bytes = 134217728
vm.mmap_min_addr = 4096
# swapping
vm.swappiness = 0
vm.vfs_cache_pressure = 20
Que podría explicar luego.
Conclusiones
Un disco SSD es un excelente recurso para incrementar el performance, pero por sus características, su tiempo de vida es limitado (con estas características acá aplicadas, el tiempo de vida de una portátil con SSD podría llegar a los 8 años, y yo jamás he tenido más de 8 años con la misma portátil y el mismo disco!), como todo, hay que aprender a optimizarlo y sacarle el mejor provecho posible.
Happy Hacking!