class RadeonNoise::AMDGPUCard

Holds data associated with each AMDGPU card and provides access to its fans and settings.

Constants

PROTECTLVL
PWMERR
PWMLVL

Attributes

cache[R]

Store the current settings for the graphics card: # detail is the sysfs hardware object data # sclk is the processor clock # mclk is the memory clock # pwm is whether fans are manual (true) or auto (false) # fan_curve is a list of values for the fan speeds # by temp.

config[R]

Store the current settings for the graphics card: # detail is the sysfs hardware object data # sclk is the processor clock # mclk is the memory clock # pwm is whether fans are manual (true) or auto (false) # fan_curve is a list of values for the fan speeds # by temp.

Public Class Methods

new(hw) click to toggle source

Constructor

    # File lib/radeonnoise/core.rb
109 def initialize(hw)
110   # Initialize the card's config
111   @config = {
112     detail: hw,
113     sclk: [], # Processor clock
114     mclk: [], # Video memory clock
115     
116     # Regexes for various settings
117     # Use these to detect the numbers for each
118     rg_pwm: /pwm\d*$/, # Fan power settings (0-255)
119     rg_freqs: /freq\d*_label/, # GPU frequencies
120     rg_power: /power\d*_average/, # GPU processor power consumption
121     rg_voltages: /in\d*_label/, # Voltage inputs
122     rg_temps: /temp\d*_label/, # Temperatures
123     rg_fans: /fan\d*_input/, # List of available fans
124     
125     # Quick access to some files
126     # Manual/Auto clocking of processor
127     dpm_level: "device/power_dpm_force_performance_level",
128     # Power cap (used in a setting, as well as a read)
129     pcap: "power1_cap",
130     
131     # Fan curve settings
132     fan_curve: {},
133     
134     # Allow unsafe operations
135     protection: :max_prot,
136   }
137   
138   # Initialize the card's stat cache
139   "#{RadeonNoise::BASEDIR}/#{@config[:detail][:dir]}".then { |d| @cache = {
140     dir: d,
141     vram_total: File.read("#{d}/device/mem_info_vram_total").to_f,
142     vbios: File.read("#{d}/device/vbios_version").strip,
143   }}
144   @cache.update(temps).update(fans).update(subsystem)
145   update
146 end

Public Instance Methods

active_s(data) click to toggle source

Split multi-line read-ins and then select the active option only (for settings)

    # File lib/radeonnoise/core.rb
301 def active_s(data)
302   data.split(/\n/)
303     .reject { |item| !item.strip.match?(/\*$/) }
304     .collect { |item| item.split(":").last.strip }
305 end
fans() click to toggle source

Read fan data

    # File lib/radeonnoise/core.rb
270 def fans
271   [@config[:rg_fans], @cache[:dir]].then { |rg, d| 
272     {fans: Component::Fan.new(rg, "#{d}/*")} }
273 end
pwmtype(val) click to toggle source

PWM control type Accepts: [Integer, String, Symbol]

If String, convert to lowercase w/o whitespace, then check if it's an Integer or Symbol, and recurse.

If Integer, check if in PWMLVL. If Symbol, check if in inverse hash of PWMLVL. Return `nil`, if not present – up to user how to handle. If anything else, return error message.

    # File lib/radeonnoise/core.rb
317 def pwmtype(val)
318   case
319   when String === val # Try to recursively check on conversion
320     val.downcase.strip.then { |v| v.match?(/^\d+$/) ? pwmtype(v.to_i) : pwmtype(v.to_sym) }
321   when Integer === val # Check the constant directly
322     PWMLVL[val] || PWMERR
323   when Symbol === val # Invert constant, check
324     PWMLVL.invert[val] || PWMERR
325   else # Wrong class of input
326     "ERROR: wrong arg type (#{val.class}) for `pwmtype`. Accepts: [Integer, String, Symbol]"
327   end
328 end
set_dpm(setting) click to toggle source

Change performance type (manual / auto) /device/power_dpm_force_performance_level “manual” / “auto”

    # File lib/radeonnoise/core.rb
