1. Synchronisation
1.1 Rappels sur les Classic RCU
RCU (Read-Copy Update) est un mécanisme de synchronisation qui a été intégré au noyau Linux durant le développement de la branche 2.5. RCU améliore le passage à l'échelle vis-à-vis des autres mécanismes de synchronisation en autorisant les lecteurs à s'exécuter en même temps que les écrivains. Les primitives de synchronisation classiques requièrent une attente des lecteurs lorsqu'un écrivain s'exécute et inversement. De plus, ces mécanismes (comme les seqlock par exemple) emploient des verrous (contrairement au RCU). Ainsi, la mise à jour de leur compteur entraîne de nombreuses invalidations de cache entre les différentes CPU dans un système multiprocesseurs et provoque alors une diminution sensible des performances globales.
RCU garantit la cohérence pour les accès en lecture en maintenant en mémoire différentes versions des structures de données protégées et en ne les libérant que lorsque toutes les sections critiques de code les employant sont terminées.
Les atouts des RCU viennent avec quelques limites également. Tout d'abord, une section critique en lecture RCU doit se situer dans l'espace noyau et ne doit pas bloquer, c'est-à-dire qu'elle doit continuer de s'exécuter sur le processeur sans être interrompu et sans s'interrompre volontairement (c’est-à-dire s'endormir). Enfin, les données à protéger par le mécanisme RCU doivent obligatoirement être conservées dans une structure, et l'accès à ces données doit être effectué uniquement à partir de l'adresse mémoire de cette structure. La nécessité de ces deux conditions d'utilisation deviendra claire par la suite.
Une section critique de lecture d'une structure de données protégée par le mécanisme RCU doit débuter par l'appel à rcu_read_lock() (qui désactive simplement la préemption) et se terminer par l'appel à rcu_read_unlock(). Lorsqu'un écrivain souhaite modifier la structure de donnée, il en effectue une copie, modifie cette copie est ensuite écrase le pointeur référençant l'ancienne structure avec l'adresse de cette copie modifiée. Cette opération étant atomique, il ne peut survenir aucun problème de synchronisation. C'est pour cela que la seconde condition est importante : l'accès en lecture aux structures de données par les sections critiques doit toujours s'effectuer à partir de l'adresse de la structure, afin qu'une lecture ayant débutée dans une structure termine toujours dans cette structure, même s'il ne s'agit plus de la dernière version à jour. Cela permet de garantir la cohérence des lectures. Il en résulte que plusieurs versions de la structure protégée peuvent se trouver en mémoire en même temps. Le problème maintenant est de pouvoir libérer les anciennes versions lorsque les lectures sont terminées. C'est ici que la première condition d'utilisation entre en jeu.
La condition de non-blocage lors des lectures est, en effet, un élément-clé pour le fonctionnement des RCU. Si cette condition est toujours vérifiée, alors, à chaque fois qu'on observe une CPU effectuer un changement de contexte ou exécuter la tâche idle ou enfin passer en mode utilisateur, on sait que toute section critique de lecture RCU qui s'exécutait précédemment sur cette CPU s'est terminée. Des états CPU de ce type sont appelés états tranquilles (quiescent state). Lorsque toutes les CPU du système sont passées par au moins un état tranquille, depuis le remplacement via le mécanisme RCU d'une structure S1 par une structure S2, on peut libérer la structure S1, car plus aucun lecteur ne l'emploie. Cette attente de libération mémoire est appelée : période de grâce du RCU. Remarquons que la libération au moment opportun des structures obsolètes se fait au travers d'une fonction callback, enregistrée au début de la période de grâce par le code de mise à jour RCU.
Enfin, mentionnons qu'il existe une variation des RCU, les Sleepable RCU qui autorise une section critique en lecture RCU à s'endormir. Ce type de RCU est utilisé lorsque l'on souhaite améliorer la préemptibilité du noyau dans le cas, a priori, d'applications temps réel.
La section suivante explique les problèmes que comportent l'implémentation des Classic RCU dans les versions antérieures du noyau et justifiant ainsi l'adoption des Tree RCU.
1.2 Deux problèmes liés à l'implémentation des Classic RCU
La structure la plus importante dans le mécanisme des Classic RCU est rcu_ctrlblk. Elle contient le champ cpumask qui comporte un bit pour chaque CPU du système. Chacun des bits de CPU est positionné à 1 au début de chaque période de grâce, et chaque CPU positionne à 0 son bit après être passé dans un état tranquille. Afin d'éviter la corruption du champ cpumask, à cause des écritures simultanées pouvant survenir depuis les différentes CPU, un spinlock (champ lock de rcu_ctrlblk) est employé pour protéger ce champ. Cependant, ce verrou peut être sévèrement disputé par les CPU si le système en comporte plusieurs centaines (la tendance aux processeurs multi-core devrait rendre ce type de système plus commun à l'avenir), entraînant une dégradation nette des performances globale (cache bouncing, etc.). Un autre problème de l'implémentation provient du fait que chaque CPU doit effacer son bit, ce qui implique que les CPU ne peuvent pas dormir durant une période de grâce, et limite ainsi la capacité de Linux à conserver l'énergie. Notamment, le mécanisme dynticks, préservant le repos des CPU oisifs en désactivant les ticks réguliers de l'horloge, devient alors inopérant.
La nouvelle implémentation des Classic RCU, baptisée Tree RCU, et intégrée au 2.6.29, résout à la fois le problème de passage à l'échelle et celui de la surconsommation énergétique.
1.3 Les Tree RCU : une implémentation plus saine des Classic RCU
Une façon efficace de diminuer la dispute sur un verrou X est de créer une arborescence de verrous dans laquelle la racine est le verrou X, et les nœuds intermédiaires (rcu_node) sont des verrous qui doivent être acquis (le long d'une branche) pour atteindre la racine depuis les nœuds feuilles. Chacune de ces feuilles contient également un verrou qui met en compétition une fraction des CPU présents dans le système. Par exemple, pour un système composé de six CPU, on peut imaginer un arbre de verrou à un seul niveau et trois branches, partant de la racine jusqu'à trois feuilles, lesquelles mettent chacune en concurrence deux CPU sur un verrou. Afin d'acquérir le verrou racine, les CPU doivent alors d'abord acquérir le verrou de la feuille à laquelle ils sont associés. Ainsi, dans notre exemple, le verrou racine ne peut être disputé par plus de trois CPU à la fois (un amoindrissement de la dispute de 50%) et seulement deux pour les verrous des feuilles (correspondant à une diminution de la dispute de 66,6%). Les Tree RCU protègent de la sorte l'accès au champ cpumask pour l'enregistrement de l'état tranquille des CPU durant une période de grâce, et améliore grandement les performances pour un système disposant de plus d'un millier de CPU.
L'implémentation des Tree RCU maintient également des données propres à chaque CPU (les per-CPU data), telles que les listes de callbacks RCU, qui sont organisées dans des structures rcu_data accessibles depuis les rcu_node du dernier niveau de l'arbre (c’est-à-dire les feuilles).
Nous n'avons examiné pour l'instant que la solution apportée au problème du passage à l'échelle. En ce qui concerne la réduction de la consommation énergétique, il s'agit de ne pas réveiller les CPU endormis lors d'une période de grâce, car ils ne se trouvent pas, de toute évidence, au cœur d'une section critique de lecture RCU. Cela est accompli en requérant que chaque CPU modifie un compteur situé dans une structure rcu_dynticks propre à chaque CPU (accessible depuis les struct rcu_data). En bref, un compteur est positionné à une valeur paire lorsque le CPU associé a été mis en sommeil par le mécanisme dyntick. Sinon, il est positionné à une valeur impaire. Pour terminer une période de grâce, RCU attend alors seulement l'enregistrement d'états tranquilles de la part des CPU ayant un compteur rcu_dynticks impair.
2. Sécurité
2.1 La réorganisation des credentials
2.1.1 Introduction : Objets, sujets et politiques de sécurité
Au sein d'un système informatique, on distingue deux catégories d'entités : les objets et les sujets.
Un objet est une entité passive sur laquelle des sujets effectuent des actions. Les sujets peuvent également jouer le rôle d'un objet. On note comme objets, notamment : les fichiers/inodes, les sockets, les files de messages, les segments de mémoire partagé, les sémaphores, les clés ; mais également les processus qui sont aussi les principaux sujets d'un système. Une partie des informations sur un objet consiste en ces références, en la description de son identité (credentials). Un sous-ensemble de ces credentials fournit un contexte pour cet objet. Ce contexte intervient dans la décision d'accepter ou de rejeter une action qui cible l'objet.
Un sujet est un objet opérant sur un autre objet. Les processus sont les sujets les plus représentatifs d'un système informatique. Un sujet dispose d'une vision additionnelle de ses credentials que l'on ne retrouve pas dans un objet inactif. Les credentials sont alors vus comme des capacités, des aptitudes, fournies au sujet pour opérer sur des objets. Une partie de ces credentials fournit un contexte pour les sujets sur lequel le système de sécurité du noyau se fonde pour valider ou non les actions du sujet. Parmi ces actions, on remarque notamment : l'écriture, la lecture, la création et la suppression de fichiers, l'acte de se dupliquer (fork()), l'envoi de signaux et le traçage de processus.
Finalement, une politique de sécurité dans le système est mise en place via la création de règles de sécurité, lesquelles dictent les lois que les sujets sont contraints de respecter dans leurs actions sur les objets. Une telle politique peut être discrète (DAC – Discretionary Access Control). Dans ce cas, les règles ne sont pas érigées pour l'ensemble du système, mais à la discrétion de chaque objet. Ainsi, les ACL (Access Control List) autorisent la création d'une politique de type DAC. Les systèmes Unix traditionnels proposent une forme limitée d'ACL au travers du masque de permissions associé à chaque objet et définissant les actions permises par le propriétaire de l'objet, le groupe, et le reste du monde. Une forme d'ACL plus riche et flexible est incarnée par les POSIX ACL. À chaque objet est alors associé une liste des sujets qui peuvent agir dessus et les actions qui leur sont autorisées.
La politique de sécurité peut sinon être globale (MAC – Mandatory Access Control). Il s'agit dans ce cas non pas de spécifier des règles pour chaque objet individuellement, mais plutôt de spécifier des règles générales qui s'appliquent à l'ensemble des actions qui s'effectuent au sein du système. Pour parvenir à cela, SELinux ou Smack mettent en œuvre un étiquetage des sujets et des objets du système. La politique de sécurité globale est alors définie au travers de règles faisant intervenir ces étiquettes. Lorsqu'une action est sur le point d'être effectuée, le module de sécurité (responsable de l'application des règles) récupère les étiquettes de l'objet et du sujet en question et parcourt ensuite l'ensemble des règles à la recherche de celles qui s'appliquent à la situation.
Par exemple, les fichiers obtenus à partir du disque ou du réseau peuvent contenir un certain nombre d'annotations (UID, GID, ACL, étiquette LSM, SUID, SGID, etc.) qui forment le contexte de sécurité de l'objet. Pour valider une opération d'une tâche sur un fichier, ce contexte est comparé au contexte du sujet.
Après cette introduction replaçant la notion de credentials dans le contexte d'un système informatique, nous abordons dans ce qui suit la restructuration de l'organisation des credentials que subit le noyau Linux pour sa version 2.6.29.
2.1.2 Une nouvelle structure pour regrouper les credentials
Dans la section précédente, les credentials ont été décrits comme la carte d'identité d'un objet ou encore comme les aptitudes, les capacités dont disposent les sujets. Voyons à présent ces différents types de credentials.
Les credentials Unix traditionnels :
L'UID (User ID) et le GID (Group ID) sont associés à la plupart des objets du système et définissent le contexte basique d'un objet. D'autres credentials sont uniquement employés par les tâches du système. On y trouve notamment l'EUID et l'EGID (Effective UID et GID) qui sont généralement utilisées en lieu et place du UID et GID lorsque l'objet agit en tant que sujet. Une liste de groupes additionnels peut également faire partie du contexte subjectif d'une tâche (ce qui lui permet d'effectuer des actions sur des objets de groupes différents).
Les capabilities :
Les capabilities sont des aptitudes que l'on peut associer aux tâches et qui leur permettent d'outrepasser les droits qui leurs sont habituellement donnés (par exemple CAP_SYS_RAWIO permet à une tâche d'effectuer des opérations sur les ports d'E/S). Chaque tâche du système dispose de quatre ensembles de capabilities : les effective qui contiennent les capabilities que peut employer la tâche ; les permitted sont celles que la tâche peut s'octroyer (c'est-à-dire placer dans l'ensemble effective) ; les inheritable sont celles qui sont héritées par la tâche lors d'un execve() à l'exception des capabilities présentes dans le dernier ensemble, le bounding set.
Les securebits :
Les securebits sont un champ de bits qui est associé à chaque tâche et qui gouverne l'interaction entre les capabilities et setuid(). Par exemple, l'activation du flag SECURE_NOROOT permet à ce qu'une tâche uid 0 n'ait aucun privilège et l'activation de SECURE_NOROOT_LOCKED empêche les futures modifications du flag SECURE_NOROOT.
Les « keys » :
Ces credentials sont associés uniquement aux tâches du système. Il s'agit de clés cryptographiques, de tokens d'authentification, etc.
Les étiquettes LSM :
Les deux principaux LSM, SELinux et Smack fondent leur décision sur l'étiquetage des objets du système (cf. section précédente). Ainsi les credentials de chaque objet contiennent aussi une étiquette.
AF_KEY :
Il s'agit d'une approche basée socket pour la gestion des credentials au sein des piles réseau. Ces credentials ne sont pas ceux d'un objet en particulier, mais ceux du système.
Pour les tâches du système, l'ensemble des credentials étaient jusqu'alors dispersés dans leurs task_struct. Ils se retrouvent à présent (à l'exception de UID et GID) au sein d'une structure struct cred laquelle est accessible à partir du champ cred des task_struct. Quelques subtilités sont introduites avec l'utilisation de cette structure, notamment :
- Une structure struct cred, une fois créée et associée à un processus ne peut plus être modifiée, à l'exception de son compteur de références, des compteurs de références des structures filles et des keyrings qu'elle contient.
- Pour effectuer une modification de la struct cred, il est nécessaire de la copier, puis de modifier cette copie et, enfin, de changer le pointeur cred de la task_struct via le mécanisme de RCU (cf. Tree RCU dans la section « Synchronisation ») pour qu'elle pointe sur cette nouvelle copie.
- Enfin, une tâche ne peut modifier que ses propres credentials (Par exemple capset() prend maintenant uniquement le PID du processus courant).
La modification des credentials d'une tâche ne nécessite pas l'emploi de verrou, car une tâche ne peut modifier que sa propre struct cred. Les différentes étapes nécessaires à la modification sont données ci-dessous :
On appelle la fonction prepare_creds() qui retourne une copie de la struct cred du processus courant.
On modifie la structure renvoyée en fonction des critères de sécurité que l'on s'est donné (current_cred() permet de récupérer le jeu actuel de credentials et ainsi de le comparer à la copie modifiée).
Quand le nouveau jeu de credentials est prêt, il faut alors l'enregistrer dans le descripteur du processus courant. Cela est effectué via la fonction commit_creds(). À cet instant, le LSM, si présent, prend la main pour valider ou non cet enregistrement. S'il est validé, la fonction rcu_assign_pointer() est employée afin d'écraser le pointeur current->cred.
Si une erreur ou un échec de validation de la modification survient entre l'appel à prepare_creds() et commit_creds(), la fonction abort_creds() doit être appelée.
Un exemple typique de modification de credentials est le suivant (check_modify_suid() matérialise la fonction de vérification de la modification) :
int modify_suid(uid_t suid)
{
struct cred *new;
int ret;
new = prepare_creds();
if (!new)
return -ENOMEM;
new->suid = suid;
ret = security_modify_suid(new);
if (ret < 0) {
abort_creds();
return ret;
}
return commit_creds(new);
}
2.1.3 Perspectives
Cette réorganisation est un travail préliminaire effectué par David Howells pour l'intégration future de son mécanisme FS-Cache, un cache local pour les systèmes de fichiers réseau. Ce cache local doit respecter les mêmes contraintes de sécurité que le système de fichiers distant. L'infrastructure struct cred est justement prévue pour garantir de cela.
2.2 Nouveaux hooks pour LSM
Kentaro Takeda a ajouté des hooks LSM (Linux Security Module) au niveau du VFS (Virtual File System) où les structures vfsmount sont disponibles.
Avant de mentionner ces différents hooks, nous rappelons brièvement les structures du VFS mises en jeu. Chaque système de fichiers est représenté en mémoire par un ensemble de structures inodes et dentry. Les inodes représentent les fichiers (et répertoires) sous-jacents, physiques. Les dentries, quant à eux, sont construits au-dessus des inodes et forme l'arborescence du système de fichiers. C'est par eux que va passer la recherche de fichiers. Ainsi, un dentry dispose d'un pointeur sur un inode (d_inode), d'un pointeur sur un dentry parent (d_parent) et d'un nom (d_name pointant sur une chaîne de caractères). Pour compléter ce schéma, les inodes ont un pointeur (i_sb) vers une structure représentant le système de fichiers les regroupant : le superblock. Ce superblock représente le plus souvent soit un système de fichiers stocké sur une partition d'un périphérique bloc du système, soit un système de fichiers présent sur un système distant (cas de NFS par exemple).
L'espace de noms de fichiers associé à un processus, c'est-à-dire l'espace de noms qu'il perçoit est composé généralement de plusieurs systèmes de fichiers, lesquels sont montés les uns sur les autres. Cette organisation est représentée par un arbre de structures vfsmount qui sont définies pour chaque point de montage. En plus des liens père/fils qui relient ces structures, chacune dispose d'un membre pointant sur la dentry définissant le racine du vfsmount (mnt_root) et d'un membre pointant sur la dentry sur laquelle est montée ce vfsmount (mnt_mountpoint).
Les hooks qui ont été rajoutés, autorisent l'observation des opérations intervenant à ce niveau par un LSM. Ces hooks sont un pré-requis à l'intégration des modules de sécurité tels que AppArmor ou TOMOYO Linux, lesquels fondent leurs décisions de sécurité en fonction des chemins d'accès aux fichiers. Ces LSM ont donc besoin d'observer et de contrôler les opérations de « modification de répertoires » quand les vfsmount sont impliqués.
Les différents hooks définis sont donnés ci-dessous. Leur nom est directement en rapport avec ceux des opérations du VFS qu'ils « contrôlent ». Leur activation se fait à la configuration du noyau via CONFIG_SECURITY_PATH.
int security_path_unlink(struct path *dir, struct dentry *dentry);
int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode);
int security_path_rmdir(struct path *dir, struct dentry *dentry);
int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
unsigned int dev);
int security_path_truncate(struct path *path, loff_t length,
unsigned int time_attrs);
int security_path_symlink(struct path *dir, struct dentry *dentry,
const char *old_name);
int security_path_link(struct dentry *old_dentry, struct path *new_dir,
struct dentry *new_dentry);
int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
struct path *new_dir, struct dentry *new_dentry);
2.3 Support pour SMACK des réseaux et systèmes distants non étiquetés
Commençons tout d'abord par mentionner que CIPSO (Commercial IP Security Option), créé par l'IETF, définit un format et des procédures pour supporter une (a priori) quelconque politique de sécurité globale au sein d'un réseau IP. La mise en œuvre de cela passe par un étiquetage des paquets IP, et par une interprétation similaire des étiquettes par les différents systèmes du réseau. Afin de supporter CIPSO, Linux intègre l'infrastructure NetLabel qui effectue le travail d'étiquetage et de vérification des paquets, via des hooks LSM.
Smack est un LSM permettant de définir une politique de sécurité globale au travers de règles (cf. la section sur les Credentials) s'appliquant sur un étiquetage de tous les objets du système. Lorsqu'un système sous la tutelle de Smack communique avec des machines ou réseaux distants, il est important que les paquets envoyés et reçus soient étiquetés afin que la politique de sécurité puisse être appliquée. Cependant, certaines machines communiquant avec un système sous la tutelle de Smack n'ont peut être pas mis en œuvre de politique de sécurité globale et donc n'étiquettent peut-être pas les paquets réseau. Pour ces réseaux et machines distantes, un étiquetage automatique peut être défini au sein du système mettant en œuvre Smack, depuis cette version 2.6.29 du noyau. Cet étiquetage s'appuie principalement sur l'infrastructure NetLabel.
La gestion de cet étiquetage se fait depuis l'espace utilisateur au travers du système de fichiers mis en place par Smack. Une nouvelle entrée nommée netlabel prend place dans /smack. L'ajout automatique d'étiquettes aux paquets IP se fait via l'écriture dans /smack/netlabel de chaîne de caractères ayant l'une des deux formes suivantes :
A.B.C.D LABEL
ou
A.B.C.D/N LABEL
A.B.C.D représente une adresse réseau, N un entier entre 0 et 32, et LABEL correspond à l'étiquette Smack que l'on souhaite employer pour ce réseau ou cette machine. N spécifie le masque réseau pour l'adresse (s'il est omis, comme dans la première forme, la valeur 32 est prise par défaut). Les entrées, spécifiées dans /smack/netlabel, sont prises en compte pour l'étiquetage des paquets en partant des plus spécifiques. La règle d'étiquetage la plus générique est définie par une chaîne qui débute par exemple par 0.0.0.0/0, alors que les règles les plus spécifiques ont la valeur de N à 32, comme 192.168.1.5/32 par exemple.
Une étiquette particulière « @ » a été définie et ne peut être associée qu'à des adresses. N'importe quel processus peut envoyer des paquets à destination d'une telle adresse, et les paquets en provenance de cette adresse peuvent être fournis à n'importe quelle socket du système. Remarquons toutefois que l'utilisation de cette étiquette particulière rend la mise en place d'une politique de sécurité globale stricte impossible. Il est donc souhaitable de s'en passer.
3. Gestion Mémoire
3.1 Exclusive I/O memory
Afin de limiter les dégâts sur l'espace noyau qui peuvent provenir de l'espace utilisateur, un mécanisme (activé via l'option de configuration CONFIG_STRICT_DEVMEM) a été mis en place dans le noyau 2.6.26 afin d'empêcher le mapping de la mémoire noyau depuis l'espace utilisateur, c'est-à-dire via l'utilisation de mmap() sur le périphérique virtuel /dev/mem. Ce mécanisme (une simple liste de régions bannies de /dev/mem) a été étendu dans la version 2.6.29 afin de couvrir les régions mémoire d'E/S. Toutefois, ce comportement n'est pas effectué par défaut. Les pilotes souhaitant réserver exclusivement les portions de l'espace d'adressage qu'ils emploient pour le pilotage de leur périphérique doivent employer les primitives : pci_request_region_exclusive(3), pci_request_regions_exclusive(2) ou encore pci_request_selected_regions_exclusive(3). Pour des raisons de développement/débogage d'un pilote, il est possible de spécifier au démarrage l'option de boot iomem=relaxed, afin de rendre possible le mapping via /dev/mem de régions d'E/S, même si le pilote associé emploie les primitives précédentes.
3.2 Information supplémentaire dans /proc/pid/smaps
Le fichier /proc/[PID]/smaps contient des informations sur les pages mappées en mémoire par le processus d'identifiant [PID]. De nouvelles informations concernant ces mappings ont été rajoutées. Elles servent à vérifier la taille des pages employées pour les régions des applications utilisant des pages larges (huge pages). Ainsi, deux nouvelles entrées sont présentes dans ce fichier pour chaque page employée par l'application que l'on surveille : KernelPageSize et MMUPageSize. La présence de ces deux entrées est nécessaire, car la taille de base employée par le noyau pour des pages larges peut ne pas correspondre à celles employées par la MMU suivant les versions de processeurs sur une même architecture. C'est par exemple le cas de l'architecture PPC64, pour laquelle de vieux processeurs nécessitent l'emploi de pages de 4 Ko, alors que le noyau utilise par défaut une taille de 64 Ko pour ses pages sur cette architecture.
4. Appels Système
4.1 f_op->poll() peut maintenant bloquer
Parmi les opérations du VFS, f_op->poll() était la seule à ne pas être autorisée à bloquer. Cela pouvait poser des problèmes d'implémentation de cette opération pour certains pilotes. Cela est désormais possible depuis la version 2.6.29 du noyau où l'implémentation des fonctions sys_select() et sys_poll() (lesquelles appellent f_op->poll()) a été revue. Ce changement est profitable notamment pour l'implémentation des systèmes de fichier en espace utilisateur comme FUSE (Filesystem in Userspace) ou 9p, car, pour ces systèmes, il est très difficile d'implémenter f_op->poll() de façon non bloquante.
5. Initialisation du système
5.1 Infrastructure pour l'appel asynchrone de fonctions
Afin de rendre le démarrage d'un système plus rapide, la tâche la plus ardue, mais aussi la plus bénéfique, concerne la parallélisation de la détection du matériel. La découverte des périphériques peut en effet s'avérer être une tâche longue et fastidieuse. L'idée de paralléliser ce travail n'est pas nouvelle (cf. le projet fastboot d’Arjan van de Ven), mais de multiples problèmes l'ont empêchée d'être intégrée à la mainline. Par exemple, l'ordre de découverte des périphériques peut varier d'un démarrage à l'autre et ainsi peut changer la façon dont ils sont nommés. De plus, les accès concurrents et d'autres soucis ont affecté la stabilité du système, ayant pour conséquence que l'initialisation du système reste pour sa majeure partie séquentielle.
Cette situation est en train de changer grâce à Arjan van de Ven, avec l'intégration dans le noyau 2.6.29 d'une infrastructure permettant l'appel asynchrone de fonctions noyau (que l'on doit pour l'instant activer via l'option de boot fastboot, car la stabilité de l'infrastructure est prévue pour la version 2.6.30 du noyau). Afin de résoudre les problèmes rencontrées lors des précédentes tentatives, Arjan a choisi de suivre une approche contrôlée de la parallélisation en évitant de tout paralléliser en une seule fois, et en concevant une API qui tente de masquer les effets problématiques de la parallélisation au reste du système. Ainsi, dans cette version, seulement les sous-systèmes de découverte des périphériques SCSI et ATA ont été modifiés. Aussi, l'API a été conçu afin de garantir que l'enregistrement des périphérique se fasse toujours dans le même ordre à chaque démarrage du système.
L'API est simple d'utilisation. Le code noyau souhaitant en profiter doit inclure le fichier async.h et écrire des fonctions pour traitement asynchrone respectant le prototype suivant.
typedef void (async_func_ptr) (void *data, async_cookie_t cookie);
Le pointeur data pointe sur des données privées et cookie est une donnée opaque que renvoie le noyau pour la synchronisation future des appels. Afin de lancer le traitement asynchrone d'une fonction du type async_func_ptr, un appel à la fonction suivante doit être effectué.
async_cookie_t async_schedule(async_func_ptr *ptr, void *data);
Lorsque cet appel est déclenché, la fonction pointée par ptr est exécutée durant l'exécution de async_schedule() ou plus tard. La valeur que renvoie async_schedule() permet d'identifier l'appel asynchrone que l'on vient d'effectuer. Il est alors possible d'attendre qu'une ou plusieurs fonctions asynchrones se termine avant de continuer dans le code. Pour cela, l'une des fonctions suivantes est employée.
void async_synchronize_cookie(async_cookie_t cookie);
void async_synchronize_full(void);
La première permet de s'assurer que tous les appels asynchrones ayant été effectués avant celui identifié par cookie sont terminés. (Elle ne retourne qu'à partir de cet instant.) La deuxième, quant à elle, retourne quand tous les appels asynchrones ont été traités. Ces fonctions sont employées notamment pour garantir l'ordre d'enregistrement des périphériques.
L'implémentation de cette infrastructure emploie deux listes chaînées : async_pending et async_running, qui contiennent respectivement les appels asynchrones qui sont en attente d'exécution et ceux qui sont en cours d'exécution. Lorsque la fonction async_schedule() est appelée, elle place dans la liste async_pending le travail à traiter, et démarre si besoin un thread noyau pour l'effectuer. Lorsqu'un de ces threads a fini le traitement d'un appel asynchrone, il vérifie que la liste async_pending est vide avant de se terminer. Sinon, il traite un nouvel appel dans cette liste.
Une déclinaison des primitives précédentes existe afin de rendre possible la synchronisation sur différents lots d'appels asynchrones. Ainsi, les fonctions suivantes sont définies.
async_cookie_t async_schedule_special(async_func_ptr *ptr, void *data,
struct list_head *running);
void async_synchronize_cookie_special(async_cookie_t cookie,
struct list_head *running);
void async_synchronize_full_special(struct list_head *list);
La seule variation consiste en la création (par l'utilisateur de l'infrastructure) de sa propre liste async_running (il s'agit du paramètre running dans le prototype des fonctions précédentes) afin de pouvoir surveiller la terminaison d'un ensemble spécifique d'appels asynchrones.
6. Virtualisation
6.1 Le système de fichier XenFS
Le système de fichier XenFS a été créé afin de permettre l'interaction entre l'hyperviseur Xen et l'espace utilisateur. Il permet l'exportation de diverses interfaces vers l'espace utilisateur. Notamment, il permet à l'espace utilisateur d'interagir avec Xenbus/Xenstore.
Xenstore est une sorte d'inventaire hiérarchique des données de configuration relatives aux domaines. Il est employé pour de multiples opérations comme la négociation au démarrage de la connexion aux pilotes de périphériques ou encore l'ajustement de l'empreinte mémoire d'un domaine, sa terminaison, etc. Les informations présentes dans cet inventaire ne sont pas statiques. Elles sont notamment scrutées par les différents domaines afin d'agir en conséquence. Xenbus est tout simplement l'interface spécifique à Linux pour l'accès à Xenstore. L'appellation vient du fait que le Xenstore est vu par Linux comme une sorte de « bus », lequel peut être sondé à la recherche de périphériques virtuels. Il fournit également une API pour les pilotes paravirtualisés afin qu'ils reçoivent des notifications et informations de la part de Xenstore et qu'ils puissent y inscrire des données.
L'interaction entre Xen et l'espace utilisateur se fait traditionnellement via l'accès à /proc/xen. Afin de ne pas rompre cette habitude et sans pour autant étendre les fonctionnalités de procfs, un point de montage sur /proc/xen a été rendu possible afin de monter un système de fichiers de type XenFS.
7. Fonctionnalités de Traçage/Débogage
7.1 Introduction à Ftrace
Ftrace est une infrastructure de traçage conçue dans l'optique d'aider les développeurs Linux à déboguer le noyau et à analyser les problèmes de latences et de performances qui s'y trouvent. Bien qu'à la base Ftrace soit un mécanisme de traçage de fonctions, son architecture a été pensée pour être extensible via des plugins, lesquels implémentent d'autres types de traçage. On trouve notamment parmi ces plugins un qui trace les changements de contexte, un autre qui mesure le temps qu'il faut pour qu'une tâche de haute priorité s'exécute après avoir été réveillée, un qui mesure le durée pendant laquelle les interruptions sont désactivées, etc.
Ftrace emploie le système de fichiers debugfs pour son pilotage, ainsi que pour l'affichage des résultats. Pour l'utiliser, il faut monter debugfs, et, pour cela, il suffit d'exécuter la commande suivante sur un système où le noyau a été compilé avec Ftrace.
# mount -t debugfs nodev /sys/kernel/debug
Est alors disponible dans /sys/kernel/debug, un dossier tracing qui contient tous les fichiers de contrôle du traçage, ainsi que les fichiers de résultats.
Les premiers traceurs à avoir vu le jour sont les suivants :
- ftrace qui trace toutes les fonctions noyau.
- sched_switch qui trace les changements de contexte entre tâches.
- irqsoff qui trace les régions de code qui désactivent les interruptions, et sauvegarde la trace ayant la plus grande latence.
- preempt_off est similaire à irqsoff, mais s'occupe des régions de code où la préemption est désactivée.
- preemptirqsoff effectue un travail similaire aux deux traceurs précédents, mais confond la désactivation des interruptions et celle de la préemption.
- wakeup trace et enregistre la latence maximale dont est victime la tâche de plus haute priorité pour être ordonnancée après avoir été réveillée.
Pour activer un traceur il faut écrire son nom dans le fichier current_tracer. Pour tous les désactiver, il suffit d'y écrire la chaîne « none ».
7.2 Améliorations apportées à Ftrace et nouveautés
La version 2.6.29 du noyau voit l'arrivée de nombreuses améliorations et nombreux ajouts de mécanismes à l'infrastructure de Ftrace. Nous en détaillons quelques-uns dans la suite de cette section.
Un nouveau traceur mesure la durée d'exécution des fonctions noyau en nanosecondes. Il faut compiler le noyau avec l'option CONFIG_FUNCTION_RET_TRACER pour l'utiliser.
Le traceur de pile stack-tracer employant l'infrastructure Ftrace examine à chaque appel de fonction la taille de la pile. Si la taille excède la valeur positionnée dans le fichier stack_max_size, alors elle y est enregistrée. La pile des appels est également visualisable. Alors que ce type de traçage n'a été implémenté que pour l'architecture x86, il ne lui est pas spécifique. C'est pourquoi un nouveau flag générique de configuration a été mis en place pour prévoir les futures implémentations.
Un autre traceur, nommé power-tracer, voit le jour dans cette version du noyau. Il permet d'aider à traquer les excès de consommation électrique. Pour cela, il génère des statistiques détaillées sur les états de consommation énergétique dans lesquels se trouvent les CPU. Il est ainsi possible d'observer précisément les décisions que prend le code de gestion de l'énergie, et non de se contenter des moyennes de trop haut niveau qui étaient jusqu'alors les seules disponibles. L'exemple suivant montre la façon d'utiliser ce traceur.
# echo cstate > /sys/kernel/debug/tracing/current_tracer
# echo 1 > /sys/kernel/debug/tracing/tracing_enabled
# sleep 1
# echo 0 > /sys/kernel/debug/tracing/tracing_enabled
# cat /sys/kernel/debug/tracing/trace | perl scripts/trace/cstate.pl > out.svg
Une nouvelle requête système SysRq-z a été définie pour afficher tous les tampons de traçage. Le mécanisme de System Request (fonction de débogage à activer dans la configuration du noyau), s'utilise via une séquence d'échappement du clavier et permet d'effectuer diverses tâches telles que l'affichage d'informations sur tous les processus du système, le remontage des systèmes de fichiers en lecture seule, le redémarrage brutal de la machine, etc.
Il est désormais possible de faire tracer un unique PID par le function graph tracer. Ce tracer enregistre les adresses de retour de la tâche courante dans sa structure thread_info. Il est alors possible de récupérer la pile ordonnée des fonctions qui ont été appelées durant l'exécution du code de la tâche. Les commandes suivantes illustre l'utilisation du mécanisme.
# echo $$ > /sys/kernel/debug/tracing/set_ftrace_pid
# echo function_graph > /sys/kernel/debug/tracing/current_tracer
Ces deux commandes déclenchent le traçage des appels de fonctions effectués par le shell courant. Ses processus fils seront également tracés. Notons que le traçage exclusif des processus swapper (idle task) de toutes les CPU peut être activé en inscrivant la valeur 0 dans le fichier set_ftrace_pid.
Un nouveau fichier permet au function graph tracer de tracer une unique fonction. Si le fichier /sys/kernel/debug/tracing/set_graph_function est vide, le tracer se comporte normalement, sinon il trace uniquement la fonction spécifiée. L'exemple suivant illustre le cas du traçage de la fonction blk_unplug.
# echo blk_unplug > /sys/kernel/debug/tracing/set_graph_function
# cat /sys/kernel/debug/tracing/trace
[...]
------------------------------------------
| 2) make-19003 => kjournald-2219
------------------------------------------
2) | blk_unplug() {
2) | dm_unplug_all() {
2) | dm_get_table() {
2) 1.381 us | _read_lock();
2) 0.911 us | dm_table_get();
2) 1. 76 us | _read_unlock();
2) + 12.912 us | }
2) | dm_table_unplug_all() {
2) | blk_unplug() {
2) 0.778 us | generic_unplug_device();
2) 2.409 us | }
2) 5.992 us | }
2) 0.813 us | dm_table_put();
2) + 29. 90 us | }
2) + 34.532 us | }
Il est actuellement possible d'ajouter 32 fonctions dans ce fichier. L'ajout se fait de la façon suivante :
# echo sys_read >> /sys/kernel/debug/tracing/set_graph_function
# cat /sys/kernel/debug/tracing/set_graph_function
blk_unplug
sys_read
L'utilisation du caractère « > » efface, quant à lui, la liste des fonctions à tracer et en inscrit une nouvelle.
Un nouveau profiler a été ajouté sur les macros likely et unlikely employées par le noyau. Ces macros spécifient au compilateur qu'une condition a soit de fortes chances de se produire (likely), soit au contraire peu de chance de se produire (unlikely). Il s'ensuit que le compilateur peut alors effectuer des choix judicieux pour optimiser l'exécution du code en favorisant les branchements conditionnels les plus probables.
Ce profiler associe à presque toutes les macros likely et unlikely un compteur. Les conditions évaluées au travers de ces macros sont alors la cible de ce profiler. Pour chaque condition, est compté le nombre de fois où elle a été satisfaite et le nombre de fois où cela n'était pas le cas. Les résultats en cours d'exécution sont disponibles dans les fichiers profile_likely et profile_unlikely. Une illustration sur le fichier profile_unlikely est donnée ci-dessous.
# cat /sys/kernel/debug/tracing/profile_unlikely | head
correct incorrect % Function File Line
------- --------- - -------- ---- ----
2167 0 0 do_arch_prctl process_64.c 832
0 0 0 do_arch_prctl process_64.c 804
2670 0 0 IS_ERR err.h 34
71230 5693 7 __switch_to process_64.c 673
76919 0 0 __switch_to process_64.c 639
43184 33743 43 __switch_to process_64.c 624
12740 64181 83 __switch_to process_64.c 594
12740 64174 83 __switch_to process_64.c 590
8. Gestion des cartes graphiques
Le redesign de la gestion de l'affichage graphique sous Linux poursuit son chemin : après l'interface de gestion mémoire des périphériques graphiques (GEM) apparue dans le précédent noyau, c'est cette fois-ci un autre composant majeur, nommé KMS pour Kernel Mode Settings qui est inclus. Il donne la possibilité de déléguer la gestion des modes (résolution, profondeur de couleurs) de la carte graphique au noyau. Plusieurs avantages de taille y sont associés. Tout d'abord, la possibilité d'avoir une résolution optimale du moniteur dès le boot, de la console jusqu'au bureau, sans clignotements de l'écran lors du passage console – framebuffer – serveur X. Jusqu'alors les différentes infrastructures d'affichage n'avaient aucune connaissance l'une de l'autre, et aucun moyen centralisé de savoir l'état d'initialisation et de paramétrage de la carte graphique ; ainsi, chacune d'entre eux (re)faisait sa propre configuration du matériel. KMS fournit un moyen commun de gérer cette problématique. Ensuite, le temps de boot s'en trouve réduit. Enfin, cette infrastructure va également permettre à court terme, pour les matériels disposant de pilotes sachant en tirer profit, d'exécuter le serveur Xorg en tant qu'utilisateur normal.
Attention, actuellement KMS ne fonctionne qu'avec les cartes graphiques Intel et requiert la dernière version du projet Xorg ; pour le tester, il faut activer l'option CONFIG_DRM_I915_KMS. En ce qui concerne les matériels des deux concurrents Nvidia et ATI, l'implémentation de KMS pour le pilote Radeon (ATI) open source est en cours. Côté Nvidia, aucune déclaration concernant le pilote propriétaire ; quant au projet Nouveau, l'implémentation de KMS est également en cours, mais rien de fonctionnel, ni stable n'est à attendre dans l'immédiat. La difficulté pour ces deux projets est qu'utiliser KMS nécessite également un gestionnaire de mémoire graphique dans le noyau, et que leurs besoins sont plus complexes que ce que fournit pour l'instant GEM.
9. Réseau
Cela faisait quelque temps que cette section n'avait pas été aussi chargée, le noyau 2.9.29 voyant en effet apparaître plusieurs évolutions significatives ayant trait au réseau. Le menu commence par l'infrastructure nommée Generic Receive Offload, qui entre dans la branche officielle de Linux. Destinée à diminuer le coût de traitement des paquets sur un système recevant un fort trafic, elle s'intercale entre le pilote du matériel (carte Ethernet...) et la pile réseau du noyau. Elle intercepte les données entrantes et, si possible, se propose de grouper en un seul paquet, plus gros, jusqu'à huit paquets reçus. Elle parse très rapidement leurs en-têtes et les relaie à la pile réseau qui, elle, effectue des traitements plus complexes, et se trouve donc soulagée d'en avoir moins à analyser. Pour l'instant, seul le pilote e1000 peut en profiter, les autres devraient être portés dans les versions à venir du noyau. Cette technologie vient compléter TSO, qui a un fonctionnement similaire, mais pour les paquets sortants.
Deuxième nouveauté marquante de cette rubrique, le support du WiMax fait son apparition à compter de cette version du noyau. Rappelons qu'il s'agit d'une norme de transmission de données à haut débit et sur de longues distances, sans fil. L'infrastructure incluse dans Linux répond pour l'instant aux besoins des matériels Intel. Elle sera enrichie et améliorée dans le futur, lorsque d'autres pilotes voudront profiter de ses fonctionnalités.
La solution de virtualisation par containers intégrée au noyau s'améliore extrêmement rapidement ; pour le noyau 2.6.29, il est possible d'activer les « Network Namespaces » et d'assigner des interfaces réseau à un container sans avoir à désactiver complètement sysfs à la compilation du noyau. On comprendra que cette limitation était un frein pour nombre d'utilisateurs.
Le mode point d'accès, jusqu'alors désactivé dans la pile Wifi générique mac80211 du noyau, est désormais actif par défaut. Ce mode ne peut cependant fonctionner sans l'aide du démon hostapd, en charge de toutes les procédures d'authentification.
La pile TCP se dote d'une version améliorée de l'algorithme de gestion de la congestion par défaut, CUBIC 2.3, qui devrait permettre une montée en vitesse plus rapide d'un transfert (phase dite de « slow start », lorsqu'un transfert commence ou a été perturbé en atteignant le débit maximum du médium de transmission), en particulier lorsque le receveur à l'autre bout est un système MS-Windows.
Enfin, Linux implémente le protocole FCoE, permettant de faire transiter le protocole de transmission Fiber Channel sur une liaison Ethernet. À noter que ce protocole n'est pas routable, ni administrable de manière classique, car il n'utilise pas TCP/IP.
10. Stockage et systèmes de fichiers
Nous voici à présent dans la rubrique qui est la plus médiatisée pour cette nouvelle version du noyau. Ceux qui suivent, même de loin, l'évolution de Linux, n'auront pu rester sans savoir qu'un futur poids-lourd des systèmes de fichiers, nommé Btrfs et déjà présenté dans notre Kernel Corner numéro 106, fait une arrivée fracassante et par la grande porte. Avant toute chose, nous nous devons de signaler qu'il doit être considéré comme un composant hautement expérimental, auquel il ne faut donc confier aucune donnée importante. Côté fonctionnalités, voici en quelques points un rappel de ses qualités :
- Redondance : Btrfs inclut sa propre gestion du RAID, et permet une redondance des données et/ou des métadonnées, pour l'instant en miroir (RAID1) et en RAID5. Le stripping (RAID0) est également possible.
- Intégrité des données : le FS effectue une somme de contrôle de chaque bloc écrit, qui permettra de vérifier que les données ne sont pas corrompues et en cas de besoin de les lire depuis un autre endroit si une redondance de données existe.
- Gestion de volumes : par besoin d'espace supplémentaire ou envie de redondance, il est possible d'ajouter une nouvelle partition, à chaud, à un volume, et même de décider d'y dupliquer les données et/ou les métadonnées existantes sur le volume.
- Snapshotting et copy-on-write : il est possible de réaliser à tout instant un nombre illimité de clichés d'un volume ou d'un répertoire, par exemple avant une manipulation dangereuse ou avant une suppression dont on n’est pas sûr des conséquences... de manière instantanée et sans gaspiller d'espace grâce au CoW : le cliché, ou snapshot, n'occupera aucun espace au début. Lorsqu'on modifiera un fichier inclus dans le snapshot, les données modifiées seront écrites à un autre endroit du volume, et les anciennes préservées et attribuées au snapshot. Chaque snapshot se présente sous la forme d'un répertoire à la racine d'un volume Btrfs monté, rendant extrêmement facile leur utilisation et leur gestion. Ils sont accessibles en écriture et eux-mêmes snapshotables.
- Les petits plus qui font saliver : compression des données à la volée, utilisation optimisée des disques SSD, stockage optimisé des petits fichiers, fsck à chaud, conversion réversible d'un volume Ext en Btrfs...
Pour le tester, on ne recommandera jamais assez de lui dédier une partition ne recevant que des données récupérables via d'autres supports de stockage en cas de problème. Il faut également disposer des outils en espace utilisateur, à télécharger à l'adresse http://www.kernel.org/pub/linux/kernel/people/mason/btrfs/.
Non, ce n'est pas le seul petit nouveau à apparaître dans ce nouveau noyau... Il nous faut également compter avec Squashfs, qui est un système de fichiers compressé, en lecture seule. Les données sont décompressées à la volée. Il est souvent utilisé dans les matériels dits « embarqués », où le système n'a pas de raisons de changer et où les contraintes en espace de stockage sont fortes.
Parmi le reste, on ne manquera pas de remarquer qu’eCryptfs permet dorénavant de chiffrer les noms de fichiers en plus de leur contenu, et que Ext4, qui devrait devenir le système de fichiers par défaut de la future Fedora 11, permet pour des raisons de performance de fonctionner sans journal (bien que l'information permettant de savoir comment faire soit inexistante).
11. Mise en veille et hibernation
Dans son long et difficile chemin vers un fonctionnement fiable de l'hibernation et de la mise en veille, le noyau intègre un ensemble de patchs génériques touchant à la gestion des périphériques PCI. Linux Torvalds a diagnostiqué que dans le cas où un pilote gère correctement les phases de suspend/resume sur certains systèmes, mais pas sur d'autres, le problème était souvent lié à la gestion des interruptions matérielles. Il estime qu'à partir de cette version ce genre d'ennuis devrait être réglé, et nous invite cordialement, nous les utilisateurs, à signaler les problèmes rencontrés lors de la mise en veille, de l'hibernation ou du réveil de nos machine, dans le cas où la faute incombe à un pilote.
Rappelons qu'il existe un moyen basique et, il faut bien l'avouer, un peu rustique, pour tester le (bon) fonctionnement de la mise en veille d'un système. Il nous faut disposer d'un noyau compilé avec l'option CONFIG_PM_DEBUG. Grâce à elle, une entrée sysfs /sys/power/pm_test est créée. Pour tester seulement l'étape concernant les périphériques et leurs pilotes, nous effectuons un :
# echo devices > /sys/power/pm_test
Le noyau doit alors geler tous les processus en cours d'exécution sur le système, puis les périphériques, attendre 5 secondes puis réveiller tout ce beau monde en commençant par le matériel. Si le test ne fonctionne pas, il faut le recommencer, au besoin plusieurs fois, après avoir déchargé manuellement un ou plusieurs pilotes, jusqu'à trouver le coupable : si le test fonctionne en ayant au préalable retiré manuellement un module, c'est très certainement le coupable.
12. Gestion du matériel et périphériques
Une fois de plus, les nouveautés sont nombreuses côté pilotes, faisant encore la part belle au Wifi. Si l'arbre « staging » apporte plusieurs nouveaux pilotes, il faut rappeler que leur qualité est considérée comme insuffisante. Pour les compiler et les utiliser, il faudra activer l'option Device Drivers --> Staging Drivers, puis, dans le sous-menu, désactiver l'option « Exclude Staging drivers from being built ». À présent, place au rapide résumé des changements les plus significatifs de ce kernel.
Pas moins de cinq nouveaux pilotes Wifi sont inclus, mais en tant que staging. Trois d'entre eux (rt2860, rt2870 et rtl8187SE) se destinent aux produits de Realtek. Le quatrième, répondant au nom de otus, se destine aux puces UB81, UB82 et UB83 du même fabricant, tandis que le dernier, agnx, gère les puces Airgo AGNX00, implémentant la norme 802.11n (MIMO). Quant au module déjà existant at76_usb, il a été réécrit pour utiliser la couche Wifi générique du noyau.
Attention, le pilote eepro100, prenant en charge les cartes Ethernet Intel Pro 100, est retiré, le module e100 le remplace.
Le module ath9k prend désormais en charge les puces Atheros ar9285.
Trois nouveaux pilotes de cartes Ethernet font leur entrée : smsc9420 pour les cartes SMSC LAN9420, ks8695net (puce Ethernet multiport), et benet (arbre staging) destiné aux interfaces ServerEngines 10GB.
Le pilote graphique i915 (Intel) gère la sortie HDMI sur les matériels de type G4X.
Le rétroéclairage de plusieurs modèles de portables Dell est maintenant géré grâce au nouveau module dell_laptop. À partir de ce noyau 2.6.29, les codes sources de ces pilotes spécifiques, appelés platform drivers et prenant en charge les batteries, rétroéclairages et boutons spéciaux ont été déplacés du dossier drivers/misc au dossier drivers/platform/x86.
Notons pour terminer que plusieurs webcams, périphériques audio et d'acquisition vidéo supplémentaires sont désormais gérés, ainsi que les cartes iSCSI 10GB Chelsio T3.
Pour conclure, il ne fait pas de mal de rappeler qu' à chaque nouveau noyau, de nombreux matériels supplémentaires sont gérés sans ajout de nouveau pilote, et ne sont donc pas cités dans ces colonnes ; il s'agit de matériels contenant des puces déjà gérables par un module existant, mais dont ledit module ne connaissait pas l'existence, c'est-à-dire ne disposait pas des identifiants uniques (Vendor ID et Product ID) du produit. S'il vous manquait une raison de compiler et tester :-)...
13. Divers
Pour compiler le noyau avec GCC, à compter de cette version, il faudra disposer d'une version au moins égale à la 4.2, les versions 4.1.0 et 4.1.1 ayant des bugs empêchant la compilation d'un noyau fonctionnel.