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