Optimisation du temps de ‘Boot’ et choix du système de fichiers parmi ext3, jffs2, yaffs2 et ubifs pour le ‘rootfs’ de la carte TQ6410

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 :

  1. Lancement du chargeur intégré à la puce SoC (chez TI : X-Loader, chez Samsung ?) qui charge U-Boot depuis la flash NAND
  2. Démarrage de U-Boot qui s’occupe du chargement de l’image noyau depuis la flash NAND en RAM
  3. Décompression du noyau et démarrage de celui-ci
  4. Montage du système de fichiers racine puis passage à INIT
  5. 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:

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:

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) :

  1. 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’.
  2. 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’.
  3. 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’.
  4. 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…
  5. 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:

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:

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:

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:

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 :

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:

On remarque que la flash NAND a des erreurs. Le système de fichiers en tiendra compte ! On monte notre « pseudo » partition dans /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) :

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’) :

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 :

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.

Paramètres noyau pour JFFS2

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:

Paramètres noyau pour le système de fichier YAFFS2Donc on redémarre sur notre carte SD pour pouvoir manipuler sans problème la flash NAND. On enchaine donc les commandes suivantes:

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:

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 :

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:

Paramètres Noyau pour UBI

Paramètres Noyau pour UBIFS

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:

J’essaye alors « d’attacher » ce mtd au système (paramètres -m 2 ci-dessous) :

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 :

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):

On regarde si ‘udev’ a créé les périphériques concernés dans /dev:

On est prêt à monter ce périphérique dans notre système et prendre quelques renseignements dessus:

On continue donc comme les autres systèmes de fichiers. On désarchive le rootfs dans /mnt et on regarde la place occupé.

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:

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 :

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.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *