Skip to content

Commit 118a613

Browse files
committed
Add new dGPU interface
1 parent 1e8e96f commit 118a613

File tree

6 files changed

+418
-0
lines changed

6 files changed

+418
-0
lines changed

Cargo.lock

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ anyhow = "1.0.38"
2020
smallvec = "1.6.1"
2121
serde = "1.0.124"
2222
serde_json = "1.0.64"
23+
udev = "0.6.0"
2324

2425
[build-dependencies]
2526
clap = "2.33.3"
@@ -30,6 +31,7 @@ anyhow = "1.0.38"
3031
smallvec = "1.6.1"
3132
serde = "1.0.124"
3233
serde_json = "1.0.64"
34+
udev = "0.6.0"
3335

3436
[profile.release]
3537
lto = true

src/cli/dgpu.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use std::convert::TryFrom;
2+
3+
use crate::{cli::Command as DynCommand, sys};
4+
use crate::sys::pci::PciDevice;
5+
6+
use anyhow::{Context, Result};
7+
use sys::Error;
8+
9+
10+
pub struct Command;
11+
12+
impl DynCommand for Command {
13+
fn name(&self) -> &'static str {
14+
"dgpu"
15+
}
16+
17+
fn build(&self) -> clap::App<'static, 'static> {
18+
use clap::{AppSettings, Arg, SubCommand};
19+
20+
clap::SubCommand::with_name(self.name())
21+
.about("Control the discrete GPU")
22+
.setting(AppSettings::SubcommandRequiredElseHelp)
23+
.setting(AppSettings::InferSubcommands)
24+
.subcommand(SubCommand::with_name("id")
25+
.alias("get-id")
26+
.about("Get the dGPU PCI device ID")
27+
.display_order(1))
28+
.subcommand(SubCommand::with_name("get-power-state")
29+
.aliases(&["ps", "get-ps", "power-state"])
30+
.about("Get the dGPU PCI power state")
31+
.display_order(2))
32+
.subcommand(SubCommand::with_name("get-runtime-pm")
33+
.aliases(&["rpm", "get-rpm"])
34+
.about("Get the dGPU runtime PM control")
35+
.display_order(3))
36+
.subcommand(SubCommand::with_name("set-runtime-pm")
37+
.alias("set-rpm")
38+
.about("Set the dGPU runtime PM control")
39+
.arg(Arg::with_name("mode")
40+
.possible_values(&["on", "off"])
41+
.required(true)
42+
.index(1))
43+
.display_order(4))
44+
}
45+
46+
fn execute(&self, m: &clap::ArgMatches) -> Result<()> {
47+
match m.subcommand() {
48+
("id", Some(m)) => self.get_id(m),
49+
("get-power-state", Some(m)) => self.get_power_state(m),
50+
("get-runtime-pm", Some(m)) => self.get_runtime_pm(m),
51+
("set-runtime-pm", Some(m)) => self.set_runtime_pm(m),
52+
_ => unreachable!(),
53+
}
54+
}
55+
}
56+
57+
impl Command {
58+
fn get_id(&self, m: &clap::ArgMatches) -> Result<()> {
59+
let dgpu = find_dgpu_device()
60+
.context("Failed to look up discrete GPU device")?
61+
.ok_or_else(|| anyhow::anyhow!("No discrete GPU found"))?;
62+
63+
let vendor_id = dgpu.vendor_id()
64+
.context("Failed to get vendor ID")?;
65+
66+
let device_id = dgpu.device_id()
67+
.context("Failed to get device ID")?;
68+
69+
if !m.is_present("quiet") {
70+
println!("Vendor: {:04x}", vendor_id);
71+
println!("Device: {:04x}", device_id);
72+
} else {
73+
println!("{:04x}:{:04x}", vendor_id, device_id);
74+
}
75+
76+
Ok(())
77+
}
78+
79+
fn get_power_state(&self, _m: &clap::ArgMatches) -> Result<()> {
80+
let dgpu = find_dgpu_device()
81+
.context("Failed to look up discrete GPU device")?
82+
.ok_or_else(|| anyhow::anyhow!("No discrete GPU found"))?;
83+
84+
let pstate = dgpu.get_power_state()
85+
.context("Failed to get device power state")?;
86+
87+
println!("{}", pstate);
88+
89+
Ok(())
90+
}
91+
92+
fn get_runtime_pm(&self, _m: &clap::ArgMatches) -> Result<()> {
93+
let dgpu = find_dgpu_device()
94+
.context("Failed to look up discrete GPU device")?
95+
.ok_or_else(|| anyhow::anyhow!("No discrete GPU found"))?;
96+
97+
let mode = dgpu.get_runtime_pm()
98+
.context("Failed to get runtime PM mode")?;
99+
100+
println!("{}", mode);
101+
102+
Ok(())
103+
}
104+
105+
fn set_runtime_pm(&self, m: &clap::ArgMatches) -> Result<()> {
106+
use clap::value_t_or_exit;
107+
let mode = value_t_or_exit!(m, "mode", sys::pci::RuntimePowerManagement);
108+
109+
let mut dgpu = find_dgpu_device()
110+
.context("Failed to look up discrete GPU device")?
111+
.ok_or_else(|| anyhow::anyhow!("No discrete GPU found"))?;
112+
113+
dgpu.set_runtime_pm(mode)
114+
.context("Failed to set runtime PM mode")?;
115+
116+
if !m.is_present("quiet") {
117+
println!("Discrete GPU runtime PM set to '{}'", mode);
118+
}
119+
120+
Ok(())
121+
}
122+
}
123+
124+
pub fn find_dgpu_device() -> crate::sys::Result<Option<PciDevice>> {
125+
let mut enumerator = udev::Enumerator::new()
126+
.map_err(|source| Error::IoError { source })?;
127+
128+
enumerator.match_subsystem("pci")
129+
.map_err(|source| Error::IoError { source })?;
130+
131+
let devices = enumerator.scan_devices()
132+
.map_err(|source| Error::IoError { source })?;
133+
134+
for device in devices {
135+
let device = PciDevice::try_from(device)
136+
.map_err(|source| Error::SysFsError { source })?;
137+
138+
let vendor_id = device.vendor_id()
139+
.map_err(|source| Error::SysFsError { source })?;
140+
141+
if vendor_id != sys::pci::VENDOR_ID_NVIDIA {
142+
continue;
143+
}
144+
145+
let class = device.class()
146+
.map_err(|source| Error::SysFsError { source })?;
147+
148+
if class.base != sys::pci::BASE_CLASS_DISPLAY {
149+
continue;
150+
}
151+
152+
return Ok(Some(device));
153+
}
154+
155+
Ok(None)
156+
}

src/cli/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod dgpu;
12
pub mod dtx;
23
pub mod perf;
34
pub mod status;
@@ -24,6 +25,7 @@ impl Registry {
2425
Box::new(status::Command),
2526
Box::new(perf::Command),
2627
Box::new(dtx::Command),
28+
Box::new(dgpu::Command),
2729
];
2830

2931
Registry {

src/sys/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod dtx;
2+
pub mod pci;
23
pub mod perf;
34

45
use thiserror::Error;
@@ -20,6 +21,9 @@ pub enum Error {
2021
#[error("DTX subsystem error")]
2122
DtxError { source: dtx::DtxError },
2223

24+
#[error("SysFS error")]
25+
SysFsError { source: pci::SysFsError },
26+
2327
#[error("Invalid data")]
2428
InvalidData,
2529
}

0 commit comments

Comments
 (0)