373 def set_dpm(setting)
374   if RadeonNoise.root then
375     if [:manual, :auto].include?(setting) then
376       File.write("#{@cache[:dir]}/#{@config[:dpm_level]}", setting, mode: "r+")
377     else
378       puts "DPM force setting can only be 'manual' or 'auto'"
379     end
380   end
381 end
set_pcap(uwatts, force=false) click to toggle source

Change the power cap on the card – microWatts

    # File lib/radeonnoise/core.rb
354 def set_pcap(uwatts, force=false)
355   # Potentially potentially damaging operation, must supply force param
356   if !force then
357     return unsafe_warning('set_pcap')
358   else
359     # Make sure user is root
360     if RadeonNoise.root then
361       # Writes the minimum value, as the minimum is either below the maximum
362       # power cap or the maximum power cap itself. Absolute value
363       # is applied, so that a negative cannot be entered
364       uwatts.to_i.then { |w|
365         File.write("#{@cache[:dir]}/#{@config[:pcap]}", [w, @cache[:power_max]].min.abs, mode: "r+")}
366     end
367   end
368 end
set_protection(val) click to toggle source

Set the level of unsafe protection

    # File lib/radeonnoise/core.rb
338     def set_protection(val)
339       if RadeonNoise.root then
340         if PROTECTLVL.include?(val) then
341           @config[:protection] = val
342         else
343           ("+" * 20).then { |sep| <<~ERR
344             #{sep}\n'#{val}' is not a valid protection setting
345             Valid options are: #{PROTECTLVL}
346             No change\n#{sep}
347           ERR
348             }.then { |e| puts e }
349         end
350       end
351     end
set_pwm(input) click to toggle source

PWM speed controller ONLY USABLE AS ROOT

Take either a string or integer and write it to the PWM speed file. The value must be less than :pwm_max, so before writing, take the min of the max and input values.

If the value is an int already, take the absolute value, to ensure only a positive (or zero) number is entered. If it is a string, match it to an int, automatically removing ANY non-digits (including negatives).

    # File lib/radeonnoise/core.rb
416 def set_pwm(input)
417   if RadeonNoise.root then
418     [@cache[:dir], @cache[:pwm_max]].then { |d, mx| 
419       if Integer === input then # If integer, write directly
420         File.write("#{d}/pwm1", [input.abs, mx].min, mode: "r+")
421       elsif String === input and input.match?(/^\d+$/) then
422         input.to_i.then { |data| # If string, convert to int
423           File.write("#{d}/pwm1", [data, mx].min, mode: "r+") }
424       else # Otherwise, error
425         puts "#{input} is not valid. Please enter a positive integer up to #{mx}"
426       end }
427   end
428 end
set_pwmt(val) click to toggle source

Set the PWM type ONLY USABLE AS ROOT

Takes a user-entered value to choose the PWM mode setting – no control, manual control, or automatic control.

Use `pwmtype` to check validity. If invalid, just print an error. Otherwise, if the value is returned as a symbol, pass it back to produce and integer to write to the file. Otherwise, write the returned integer directly

    # File lib/radeonnoise/core.rb
393 def set_pwmt(val)
394   if RadeonNoise.root then
395     pwmtype(val).then { |v| # If the pwmtype output is valid
396       if Integer === v or Symbol === v then
397         (Integer === v ? v : pwmtype(v)).then { |data| # Only use Integers
398           File.write("#{@cache[:dir]}/pwm1_enable", data, mode: "r+") }
399       else # If the pwmtype output is any kind of error message
400         puts "#{v}"
401       end }
402   end
403 end
stat() click to toggle source

Stat the current cached data

    # File lib/radeonnoise/core.rb
149 def stat() @cache end
subsystem() click to toggle source

Attach the `lspci` subsystem data Essentially, this is the most useful data that is really relevant from the command, at least for the purpose of this interface. I may add more, though.

    # File lib/radeonnoise/core.rb
164 def subsystem
165   # There should only be one of these per card, as they
166   # are matched by bus ID. As such, only the first is
167   # selected.
168   RadeonNoise.pci.reject { |item|
169     item[:slot] != @config[:detail][:pci_slot_name]}
170     .first.then { |item| {subsystem: item} }
171 end
temps() click to toggle source

Temperatures

    # File lib/radeonnoise/core.rb
