Automatica eGPU detection with startx
 
Notifications
Clear all

Automatica eGPU detection with startx  

  RSS

jpandrusky
(@jpandrusky)
New Member
Joined: 1 month ago
 

Just to be clear, none of this is original, I've gathered ideas and solutions from multiple sources. I'm posting here so that others might use this and make improvements to it. Everything has been running great for me with this set up:

  • if startx detects the eGPU, it is automatically used, otherwise startx falls back to the iGPU
  • the eGPU can be unplugged without shutting down or rebooting the system (the X server still needs to be shutdown)

Issues that can't be avoided:

  • no hot unplug of the eGPU while running X
  • to suspend/sleep/hibernate laptop: quit X and run the egpu-unplug script, then sleep/suspend/hibernate

None of this is perfect, but it has been working reliably for me for almost a year.

 

This configuration is for running Xorg on an external nVidia GPU and internal Intel GPU, but it should work for other GPUs with modification. This only works if X is started from the console using startx, it will not work with login managers. All drivers needed are assumed to already be installed. These scripts require sudo to be installed. The eGPU has a monitor connected directly to it, it does not use the laptop screen for display. This is being run on Arch.

This was all tested with the following hardware:

Where I've gotten things from:

How it Works

When startx is run, it checks to see if the eGPU is connected and if it is the egpu layout is used, otherwise the igpu layout is used. As the igpu layout is the fallback, startx should always be able to start the X server (great for if you automatically start X from .profile). To unplug the eGPU, X must first be stopped (quit back to the console), then the egpu-unplug script can be run. The script removes the eGPU from the system allowing for a safe unplug; all the fans in the eGPU and on the graphics card will spin down. The eGPU can be left connected to continue powering the system. The X startup script will force a rescan of the pci bus and reconnect the eGPU if it was unplugged but left connected. The system does not need to be rebooted to reconnect the eGPU.

Configurations

/etc/X11/xorg.conf.d/20-nvidia.conf:

Section "Screen"
  Identifier "nvidia"
  Device "nvidia"
EndSection

Section "ServerLayout"
  Identifier "egpu"
  Screen 0 "nvidia"
EndSection

Section "Device"
  Identifier "Device0"
  Driver "nvidia"
  BusID "PCI:48:0:0" # Replace this with values obtained from egpu-busid script
  Option "AllowEmptyInitialConfiguration"
  Option "AllowExternalGpus" "True"
EndSection

/etc/X11/xorg.conf.d/30-intel.conf:

Section "Screen"
  Identifier "intel"
  Device "intel"
EndSection

Section "ServerLayout"
  Identifier "igpu"
  Screen 0 "intel"
EndSection

Section "Device"
  Identifier "intel"
  Driver "intel"
  Option "AccelMethod" "SNA"
  Option "TearFree" "true"
EndSection

/etc/sudoers.d/rescan (modify this for a specific user to be more secure):

ALL ALL=(ALL) NOPASSWD: /usr/bin/tee /sys/bus/pci/rescan

Scripts

/etc/X11/xinit/xserverrc:

#!/bin/bash

xfile_egpu=/etc/X11/xorg.conf.d/20-nvidia.conf

if ! [ -r $xfile_egpu ]; then
  exec /usr/bin/X -nolisten tcp "[email protected]"
fi

# read pci id from xorg.conf.egpu
declare egpu_pci_id=$(cat $xfile_egpu | grep -Ei "BusID" | grep -oEi '[0-9]+:[0-9]+:[0-9]+')

# create an array by splitting the BUS-ID on ':'
declare busArray=(${egpu_pci_id//:/ })
declare bus1d=${busArray[0]}
declare bus2d=${busArray[1]}
declare bus3d=${busArray[2]}

# convert dec to hex
declare bus1h=$(printf "%02x" $bus1d)
declare bus2h=$(printf "%02x" $bus2d)
declare bus3h=$(printf "%01x" $bus3d)

# force a rescan for egpu
echo 1 | sudo /usr/bin/tee /sys/bus/pci/rescan &> /dev/null

if [ $(lspci | grep -iEc "$bus1h:$bus2h.$bus3h") -eq 1 ]; then
  echo "loading egpu"
  exec /usr/bin/X -nolisten tcp "[email protected]" -layout egpu
else
  exec /usr/bin/X -nolisten tcp "[email protected]" -layout igpu
fi

/usr/local/bin/egpu-unplug (run with sudo):

#!/bin/bash

xfile_egpu=/etc/X11/xorg.conf.d/20-nvidia.conf

if [ "$(id -u)" != "0" ]; then
  echo "Please run using sudo. Exiting."
  exit 1
fi

# read pci id from xorg config
declare egpu_pci_id=$(cat $xfile_egpu | grep -Ei "BusID" | grep -oEi '[0-9]+:[0-9]+:[0-9]+')

# create an array by splitting the BUS-ID on ':'
declare busArray=(${egpu_pci_id//:/ })
declare bus1d=${busArray[0]}
declare bus2d=${busArray[1]}
declare bus3d=${busArray[2]}

# convert dec to hex
declare bus1h=$(printf "%02x" $bus1d)
declare bus2h=$(printf "%02x" $bus2d)
declare bus3h=$(printf "%01x" $bus3d)

declare tbt_chain=$(find /sys/bus/pci/devices -maxdepth 1 | grep 0000:$bus1h:$bus2h)

for dev in ${tbt_chain}; do
  if [ -e $dev/remove ]; then
    echo 1 > $dev/remove
  fi
done

rmmod nvidia_drm &>/dev/null
rmmod nvidia_modeset &>/dev/null
rmmod nvidia &>/dev/null

echo "It is safe to unplug eGPU."

exit 0

/usr/local/bin/egpu-busid:

#!/bin/bash

declare egpu_pci_id=$(lspci | grep "VGA" | awk '{print $1}')

for id in ${egpu_pci_id}; do
  declare busArray=(${id//[:.]/ })

  declare bus1h=${busArray[0]}
  declare bus2h=${busArray[1]}
  declare bus3h=${busArray[2]}

  declare bus1d=$(printf "%02d" 0x$bus1h)
  declare bus2d=$(printf "%02d" 0x$bus2h)
  declare bus3d=$(printf "%01d" 0x$bus3h)

  echo -n "$bus1d:$bus2d:$bus3d"
  lspci | grep "$id" | awk '{$1=""; print $0}'
done

exit 0

Possible Modifications

Rather than have everything be automatic, the xserverrc script could be skipped and aliases could be set up in .bashrc to manually choose which GPU to use.

alias egpux='(echo 1 | sudo /usr/bin/tee /sys/bus/pci/rescan &> /dev/null) && startx -- -layout egpu'
alias igpux='startx -- -layout igpu'

A normal startx would not work with this, but the xserverrc script could be modified to add the igpu layout if no layout is passed when calling startx (the alias for igpux could then be omitted):

#!/bin/sh
if [ "$(echo "[email protected]" | grep '-layout')" ]; then
  exec /usr/bin/X -nolisten tcp "[email protected]"
else
  exec /usr/bin/X -nolisten tcp "[email protected]" -layout igpu
fi

To do: Create my signature with system and expected eGPU configuration information to give context to my posts. I have no builds.

.

ReplyQuote