Trial of blocking vfio/compute unless in integrated mode

This commit is contained in:
Luke D. Jones
2021-06-07 11:02:21 +12:00
parent 8deeffcdad
commit 1b34079d14
5 changed files with 98 additions and 61 deletions

View File

@@ -10,13 +10,10 @@ use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
use rog_aura::{self, AuraEffect}; use rog_aura::{self, AuraEffect};
use rog_dbus::RogDbusClient; use rog_dbus::RogDbusClient;
use rog_profiles::profiles::Profile; use rog_profiles::profiles::Profile;
use rog_types::{ use rog_types::{gfx_vendors::{GfxRequiredUserAction, GfxVendors}, supported::{
gfx_vendors::GfxVendors,
supported::{
FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions, FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions,
SupportedFunctions, SupportedFunctions,
}, }};
};
use std::{env::args, path::Path}; use std::{env::args, path::Path};
use yansi_term::Colour::Green; use yansi_term::Colour::Green;
use yansi_term::Colour::Red; use yansi_term::Colour::Red;
@@ -309,11 +306,27 @@ fn do_gfx(
err err
})?; })?;
let res = dbus.gfx_wait_changed()?; let res = dbus.gfx_wait_changed()?;
println!( match res {
"Graphics mode changed to {}. User action required is: {}", GfxRequiredUserAction::Integrated => {
<&str>::from(mode), println!(
<&str>::from(&res) "You must change to Integrated before you can change to {}",
); <&str>::from(mode)
);
},
GfxRequiredUserAction::Logout | GfxRequiredUserAction::Reboot => {
println!(
"Graphics mode changed to {}. User action required is: {}",
<&str>::from(mode),
<&str>::from(&res)
);
},
GfxRequiredUserAction::None => {
println!(
"Graphics mode changed to {}",
<&str>::from(mode)
);
},
}
std::process::exit(0) std::process::exit(0)
} }
if command.get { if command.get {

View File

@@ -8,8 +8,8 @@ use logind_zbus::{
ManagerProxy, SessionProxy, ManagerProxy, SessionProxy,
}; };
use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}; use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
use std::iter::FromIterator;
use std::{io::Write, ops::Add, path::Path, time::Instant}; use std::{io::Write, ops::Add, path::Path, time::Instant};
use std::{iter::FromIterator, thread::JoinHandle};
use std::{process::Command, thread::sleep, time::Duration}; use std::{process::Command, thread::sleep, time::Duration};
use std::{str::FromStr, sync::mpsc}; use std::{str::FromStr, sync::mpsc};
use std::{sync::Arc, sync::Mutex}; use std::{sync::Arc, sync::Mutex};
@@ -126,7 +126,7 @@ impl CtrlGraphics {
} }
/// Associated method to get which vendor mode is set /// Associated method to get which vendor mode is set
pub fn get_gfx_mode(&self) -> Result<GfxVendors, RogError> { pub(super) fn get_gfx_mode(&self) -> Result<GfxVendors, RogError> {
if let Ok(config) = self.config.lock() { if let Ok(config) = self.config.lock() {
if let Some(mode) = config.gfx_tmp_mode { if let Some(mode) = config.gfx_tmp_mode {
return Ok(mode); return Ok(mode);
@@ -275,6 +275,7 @@ impl CtrlGraphics {
.map_err(|err| RogError::Command("device unbind error".into(), err)) .map_err(|err| RogError::Command("device unbind error".into(), err))
} }
/// Add or remove driver modules
fn do_driver_action(driver: &str, action: &str) -> Result<(), GfxError> { fn do_driver_action(driver: &str, action: &str) -> Result<(), GfxError> {
let mut cmd = Command::new(action); let mut cmd = Command::new(action);
cmd.arg(driver); cmd.arg(driver);
@@ -359,14 +360,15 @@ impl CtrlGraphics {
let mut count = 0; let mut count = 0;
while count <= 5 { while count <= (4 * 3) {
// 3 seconds max
let output = cmd let output = cmd
.output() .output()
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?; .map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
if output.stdout.starts_with(state.as_bytes()) { if output.stdout.starts_with(state.as_bytes()) {
return Ok(()); return Ok(());
} }
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(250));
count += 1; count += 1;
} }
Err(GfxError::DisplayManagerTimeout(state.into()).into()) Err(GfxError::DisplayManagerTimeout(state.into()).into())
@@ -374,9 +376,10 @@ impl CtrlGraphics {
/// Determine if we need to logout/thread. Integrated<->Vfio mode does not /// Determine if we need to logout/thread. Integrated<->Vfio mode does not
/// require logout. /// require logout.
fn logout_required(&self, vendor: GfxVendors) -> GfxRequiredUserAction { fn is_logout_required(&self, vendor: GfxVendors) -> GfxRequiredUserAction {
if let Ok(config) = self.config.lock() { if let Ok(config) = self.config.lock() {
let current = config.gfx_mode; let current = config.gfx_mode;
// Modes that can switch without logout
if matches!( if matches!(
current, current,
GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute GfxVendors::Integrated | GfxVendors::Vfio | GfxVendors::Compute
@@ -386,22 +389,28 @@ impl CtrlGraphics {
) { ) {
return GfxRequiredUserAction::None; return GfxRequiredUserAction::None;
} }
// Modes that require a switch to integrated first
if matches!(current, GfxVendors::Nvidia | GfxVendors::Hybrid)
&& matches!(vendor, GfxVendors::Compute | GfxVendors::Vfio)
{
return GfxRequiredUserAction::Integrated;
}
} }
GfxRequiredUserAction::Logout GfxRequiredUserAction::Logout
} }
/// Write the config changes and add/remove drivers and devices depending /// Do a full setup flow for the chosen mode:
/// on selected mode:
/// ///
/// Tasks: /// Tasks:
/// - rescan for devices
/// - write xorg config /// - write xorg config
/// - write modprobe config /// - write modprobe config
/// - rescan for devices
/// + add drivers /// + add drivers
/// + or remove drivers and devices /// + or remove drivers and devices
/// ///
/// The daemon needs direct access to this function when it detects that the /// The daemon needs direct access to this function when it detects that the
pub fn do_vendor_tasks( /// bios has G-Sync switch is enabled
pub fn do_mode_setup_tasks(
vendor: GfxVendors, vendor: GfxVendors,
vfio_enable: bool, vfio_enable: bool,
devices: &[GraphicsDevice], devices: &[GraphicsDevice],
@@ -425,8 +434,14 @@ impl CtrlGraphics {
dev.set_runtime_pm(sysfs_class::RuntimePowerManagement::On)?; dev.set_runtime_pm(sysfs_class::RuntimePowerManagement::On)?;
} }
} }
// // Only these modes should have xorg config
Self::write_xorg_conf(vendor)?; if matches!(
vendor,
GfxVendors::Nvidia | GfxVendors::Hybrid | GfxVendors::Integrated
) {
Self::write_xorg_conf(vendor)?;
}
// Write different modprobe to enable boot control to work // Write different modprobe to enable boot control to work
Self::write_modprobe_conf(vendor, devices)?; Self::write_modprobe_conf(vendor, devices)?;
@@ -492,7 +507,7 @@ impl CtrlGraphics {
} }
/// Spools until all user sessions are ended then switches to requested mode /// Spools until all user sessions are ended then switches to requested mode
fn fire_starter( fn create_mode_change_thread(
vendor: GfxVendors, vendor: GfxVendors,
devices: Vec<GraphicsDevice>, devices: Vec<GraphicsDevice>,
bus: PciBus, bus: PciBus,
@@ -538,7 +553,7 @@ impl CtrlGraphics {
Self::do_display_manager_action("stop")?; Self::do_display_manager_action("stop")?;
Self::wait_display_manager_state("inactive")?; Self::wait_display_manager_state("inactive")?;
let mut mode_to_save = vendor; let mut mode_to_save = vendor;
// Need to change to integrated before we can change to vfio or compute // Need to change to integrated before we can change to vfio or compute
if let Ok(mut config) = config.try_lock() { if let Ok(mut config) = config.try_lock() {
// Since we have a lock, reset tmp to none. This thread should only ever run // Since we have a lock, reset tmp to none. This thread should only ever run
@@ -547,17 +562,16 @@ impl CtrlGraphics {
// //
let vfio_enable = config.gfx_vfio_enable; let vfio_enable = config.gfx_vfio_enable;
// Failsafe. In the event this loop is run with a switch from nvidia in use
// to vfio or compute do a forced switch to integrated instead to prevent issues
if matches!(vendor, GfxVendors::Compute | GfxVendors::Vfio) if matches!(vendor, GfxVendors::Compute | GfxVendors::Vfio)
&& matches!(config.gfx_mode, GfxVendors::Nvidia | GfxVendors::Hybrid) && matches!(config.gfx_mode, GfxVendors::Nvidia | GfxVendors::Hybrid)
{ {
Self::do_vendor_tasks(GfxVendors::Integrated, vfio_enable, &devices, &bus)?; Self::do_mode_setup_tasks(GfxVendors::Integrated, vfio_enable, &devices, &bus)?;
Self::do_display_manager_action("restart")?; Self::do_display_manager_action("restart")?;
sleep(Duration::from_millis(1500)); // Allow some time for the desktop to start
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?;
config.gfx_tmp_mode = Some(vendor);
mode_to_save = GfxVendors::Integrated; mode_to_save = GfxVendors::Integrated;
} else { } else {
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?; Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
Self::do_display_manager_action("restart")?; Self::do_display_manager_action("restart")?;
} }
} }
@@ -572,7 +586,7 @@ impl CtrlGraphics {
} }
/// Before starting a new thread the old one *must* be cancelled /// Before starting a new thread the old one *must* be cancelled
fn cancel_thread(&self) { fn cancel_mode_change_thread(&self) {
if let Ok(lock) = self.thread_kill.lock() { if let Ok(lock) = self.thread_kill.lock() {
if let Some(tx) = lock.as_ref() { if let Some(tx) = lock.as_ref() {
// Cancel the running thread // Cancel the running thread
@@ -587,7 +601,7 @@ impl CtrlGraphics {
} }
/// The thread is used only in cases where a logout is required /// The thread is used only in cases where a logout is required
fn setup_thread(&mut self, vendor: GfxVendors) { fn setup_mode_change_thread(&mut self, vendor: GfxVendors) {
let config = self.config.clone(); let config = self.config.clone();
let devices = self.nvidia.clone(); let devices = self.nvidia.clone();
let bus = self.bus.clone(); let bus = self.bus.clone();
@@ -595,16 +609,16 @@ impl CtrlGraphics {
if let Ok(mut lock) = self.thread_kill.lock() { if let Ok(mut lock) = self.thread_kill.lock() {
*lock = Some(tx); *lock = Some(tx);
} }
let killer = self.thread_kill.clone(); let thread_kill = self.thread_kill.clone();
let _join: JoinHandle<()> = std::thread::spawn(move || { std::thread::spawn(move || {
Self::fire_starter(vendor, devices, bus, rx, config) Self::create_mode_change_thread(vendor, devices, bus, rx, config)
.map_err(|err| { .map_err(|err| {
error!("GFX: {}", err); error!("GFX: {}", err);
}) })
.ok(); .ok();
// clear the tx/rx when done // clear the tx/rx when done
if let Ok(mut lock) = killer.try_lock() { if let Ok(mut lock) = thread_kill.try_lock() {
*lock = None; *lock = None;
} }
}); });
@@ -615,10 +629,7 @@ impl CtrlGraphics {
/// to switch modes. /// to switch modes.
/// ///
/// For manually calling (not on boot/startup) via dbus /// For manually calling (not on boot/startup) via dbus
pub fn set_gfx_config( pub fn set_gfx_mode(&mut self, vendor: GfxVendors) -> Result<GfxRequiredUserAction, RogError> {
&mut self,
vendor: GfxVendors,
) -> Result<GfxRequiredUserAction, RogError> {
if let Ok(gsync) = CtrlRogBios::get_gfx_mode() { if let Ok(gsync) = CtrlRogBios::get_gfx_mode() {
if gsync == 1 { if gsync == 1 {
return Err(GfxError::GsyncModeActive.into()); return Err(GfxError::GsyncModeActive.into());
@@ -636,29 +647,40 @@ impl CtrlGraphics {
} }
// Must always cancel any thread running // Must always cancel any thread running
self.cancel_thread(); self.cancel_mode_change_thread();
// determine which method we need here // determine which method we need here
let action_required = self.logout_required(vendor); let action_required = self.is_logout_required(vendor);
if matches!(action_required, GfxRequiredUserAction::Logout) {
// Yeah need the thread to check if all users are logged out match action_required {
info!("GFX: mode change requires a logout to complete"); GfxRequiredUserAction::Logout => {
self.setup_thread(vendor); info!("GFX: mode change requires a logout to complete");
} else { self.setup_mode_change_thread(vendor);
// Okay cool, we can switch on/off vfio }
info!("GFX: mode change does not require logout"); GfxRequiredUserAction::Reboot => {
let devices = self.nvidia.clone(); info!("GFX: mode change requires reboot");
let bus = self.bus.clone(); let devices = self.nvidia.clone();
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?; let bus = self.bus.clone();
info!("GFX: Graphics mode changed to {}", <&str>::from(vendor)); Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
if let Ok(mut config) = self.config.try_lock() { info!("GFX: Graphics mode changed to {}", <&str>::from(vendor));
if matches!(vendor, GfxVendors::Vfio | GfxVendors::Compute) { },
config.gfx_tmp_mode = Some(vendor); GfxRequiredUserAction::Integrated => {
} else { info!("GFX: mode change requires user to be in Integrated mode first");
}
GfxRequiredUserAction::None => {
info!("GFX: mode change does not require logout");
let devices = self.nvidia.clone();
let bus = self.bus.clone();
Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
info!("GFX: Graphics mode changed to {}", <&str>::from(vendor));
if let Ok(mut config) = self.config.try_lock() {
config.gfx_tmp_mode = None; config.gfx_tmp_mode = None;
if matches!(vendor, GfxVendors::Vfio | GfxVendors::Compute) {
config.gfx_tmp_mode = Some(vendor);
}
} }
} }
} }
// TODO: undo if failed? Save last mode, catch errors...
Ok(action_required) Ok(action_required)
} }
@@ -674,7 +696,7 @@ impl CtrlGraphics {
false false
}; };
Self::do_vendor_tasks(vendor, vfio_enable, &devices, &bus)?; Self::do_mode_setup_tasks(vendor, vfio_enable, &devices, &bus)?;
Self::toggle_fallback_service(vendor)?; Self::toggle_fallback_service(vendor)?;
Ok(()) Ok(())
} }

View File

@@ -25,7 +25,7 @@ impl CtrlGraphics {
fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result<GfxRequiredUserAction> { fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result<GfxRequiredUserAction> {
info!("GFX: Switching gfx mode to {}", <&str>::from(vendor)); info!("GFX: Switching gfx mode to {}", <&str>::from(vendor));
let msg = self.set_gfx_config(vendor).map_err(|err| { let msg = self.set_gfx_mode(vendor).map_err(|err| {
error!("GFX: {}", err); error!("GFX: {}", err);
zbus::fdo::Error::Failed(format!("GFX fail: {}", err)) zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
})?; })?;

View File

@@ -155,7 +155,7 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode"); warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
let devices = ctrl.devices(); let devices = ctrl.devices();
let bus = ctrl.bus(); let bus = ctrl.bus();
CtrlGraphics::do_vendor_tasks( CtrlGraphics::do_mode_setup_tasks(
GfxVendors::Nvidia, GfxVendors::Nvidia,
false, false,
&devices, &devices,
@@ -165,7 +165,7 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
info!("Dedicated GFX toggle is off"); info!("Dedicated GFX toggle is off");
let devices = ctrl.devices(); let devices = ctrl.devices();
let bus = ctrl.bus(); let bus = ctrl.bus();
CtrlGraphics::do_vendor_tasks( CtrlGraphics::do_mode_setup_tasks(
config.gfx_mode, config.gfx_mode,
false, false,
&devices, &devices,

View File

@@ -86,6 +86,7 @@ impl From<GfxVendors> for &str {
pub enum GfxRequiredUserAction { pub enum GfxRequiredUserAction {
Logout, Logout,
Reboot, Reboot,
Integrated,
None, None,
} }
@@ -94,6 +95,7 @@ impl From<&GfxRequiredUserAction> for &str {
match gfx { match gfx {
GfxRequiredUserAction::Logout => "logout", GfxRequiredUserAction::Logout => "logout",
GfxRequiredUserAction::Reboot => "reboot", GfxRequiredUserAction::Reboot => "reboot",
GfxRequiredUserAction::Integrated => "switch to integrated first",
GfxRequiredUserAction::None => "no action", GfxRequiredUserAction::None => "no action",
} }
} }