DKMS : Donnez l'indépendance à vos pilotes

Pascal Terjan <pterjan@mandriva.com>

Cet article a été initialement publié dans le GNU/Linux Magazine 80 de Février 2006

Contexte

Sous Linux, les pilotes (le logiciel qui permet au système d'exploitation de communiquer avec le matériel) peuvent soit être directement inclus dans le noyau soit être disponibles sous la forme de modules à charger dans le noyau. Un tel module est compilé pour un noyau particulier et ne peut être utilisé sur un autre.

En ce qui concerne les pilotes libres, cela ne pose en général aucun problème car ils sont soit livrés avec votre noyau, soit compilés en même temps que le noyau que vous compilez vous-même.

Dans le cas de pilotes non libres (comme ceux de nVidia ou ATI) ou de pilotes distribués séparément et que vous compilez à la main, le pilote doit être recompilé à chaque version de noyau, ce qui peut devenir fastidieux.

De plus, si vous souhaitez utiliser les pilotes distribués par votre distributeur Linux, vous devez attendre que les pilotes soient recompilés pour le nouveau noyau, et ne pouvez espérer qu'ils fonctionnent avec un noyau que vous auriez personnalisé.

Objectifs de DKMS

DKMS (Dynamic Kernel Module Support ou en français Gestion Dynamique des Modules Noyau) est un système très simple conçu par Dell et permettant de compiler dynamiquement et facilement les modules noyau pour tous les noyaux de votre système. Ce système est à la base conçu pour faciliter à Dell la distribution de pilotes Linux et de correctifs.

Cela évite à la fois aux éditeurs et aux utilisateurs de devoir passer par des mises à jour du noyau pour une correction sur un pilote particulier et permet de distribuer un pilote supplémentaire sous la forme d'un seul paquetage quel que soit le noyau de l'utilisateur.

Cet article a pour but de présenter ce système, à la fois aux développeurs et distributeurs de modules noyau qui pourraient l'utiliser pour faciliter la vie à leurs utilisateurs, et aux utilisateurs qui peuvent également l'utiliser tout seuls.

Comment ça marche

Le principe de DKMS est très simple. À condition que le module soit prévu pour être utilisé avec DKMS (fourniture d'un dkms.conf), l'utilisation d'un nouveau module en passant par DKMS consiste à exécuter les 3 étapes suivantes :

Ces étapes sont séparées et ne sont pas forcement toutes réalisées à la suite. La figure 1 montre les différents états d'un module dans le système DKMS, et les transitions possibles entre ces états à l'aide des actions de la commande dkms.

Figure 1 : États d'un module dans DKMS
Figure 1 : États d'un module dans le système DKMS

Structure sur le disque

Afin de bien séparer la gestion des différentes versions de module et de la version du noyau, le fonctionnement de DKMS se base sur la présence de 3 arborescences : Les chemins indiqués sont ceux par défaut et il est possible de les modifier dans /etc/dkms/framework.conf mais nous supposerons dans le reste de l'article que les valeurs par défaut ont été conservées.

Mise en oeuvre de DKMS par un utilisateur

La commande dkms

La gestion des modules au sein du système DKMS se fait à l'aide de la commande dkms. Cette commande a une syntaxe très simple : dkms <action> [options]

Les actions et les options sont par contre plutôt nombreuses et même si je vous les explique ci-dessous, seules les principales devraient être utiles à la majorité d'entre vous (à savoir: add, build, install, remove en ce qui concerne les actions et -m, -v et --all en ce qui concerne les options).

Voici tout d'abord la liste des options génériques s'appliquant à diverses commandes, celles restreintes à une commande particulière seront expliquées dans la description de celle-ci.

Nous allons maintenant étudier ce que permet de faire DKMS à travers le parcours des actions que l'on peut faire effectuer à la commande dkms.

Utilisation basique

Après vous avoir noyé sous les commandes et les options, je pense qu'un exemple d'utilisation classique est nécessaire pour vous persuader de la simplicité d'utilisation. Voici donc comment installer un module exemple, fourni sous la forme de exemple-1.0.tar.gz contenant un unique répertoire exemple-1.0 avec les sources du module et un dkms.conf.
cd /usr/src
tar xzf /tmp/exemple-1.0.tar.gz
dkms add -m exemple -v 1.0
dkms build -m exemple -v 1.0
dkms install -m exemple -v 1.0
Listing 1 : Ajout d'un pilote en utilisant DKMS

Si tout s'est bien passé, le module est compilé et installé, et modprobe exemple fonctionne ! Si cela ne s'est pas bien passé, c'est vraisemblablement lors de l'étape build, et vous pouvez essayer de déterminer le problème en allant lire le fichier /var/lib/dkms/exemple/1.0/build/make.log. Si vous n'avez pas les compétences pour cela, le plus simple est d'envoyer ce fichier à l'auteur du dkms.conf afin qu'il corrige l'erreur.

Un mode de distribution courant, que nous détaillerons plus loin, consiste à distribuer les sources et le dkms.conf sous la forme d'un package RPM et non pas d'un tar.gz. Dans ce cas, installer le package RPM suffit et exécutera automatiquement les différentes commandes.

Mise en oeuvre de DKMS par un distributeur

Adaptation des sources

DKMS a besoin des sources du module dans le répertoire /usr/src/<nom du module>-<version du module>/ et d'un fichier dkms.conf dans ce même répertoire. Nous allons étudier la syntaxe de ce fichier sur un exemple classique avant de détailler toutes les options disponibles.

Exemple de dkms.conf pour l'ipw2200 1.0.4:

PACKAGE_VERSION="1.0.4"
PACKAGE_NAME="ipw2200"

MAKE[0]="make -C ${kernel_source_dir} SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules HOSTAP_SRC=${kernel_source_dir}/3rdparty/hostap/"
CLEAN="make -C ${kernel_source_dir} SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean"

BUILT_MODULE_NAME[0]="$PACKAGE_NAME"
BUILT_MODULE_NAME[1]="ieee80211_crypt_ccmp"
BUILT_MODULE_NAME[2]="ieee80211_crypt"
BUILT_MODULE_NAME[3]="ieee80211_crypt_tkip"
BUILT_MODULE_NAME[4]="ieee80211_crypt_wep"
BUILT_MODULE_NAME[5]="ieee80211"
DEST_MODULE_LOCATION[0]="/kernel/drivers/net/wireless/ipw2200/"
DEST_MODULE_LOCATION[1]="/kernel/drivers/net/wireless/ipw2200/"
DEST_MODULE_LOCATION[2]="/kernel/drivers/net/wireless/ipw2200/"
DEST_MODULE_LOCATION[3]="/kernel/drivers/net/wireless/ipw2200/"
DEST_MODULE_LOCATION[4]="/kernel/drivers/net/wireless/ipw2200/"
DEST_MODULE_LOCATION[5]="/kernel/drivers/net/wireless/ipw2200/"

MODULES_CONF_ALIAS_TYPE[0]="eth"

REMAKE_INITRD="no"
AUTOINSTALL="yes"
Listing 2: Exemple de dkms.conf pour le pilote ipw2200 version 1.0.4

Comme vous pouvez le voir ce fichier a une syntaxe très simple, une option par ligne sous la forme OPTION="valeur". Voyons maintenant plus précisément à quoi correspondent les options définies dans cet exemple.

PACKAGE_VERSION et PACKAGE_NAME ne méritent je pense aucune explication. Il est toutefois intéressant de noter que lors de la sortie d'une nouvelle version du module, la seule ligne à modifier dans le dkms.conf est en général PACKAGE_VERSION.

MAKE[0] contient la commande à exécuter pour compiler le module, elle peut utiliser de nombreuses variables, listées dans la section suivante. La majorité des modules peuvent se compiler avec la commande suivante : make -C ${kernel_source_dir} SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules

CLEAN contient une commande permettant de nettoyer le répertoire de construction, elle est appelée avant et après la compilation du module.

Les 12 lignes suivantes indiquent les modules générés (ici il y en a 6) et ou il faut les installer. BUILT_MODULE_NAME[i] contient le nom du module numéro i (en commençant à 0) et DEST_MODULE_LOCATION[i] le chemin où installer le module numéro i. Le chemin est relatif à l'arborescence des modules du noyau concerné, /lib/modules/<version du noyau>.

L'option MODULES_CONF_ALIAS_TYPE[0]="eth" indique qu'il faudra ajouter dans /etc/modprobe.conf (ou modules.conf) un alias vers ce module qui s'appelera eth0 (ou eth1, eth2, ... selon ce qui est déjà pris).

REMAKE_INITRD="no" indique que ce pilote n'a pas besoin d'être dans l'initrd. L'initrd est l'image contenant tout les modules et commandes nécessaires pour accéder au disque dur, cette option a donc rarement besoin d'être à yes en dehors des pilotes de contrôleur disque.

AUTOINSTALL="yes" indique que l'on veut que ce module soit compilé et installé automatiquement lorsque l'on démarre sur un nouveau noyau.

Variables disponibles

Quelques variables peut être utilisées dans les options et les commandes comme vous pouvez le voir dans la commande MAKE[0] de l'exemple précédent. Ces variables sont définies par la configuration de DKMS ou par les options passées à la commande dkms.

Options que l'on peut définir dans le fichier dkms.conf

Tout d'abord, le nom des options est sensible à la casse donc elles doivent être écrites en majuscules. Seules 4 options sont obligatoires : De nombreuses autres options bien pratiques mais facultatives dont disponibles :

Intégration avec rpm

Les possibilités d'intégration de DKMS avec RPM sont doubles :

En ce qui concerne le premier cas, la création du RPM est assez simple si vous savez construire des RPM. Vous indiquez comme source le tar.gz des sources du module et dans la section %install vous copiez les sources vers /usr/src/<nom du module>-<version du module> et vous y écrivez le dkms.conf (Il est également possible de mettre le dkms.conf dans un fichier annexe et de l'inclure comme source, mais l'avoir inclus permet d'utiliser les macros RPM %name, %version, ... afin de créer rapidement un .spec pour un nouveau module). Ensuite vous indiquez en %post et %preun les commandes DKMS à invoquer (add, build et install en %post et remove en %preun).

Le modèle de fichier spec suivant pourra vous servir de base dans la majorité des cas.
%define module_name rt2500

Name:           dkms-%{module_name}
Version:        1.4.6.2
Release:        1mdk
Summary:        DKMS-ready kernel-source for the RT2500 driver
License:        GPL
URL:            http://www.ralinktech.com/supp-1.htm
Source:         http://www.ralinktech.com/drivers/Linux/RT2500-Linux-STA-%{version}.tar.bz2
Group:          System/Kernel and hardware
Requires(pre):  dkms
Requires(post): dkms
Buildroot:      %{_tmppath}/%{name}-%{version}-root
Buildarch:      noarch

%description
Driver for the Ralink RT2500 802.11g chipset

%prep
%setup -q -n RT2500-Linux-STA-%{version}
chmod 0755  Module/2.*.x

%build

%clean
rm -fr $RPM_BUILD_ROOT

%install
mkdir -p %{buildroot}/usr/src/%{module_name}-%{version}-%{release}
cp -a Module/* %{buildroot}/usr/src/%{module_name}-%{version}-%{release}
cp -af Module/2.6.x/Makefile %{buildroot}/usr/src/%{module_name}-%{version}-%{release}
cat > %{buildroot}/usr/src/%{module_name}-%{version}-%{release}/dkms.conf <<EOF

PACKAGE_VERSION="%{version}-%{release}"

# Items below here should not have to change with each driver version
PACKAGE_NAME="%{module_name}"
MAKE[0]="make -C \${kernel_source_dir} SUBDIRS=\${dkms_tree}/\${PACKAGE_NAME}/\${PACKAGE_VERSION}/build modules"
CLEAN="make -C \${kernel_source_dir} SUBDIRS=\${dkms_tree}/\${PACKAGE_NAME}/\${PACKAGE_VERSION}/build clean"

BUILT_MODULE_NAME[0]="\$PACKAGE_NAME"

DEST_MODULE_LOCATION[0]="/kernel/drivers/net/wireless/"

MODULES_CONF_ALIAS_TYPE[0]="ra"

REMAKE_INITRD="no"
AUTOINSTALL=yes

EOF

%post
dkms add -m %{module_name} -v %{version}-%{release} --rpm_safe_upgrade
dkms build -m %{module_name} -v %{version}-%{release} --rpm_safe_upgrade
dkms install -m %{module_name} -v %{version}-%{release} --rpm_safe_upgrade

%preun
dkms remove -m %{module_name} -v %{version}-%{release} --rpm_safe_upgrade --all ||:

%files
%defattr(-,root,root)
/usr/src/%{module_name}-%{version}-%{release}
Listing 7: Exemple de fichier .spec pour un package DKMS

Il y a peu de remarques à faire sur ce fichier si ce n'est l'importance de ne pas oublier --rpm_safe_upgrade et surtout ||: dans le %preun qui permet que le rpm puisse être désinstallé même si le remove échoue (par exemple parce que le build avait échoué donc le module n'est pas installé).

Pour ce qui est du deuxième cas (création de RPM binaires), c'est encore plus simple, il suffit de compiler le module puis d'utiliser dkms mkrpm en précisant le module, sa version et le ou les noyaux qui vous intéressent. Contrairement aux RPM créés à la main comme précédemment, cette méthode nécessite par contre d'être root sur la machine générant le package (ou d'avoir par exemple un sudo sur la commande dkms).

Avantages et inconvénients

Pour finir, voici une petite liste des avantages et des inconvénients que je vois à utiliser DKMS. Cette liste vous aidera probablement à décider si vous souhaiter essayer de gérer vos pilotes additionnels avec DKMS.

Avantages

Inconvénients

Références