261 def temps
262   [@config[:rg_temps], @cache[:dir]].then { |rg, d| 
263     {temps: Component::Temp.new(rg, "#{d}/*")} }
264 end
udevice() click to toggle source

Update core device stats

    # File lib/radeonnoise/core.rb
187 def udevice
188   @cache[:dir].then { |d| {
189     level: File.read("#{d}/#{@config[:dpm_level]}").strip,
190     busy_proc: File.read("#{d}/device/gpu_busy_percent").to_f,
191     busy_mem: File.read("#{d}/device/mem_busy_percent").to_f,
192   }}
193 end
ufans() click to toggle source

Update the fan data

    # File lib/radeonnoise/core.rb
276 def ufans() @cache[:fans].update end
ufreqs() click to toggle source

Frequencies sclk is the processor clock mclk is the VRAM clock

    # File lib/radeonnoise/core.rb
232 def ufreqs
233   @cache[:dir].then { |d| {
234     freq_core: active_s(File.read("#{d}/device/pp_dpm_sclk"))
235       .collect { |item| item.to_f },
236     freq_vram: active_s(File.read("#{d}/device/pp_dpm_mclk"))
237       .collect { |item| item.to_f },
238   }}
239 end
unsafe_warning(fn) click to toggle source

Warn the user about unsafe operations

    # File lib/radeonnoise/core.rb
286     def unsafe_warning(fn)
287       ("+" * 20).then { |sep| <<~MSG
288         #{sep}\n'#{fn}' is a potentially damaging operation, and you must supply
289         the parameter 'force=true' to allow it to complete. This function
290         may cause graphics card crashes or damage to components, if used
291         incorrectly. During erroneous usage, data loss/corruption, due to a system
292         crash might be considered 'optimistic'.\n#{sep}
293         MSG
294         }.then { |msg| puts msg }
295       false
296     end
upcie() click to toggle source

PCIe speed Although this is a

    # File lib/radeonnoise/core.rb
243 def upcie
244   active_s(File.read("#{@cache[:dir]}/device/pp_dpm_pcie"))
245   .collect do |item| 
246     item.split(",").then { |tf, mul|
247       {multiplier: mul.gsub(/\W/, ''), tfspeed: tf} }
248   end
249 end
update() click to toggle source

Update the card values

    # File lib/radeonnoise/core.rb
174 def update
175   @cache.update(udevice)
176     .update({slot: upcie})
177     .update(upwm)
178     .update(ufreqs)
179     .update(uvolts)
180     .update(upower)
181     .then { utemps }
182     .then { ufans }
183     .then { @cache }
184 end
upower() click to toggle source

Power consumption This monitors the current and maximum possible power usage of the card. 'pcap', however, can be lower than the card's physical limit, if set as such

    # File lib/radeonnoise/core.rb
222 def upower
223   @cache[:dir].then { |d| {
224     power_cap: File.read("#{d}/#{@config[:pcap]}").strip,
225     power_usage: File.read("#{d}/power1_average").strip,
226   }}
227 end
upwm() click to toggle source

PWM (fan power)

    # File lib/radeonnoise/core.rb
196 def upwm
197   @cache[:dir].then { |d| {
198     pwm_control: pwmtype(File.read("#{d}/pwm1_enable").to_i),
199     pwm_max: File.read("#{d}/pwm1_max").to_i,
200     pwm_current: File.read("#{d}/pwm1").to_i,
201   }}
202 end
utemps() click to toggle source

Update the temperatures

    # File lib/radeonnoise/core.rb
267 def utemps() @cache[:temps].update end
uvolts() click to toggle source

Voltage The inputs were re-classified from a previous commit: they have a specific meaning and are not actually variable. in0 is the graphics processor, while in1 is the northbridge, if a sensor is present.

As such, will update with a value or empty.

    # File lib/radeonnoise/core.rb
211 def uvolts
212   [@cache[:dir], "in0_input", "in1_input"].then { |d,c,n| {
213     volt_core: File.read("#{d}/#{c}").to_f,
214     volt_northbridge: File.exist?("#{d}/#{n}") ? File.read("#{d}/#{n}").to_f : nil,
215   }}
216 end