# [script init] choix pilote nvidia/nouveau à chaque boot

## netfab

À l'origine cette astuce découle du sujet suivant : [nvidia] le nouveaux choix du libre.

Historique :

 05/03/2013 : réécriture complète de la technique et du script d'initialisation, permettant ainsi de n'utiliser (et de maintenir) qu'un seul kernel avec des paramètres différents, merci à GentooUser@Clubic plus loin dans le topic pour l'inspiration.

Choix entre nouveau/KMS ou nvidia/uvesafb à chaque boot

J'utilise grub2. Le framebuffer uvesafb est activé si le pilote nvidia est choisi, sinon il est désactivé avec le pilote nouveau.

Si vous préférez utiliser vesafb (qui doit être compatible avec KMS),  voyez le post de GentooUser@Clubic plus bas. Je n'ai pas encore essayé avec, j'essaierai à l'occasion et mettrai à jour cette astuce si besoin.

Configuration kernel :

Tout ce qui suit se fait avec un kernel relativement récent configuré de la manière suivante :

```

Device Drivers  --->

   Graphics support  --->

      <*> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)  --->

         <*>   Nouveau (nVidia) cards

      <*> Support for frame buffer devices  --->

         -*-   Enable Video Mode Handling Helpers

         <*>   Userspace VESA VGA graphics support

```

