Maintenant que la plateforme de test générique est fonctionnelle, nous allons faire quelques tests pour optimiser l’ensemble. Nous allons nous consacrer à réduire le temps de chargement du noyau jusqu’au passage au système de fichiers racine. Puis dans un second temps nous choisirons le système de fichiers du rootfs. Une grande partie des optimisations mises en place viennent de remarque de ce site : eLinux.
Le démarrage de la carte à froid (cold boot) se déroule dans cet ordre :
- Lancement du chargeur intégré à la puce SoC (chez TI : X-Loader, chez Samsung ?) qui charge U-Boot depuis la flash NAND
- Démarrage de U-Boot qui s’occupe du chargement de l’image noyau depuis la flash NAND en RAM
- Décompression du noyau et démarrage de celui-ci
- Montage du système de fichiers racine puis passage à INIT
- Fin de l’initialisation : « UserLand » : vos applications
Le chargeur de la puce ne m’est pas accessible donc pas d’optimisation la dessus ! Nous allons donc commencer par U-Boot. Mais pour optimiser le temps de chargement, il faut pouvoir mesurer le temps de chaque étape. J’ai choisi d’utiliser GrabSerial (présentation et installation ici) pour faire les mesures de temps car il n’apporte pas de surcharge du coté système embarqué. GrabSerial utilise Python et necessite un module python à installer avec la commande suivante:
1 |
root@casimir:~# aptitude install python-serial |
Il faut ensuite télécharger le script python GrabSerial, le rendre exécutable (chmod +x) et enfin le lancer avec une commande du style:
1 |
root@casimir:~# ./grabserial.py -v -t -m "Starting kernel.*" |
qui va permettre d’intercepter les caractères arrivant sur la voie série ttyS0 et qui remet à 0 le compteur de temps lorsque « Starting kernel » apparaitra… Vous avez un exemple d’une trace issu de GrabSerial en fin d’article sur la personnalisation de la carte TQ6410.
Optimisation de U-Boot
Nous allons commencer par les optimisations simples (sous entendu sans recompilation du code) :
- Le ‘u-Boot’ fourni avec la carte attend pendant 3s que vous tapiez sur une touche pour arrêter le chargement. On peut réduire ce temps à ‘0s’ et ainsi gagner 3s mais vous n’aurez plus accès au menu de U-Boot. Il faudra alors reflasher le U-Boot d’origine depuis la flash NOR. Pour fixer ce temps à ‘0’, quitter le menu de U-Boot pour passer à l’interpréteur de commande : Quit to shell (lettre ‘q’) puis tapez ‘set bootdelay 0’. Si vous souhaitez conserver cette option, tapez ‘saveenv’.
- Vérifiez que la vitesse de transmission du port série est à 115200. Toujours depuis le shell de U-Boot, tapez ‘printenv’ puis vérifiez que la variable ‘baudrate’ est à 115200 sinon la fixer avec ‘set baudrate 115200’.
- U-Boot effectue une somme de contrôle sur le noyau qu’il charge. On peut désactiver cette option en tapant ‘set verify no’.
- Toutes les sorties vers la voie série ralentissent énormément U-Boot. Pour désactiver les sorties il faut que le code soit compiler avec ‘#define CONFIG_SILENT_CONSOLE’ et mettre à 1 la variable silent: ‘set silent 1’. J’ai essayé de recompiler le code fourni avec la carte avec l’option ci-dessus mais cela n’a rien changé sur le fait qu’il m’affiche les messages de lecture de la flash NAND lors du chargement du noyau. Il faut donc que je trouve dans le code ou est ce « printf » qui ralentit énormément la lecture en flash NAND…
- Par défaut si on ne touche pas au menu de U-Boot, au bout de 3s, la commande ‘bootcmd’ est exécutée. Or cette commande lit 0x500000 octets soit 5 242 880 octets avec le U-Boot fourni par le vendeur de la carte. Ceci même si votre noyau fait 1,2Mo. Il faut donc modifier cette commande pour ne lire que ce qui est nécessaire en flash NAND pour démarrer le noyau. Par exemple, un des noyaux (uImage) que j’ai compilé pour cette plateforme fait 1 246 156 octets qu’il faut aligner sur la flash NAND (padder en anglais) sur des secteurs de 2 048 octets donc on fait 1 246 156/2 048 = 608,47. On prend 609+1=610 et on remultiplie par 2048, on obtient 1 249 280 octets soit 0x13 100 en hexadécimal. On tapera donc la commande suivante pour fixer ‘bootcmd’ : set bootcmd ‘nand read.i c0008000 80000 13100; bootm c0008000’. On charge en RAM (0xc000800) à partir de l’adresse 0x80000 de la flash NAND (adresse ou l’on télécharge le noyau en flash avec la lettre ‘k’ du menu) 0x131000 octets. On gagne ainsi un temps précieux. Remarquez que si vous tapez la lettre ‘b’ du menu, c’est toujours 5Mo qui seront lu depuis la flash car la commande est codé en « dur » dans le code.
On obtient la trace suivante:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[ 0.000003] [ 0.289005] [ 0.289216] U-Boot 1.1.6 (Aug 5 2011 - 11:50:17) for TQ6410 [ 0.293653] [ 0.293860] [ 0.294013] CPU: S3C6410@532MHz [ 0.296218] Fclk = 532MHz, Hclk = 133MHz, Pclk = 66MHz, Serial = CLKUART (SYNC Mode) [ 0.303626] Board: TQ6410 [ 0.305115] DRAM: 128 MB [ 0.306488] Flash: 0 kB [ 0.307700] NAND: 256 MB [ 0.399982] In: serial [ 0.401473] Out: serial [ 0.402932] Err: serial [ 0.483943] MAC: 08:90:90:90:90:90 [ 0.486062] Hit any key to stop autoboot: 3 2 1 0 [ 3.468708] [ 3.468897] NAND read: device 0 offset 0x80000, size 0x131000 [ 3.473340] [ 3.473511] Reading data from 0x80000 -- 0% complete.Reading data from 0x83000 -- 1% complete.Reading data from 0x86000 -- 2% complete.Reading data from 0x89000 -- 3% complete.Reading data from 0x8c000 -- 4% complete.Reading data from 0x8f000 -- 5% complete.Reading data from 0x92000 -- 6% complete.Reading data from 0x95000 -- 7% complete.Reading data from 0x98000 -- 8% complete.Reading data from 0x9b000 -- 9% complete.Reading data from 0x9e000 -- 10% complete.Reading data from 0xa1800 -- 11% complete.Reading data from 0xa4800 -- 12% complete.Reading data from 0xa7800 -- 13% complete.Reading data from 0xaa800 -- 14% complete.Reading data from 0xad800 -- 15% complete.Reading data from 0xb0800 -- 16% complete.Reading data from 0xb3800 -- 17% complete.Reading data from 0xb6800 -- 18% complete.Reading data from 0xb9800 -- 19% complete.Reading data from 0xbc800 -- 20% complete.Reading data from 0xc0000 -- 21% complete.Reading data from 0xc3000 -- 22% complete.Reading data from 0xc6000 -- 23% complete.Reading data from 0xc9000 -- 24% complete.Reading data from 0xcc000 -- 25% complete.Reading data from 0xcf000 -- 26% complete.Reading data from 0xd2000 -- 27% complete.Reading data from 0xd5000 -- 28% complete.Reading data from 0xd8000 -- 29% complete.Reading data from 0xdb000 -- 30% complete.Reading data from 0xde800 -- 31% complete.Reading data from 0xe1800 -- 32% complete.Reading data from 0xe4800 -- 33% complete.Reading data from 0xe7800 -- 34% complete.Reading data from 0xea800 -- 35% complete.Reading data from 0xed800 -- 36% complete.Reading data from 0xf0800 -- 37% complete.Reading data from 0xf3800 -- 38% complete.Reading data from 0xf6800 -- 39% complete.Reading data from 0xf9800 -- 40% complete.Reading data from 0xfd000 -- 41% complete.Reading data from 0x100000 -- 42% complete.Reading data from 0x103000 -- 43% complete.Reading data from 0x106000 -- 44% complete.Reading data from 0x109000 -- 45% complete.Reading data from 0x10c000 -- 46% complete.Reading data from 0x10f000 -- 47% complete.Reading data from 0x112000 -- 48% complete.Reading data from 0x115000 -- 49% complete.Reading data from 0x118000 -- 50% complete.Reading data from 0x11b800 -- 51% complete.Reading data from 0x11e800 -- 52% complete.Reading data from 0x121800 -- 53% complete.Reading data from 0x124800 -- 54% complete.Reading data from 0x127800 -- 55% complete.Reading data from 0x12a800 -- 56% complete.Reading data from 0x12d800 -- 57% complete.Reading data from 0x130800 -- 58% complete.Reading data from 0x133800 -- 59% complete.Reading data from 0x136800 -- 60% complete.Reading data from 0x13a000 -- 61% complete.Reading data from 0x13d000 -- 62% complete.Reading data from 0x140000 -- 63% complete.Reading data from 0x143000 -- 64% complete.Reading data from 0x146000 -- 65% complete.Reading data from 0x149000 -- 66% complete.Reading data from 0x14c000 -- 67% complete.Reading data from 0x14f000 -- 68% complete.Reading data from 0x152000 -- 69% complete.Reading data from 0x155000 -- 70% complete.Reading data from 0x158800 -- 71% complete.Reading data from 0x15b800 -- 72% complete.Reading data from 0x15e800 -- 73% complete.Reading data from 0x161800 -- 74% complete.Reading data from 0x164800 -- 75% complete.Reading data from 0x167800 -- 76% complete.Reading data from 0x16a800 -- 77% complete.Reading data from 0x16d800 -- 78% complete.Reading data from 0x170800 -- 79% complete.Reading data from 0x173800 -- 80% complete.Reading data from 0x177000 -- 81% complete.Reading data from 0x17a000 -- 82% complete.Reading data from 0x17d000 -- 83% complete.Reading data from 0x180000 -- 84% complete.Reading data from 0x183000 -- 85% complete.Reading data from 0x186000 -- 86% complete.Reading data from 0x189000 -- 87% complete.Reading data from 0x18c000 -- 88% complete.Reading data from 0x18f000 -- 89% complete.Reading data from 0x192000 -- 90% complete.Reading data from 0x195800 -- 91% complete.Reading data from 0x198800 -- 92% complete.Reading data from 0x19b800 -- 93% complete.Reading data from 0x19e800 -- 94% complete.Reading data from 0x1a1800 -- 95% complete.Reading data from 0x1a4800 -- 96% complete.Reading data from 0x1a7800 -- 97% complete.Reading data from 0x1aa800 -- 98% complete.Reading data from 0x1ad800 -- 99% complete.Reading data from 0x1b0800 -- 100% complete. [ 4.672055] 1249280 bytes read: OK [ 4.678741] ## Booting image at c0008000 ... [ 4.679811] Image Name: Linux-2.6.28.6-IRIS-Turgot-O.Dar [ 4.684310] Image Type: ARM Linux Kernel Image (uncompressed) [ 4.686018] Data Size: 1246092 Bytes = 1.2 MB [ 4.688032] Load Address: 50008000 [ 4.692227] Entry Point: 50008000 [ 4.999951] OK [ 5.000117] [ 5.000197] Starting kernel ... |
J’ai laissé volontairement la ‘longuuuuue’ chaine qui indique la progression de la lecture en flash. Je pense que cette sortie vers la voie série ralentit énormément le boot. Je recherche donc dans les codes sources comment supprimer cette sortie… Je viens de trouver ! C’est dans le fichiers drivers/nand/nand_util.c…faire une recherche avec « Reading » et commenter la ligne : printf(« \rReading data from…percent);. Je recompile le tout et je fais un test avec et sans cette sortie avec GrabSerial:
- avec la sortie « Reading… » : 1,204s pour charger le noyau (1,2Mo) depuis la flash NAND
- sans la sortie « Reading…. » : 0,832s pour charger le noyau (1,2Mo) depuis la flash NAND
Conclusion : prêt de 30% de temps de gagner ! Dans la plupart des cas limiter les sorties vers l’écran ou la voie série permet de gagner du temps (voir plus loin pour le noyau avec le ‘printk’ et l’option ‘quiet’). J’obtiens un temps de 1,6s depuis le démarrage à froid jusqu’au message ‘Starting kernel’ et 14,4s jusqu’à l’affichage du login avec un rootfs en ext3 sur carte SD.
Optimisation du noyau Linux
J’ai modifié la configuration de base fourni par le vendeur pour qu’elle corresponde à nos besoins. En clair moins vous mettez de ‘choses’ dans votre noyau, plus celui-ci est petit et plus il charge vite. Actuellement j’ai gardé le nécessaire pour faire tourner la carte correctement, cependant comme je n’ai pas encore choisi le système de fichiers du ‘rootfs’ j’ai gardé le support du ext3, de yaffs, de jffs2 avec tous les systèmes de compression et ubifs. Une fois que j’aurais décidé lequel je garde j’éliminerai les autres et je gagnerai du temps au boot. De plus afin d’avoir des indication de temps par le noyau j’ai compilé celui-ci avec le support du « printk », on pourra l’éliminer par la suite.
Pour l’instant on peut appliquer des modifications à la ligne de commande du noyau Linux. Comme précédemment, limiter les sorties vers l’écran ou la voie série permet de gagner du temps. Donc la première chose à changer et de rajouter le paramètre ‘quiet’. Un petit test avec GrabSerial et on obtient :
- messages du noyau affichés : de « Starting kernel » au lancement de INIT : 2,255s
- messages du noyau cachés : de « Starting kernel » au lancement de INIT : 1,707s
Conclusion: on gagne 548ms rien qu’avec ce paramètre !
A chaque démarrage, on recalibre la durée d’une boucle appelée ‘loop_per_jiffy’ ou LPJ associée au BogoMips. Or le noyau nous donne la valeur de la calibration pendant le boot. Il suffit donc de passer ce paramètre au noyau pour éviter cette calibration. Dans mon cas, la ligne qui nous interesse est la suivante:
1 |
Calibrating delay loop... 353.89 BogoMIPS (lpj=884736) |
A priori cette opération dure 110ms mais la sortie ‘printk’ du noyau n’est sans doute pas assez précise. Faisons un test avec GrabSerial. J’obtiens:
- avec boucle de calibrage: de « Starting kernel » au lancement de INIT : 1,707s
- sans boucle de calibrage: de « Starting kernel » au lancement de INIT : 1,599s
Conclusion: environ 110ms de gagner ! Dans les messages noyau on obtient:
1 |
Calibrating delay loop (skipped) preset value.. 353.89 BogoMIPS |
On obtient actuellement un temps de 13,184s pour l’arrivée jusqu’au login mais surtout on met 3,228s jusqu’à INIT.
La décompression du noyau au départ prend 560ms, ce qui est loin d’être négligeable. J’ai testé avec un noyau non compressé mais c’est plus défavorable donc il faudrait que la décompression soit plus rapide. Dans la version de mon noyau (2.6.38), l’image est compressé avec ZLIB et je n’ai pas d’autres choix. Apparemment si vous pouvez choisir votre système de décompression, il faut prendre LZO !
Choix du système de fichiers racine parmi ext3, JFFS2, YAFFS2 et UBIFS
Pour continuer à optimiser le temps de boot, je me suis dit que l’on pouvait certainement gagner sur le temps de montage du rootfs. Pendant tous les tests précédents on monte le rootfs depuis une carte SD formatée en ext3. Le paramètre ‘rootwait’ du noyau permet d’attendre le système de fichiers. Cette attente est donnée dans les messages du noyau, ici elle vaut (du message ‘Waiting for root device /dev/mmcblk0p1…’ à ‘VFS: Mounted root (ext3 filesystem)’) 435ms.
Au boot 3 « pseudos » partitions sont créées aux adresses indiquées ci-dessous dans la flash NAND, on le voit avec les messages du noyau suivant:
1 2 3 4 5 6 7 |
S3C NAND Driver, (c) 2008 Samsung Electronics S3C NAND Driver is using hardware ECC. NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit) Creating 3 MTD partitions on "NAND 256MiB 3,3V 8-bit": 0x00000000-0x00080000 : "Bootloader" 0x00080000-0x00580000 : "Kernel" 0x00580000-0x10000000 : "File System" |
On va donc s’interresser à la troisième partition « File System » qui va contenir le système de fichiers racine. Comme on démarre actuellement sur la carte SD, on peut effacer sans problème cette « partition ». Pour cela vous devez disposer des outils de manipulation des Flashs NOR et NAND. Ceux-ci s’installent avec la commande suivante depuis le système embarqué et nous allons tester quelques commandes :
1 2 3 4 5 6 7 8 9 10 11 |
root@TQ6410-Iris:~# aptitude install mtd-utils root@TQ6410-Iris:~# mtdinfo /dev/mtd2 mtd2 Name: File System Type: nand Eraseblock size: 131072 bytes, 128.0 KiB Amount of eraseblocks: 2004 (262668288 bytes, 250.5 MiB) Minimum input/output unit size: 2048 bytes Sub-page size: unknown Bad blocks are allowed: true Device is writable: true |
On remarque bien que la flash NAND s’efface par blocs de 128Ko et que l’unité de base est de 2048octets.
Système de fichiers JFFS2 sur flash NAND
On va commencer par tester le système de fichiers JFFS2. Pour mémoire dans la configuration du noyau, j’ai activé toutes les méthodes de compression avec une préférence pour LZO (compression plus faible que ZLIB ou BZip2 mais vitesse de décompression trés rapide). On va donc initialiser cette partition pour qu’elle acceuille un système de fichiers JFFS2:
1 2 3 4 5 6 7 8 |
root@TQ6410-Iris:~# flash_eraseall -j /dev/mtd2 Erasing 128 Kibyte @ 62c0000 -- 39 % complete. Cleanmarker written at 62c0000. Skipping bad block at 0x062e0000 Erasing 128 Kibyte @ 8e60000 -- 56 % complete. Cleanmarker written at 8e60000. Skipping bad block at 0x08e80000 Erasing 128 Kibyte @ 95e0000 -- 59 % complete. Cleanmarker written at 95e0000. Skipping bad block at 0x09600000 Erasing 128 Kibyte @ fa80000 -- 100 % complete.Cleanmarker written at fa60000. |
On remarque que la flash NAND a des erreurs. Le système de fichiers en tiendra compte ! On monte notre « pseudo » partition dans /mnt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
root@TQ6410-Iris:~# mount -t jffs2 /dev/mtdblock2 /mnt root@TQ6410-Iris:~# mount tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755) proc on /proc type proc (rw,noexec,nosuid,nodev) sysfs on /sys type sysfs (rw,noexec,nosuid,nodev) udev on /dev type tmpfs (rw,mode=0755) tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev) devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620) rootfs on / type rootfs (rw) /dev/mtdblock2 on /mnt type jffs2 (rw) root@TQ6410-Iris:~# df -h Filesystem Size Used Avail Use% Mounted on rootfs 1.9G 477M 1.4G 26% / /dev/mtdblock2 251M 5.9M 245M 3% /mnt |
Nous disposons donc d’un espace conséquent pour le stockage de notre système de fichiers racine (prés de 245Mo avant compression). Pour installer le système de fichiers racine dans cette espace, j’ai créé auparavant une archive tar.bz2 de mon système qui tourne actuellement sur la carte SD. Comment faire ? Trés simple, sur votre système de développement montez cette carte SD avec une commande du style : mount -t auto /dev/sdd1 /mnt puis créez l’archive avec tar jcf rootfs-debian-squeeze-grip-for-TQ6410.tar.bz2 /mnt/*. Vous obtenez alors une archive de votre système de fichiers racine (chez moi cette archive fait 22Mo) que vous allez copier sur la carte SD avec cp rootfs-debian-squeeze-grip-for-TQ6410.tar.bz2 /mnt pour ensuite le désarchiver vers la NAND Flash. Démontez votre carte SD avec umount /mnt. On admet que l’on reprend la suite des commandes ci-dessus et que vous disposez de l’archive à la racine de la carte SD 🙂 Au fait si vous n’arrivez pas à désarchivez l’archive c’est que vous n’avez probablement pas bzip2 d’installé…un petit aptitude install bzip2 réglera l’affaire !
On désarchive donc notre système de fichiers racine vers /mnt qui représente le mtd2 de la flash NAND (attention soyez patient) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
root@TQ6410-Iris:~# cd /mnt root@TQ6410-Iris:/mnt# tar jxf /debian-squeeze-grip-OD.tar.bz2 . root@TQ6410-Iris:/mnt# ls /mnt bin dev home lost+found mnt proc sbin srv tmp var boot etc lib media opt root selinux sys usr root@TQ6410-Iris:/mnt# df -h Filesystem Size Used Avail Use% Mounted on rootfs 1.9G 477M 1.4G 26% / /dev/mtdblock2 251M 42M 210M 17% /mnt root@TQ6410-Iris:/mnt# du -h --max-depth=1 /mnt 3.9M /mnt/bin 512 /mnt/dev 361K /mnt/etc 7.3M /mnt/lib 0 /mnt/mnt 0 /mnt/opt 0 /mnt/srv 0 /mnt/tmp 0 /mnt/sys 8.3M /mnt/var 40M /mnt/usr 0 /mnt/boot 0 /mnt/home 0 /mnt/proc 3.3M /mnt/sbin 14K /mnt/root 0 /mnt/media 0 /mnt/selinux 0 /mnt/lost+found 63M /mnt |
On vérifie le contenu de la flash NAND avec un ‘ls’. On regarde combien on a mangé de place (ici 42Mo) et combien on occupe effectivement de place (ici 63Mo). On travaille donc bien sur un système de fichiers compressé mais adapté au mémoire flash (c’est à dire que l’on réparti les écritures pour moyenner l’usure (wear-levelling)) et relativement résistant au coupure électrique brusque puisque journalisé ! Reste à savoir le temps de montage au boot. Redémarrez puis arrêtez U-Boot car il nous faut modifier la ligne de passage d’arguments au noyau (lettre ‘s’) :
1 |
console=ttySAC0,115200 root=/dev/mtdblock2 rootfstype=jffs2 init=/sbin/init quiet noswap lpj=884736 |
Je retire la carte SD puis on fait donc le test du temps de boot avec GrabSerial. J’obtiens un temps de 18,383s depuis la mise sous tension jusqu’a l’obtention du login. Mais ce n’est pas vraiment ce qui m’interesse. Il faut fouiller les messages du noyau pour tenter de repérer le temps de montage :
1 2 3 4 5 6 7 |
[21474536.705000] TCP cubic registered [21474536.705000] NET: Registered protocol family 17 [21474536.705000] VFP support v0.3: implementor 41 architecture 1 part 20 varia5 [21474536.710000] s3c2410-rtc s3c2410-rtc: setting system clock to 2011-08-08 2) [21474539.275000] s3c64xx_clk_doutmpll_get_rate: parent is 532000000 [21474539.560000] VFS: Mounted root (jffs2 filesystem). [21474539.560000] Freeing init memory: 96K |
On passe de 36.71s à 39.275s or ce n’est pas la synchronisation de l’horloge système avec la RTC qui prend aussi longtemps. J’estime donc que le temps de montage est 39.56 – 36.71 = 2,85s pour monter les 250Mo de cette partition. Si vous cherchez un peu sur Internet vous verrez que le reproche fait à JFFS2 est son temps de montage or ici il est loin d’être catastrophique…bon j’ai triché un peu :-). Il existe un paramètre pour le système de fichiers JFFS2 qui permet de monter beaucoup plus vite le système de fichiers dans le noyau : [*] JFFS2 summary support. J’ai fait le test sans ce paramètre et le temps de montage sur la même partition est alors de 14s ! Bon on s’éloigne de notre but : démarrez plus vite mais on s’est rapproché de coté ‘clair’ de la force : on a un système rootfs sur une flash NAND et pas sur une SD.
Conclusion: globalement on va moins vite mais on s’est rapproché d’un ‘vrai’ système embarqué…de plus la compression, la jounalisation et le wear-levelling sont interessants.
Système de fichiers YAFFS2 sur flash NAND
On va recommencer un peu la même chose que précédemment avec un système de fichiers YAFFS2. Cette fois-ci je commence par la capture d’écran des paramètres noyau pour le système de fichiers YAFFS2:
Donc on redémarre sur notre carte SD pour pouvoir manipuler sans problème la flash NAND. On enchaine donc les commandes suivantes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
root@TQ6410-Iris:~# flash_eraseall /dev/mtd2 Erasing 128 Kibyte @ 62c0000 -- 39 % complete. Skipping bad block at 0x062e0000 Erasing 128 Kibyte @ 8e60000 -- 56 % complete. Skipping bad block at 0x08e80000 Erasing 128 Kibyte @ 95e0000 -- 59 % complete. Skipping bad block at 0x09600000 Erasing 128 Kibyte @ fa80000 -- 100 % complete. root@TQ6410-Iris:~# mount -t yaffs2 /dev/mtdblock2 /mnt [21474611.890000] yaffs: dev is 32505858 name is "mtdblock2" [21474611.890000] yaffs: passed flags "" [21474611.895000] yaffs: Attempting MTD mount on 31.2, "mtdblock2" [21474612.095000] yaffs_read_super: isCheckpointed 0 root@TQ6410-Iris:~# mount tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755) proc on /proc type proc (rw,noexec,nosuid,nodev) sysfs on /sys type sysfs (rw,noexec,nosuid,nodev) udev on /dev type tmpfs (rw,mode=0755) tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev) devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620) rootfs on / type rootfs (rw) /dev/mtdblock2 on /mnt type yaffs2 (rw) root@TQ6410-Iris:~# df -h Filesystem Size Used Avail Use% Mounted on rootfs 1.9G 477M 1.4G 26% / /dev/mtdblock2 251M 1.5M 249M 1% /mnt root@TQ6410-Iris:~# cd /mnt root@TQ6410-Iris:/mnt# tar jxf /debian-squeeze-grip-OD.tar.bz2 . root@TQ6410-Iris:/mnt# ls /mnt bin dev home lost+found mnt proc sbin srv tmp var boot etc lib media opt root selinux sys usr root@TQ6410-Iris:/mnt# df -h Filesystem Size Used Avail Use% Mounted on rootfs 1.9G 477M 1.4G 26% / /dev/mtdblock2 251M 81M 170M 33% /mnt root@TQ6410-Iris:/mnt# du -h --max-depth=1 /mnt 532K /mnt/etc 2.0K /mnt/boot 18K /mnt/root 2.0K /mnt/home 2.0K /mnt/mnt 2.0K /mnt/srv 41M /mnt/usr 3.3M /mnt/sbin 2.0K /mnt/tmp 2.0K /mnt/opt 2.0K /mnt/selinux 2.0K /mnt/media 3.9M /mnt/bin 8.4M /mnt/var 2.0K /mnt/proc 92K /mnt/dev 2.0K /mnt/sys 7.3M /mnt/lib 2.0K /mnt/lost+found 64M /mnt |
Quelques remarques sur ce qui précède : on efface d’abord la flash NAND, puis on monte cette partition en YAFFS2, on voit que trés peu de place est occupé au montage (à peine 1,5Mo). On désarchive le ‘rootfs’. Puis on regarde la place occupée sur le support : prés de 80Mo !! sur 64Mo effectif…on perd donc beaucoup de bloc mémoire. Le système de fichiers n’est pas compressé. On redémarre sur ce système de fichier avec les paramètres suivants:
1 |
console=ttySAC0,115200 root=/dev/mtdblock2 rootfstype=yaffs2 init=/sbin/init quiet noswap lpj=884736 |
A nouveau on regarde ce que ça donne au point de vue temps de boot avec GrabSerial. J’obtiens un temps de 13,215s depuis la mise sous tension jusqu’à l’obtention du login. Ce n’est pas mal du tout puisque l’on est au même niveau que le boot sur la SD en ext3. Fouillons les messages du noyau pour tenter de repérer le temps de montage :
1 2 3 4 5 6 |
[21474536.710000] yaffs: dev is 32505858 name is "mtdblock2" [21474536.710000] yaffs: passed flags "" [21474536.710000] yaffs: Attempting MTD mount on 31.2, "mtdblock2" [21474536.960000] yaffs: restored from checkpoint [21474536.960000] yaffs_read_super: isCheckpointed 1 [21474536.960000] VFS: Mounted root (yaffs2 filesystem). |
On obtient un temps de 36.96 – 36.71 = 250ms ce qui est assez remarquable…à moins que je me sois trompé quelque part mais là je vois pas où car j’ai suivi la même procédure que pour le JFFS2.
Conclusion: on est au même niveau que la carte SD en ext3 mais avec un système de fichier pour flash NAND journalisé. On est aussi plus ‘système embarqué’. Le seul souci c’est qu’il ne compresse pas à la volée. Je n’ai pas cherché si cela est possible…En tout cas cela semble être un bon choix comme futur système de fichiers pour notre système.
Système de fichiers UBIFS sur flash NAND
C’est sans doute le système de fichiers le plus compliqué à mettre en place avec une méthode identique aux précédents. Les paramètres du noyau sont les suivants:
Pour mémoire on travaille sur la MTD2 qui doit contenir le rootfs.On va donc commencer par effacer la flash NAND puis formater le MTD avec ubiformat:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
root@TQ6410-Iris:~# flash_eraseall /dev/mtd2 Erasing 128 Kibyte @ 62c0000 -- 39 % complete. Skipping bad block at 0x062e0000 Erasing 128 Kibyte @ 8e60000 -- 56 % complete. Skipping bad block at 0x08e80000 Erasing 128 Kibyte @ 95e0000 -- 59 % complete. Skipping bad block at 0x09600000 Erasing 128 Kibyte @ fa80000 -- 100 % complete. root@TQ6410-Iris:~# ubiformat /dev/mtd2 ubiformat: warning!: your MTD system is old and it is impossible to detect sub-page size. Use -s to get rid of this warning ubiformat: assume sub-page to be 2048 ubiformat: mtd2 (nand), size 262668288 bytes (250.5 MiB), 2004 eraseblocks of 131072 bytes (128.0 KiB), min. I/O size 2048 bytes libscan: scanning eraseblock 2003 -- 100 % complete ubiformat: 2001 eraseblocks have valid erase counter, mean value is 0 ubiformat: bad eraseblocks: 791, 1140, 1200 ubiformat: formatting eraseblock 2003 -- 100 % complete |
J’essaye alors « d’attacher » ce mtd au système (paramètres -m 2 ci-dessous) :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
oot@TQ6410-Iris:~# ubiattach /dev/ubi_ctrl -m 2 [ 748.290000] UBI: attaching mtd2 to ubi0 [ 748.290000] UBI: physical eraseblock size: 131072 bytes (128 KiB) [ 748.295000] UBI: logical eraseblock size: 129024 bytes [ 748.300000] UBI: smallest flash I/O unit: 2048 [ 748.305000] UBI: sub-page size: 512 [ 748.310000] UBI: VID header offset: 512 (aligned 512) [ 748.315000] UBI: data offset: 2048 [ 748.330000] UBI error: validate_ec_hdr: bad VID header offset 2048, expected 512 [ 748.330000] UBI error: validate_ec_hdr: bad EC header [ 748.335000] UBI error: ubi_io_read_ec_hdr: validation failed for PEB 0 ubiattach: error!: cannot attach mtd2 error 22 (Invalid argument) |
Bon il y a un soucis…Aprés une petite recherche sur internet, je trouve la réponse sur ce site : http://www.linux-mtd.infradead.org/faq/ubifs.html#L_mount_ubifs. Il faut reformatter la MTD en précisant la taille des « sous-pages » puis on retente d’attacher :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
root@TQ6410-Iris:~# ubiformat /dev/mtd2 -s 512 ubiformat: mtd2 (nand), size 262668288 bytes (250.5 MiB), 2004 eraseblocks of 131072 bytes (128.0 KiB), min. I/O size 2048 bytes libscan: scanning eraseblock 2003 -- 100 % complete ubiformat: 2001 eraseblocks have valid erase counter, mean value is 1 ubiformat: bad eraseblocks: 791, 1140, 1200 ubiformat: warning!: VID header and data offsets on flash are 2048 and 4096, which is different to requested offsets 512 and 2048 ubiformat: use new offsets 512 and 2048? (yes/no) yes ubiformat: use offsets 512 and 2048 ubiformat: formatting eraseblock 2003 -- 100 % complete root@TQ6410-Iris:~# ubiattach /dev/ubi_ctrl -m 2 [ 1443.140000] UBI: attaching mtd2 to ubi0 [ 1443.140000] UBI: physical eraseblock size: 131072 bytes (128 KiB) [ 1443.145000] UBI: logical eraseblock size: 129024 bytes [ 1443.150000] UBI: smallest flash I/O unit: 2048 [ 1443.155000] UBI: sub-page size: 512 [ 1443.160000] UBI: VID header offset: 512 (aligned 512) [ 1443.165000] UBI: data offset: 2048 [ 1444.270000] UBI: attached mtd2 to ubi0 [ 1444.270000] UBI: MTD device name: "File System" [ 1444.275000] UBI: MTD device size: 250 MiB [ 1444.280000] UBI: number of good PEBs: 2001 [ 1444.285000] UBI: number of bad PEBs: 3 [ 1444.290000] UBI: max. allowed volumes: 128 [ 1444.295000] UBI: wear-leveling threshold: 4096 [ 1444.300000] UBI: number of internal volumes: 1 [ 1444.305000] UBI: number of user volumes: 0 [ 1444.305000] UBI: available PEBs: 1977 [ 1444.310000] UBI: total number of reserved PEBs: 24 [ 1444.315000] UBI: number of PEBs reserved for bad PEB handling: 20 [ 1444.320000] UBI: max/mean erase counter: 2/2 [ 1444.345000] UBI: background thread "ubi_bgt0d" started, PID 1727 UBI device number 0, total 2001 LEBs (258177024 bytes, 246.2 MiB), available 1977 LEBs (255080448 bytes, 243.3 MiB), LEB size 129024 bytes (126.0 KiB) |
On vient d’attacher un périphérique ‘ubi0’ à notre système, il faut maintenant créer un volume sur celui-ci (le paramètre ‘-m’ permet d’utiliser la totalité de l’espace disponible dans ‘ubi0’ et -N spécifie le nom : ici ‘rootfs’ qui sera utilisé plus tard):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
root@TQ6410-Iris:~# ubimkvol -h ubimkvol version 1.0 - a tool to create UBI volumes. Usage: ubimkvol <UBI device node file name> [-h] [-a <alignment>] [-n <volume ID>] [-N <name>] [-s <bytes>] [-S <LEBs>] [-t <static|dynamic>] [-V] [-m] [--alignment=<alignment>][--vol_id=<volume ID>] [--name=<name>] [--size=<bytes>] [--lebs=<LEBs>] [--type=<static|dynamic>] [--help] [--version] [--maxavsize] Example: ubimkvol /dev/ubi0 -s 20MiB -N config_data - create a 20 Megabytes volume named "config_data" on UBI device /dev/ubi0. -a, --alignment=<alignment> volume alignment (default is 1) -n, --vol_id=<volume ID> UBI volume ID, if not specified, the volume ID will be assigned automatically -N, --name=<name> volume name -s, --size=<bytes> volume size volume size in bytes, kilobytes (KiB) or megabytes (MiB) -S, --lebs=<LEBs count> alternative way to give volume size in logical eraseblocks -m, --maxavsize set volume size to maximum available size -t, --type=<static|dynamic> volume type (dynamic, static), default is dynamic -h, -?, --help print help message -V, --version print program version The following is a compatibility option which is deprecated, do not use it -d, --devn=<devn> UBI device number - may be used instead of the UBI device node name in which case the utility assumes that the device node is "/dev/ubi<devn>" root@TQ6410-Iris:~# ubimkvol /dev/ubi0 -N rootfs -m Set volume size to 255080448 Volume ID 0, size 1977 LEBs (255080448 bytes, 243.3 MiB), LEB size 129024 bytes (126.0 KiB), dynamic, name "rootfs", alignment 1 |
On regarde si ‘udev’ a créé les périphériques concernés dans /dev:
1 2 |
root@TQ6410-Iris:~# ls /dev/ubi* /dev/ubi0 /dev/ubi0_0 /dev/ubi_ctrl |
On est prêt à monter ce périphérique dans notre système et prendre quelques renseignements dessus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
root@TQ6410-Iris:~# mount -t ubifs ubi0:rootfs /mnt [ 2589.845000] UBIFS: default file-system created [ 2589.950000] UBIFS: mounted UBI device 0, volume 0, name "rootfs" [ 2589.950000] UBIFS: file system size: 253532160 bytes (247590 KiB, 241 MiB, 1965 LEBs) [ 2589.955000] UBIFS: journal size: 12644352 bytes (12348 KiB, 12 MiB, 98 LEBs) [ 2589.960000] UBIFS: media format: 4 (latest is 4) [ 2589.965000] UBIFS: default compressor: LZO [ 2589.970000] UBIFS: reserved for root: 5182151 bytes (5060 KiB) root@TQ6410-Iris:~# mount tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755) proc on /proc type proc (rw,noexec,nosuid,nodev) sysfs on /sys type sysfs (rw,noexec,nosuid,nodev) udev on /dev type tmpfs (rw,mode=0755) tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev) devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620) rootfs on / type rootfs (rw) ubi0:rootfs on /mnt type ubifs (rw) root@TQ6410-Iris:~# df -h Filesystem Size Used Avail Use% Mounted on rootfs 1.9G 477M 1.4G 26% / ubi0:rootfs 227M 0 223M 0% /mnt |
On continue donc comme les autres systèmes de fichiers. On désarchive le rootfs dans /mnt et on regarde la place occupé.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
root@TQ6410-Iris:~# cd /mnt root@TQ6410-Iris:/mnt# tar jxf /debian-squeeze-grip-OD.tar.bz2 . root@TQ6410-Iris:/mnt# df -h Filesystem Size Used Avail Use% Mounted on rootfs 1.9G 477M 1.4G 26% / ubi0:rootfs 227M 47M 176M 21% /mnt root@TQ6410-Iris:/mnt# du -h --max-depth=1 /mnt 4.0M /mnt/bin 4.0K /mnt/dev 896K /mnt/etc 8.0M /mnt/lib 0 /mnt/mnt 0 /mnt/opt 0 /mnt/srv 0 /mnt/tmp 0 /mnt/sys 11M /mnt/var 48M /mnt/usr 0 /mnt/boot 0 /mnt/home 0 /mnt/proc 3.5M /mnt/sbin 24K /mnt/root 0 /mnt/media 0 /mnt/lost+found 0 /mnt/selinux 75M /mnt |
La compression utilisée ici est LZO. On peut la changer en le spécifiant sur la commande mount. On a bien affaire à un système de fichiers compressés 47Mo utilisé sur 227Mo disponible et 75Mo de fichiers réels. Notez au passage que c’est un peu moins bien que JFFS2. On va pouvoir tester le démarrage sur ce système de fichier. Il faut encore une fois modifié la ligne d’arguments passé au noyau par U-Boot:
1 |
console=ttySAC0,115200 ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs init=/sbin/init quiet noswap lpj=884736 |
A nouveau on regarde ce que ça donne au point de vue temps de boot avec GrabSerial. Fouillons les messages du noyau pour tenter de repérer le temps de montage :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[21474536.690000] UBI: attaching mtd2 to ubi0 [21474536.690000] UBI: physical eraseblock size: 131072 bytes (128 KiB) [21474536.690000] UBI: logical eraseblock size: 129024 bytes [21474536.690000] UBI: smallest flash I/O unit: 2048 [21474536.690000] UBI: sub-page size: 512 [21474536.690000] UBI: VID header offset: 512 (aligned 512) [21474536.690000] UBI: data offset: 2048 [21474537.760000] UBI: attached mtd2 to ubi0 [21474537.760000] UBI: MTD device name: "File System" [21474537.760000] UBI: MTD device size: 250 MiB [21474537.760000] UBI: number of good PEBs: 2001 [21474537.760000] UBI: number of bad PEBs: 3 [21474537.760000] UBI: max. allowed volumes: 128 [21474537.760000] UBI: wear-leveling threshold: 4096 [21474537.760000] UBI: number of internal volumes: 1 [21474537.760000] UBI: number of user volumes: 1 [21474537.760000] UBI: available PEBs: 0 [21474537.760000] UBI: total number of reserved PEBs: 2001 [21474537.760000] UBI: number of PEBs reserved for bad PEB handling: 20 [21474537.760000] UBI: max/mean erase counter: 3/2 ... [21474537.915000] UBIFS: mounted UBI device 0, volume 0, name "rootfs" [21474537.915000] UBIFS: file system size: 253532160 bytes (247590 KiB, 241 Mi B, 1965 LEBs) [21474537.915000] UBIFS: journal size: 12644352 bytes (12348 KiB, 12 MiB, 98 LEBs) [21474537.915000] UBIFS: media format: 4 (latest is 4) [21474537.915000] UBIFS: default compressor: LZO [21474537.915000] UBIFS: reserved for root: 5182151 bytes (5060 KiB) [21474537.920000] VFS: Mounted root (ubifs filesystem). |
On remarque qu’il y a deux parties distinctes dans les message du noyau: la partie association du MTD avec le système UBI qui prend 37.76-36.69=1,07s et le montage du système de fichiers UBIFS qui prend 0,005s. Pour comparer aux autres systèmes de fichiers, le temps de démarrage global est de 16,8s. C’est donc un peu plus lent que YAFFS2 et du même niveau que JFFS2. Cependant UBI et UBIFS semble être promis à un bel avenir comme on peut le voir sur ces tests.
Conclusion: c’est un bon compromis entre yaffs2 et jffs2. De plus il semble que ce système se développe dans les noyaux récents. De plus il a de trés bonne performance en lecture/écriture par rapport à YAFFS2. La mise en oeuvre est juste un peu plus délicate….
Conclusion sur les systèmes de fichiers
Pendant la phase de développement, je dirais que le plus simple est de travailler avec une carte SD : c’est pas cher, vous avez des capacités énormes mais « ca fait pas embarqué » comme dirais Denis Bodor dans ces articles. Au niveau du choix du système de fichiers sur la flash NAND il faudra tester à l’utilisation YAFFS2 et UBIFS. YAFFS2 a pour lui l’ancienneté et une trés bonne stabilité, UBI quand à lui est plus récent et évolue encore assez fortement.