Skip to main content

amdgpu_sysfs/gpu_handle/overdrive/
gcn.rs

1//! The format used by Vega10 and older GPUs.
2use super::{parse_range_line, push_level_line, ClocksLevel, ClocksTable, ClocksTableGen, Range};
3use crate::{
4    error::{Error, ErrorKind::ParseError},
5    Result,
6};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::{cmp, io::Write, str::FromStr};
10
11/// Vega10 clocks table.
12#[derive(Debug, Clone)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct Table {
15    /// List of core clock levels.
16    pub sclk_levels: Vec<ClocksLevel>,
17    /// List of memory clock levels.
18    pub mclk_levels: Vec<ClocksLevel>,
19    /// The allowed ranges for clockspeeds and voltages.
20    pub od_range: OdRange,
21}
22
23impl ClocksTable for Table {
24    fn write_commands<W: Write>(
25        &self,
26        writer: &mut W,
27        _previous_table: &ClocksTableGen,
28    ) -> Result<()> {
29        let max_len = cmp::max(self.sclk_levels.len(), self.mclk_levels.len());
30        for i in 0..max_len {
31            if let Some(sclk_level) = self.sclk_levels.get(i) {
32                let command = level_command(*sclk_level, i, 's');
33                writer.write_all(command.as_bytes())?;
34            }
35
36            if let Some(mclk_level) = self.mclk_levels.get(i) {
37                let command = level_command(*mclk_level, i, 'm');
38                writer.write_all(command.as_bytes())?;
39            }
40        }
41
42        Ok(())
43    }
44
45    fn get_max_sclk_range(&self) -> Option<Range> {
46        Some(self.od_range.sclk)
47    }
48
49    fn get_min_sclk_range(&self) -> Option<Range> {
50        Some(self.od_range.sclk)
51    }
52
53    fn get_max_mclk_range(&self) -> Option<Range> {
54        self.od_range.mclk
55    }
56
57    fn get_min_mclk_range(&self) -> Option<Range> {
58        self.od_range.mclk
59    }
60
61    fn get_max_voltage_range(&self) -> Option<Range> {
62        self.od_range.vddc
63    }
64
65    fn get_min_voltage_range(&self) -> Option<Range> {
66        self.od_range.vddc
67    }
68
69    fn get_current_voltage_range(&self) -> Option<Range> {
70        let min = self.sclk_levels.first().map(|level| level.voltage);
71        let max = self.sclk_levels.last().map(|level| level.voltage);
72        Some(Range { min, max })
73    }
74
75    fn get_current_sclk_range(&self) -> Range {
76        let min = self.sclk_levels.first().map(|level| level.clockspeed);
77        let max = self.sclk_levels.last().map(|level| level.clockspeed);
78        Range { min, max }
79    }
80
81    fn get_current_mclk_range(&self) -> Range {
82        let min = self.mclk_levels.first().map(|level| level.clockspeed);
83        let max = self.mclk_levels.last().map(|level| level.clockspeed);
84        Range { min, max }
85    }
86
87    fn set_max_sclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
88        let len = self.sclk_levels.len();
89        if len == 0 {
90            return Ok(());
91        }
92
93        self.sclk_levels[len - 1].clockspeed = clockspeed;
94        for clock_level in &mut self.sclk_levels[0..len - 1] {
95            clock_level.clockspeed = cmp::min(clock_level.clockspeed, clockspeed);
96        }
97
98        Ok(())
99    }
100
101    fn set_min_sclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
102        let len = self.sclk_levels.len();
103        if len == 0 {
104            return Ok(());
105        }
106
107        self.sclk_levels[0].clockspeed = clockspeed;
108        for clock_level in &mut self.sclk_levels[1..len] {
109            clock_level.clockspeed = cmp::max(clock_level.clockspeed, clockspeed);
110        }
111
112        Ok(())
113    }
114
115    fn set_max_mclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
116        let len = self.mclk_levels.len();
117        if len == 0 {
118            return Ok(());
119        }
120
121        self.mclk_levels[len - 1].clockspeed = clockspeed;
122        for clock_level in &mut self.mclk_levels[0..len - 1] {
123            clock_level.clockspeed = cmp::min(clock_level.clockspeed, clockspeed);
124        }
125
126        Ok(())
127    }
128
129    fn set_min_mclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
130        let len = self.mclk_levels.len();
131        if len == 0 {
132            return Ok(());
133        }
134
135        self.mclk_levels[0].clockspeed = clockspeed;
136        for clock_level in &mut self.mclk_levels[1..len] {
137            clock_level.clockspeed = cmp::max(clock_level.clockspeed, clockspeed);
138        }
139
140        Ok(())
141    }
142
143    fn set_max_voltage_unchecked(&mut self, voltage: i32) -> Result<()> {
144        let len = self.sclk_levels.len();
145        if len == 0 {
146            return Ok(());
147        }
148
149        self.sclk_levels[len - 1].voltage = voltage;
150        for clock_level in &mut self.sclk_levels[0..len - 1] {
151            clock_level.voltage = cmp::min(clock_level.voltage, voltage);
152        }
153
154        Ok(())
155    }
156
157    fn set_min_voltage_unchecked(&mut self, voltage: i32) -> Result<()> {
158        let len = self.sclk_levels.len();
159        if len == 0 {
160            return Ok(());
161        }
162
163        self.sclk_levels[0].voltage = voltage;
164        for clock_level in &mut self.sclk_levels[1..len] {
165            clock_level.voltage = cmp::max(clock_level.voltage, voltage);
166        }
167
168        Ok(())
169    }
170
171    fn get_max_sclk_voltage(&self) -> Option<i32> {
172        self.sclk_levels.last().map(|level| level.voltage)
173    }
174}
175
176/// The ranges for overclocking values which the GPU allows to be used.
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
179pub struct OdRange {
180    /// Clocks range for sclk (in MHz). Should be present on all GPUs.
181    pub sclk: Range,
182    /// Clocks range for mclk (in MHz). Present on discrete GPUs only.
183    pub mclk: Option<Range>,
184    /// Voltage range (in mV). Present on Vega10 and older GPUs only.
185    pub vddc: Option<Range>,
186}
187
188impl FromStr for Table {
189    type Err = Error;
190
191    fn from_str(s: &str) -> Result<Self> {
192        let mut sclk_levels = Vec::with_capacity(7);
193        let mut mclk_levels = Vec::with_capacity(2);
194        let mut sclk_range = None;
195        let mut mclk_range = None;
196        let mut vddc_range = None;
197
198        let mut current_section = None;
199
200        let mut i = 1;
201        for line in s.lines().map(str::trim).filter(|line| !line.is_empty()) {
202            match line {
203                "OD_SCLK:" => current_section = Some(Section::Sclk),
204                "OD_MCLK:" => current_section = Some(Section::Mclk),
205                "OD_RANGE:" => current_section = Some(Section::Range),
206                line => match current_section {
207                    Some(Section::Sclk) => {
208                        push_level_line(line, &mut sclk_levels, i)?;
209                    }
210                    Some(Section::Mclk) => {
211                        push_level_line(line, &mut mclk_levels, i)?;
212                    }
213                    Some(Section::Range) => {
214                        let (range, name) = parse_range_line(line, i)?;
215                        match name {
216                            "SCLK" => sclk_range = Some(range),
217                            "MCLK" => mclk_range = Some(range),
218                            "VDDC" => vddc_range = Some(range),
219                            other => {
220                                return Err(ParseError {
221                                    msg: format!("Unexpected range item: {other}"),
222                                    line: i,
223                                }
224                                .into())
225                            }
226                        }
227                    }
228                    None => {
229                        return Err(ParseError {
230                            msg: "Could not find section".to_owned(),
231                            line: i,
232                        }
233                        .into())
234                    }
235                },
236            }
237            i += 1;
238        }
239
240        sclk_levels.shrink_to_fit();
241        mclk_levels.shrink_to_fit();
242
243        let od_range = OdRange {
244            sclk: sclk_range.ok_or_else(|| ParseError {
245                msg: "No sclk range found".to_owned(),
246                line: i,
247            })?,
248            mclk: mclk_range,
249            vddc: vddc_range,
250        };
251
252        Ok(Self {
253            sclk_levels,
254            mclk_levels,
255            od_range,
256        })
257    }
258}
259
260fn level_command(level: ClocksLevel, i: usize, symbol: char) -> String {
261    let ClocksLevel {
262        clockspeed,
263        voltage,
264    } = level;
265    format!("{symbol} {i} {clockspeed} {voltage}\n")
266}
267
268#[derive(PartialEq)]
269enum Section {
270    Sclk,
271    Mclk,
272    Range,
273}
274
275#[cfg(test)]
276mod tests {
277    use super::{ClocksLevel, Table};
278    use crate::{
279        gpu_handle::overdrive::{arr_commands, gcn::OdRange, ClocksTable, Range},
280        include_table,
281    };
282    use pretty_assertions::assert_eq;
283    use std::str::FromStr;
284
285    const TABLE_RX580: &str = include_table!("rx580");
286
287    #[test]
288    fn parse_full_table() {
289        let table = Table::from_str(TABLE_RX580).unwrap();
290
291        let sclk_levels = [
292            (300, 750),
293            (600, 769),
294            (900, 912),
295            (1145, 1125),
296            (1215, 1150),
297            (1257, 1150),
298            (1300, 1150),
299            (1366, 1150),
300        ]
301        .map(|(clockspeed, voltage)| ClocksLevel {
302            clockspeed,
303            voltage,
304        });
305        let mclk_levels =
306            [(300, 750), (1000, 825), (1750, 975)].map(|(clockspeed, voltage)| ClocksLevel {
307                clockspeed,
308                voltage,
309            });
310        let ranges = OdRange {
311            sclk: Range::full(300, 2000),
312            mclk: Some(Range::full(300, 2250)),
313            vddc: Some(Range::full(750, 1200)),
314        };
315
316        assert_eq!(table.sclk_levels, sclk_levels);
317        assert_eq!(table.mclk_levels, mclk_levels);
318        assert_eq!(table.od_range, ranges);
319    }
320
321    #[test]
322    fn table_into_commands() {
323        let mut table = Table::from_str(TABLE_RX580).unwrap();
324
325        table.set_max_sclk(1500).unwrap();
326        table.set_max_mclk(2250).unwrap();
327
328        table.set_min_sclk(350).unwrap();
329        table.set_min_mclk(360).unwrap();
330
331        table.set_min_voltage(800).unwrap();
332        table.set_max_voltage(1200).unwrap();
333
334        let mut buf = Vec::new();
335        table
336            .write_commands(&mut buf, &table.clone().into())
337            .unwrap();
338        let commands = String::from_utf8(buf).unwrap();
339
340        let expected_commands = arr_commands([
341            "s 0 350 800",
342            "m 0 360 750",
343            "s 1 600 800",
344            "m 1 1000 825",
345            "s 2 900 912",
346            "m 2 2250 975",
347            "s 3 1145 1125",
348            "s 4 1215 1150",
349            "s 5 1257 1150",
350            "s 6 1300 1150",
351            "s 7 1500 1200",
352        ]);
353
354        assert_eq!(expected_commands, commands);
355    }
356
357    #[test]
358    fn generic_actions() {
359        let mut table = Table::from_str(TABLE_RX580).unwrap();
360        let sclk = table.get_max_sclk().unwrap();
361        assert_eq!(sclk, 1366);
362        let mclk = table.get_max_mclk().unwrap();
363        assert_eq!(mclk, 1750);
364        let voltage = table.get_max_sclk_voltage().unwrap();
365        assert_eq!(voltage, 1150);
366
367        table.set_max_sclk(1400).unwrap();
368        let sclk = table.get_max_sclk().unwrap();
369        assert_eq!(sclk, 1400);
370        assert_eq!(table.sclk_levels[7].clockspeed, 1400);
371
372        table.set_max_mclk(1800).unwrap();
373        let mclk = table.get_max_mclk().unwrap();
374        assert_eq!(mclk, 1800);
375        assert_eq!(table.mclk_levels[2].clockspeed, 1800);
376
377        let sclk_range = table.get_max_sclk_range();
378        let mclk_range = table.get_max_mclk_range();
379        let voltage_range = table.get_max_voltage_range();
380        assert_eq!(sclk_range, Some(Range::full(300, 2000)));
381        assert_eq!(mclk_range, Some(Range::full(300, 2250)));
382        assert_eq!(voltage_range, Some(Range::full(750, 1200)));
383    }
384
385    #[test]
386    fn undervolt_normalize() {
387        let mut table = Table::from_str(TABLE_RX580).unwrap();
388        table.set_max_voltage(1100).unwrap();
389        assert!(table.sclk_levels.iter().all(|level| level.voltage <= 1100));
390    }
391
392    #[test]
393    fn underclock_normalize() {
394        let mut table = Table::from_str(TABLE_RX580).unwrap();
395        table.set_max_sclk(1200).unwrap();
396        assert!(table
397            .sclk_levels
398            .iter()
399            .all(|level| level.clockspeed <= 1200));
400    }
401
402    #[test]
403    fn underclock_memory_normalize() {
404        let mut table = Table::from_str(TABLE_RX580).unwrap();
405        table.set_max_mclk(900).unwrap();
406        assert!(table
407            .mclk_levels
408            .iter()
409            .all(|level| level.clockspeed <= 900));
410    }
411
412    #[test]
413    fn min_voltage_normalize() {
414        let mut table = Table::from_str(TABLE_RX580).unwrap();
415        table.set_min_voltage(800).unwrap();
416        assert!(table.sclk_levels.iter().all(|level| level.voltage >= 800));
417    }
418
419    #[test]
420    fn min_clockspeed_normalize() {
421        let mut table = Table::from_str(TABLE_RX580).unwrap();
422        table.set_min_sclk(750).unwrap();
423        assert!(table
424            .sclk_levels
425            .iter()
426            .all(|level| level.clockspeed >= 750));
427    }
428
429    #[test]
430    fn min_memory_clockspeed_normalize() {
431        let mut table = Table::from_str(TABLE_RX580).unwrap();
432        table.set_min_mclk(1100).unwrap();
433        assert!(table
434            .mclk_levels
435            .iter()
436            .all(|level| level.clockspeed >= 1100));
437    }
438}