Au moment de la compilation du kernel il est possible que vous ayez ces avertissements (c'est le cas avec un kernel vanilla-3.4.34) :

 *Quote:*   

> 
> 
> warning: (DRM_NOUVEAU) selects ACPI_WMI which has unmet direct dependencies (X86 && X86_PLATFORM_DEVICES && ACPI)
> 
> warning: (DRM_NOUVEAU) selects MXM_WMI which has unmet direct dependencies (X86 && X86_PLATFORM_DEVICES && ACPI_WMI)
> ...

 

Petit problème de dépendance du pilote nouveau, à rectifier en activant cette option :

```

Device Drivers  --->

   [*] X86 Platform Specific Device Drivers  --->

```

Configuration Xorg :

Pour xorg je crée deux fichiers de configuration indépendants. Lors de l'éxécution du script d'initialisation, je crée un lien symbolique vers l'un de ces fichiers dans le répertoire xorg.conf.d.

```

$ ls -l /etc/X11 | grep xorg

-rw-r--r-- 1 root root  235 23 janv. 14:53 xorg-nouveau.conf

-rw-r--r-- 1 root root  322 25 janv. 18:51 xorg-nvidia.conf

drwxr-xr-x 2 root root 4096 25 janv. 23:52 xorg.conf.d

```

Dans le fichier xorg-nvidia.conf, en plus de ma configuration habituelle pour le driver propriétaire, j'ai ceci, afin d'éviter le chargement automatique du driver nouveau :

```

Section "Module"

        Disable     "nouveau"

EndSection

```

Le fichier xorg-nouveau.conf contient quant à lui :

```

Section "Module"

   Disable     "nvidia"

EndSection

Section "Device"

   Identifier  "Card0"

   Driver      "nouveau"

EndSection

```

Script d'initialisation :

À modifier selon vos besoins (xvmc par exemple) et à lancer au runlevel default :

```

#!/sbin/runscript

# Copyright 1999-2013 Gentoo Foundation

# Distributed under the terms of the GNU General Public License v2

# $Header: $

depend() {

   need localmount

   before xdm

}

_switch_to() {

   local DRIVER=$1 OPENGL=${2:-$1}

   ebegin "Setting up X environment for ${DRIVER} driver"

   ln -snf /etc/X11/xorg-${DRIVER}.conf /etc/X11/xorg.conf.d/99-videocard.conf

   eend $? || return 1

   if [ ! ${OPENGL} = `eselect opengl show` ]; then

      ebegin "Switching to ${OPENGL} OpenGL implementation"

      eselect opengl set "${OPENGL}" >/dev/null 2>&1

      eend $? "Failed to set ${OPENGL} OpenGL interface" || return 1

   fi

   return 0

}

_switch_to_nvidia() {

   _switch_to nvidia

   exit $?

}

_switch_to_nouveau() {

   _switch_to nouveau xorg-x11

   exit $?

}

start() {

   for i in `cat /proc/cmdline` ; do

      case $i in

         nouveau|nvidia) eval _switch_to_$i ;;

      esac

   done

   # default driver if nothing found on kernel command line

   local default=nvidia

   einfo "No X driver asked on kernel command line. Using default : $default"

   eval _switch_to_$default

}

```

Configuration de grub2 :

Dans la configuration de grub2, je choisis d'activer par défaut nvidia/uvesafb.

Dans le menu de grub, j'ajoute ensuite une entrée supplémentaire avec des paramètres kernel différents pour désactiver uvesafb et activer nouveau.

Dans /etc/default/grub, j'ajoute à la variable GRUB_CMDLINE_LINUX_DEFAULT les paramètres suivants :

```
nomodeset nvidia video=uvesafb:mtrr:3,ywrap,1680x1050-32@60
```

Ainsi, tous les kernels détectés par grub2 booteront avec ces paramètres.

Ensuite je génère la configuration de grub2 :

```

# grub2-mkconfig -o /boot/grub2/grub.cfg

```

Enfin, depuis le fichier de configuration de grub fraîchement généré, je copie la première entrée du menu, qui ressemble à ceci (environ une quinzaine de lignes, dépend de votre configuration grub) :

```

menuentry 'Gentoo GNU/Linux' --class gentoo --class gnu-linux [...] {

   [...]

   echo    'Chargement de Linux 3.4.34…'

        linux   /boot/kernel-3.4.34 [...]  nomodeset nvidia video=uvesafb:mtrr:3,ywrap,1680x1050-32@60 [...]

   [...]

}

```

Je colle donc cette entrée dans le fichier /etc/grub.d/40_custom en modifiant le nom de l'entrée et les paramètres kernel pour désactiver uvesafb et utiliser nouveau :

```

menuentry 'Gentoo GNU/Linux -- nouveau' --class gentoo --class gnu-linux [...] {

   [...]

   echo    'Chargement de Linux 3.4.34…'

        linux   /boot/kernel-3.4.34 [...]  video=uvesafb:off nouveau [...]

   [...]

}

```

Et je régénère la configuration de grub2 pour prendre en compte l'entrée du menu pour nouveau.

```

# grub2-mkconfig -o /boot/grub2/grub.cfg

```

Last edited by netfab on Tue Mar 05, 2013 3:32 pm; edited 4 times in total

----------

## Leander256

Merci beaucoup pour cette astuce (que j'ai mise en oeuvre chez moi), je trouve ceci dit un peu dommage de devoir créer 2 runlevels qu'il va falloir garder synchrones avec default. Du coup je me demande si il ne serait pas plus pratique de compiler le noyau avec l'option pour stocker son fichier config dans /proc/config.gz, puis de faire simplement un grep dans le script (qui serait donc dans default) pour déterminer de quel type de noyau il s'agit.

----------

## netfab

Alors là, je dois avouer que je n'avais pas pensé une seule seconde à greper la configuration du kernel, j'etais parti directement sur l'option des runlevels, et je dois dire que cela me gênait un peu aussi. J'ai modifié le script et le post original, effectivement çà simplifie tout, merci  :Very Happy: 

----------

## Leander256

Merci pour la modif, j'ai fait les changements sur mon système mais je n'ai pas encore redémarré l'ordi (puisqu'il a arrêté de planter maintenant que je suis repassé au pilote nvidia   :Rolling Eyes:  ).

----------

## mazes80

Peut être faudrait-il aussi fixer l'implémentation de xvmc (si utilisé):

```
eval eselect xvmc "${impl}"
```

Une alternative au zgrep sur /proc/config.gz peut être utilisé grâce à CONFIG_LOCALVERSION

_xorg-x11

_nvidiaOn peut ainsi récupérer l'implémentation à utiliser avec:

```
uname -r | sed 's/^.*_//'
```

Après à chacun d'adapter ce script en fonction de ses besoins et de ses préférences.

Merci pour l'idée.

P.S.: quel est l'intérêt de l'utilisation systématique de eval ?

----------

## netfab

Oui, chacun peut adapter en fonction de ses préférences, j'essaierai avec CONFIG_LOCALVERSION dès que j'aurai 2 minutes.

J'ai ajouté l'eselect pour XvMC, merci.

 *Quote:*   

> P.S.: quel est l'intérêt de l'utilisation systématique de eval ?

 

Simple question d'habitude, je t'avouerai que je n'avais même pas remarqué.

----------

## GentooUser@Clubic

Up pour dire merci  pour ce script   :Wink: 

Par contre je sait pas si c'est chez moi mais je trouve "eselect opengl set xxx" est assez lent à rendre la main :

 *Quote:*   

> 
> 
> eselect opengl set nvidia  0,40s user 0,12s system 51% cpu 0,997 total
> 
> eselect opengl set xorg-x11  0,44s user 0,07s system 52% cpu 0,982 total
> ...

 

J'ai donc un peu modifié le script pour rajouter un test et n’exécuter la commande que si c’est nécessaire :

```

        if [ ! "$(eselect opengl show)" = "${impl}" ]; then

                ebegin "Switching to ${impl} OpenGL implementation"

                eval eselect opengl set "${impl}" >/dev/null 2>&1

                eend $? "Failed to set ${impl} OpenGL interface" || return 1

        fi

```

Si aucun changement n'est nécessaire, c'est tout benef :

 *Quote:*   

> eselect opengl show  0,01s user 0,01s system 42% cpu 0,032 total

 

----------

## GentooUser@Clubic

Bon si ça intéresse toujours du monde y'a une nouvelle recette maintenant, avec un seul noyau   :Very Happy: 

- Activez nouveau dans le noyau, en dur ou module suivant votre préférence (perso je préfère en dur, pour profiter de la console hd le plus tôt possible)

- Activez vesafb dans le noyau, si vous voulez profiter du framebuffer avec les drivers nvidia (pas uvesafb, j'ai pas testé mais c’est marqué incompatible) et de toute façon :

 *Quote:*   

> 
> 
> You may have read people use uvesafb over vesafb, as it had better performance. This WAS generally true, but not in a modern distro with modern Hardware. If your Graphics Hardware supports protected mode VESA (VESA >= 2.0 ), and you have a somewhat recent kernel vesafb is now a better choice.
> 
> 

 

- Bien sûr installez les drivers nvidia et nouveau, mais ça vous savez le faire   :Laughing: 

- Pour booter avec nouveau rien à faire : voici ma ligne de commande grub : 

```

# (0) Gentoo Linux (vmlinuz-3.8.1) (nouveau driver)

menuentry "Gentoo Linux (vmlinuz-3.8.1) (nouveau driver)" {

   linux /vmlinuz-3.8.1 root=UUID=6540fc3c-d0cf-4301-b5c5-e4e8cfb20700 rootfstype=ext4 quiet splash nouveau init=/bin/systemd

   initrd /initramfs-generic.img

}

```

- Pour booter avec nvidia rajoutez nomodeset à la ligne de commande (ça désactivera nouveau) et si vous voulez vesafb, utilisez set gfxpayload=<resolution> dans grub2 (vga=xxx n'est plus supporté) ou référez vous à la documentation de votre bootloader.

```

# (1) Gentoo Linux (vmlinuz-3.8.1) (nvidia-driver)

menuentry "Gentoo Linux (vmlinuz-3.8.1) (nvidia-driver)" {

   set gfxpayload=1920x1200-32

   linux /vmlinuz-3.8.1 root=UUID=6540fc3c-d0cf-4301-b5c5-e4e8cfb20700 rootfstype=ext4 quiet splash nomodeset nvidia init=/bin/systemd

   initrd /initramfs-generic.img

}

```

Bon comme nouveau est activé même dans le noyau nvidia  les scripts qu'on trouves plus haut dans ce topic ne marchent plus, j'en ai fait un nouveau qui cherche "nvidia" ou "nouveau" dans la ligne de commande du noyau et active l’implémentation correspondante :

```

#!/bin/sh

set_x11_drv() {

   local _drv=$1

   

   ln -snf ../xorg-${_drv}.conf /etc/X11/xorg.conf.d/99-device.conf

}

set_gl_impl() {

   local _impl=$1

   local _curr=`eselect opengl show`

   [ ! $_curr = $_impl ] && eselect opengl set $_impl >/dev/null 2>&1   

}

x11_drv="nouveau"

gl_impl="xorg-x11"

found=0

for i in `cat /proc/cmdline` ; do

   case $i in

      nouveau)

         x11_drv="nouveau"

         gl_impl="xorg-x11"

         found=1

         ;;         

      nvidia)

         x11_drv="nvidia"

         gl_impl="nvidia"

         found=1

         ;;

   esac

done

[ $found -eq 0 ] && echo "no driver specified in kernel cmdline, using default"

   

echo "configure system to use $x11_drv driver"

set_x11_drv $x11_drv

set_gl_impl $gl_impl

exit 0

```

Bon par contre c'est pas un rc-script faudra donc en ajouter un (ou un service systemd) pour l’exécuter à chaque boot.

Il faut toujours un /etc/X11/xorg-nouveau.conf et un  /etc/X11/xorg-nvidia.conf (voir premier post du topic)

Aussi je change pas l’implémentation d'OpenCL ou vdpau pour des raisons de temps de démarrage, mais vous pouvez le rajouter.

J'ai fait une version C (pour gagner 0,01s à chaque boot   :Laughing: ), ça compile avec gcc -std=gnu99 -O3 -Wall

```

#include <unistd.h>

#include <string.h>

#include <stdlib.h>

#include <stdio.h>

#define SUCCESS 0

#define ERROR   1

int set_x11_drv(const char *drv)

{

   const char fmt[] = "/etc/X11/xorg-%s.conf";

   const char dst[] = "/etc/X11/xorg.conf.d/99-device.conf";

   char src[sizeof(fmt) + strlen(drv) + 1];

   snprintf(src, sizeof(src), fmt, drv);

   /* first try to unlink dst if exist */

   unlink(dst);

   /* make new symlink */

   printf("Symlink %s to %s\n", src, dst);

   

   return symlink(src, dst);

}

int get_gl_impl(char *buffer, size_t size)

{

   const char cmd[] = "eselect opengl show";

   FILE *pipe = NULL;

   pipe = popen(cmd, "r");

   if(!pipe) {

      fprintf(stderr, "ERROR: Failed to execute %s", cmd);

      return ERROR;

   }

   

   if(!fgets(buffer, size, pipe)) {

      pclose(pipe);

      return ERROR;

   }

   /* remove new line */

   buffer[strcspn(buffer, "\n")] = '\0';

   return pclose(pipe);

}

int set_gl_impl(const char *impl)

{

   const char fmt[] = "eselect opengl set %s &>/dev/null 2>&1";

   char cmd[sizeof(fmt) + strlen(impl) + 1];

   char curr[10];

   get_gl_impl(curr, sizeof(curr));

   /* switch implementation if needed */

   if(strcmp(impl, curr) == 0 ){

      return SUCCESS;

   }

   printf("Switch to %s OpenGL implementation\n", impl);

   snprintf(cmd, sizeof(cmd), fmt, impl);

   return system(cmd);

}

int main(int argc, char **argv)

{

   char *x11_drv = "nouveau";

   char *gl_impl = "xorg-x11";

   FILE *fhandle = NULL;

   char *fbuffer = NULL;

   int rcode = 0;

   int found = 0;

   fhandle = fopen("/proc/cmdline", "r");

   if(fhandle == NULL) {

      perror("ERROR: Unable to open /proc/cmdline");

   }

   else {

      while(fscanf(fhandle, "%ms", &fbuffer) == 1) {

         if(strcmp(fbuffer, "nouveau") == 0) {

            x11_drv = "nouveau";

            gl_impl = "xorg-x11";

            found = 1;

         } else if(strcmp(fbuffer, "nvidia") == 0){

            x11_drv = "nvidia";

            gl_impl = "nvidia";

            found = 1;

         }

         free(fbuffer);

      }

      fclose(fhandle);

   }

   if(found == 0) {

      printf("No driver specified in kernel cmdline, using default\n");

   }

   printf("Configure system to use %s X11 video driver\n", x11_drv);

   if(set_x11_drv(x11_drv) != SUCCESS) {

      perror("ERROR: Unable to set X11 video driver");

      rcode = 1;

   }

   if(set_gl_impl(gl_impl) != SUCCESS) {

      fprintf(stderr, "ERROR: Unable to switch OpenGL implementation\n");

      rcode = 1;   

   }

   return rcode;

}

```

Perso je suis très soulagé de n'avoir, enfin, plus besoin de maintenir deux noyaux avec des options différentes (pour nouveau et nvidia)

----------

## netfab

 *GentooUser@Clubic wrote:*   

> Bon si ça intéresse toujours du monde y'a une nouvelle recette maintenant, avec un seul noyau  
> 
> Perso je suis très soulagé de n'avoir, enfin, plus besoin de maintenir deux noyaux avec des options différentes (pour nouveau et nvidia)

 

Je suis justement en train de concevoir une nouvelle procédure pour n'avoir qu'un seul noyau, je viens ici pour revoir un truc, et je découvre ton post   :Laughing: 

 *GentooUser@Clubic wrote:*   

> - Activez vesafb dans le noyau, si vous voulez profiter du framebuffer avec les drivers nvidia (pas uvesafb, j'ai pas testé mais c’est marqué incompatible) 

 

J'utilise justement uvesafb, je vais voir pour faire avec  :Smile: 

En revanche, ce que je cherche à faire, c'est de pouvoir switcher entre nvidia et nouveau sans avoir à rebooter. Je donnerai des nouvelles si j'y parviens.

----------

## GentooUser@Clubic

uvesafb en dur en tout cas je vient de tester, ça marche pas, il  se  charge inconditionnellement et empêche nouveau de se lancer, même avec video=none. Après en module ça doit pouvoir être jouable avec modprobe.blacklist=.

Pour switcher juste en relançant X j'ai vu un tuto pour Ubuntu y'a quelques temps, faut compiler nouveau en module et jouer avec modprobe -r, perso je suis pas intéressé quitte à relancer X, avec systemd, kexec et un SSD autant relancer le système.

----------

## netfab

 *GentooUser@Clubic wrote:*   

> uvesafb en dur en tout cas je vient de tester, ça marche pas, il  se  charge inconditionnellement et empêche nouveau de se lancer, même avec video=none. Après en module ça doit pouvoir être jouable avec modprobe.blacklist=.
> 
> 

 

Le paramètre pour désactiver uvesafb sur la ligne de commande du kernel est le suivant : video=uvesafb:off

 *GentooUser@Clubic wrote:*   

> 
> 
> Pour switcher juste en relançant X j'ai vu un tuto pour Ubuntu y'a quelques temps, faut compiler nouveau en module et jouer avec modprobe -r

 

Bon, j'ai effectué diverses expériences. J'arrive sans problème à passer de nouveau/kms à nvidia/uvesafb, mais lors de l'opération inverse, çà coince.

En fait, une fois que le pilote nvidia a été chargé puis déchargé, plus moyen de relancer nouveau, çà se termine invariablement par :

```

nouveau: probe of 0000:01:00.0 failed with error -16

```

Et à partir de là, si j'essaie de persévérer en jouant à l'apprenti-sorcier, au bout d'un moment l'écran passe au noir (plus de signal) et les ventilos de la carte graphique se mettent à turbiner au max  :Laughing: 

Alors pour le moment je me contenterai d'un reboot pour passer de nvidia/uvesafb vers nouveau/kms et vice-versa: en m'inspirant de ta technique et de ton code ci-dessus, j'ai recompilé mon kernel avec les options adéquat et réécrit le service à lancer, çà fonctionne parfaitement, je posterai tout çà un peu plus tard et en profiterai pour éditer le premier post du topic.

----------

## GentooUser@Clubic

 *netfab wrote:*   

> 
> 
> Le paramètre pour désactiver uvesafb sur la ligne de commande du kernel est le suivant : video=uvesafb:off
> 
> 

 

Merci ! Je suis passé de Grub à Syslinux et n'ayant pas trouver comment passer le "gfxpayload" au noyau avec Syslinux j'en suis revenu à uvesab.

----------

