J'ai un périphérique PCIe qui ne fonctionne correctement que lorsque l'ordinateur est complètement éteint puis rallumé. L'émission d'une simple commande reboot
ou reboot -p
Ne semble pas mettre hors tension la carte PCIe, ce qui l'empêche de fonctionner après le redémarrage.
Existe-t-il un moyen, à partir du système d'exploitation, de redémarrer un périphérique dans un emplacement PCIe? Je peux le trouver dans /sys/bus/pci/devices/0000*/
, Mais je ne sais pas comment réinitialiser correctement la carte. Basculer le pouvoir semble être le seul moyen.
Sauf cela, puis-je modifier un paramètre quelque part qui provoquera un cycle d'alimentation complet sur une commande reboot
?
Soit dit en passant, j'utilise Ubuntu 12.10.
Je pense que vous pouvez le faire avec ces commandes:
désactiver
echo 0 > /sys/bus/pci/slots/$NUMBER/power
activer
echo 1 > /sys/bus/pci/slots/$NUMBER/power
Où $NUMBER
est le numéro de l'emplacement PCI.
lspci -vv
peut aider à identifier le périphérique. Ce n'est pas très bien documenté ...
Je suis tombé sur ce fil sur U&L , problème similaire: il y a des réponses à cette question qui disent que vous pouvez réinitialiser avec cette commande:
echo "1" > /sys/bus/pci/devices/$NUMBER/reset
Cependant, je lirais les réponses là-bas! Il y a des conditions pour le faire de cette façon! Plus précisément, je voudrais lire cette réponse !
Il existe une commande Unix, setpci
, qui peut vous donner une méthode pour réinitialiser un périphérique dans le bus PCI.
Je n'ai vu aucun exemple spécifique avec cette commande, vous devrez donc rechercher des exemples sur Google et parcourir la page de manuel . Je marcherais légèrement avec cette commande jusqu'à ce que vous ayez confiance en son utilisation. D'après ce que j'ai lu à ce sujet, il s'agit de manipuler le matériel directement et il y a donc toujours des risques à le faire vous-même par rapport à l'utilisation d'un outil qui expose ce type de fonctionnalité!
remove
et rescan
permettront au noyau d'alimenter par cyclage le périphérique PCI sans reboot
:
echo "1" > /sys/bus/pci/devices/DDDD\:BB\:DD.F//remove
sleep 1
echo "1" > /sys/bus/pci/rescan
où DDDD.BB.DD.F = Domaine: Bus: Device.Function
Les réinitialisations dans PCI express sont un peu complexes. Il existe deux principaux types de réinitialisations: réinitialisation conventionnelle et réinitialisation au niveau de la fonction. Il existe également deux types de réinitialisations conventionnelles, les réinitialisations fondamentales et les réinitialisations non fondamentales. Voir la spécification PCI express pour tous les détails.
Une "réinitialisation à froid" est une réinitialisation fondamentale qui a lieu après la mise sous tension d'un périphérique PCIe. Il ne semble pas y avoir de moyen standard de déclencher une réinitialisation à froid, sauf pour éteindre et rallumer le système. Sur mes machines, le /sys/bus/pci/slots
le répertoire est vide.
Une "réinitialisation à chaud" est une réinitialisation fondamentale qui est déclenchée sans couper l'alimentation de l'appareil. Il ne semble y avoir aucun moyen standard de déclencher une réinitialisation à chaud.
Une "réinitialisation à chaud" est une réinitialisation conventionnelle qui est déclenchée sur une liaison PCI express. Une réinitialisation à chaud est déclenchée soit lorsqu'une liaison est forcée au repos électrique ou en envoyant des ensembles ordonnés TS1 et TS2 avec le bit de réinitialisation à chaud défini. Le logiciel peut lancer une réinitialisation à chaud en définissant puis en effaçant le bit de réinitialisation du bus secondaire dans le registre de contrôle de pont dans l'espace de configuration PCI du port de pont en amont de l'appareil.
Une "réinitialisation au niveau des fonctions" (FLR) est une réinitialisation qui n'affecte qu'une seule fonction d'un périphérique PCI express. Il ne doit pas réinitialiser l'ensemble du périphérique PCIe. L'implémentation de réinitialisations au niveau des fonctions n'est pas requise par la spécification PCIe. Une réinitialisation au niveau de la fonction est lancée en définissant le bit de réinitialisation au niveau de la fonction de lancement dans le registre de commande de périphérique de la fonction dans la structure de capacité PCI express dans l'espace de configuration PCI.
Linux expose la fonctionnalité de réinitialisation au niveau de la fonction sous la forme de /sys/bus/pci/devices/$dev/reset
. L'écriture d'un 1 dans ce fichier lancera une réinitialisation au niveau de la fonction sur la fonction correspondante. Notez que cela n'affecte que cette fonction spécifique du périphérique, pas l'ensemble du périphérique, et les périphériques ne sont pas nécessaires pour implémenter des réinitialisations au niveau des fonctions selon la spécification PCIe.
Je ne connais aucune méthode "Nice" pour déclencher une réinitialisation à chaud (il n'y a pas d'entrée sysfs pour cela). Cependant, il est possible d'utiliser setpci pour ce faire:
#!/bin/bash
dev=$1
if [ -z "$dev" ]; then
echo "Error: no device specified"
exit 1
fi
if [ ! -e "/sys/bus/pci/devices/$dev" ]; then
dev="0000:$dev"
fi
if [ ! -e "/sys/bus/pci/devices/$dev" ]; then
echo "Error: device $dev not found"
exit 1
fi
port=$(basename $(dirname $(readlink "/sys/bus/pci/devices/$dev")))
if [ ! -e "/sys/bus/pci/devices/$port" ]; then
echo "Error: device $port not found"
exit 1
fi
echo "Removing $dev..."
echo 1 > "/sys/bus/pci/devices/$dev/remove"
echo "Performing hot reset of port $port..."
bc=$(setpci -s $port BRIDGE_CONTROL)
echo "Bridge control:" $bc
setpci -s $port BRIDGE_CONTROL=$(printf "%04x" $(("0x$bc" | 0x40)))
sleep 0.01
setpci -s $port BRIDGE_CONTROL=$bc
sleep 0.5
echo "Rescanning bus..."
echo 1 > "/sys/bus/pci/devices/$port/rescan"
Assurez-vous que tous les pilotes connectés sont déchargés avant d'exécuter ce script. Ce script tentera de supprimer le périphérique PCIe, puis commandera au port du commutateur en amont d'émettre une réinitialisation à chaud, puis tentera de réanalyser le bus PCIe. Ce script a également été testé uniquement sur des appareils avec une seule fonction, il peut donc avoir besoin d'être retravaillé pour les appareils avec plusieurs fonctions.
S'appuyant sur la réponse publiée par alex.forencich
J'ai dû apporter quelques modifications pour que cela fonctionne sur CentOS 7, en partie parce que je ne tourne pas en tant que root. Cette version affiche les commandes en cours d'exécution.
#!/bin/bash
# e.g. $ ./pcie_hot_reset.sh 04:00.0
DEV=$1
if [ -z "$DEV" ]; then
echo "Error: no device specified"
exit 1
fi
if [ ! -e "/sys/bus/pci/devices/$DEV" ]; then
DEV="0000:$DEV"
fi
if [ ! -e "/sys/bus/pci/devices/$DEV" ]; then
echo "Error: device $DEV not found"
exit 1
fi
PORT=$(basename $(dirname $(readlink "/sys/bus/pci/devices/$DEV")))
if [ ! -e "/sys/bus/pci/devices/$PORT" ]; then
echo "Error: device $PORT not found"
exit 1
fi
echo -e "\nRemoving $DEV"
CMD="echo 1 | Sudo tee /sys/bus/pci/devices/$DEV/remove"
printf "> $CMD\n"
eval $CMD
echo -e "\nPerforming hot reset of port $PORT"
CMD="setpci -s $PORT BRIDGE_CONTROL"
printf "> $CMD\n"
BR_CTRL=$(eval $CMD)
echo "Bridge control: $BR_CTRL"
CMD="Sudo setpci -s $PORT BRIDGE_CONTROL=$(printf "%04x" $((0x${BR_CTRL} | 0x40)))"
printf "> $CMD\n"
eval $CMD
sleep 0.01
CMD="Sudo setpci -s $PORT BRIDGE_CONTROL=$BR_CTRL"
printf "> $CMD\n"
eval $CMD
sleep 0.5
echo -e "\nRescanning bus"
CMD="echo 1 | Sudo tee /sys/bus/pci/devices/$PORT/rescan"
printf "> $CMD\n"
eval $CMD
Exemple de sortie:
$ ./pcie_hot_reset.sh 04:00.0
Removing 0000:04:00.0
> echo 1 | Sudo tee /sys/bus/pci/devices/0000:04:00.0/remove
1
Performing hot reset of port 0000:00:03.0
> setpci -s 0000:00:03.0 BRIDGE_CONTROL
Bridge control: 0010
> Sudo setpci -s 0000:00:03.0 BRIDGE_CONTROL=0050
> Sudo setpci -s 0000:00:03.0 BRIDGE_CONTROL=0010
Rescanning bus
> echo 1 | Sudo tee /sys/bus/pci/devices/0000:00:03.0/rescan
1