nvml_wrapper/struct_wrappers/
device.rs

1use crate::bitmasks::device::FbcFlags;
2use crate::enum_wrappers::device::{
3    BridgeChip, Clock, EncoderType, FbcSessionType, PerformanceState, SampleValueType,
4};
5use crate::enums::device::{FirmwareVersion, SampleValue, UsedGpuMemory};
6use crate::error::{nvml_try, Bits, NvmlError};
7use crate::ffi::bindings::*;
8use crate::structs::device::FieldId;
9#[cfg(feature = "serde")]
10use serde_derive::{Deserialize, Serialize};
11use std::{
12    cmp::Ordering,
13    ffi::{CStr, CString},
14};
15use std::{
16    convert::{TryFrom, TryInto},
17    os::raw::c_char,
18};
19
20/// PCI information about a GPU device.
21// Checked against local
22// Tested
23#[derive(Debug, Clone, Eq, PartialEq, Hash)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct PciInfo {
26    /// The bus on which the device resides, 0 to 0xff.
27    pub bus: u32,
28    /// The PCI identifier.
29    pub bus_id: String,
30    /// The device's ID on the bus, 0 to 31.
31    pub device: u32,
32    /// The PCI domain on which the device's bus resides, 0 to 0xffff.
33    pub domain: u32,
34    /// The combined 16-bit device ID and 16-bit vendor ID.
35    pub pci_device_id: u32,
36    /**
37    The 32-bit Sub System Device ID.
38
39    Will always be `None` if this `PciInfo` was obtained from `NvLink.remote_pci_info()`.
40    NVIDIA says that the C field that this corresponds to "is not filled ... and
41    is indeterminate" when being returned from that specific call.
42
43    Will be `Some` in all other cases.
44    */
45    pub pci_sub_system_id: Option<u32>,
46}
47
48impl PciInfo {
49    /**
50    Try to create this struct from its C equivalent.
51
52    Passing `false` for `sub_sys_id_present` will set the `pci_sub_system_id`
53    field to `None`. See the field docs for more.
54
55    # Errors
56
57    * `Utf8Error`, if the string obtained from the C function is not valid Utf8
58    */
59    pub fn try_from(struct_: nvmlPciInfo_t, sub_sys_id_present: bool) -> Result<Self, NvmlError> {
60        unsafe {
61            let bus_id_raw = CStr::from_ptr(struct_.busId.as_ptr());
62
63            Ok(Self {
64                bus: struct_.bus,
65                bus_id: bus_id_raw.to_str()?.into(),
66                device: struct_.device,
67                domain: struct_.domain,
68                pci_device_id: struct_.pciDeviceId,
69                pci_sub_system_id: if sub_sys_id_present {
70                    Some(struct_.pciSubSystemId)
71                } else {
72                    None
73                },
74            })
75        }
76    }
77}
78
79impl TryInto<nvmlPciInfo_t> for PciInfo {
80    type Error = NvmlError;
81
82    /**
83    Convert this `PciInfo` back into its C equivalent.
84
85    # Errors
86
87    * `NulError`, if a nul byte was found in the bus_id (shouldn't occur?)
88    * `StringTooLong`, if `bus_id.len()` exceeded the length of
89    `NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE`. This should (?) only be able to
90    occur if the user modifies `bus_id` in some fashion. We return an error
91    rather than panicking.
92    */
93    fn try_into(self) -> Result<nvmlPciInfo_t, Self::Error> {
94        // This is more readable than spraying `buf_size as usize` everywhere
95        const fn buf_size() -> usize {
96            NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE as usize
97        }
98
99        let mut bus_id_c: [c_char; buf_size()] = [0; buf_size()];
100        let mut bus_id = CString::new(self.bus_id)?.into_bytes_with_nul();
101
102        // Make the string the same length as the array we need to clone it to
103        match bus_id.len().cmp(&buf_size()) {
104            Ordering::Less => {
105                while bus_id.len() != buf_size() {
106                    bus_id.push(0);
107                }
108            }
109            Ordering::Equal => {
110                // No need to do anything; the buffers are already the same length
111            }
112            Ordering::Greater => {
113                return Err(NvmlError::StringTooLong {
114                    max_len: buf_size(),
115                    actual_len: bus_id.len(),
116                })
117            }
118        }
119
120        bus_id_c.clone_from_slice(&bus_id.into_iter().map(|b| b as c_char).collect::<Vec<_>>());
121
122        Ok(nvmlPciInfo_t {
123            busIdLegacy: [0; NVML_DEVICE_PCI_BUS_ID_BUFFER_V2_SIZE as usize],
124            domain: self.domain,
125            bus: self.bus,
126            device: self.device,
127            pciDeviceId: self.pci_device_id,
128            // This seems the most correct thing to do? Since this should only
129            // be none if obtained from `NvLink.remote_pci_info()`.
130            pciSubSystemId: self.pci_sub_system_id.unwrap_or(0),
131            busId: bus_id_c,
132        })
133    }
134}
135
136/// BAR1 memory allocation information for a device (in bytes)
137// Checked against local
138#[derive(Debug, Clone, Eq, PartialEq, Hash)]
139#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140pub struct BAR1MemoryInfo {
141    /// Unallocated
142    pub free: u64,
143    /// Total memory
144    pub total: u64,
145    /// Allocated
146    pub used: u64,
147}
148
149impl From<nvmlBAR1Memory_t> for BAR1MemoryInfo {
150    fn from(struct_: nvmlBAR1Memory_t) -> Self {
151        Self {
152            free: struct_.bar1Free,
153            total: struct_.bar1Total,
154            used: struct_.bar1Used,
155        }
156    }
157}
158
159/// Information about a bridge chip.
160// Checked against local
161#[derive(Debug, Clone, Eq, PartialEq, Hash)]
162#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
163pub struct BridgeChipInfo {
164    pub fw_version: FirmwareVersion,
165    pub chip_type: BridgeChip,
166}
167
168impl TryFrom<nvmlBridgeChipInfo_t> for BridgeChipInfo {
169    type Error = NvmlError;
170
171    /**
172    Construct `BridgeChipInfo` from the corresponding C struct.
173
174    # Errors
175
176    * `UnexpectedVariant`, for which you can read the docs for
177    */
178    fn try_from(value: nvmlBridgeChipInfo_t) -> Result<Self, Self::Error> {
179        let fw_version = FirmwareVersion::from(value.fwVersion);
180        let chip_type = BridgeChip::try_from(value.type_)?;
181
182        Ok(Self {
183            fw_version,
184            chip_type,
185        })
186    }
187}
188
189/**
190This struct stores the complete hierarchy of the bridge chip within the board.
191
192The immediate bridge is stored at index 0 of `chips_hierarchy`. The parent to
193the immediate bridge is at index 1, and so forth.
194*/
195// Checked against local
196#[derive(Debug, Clone, Eq, PartialEq, Hash)]
197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
198pub struct BridgeChipHierarchy {
199    /// Hierarchy of bridge chips on the board.
200    pub chips_hierarchy: Vec<BridgeChipInfo>,
201    /// Number of bridge chips on the board.
202    pub chip_count: u8,
203}
204
205impl TryFrom<nvmlBridgeChipHierarchy_t> for BridgeChipHierarchy {
206    type Error = NvmlError;
207
208    /**
209    Construct `BridgeChipHierarchy` from the corresponding C struct.
210
211    # Errors
212
213    * `UnexpectedVariant`, for which you can read the docs for
214    */
215    fn try_from(value: nvmlBridgeChipHierarchy_t) -> Result<Self, Self::Error> {
216        let chips_hierarchy = value
217            .bridgeChipInfo
218            .iter()
219            .map(|bci| BridgeChipInfo::try_from(*bci))
220            .collect::<Result<_, NvmlError>>()?;
221
222        Ok(Self {
223            chips_hierarchy,
224            chip_count: value.bridgeCount,
225        })
226    }
227}
228
229/// Information about compute processes running on the GPU.
230// Checked against local
231#[derive(Debug, Clone, Eq, PartialEq, Hash)]
232#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233pub struct ProcessInfo {
234    // Process ID.
235    pub pid: u32,
236    /// Amount of used GPU memory in bytes.
237    pub used_gpu_memory: UsedGpuMemory,
238    /// The ID of the GPU instance this process is running on, if applicable.
239    ///
240    /// MIG (Multi-Instance GPU) must be enabled on the device for this field
241    /// to be set.
242    pub gpu_instance_id: Option<u32>,
243    /// The ID of the compute instance this process is running on, if applicable.
244    ///
245    /// MIG (Multi-Instance GPU) must be enabled on the device for this field
246    /// to be set.
247    pub compute_instance_id: Option<u32>,
248}
249
250impl From<nvmlProcessInfo_t> for ProcessInfo {
251    fn from(struct_: nvmlProcessInfo_t) -> Self {
252        const NO_VALUE: u32 = 0xFFFFFFFF;
253
254        let gpu_instance_id = Some(struct_.gpuInstanceId).filter(|id| *id != NO_VALUE);
255        let compute_instance_id = Some(struct_.computeInstanceId).filter(|id| *id != NO_VALUE);
256
257        Self {
258            pid: struct_.pid,
259            used_gpu_memory: UsedGpuMemory::from(struct_.usedGpuMemory),
260            gpu_instance_id,
261            compute_instance_id,
262        }
263    }
264}
265
266/// Detailed ECC error counts for a device.
267// Checked against local
268#[derive(Debug, Clone, Eq, PartialEq, Hash)]
269#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
270pub struct EccErrorCounts {
271    pub device_memory: u64,
272    pub l1_cache: u64,
273    pub l2_cache: u64,
274    pub register_file: u64,
275}
276
277impl From<nvmlEccErrorCounts_t> for EccErrorCounts {
278    fn from(struct_: nvmlEccErrorCounts_t) -> Self {
279        Self {
280            device_memory: struct_.deviceMemory,
281            l1_cache: struct_.l1Cache,
282            l2_cache: struct_.l2Cache,
283            register_file: struct_.registerFile,
284        }
285    }
286}
287
288/// Memory allocation information for a device (in bytes).
289// Checked against local
290#[derive(Debug, Clone, Eq, PartialEq, Hash)]
291#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
292pub struct MemoryInfo {
293    /// Unallocated FB memory.
294    pub free: u64,
295
296    /// Reserved FB memory.
297    pub reserved: u64,
298
299    /// Total installed FB memory.
300    pub total: u64,
301    /// Allocated FB memory.
302    ///
303    /// Note that the driver/GPU always sets aside a small amount of memory for
304    /// bookkeeping.
305    pub used: u64,
306
307    /// Struct version, must be set according to API specification before calling the API.
308    pub version: u32,
309}
310
311impl From<nvmlMemory_v2_t> for MemoryInfo {
312    fn from(struct_: nvmlMemory_v2_t) -> Self {
313        Self {
314            free: struct_.free,
315            reserved: struct_.reserved,
316            total: struct_.total,
317            used: struct_.used,
318            version: struct_.version,
319        }
320    }
321}
322
323/// Utilization information for a device. Each sample period may be between 1
324/// second and 1/6 second, depending on the product being queried.
325// Checked against local
326#[derive(Debug, Clone, Eq, PartialEq, Hash)]
327#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
328pub struct Utilization {
329    /// Percent of time over the past sample period during which one or more
330    /// kernels was executing on the GPU.
331    pub gpu: u32,
332    /// Percent of time over the past sample period during which global (device)
333    /// memory was being read or written to.
334    pub memory: u32,
335}
336
337impl From<nvmlUtilization_t> for Utilization {
338    fn from(struct_: nvmlUtilization_t) -> Self {
339        Self {
340            gpu: struct_.gpu,
341            memory: struct_.memory,
342        }
343    }
344}
345
346/// Performance policy violation status data.
347// Checked against local
348#[derive(Debug, Clone, Eq, PartialEq, Hash)]
349#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
350pub struct ViolationTime {
351    /// Represents CPU timestamp in microseconds.
352    pub reference_time: u64,
353    /// Violation time in nanoseconds.
354    pub violation_time: u64,
355}
356
357impl From<nvmlViolationTime_t> for ViolationTime {
358    fn from(struct_: nvmlViolationTime_t) -> Self {
359        Self {
360            reference_time: struct_.referenceTime,
361            violation_time: struct_.violationTime,
362        }
363    }
364}
365
366/**
367Accounting statistics for a process.
368
369There is a field: `unsigned int reserved[5]` present on the C struct that this wraps
370that NVIDIA says is "reserved for future use." If it ever gets used in the future,
371an equivalent wrapping field will have to be added to this struct.
372*/
373// Checked against local
374#[derive(Debug, Clone, Eq, PartialEq, Hash)]
375#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
376pub struct AccountingStats {
377    /**
378    Percent of time over the process's lifetime during which one or more kernels was
379    executing on the GPU. This is just like what is returned by
380    `Device.utilization_rates()` except it is for the lifetime of a process (not just
381    the last sample period).
382
383    It will be `None` if `Device.utilization_rates()` is not supported.
384    */
385    pub gpu_utilization: Option<u32>,
386    /// Whether the process is running.
387    pub is_running: bool,
388    /// Max total memory in bytes that was ever allocated by the process.
389    ///
390    /// It will be `None` if `ProcessInfo.used_gpu_memory` is not supported.
391    pub max_memory_usage: Option<u64>,
392    /**
393    Percent of time over the process's lifetime during which global (device) memory
394    was being read from or written to.
395
396    It will be `None` if `Device.utilization_rates()` is not supported.
397    */
398    pub memory_utilization: Option<u32>,
399    /// CPU timestamp in usec representing the start time for the process.
400    pub start_time: u64,
401    /// Amount of time in ms during which the compute context was active. This
402    /// will be zero if the process is not terminated.
403    pub time: u64,
404}
405
406impl From<nvmlAccountingStats_t> for AccountingStats {
407    fn from(struct_: nvmlAccountingStats_t) -> Self {
408        let not_avail_u64 = (NVML_VALUE_NOT_AVAILABLE) as u64;
409        let not_avail_u32 = (NVML_VALUE_NOT_AVAILABLE) as u32;
410
411        #[allow(clippy::match_like_matches_macro)]
412        Self {
413            gpu_utilization: match struct_.gpuUtilization {
414                v if v == not_avail_u32 => None,
415                _ => Some(struct_.gpuUtilization),
416            },
417            is_running: match struct_.isRunning {
418                0 => false,
419                // NVIDIA only says 1 is for running, but I don't think anything
420                // else warrants an error (or a panic), so
421                _ => true,
422            },
423            max_memory_usage: match struct_.maxMemoryUsage {
424                v if v == not_avail_u64 => None,
425                _ => Some(struct_.maxMemoryUsage),
426            },
427            memory_utilization: match struct_.memoryUtilization {
428                v if v == not_avail_u32 => None,
429                _ => Some(struct_.memoryUtilization),
430            },
431            start_time: struct_.startTime,
432            time: struct_.time,
433        }
434    }
435}
436
437/// Holds encoder session information.
438#[derive(Debug, Clone, Eq, PartialEq, Hash)]
439#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
440pub struct EncoderSessionInfo {
441    /// Unique ID for this session.
442    pub session_id: u32,
443    /// The ID of the process that owns this session.
444    pub pid: u32,
445    /// The ID of the vGPU instance that owns this session (if applicable).
446    // TODO: Stronger typing if vgpu stuff gets wrapped
447    pub vgpu_instance: Option<u32>,
448    pub codec_type: EncoderType,
449    /// Current horizontal encoding resolution.
450    pub hres: u32,
451    /// Current vertical encoding resolution.
452    pub vres: u32,
453    /// Moving average encode frames per second.
454    pub average_fps: u32,
455    /// Moving average encode latency in μs.
456    pub average_latency: u32,
457}
458
459impl TryFrom<nvmlEncoderSessionInfo_t> for EncoderSessionInfo {
460    type Error = NvmlError;
461
462    /**
463    Construct `EncoderSessionInfo` from the corresponding C struct.
464
465    # Errors
466
467    * `UnexpectedVariant`, for which you can read the docs for
468    */
469    fn try_from(value: nvmlEncoderSessionInfo_t) -> Result<Self, Self::Error> {
470        Ok(Self {
471            session_id: value.sessionId,
472            pid: value.pid,
473            vgpu_instance: match value.vgpuInstance {
474                0 => None,
475                other => Some(other),
476            },
477            codec_type: EncoderType::try_from(value.codecType)?,
478            hres: value.hResolution,
479            vres: value.vResolution,
480            average_fps: value.averageFps,
481            average_latency: value.averageLatency,
482        })
483    }
484}
485
486/// Sample info.
487// Checked against local
488#[derive(Debug, Clone, PartialEq)]
489#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
490pub struct Sample {
491    /// CPU timestamp in μs
492    pub timestamp: u64,
493    pub value: SampleValue,
494}
495
496impl Sample {
497    /// Given a tag and an untagged union, returns a Rust enum with the correct
498    /// union variant.
499    pub fn from_tag_and_struct(tag: &SampleValueType, struct_: nvmlSample_t) -> Self {
500        Self {
501            timestamp: struct_.timeStamp,
502            value: SampleValue::from_tag_and_union(tag, struct_.sampleValue),
503        }
504    }
505}
506
507#[derive(Debug, Clone, Eq, PartialEq, Hash)]
508#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
509pub struct ProcessUtilizationSample {
510    pub pid: u32,
511    /// CPU timestamp in μs
512    pub timestamp: u64,
513    /// SM (3D / compute) utilization
514    pub sm_util: u32,
515    /// Frame buffer memory utilization
516    pub mem_util: u32,
517    /// Encoder utilization
518    pub enc_util: u32,
519    /// Decoder utilization
520    pub dec_util: u32,
521}
522
523impl From<nvmlProcessUtilizationSample_t> for ProcessUtilizationSample {
524    fn from(struct_: nvmlProcessUtilizationSample_t) -> Self {
525        Self {
526            pid: struct_.pid,
527            timestamp: struct_.timeStamp,
528            sm_util: struct_.smUtil,
529            mem_util: struct_.memUtil,
530            enc_util: struct_.encUtil,
531            dec_util: struct_.decUtil,
532        }
533    }
534}
535
536/// Struct that stores information returned from `Device.field_values_for()`.
537// TODO: Missing a lot of derives because of the `Result`
538#[derive(Debug)]
539pub struct FieldValueSample {
540    /// The field that this sample is for.
541    pub field: FieldId,
542    /// This sample's CPU timestamp in μs (Unix time).
543    pub timestamp: i64,
544    /**
545    How long this field value took to update within NVML, in μs.
546
547    This value may be averaged across several fields serviced by the same
548    driver call.
549    */
550    pub latency: i64,
551    /// The value of this sample.
552    ///
553    /// Will be an error if retrieving this specific value failed.
554    pub value: Result<SampleValue, NvmlError>,
555}
556
557impl TryFrom<nvmlFieldValue_t> for FieldValueSample {
558    type Error = NvmlError;
559
560    /**
561    Construct `FieldValueSample` from the corresponding C struct.
562
563    # Errors
564
565    * `UnexpectedVariant`, for which you can read the docs for
566    */
567    fn try_from(value: nvmlFieldValue_t) -> Result<Self, Self::Error> {
568        Ok(Self {
569            field: FieldId(value.fieldId),
570            timestamp: value.timestamp,
571            latency: value.latencyUsec,
572            value: match nvml_try(value.nvmlReturn) {
573                Ok(_) => Ok(SampleValue::from_tag_and_union(
574                    &SampleValueType::try_from(value.valueType)?,
575                    value.value,
576                )),
577                Err(e) => Err(e),
578            },
579        })
580    }
581}
582
583/// Holds global frame buffer capture session statistics.
584#[derive(Debug, Clone, Eq, PartialEq)]
585#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
586pub struct FbcStats {
587    /// The total number of sessions
588    pub sessions_count: u32,
589    /// Moving average of new frames captured per second for all capture sessions
590    pub average_fps: u32,
591    /// Moving average of new frame capture latency in microseconds for all capture sessions
592    pub average_latency: u32,
593}
594
595impl From<nvmlFBCStats_t> for FbcStats {
596    fn from(struct_: nvmlFBCStats_t) -> Self {
597        Self {
598            sessions_count: struct_.sessionsCount,
599            average_fps: struct_.averageFPS,
600            average_latency: struct_.averageLatency,
601        }
602    }
603}
604
605/// Information about a frame buffer capture session.
606#[derive(Debug, Clone, Eq, PartialEq)]
607#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
608pub struct FbcSessionInfo {
609    /// Unique session ID
610    pub session_id: u32,
611    /// The ID of the process that owns this session
612    pub pid: u32,
613    /// The ID of the vGPU instance that owns this session (if applicable).
614    // TODO: Stronger typing if vgpu stuff gets wrapped
615    pub vgpu_instance: Option<u32>,
616    /// The identifier of the display this session is running on
617    pub display_ordinal: u32,
618    /// The type of this session
619    pub session_type: FbcSessionType,
620    /// Various flags with info
621    pub session_flags: FbcFlags,
622    /// The maximum horizontal resolution supported by this session
623    pub hres_max: u32,
624    /// The maximum vertical resolution supported by this session
625    pub vres_max: u32,
626    /// The horizontal resolution requested by the caller in the capture call
627    pub hres: u32,
628    /// The vertical resolution requested by the caller in the capture call
629    pub vres: u32,
630    /// Moving average of new frames captured per second for this session
631    pub average_fps: u32,
632    /// Moving average of new frame capture latency in microseconds for this session
633    pub average_latency: u32,
634}
635
636impl TryFrom<nvmlFBCSessionInfo_t> for FbcSessionInfo {
637    type Error = NvmlError;
638
639    /**
640    Construct `FbcSessionInfo` from the corresponding C struct.
641
642    # Errors
643
644    * `UnexpectedVariant`, for which you can read the docs for
645    * `IncorrectBits`, if the `sessionFlags` from the given struct do match the
646    wrapper definition
647    */
648    fn try_from(value: nvmlFBCSessionInfo_t) -> Result<Self, Self::Error> {
649        Ok(Self {
650            session_id: value.sessionId,
651            pid: value.pid,
652            vgpu_instance: match value.vgpuInstance {
653                0 => None,
654                other => Some(other),
655            },
656            display_ordinal: value.displayOrdinal,
657            session_type: FbcSessionType::try_from(value.sessionType)?,
658            session_flags: FbcFlags::from_bits(value.sessionFlags)
659                .ok_or(NvmlError::IncorrectBits(Bits::U32(value.sessionFlags)))?,
660            hres_max: value.hMaxResolution,
661            vres_max: value.vMaxResolution,
662            hres: value.hResolution,
663            vres: value.vResolution,
664            average_fps: value.averageFPS,
665            average_latency: value.averageLatency,
666        })
667    }
668}
669
670/// Hardware level attributes from a GPU device
671#[derive(Debug, Clone, Eq, PartialEq, Hash)]
672#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
673pub struct DeviceAttributes {
674    /// Streaming MultiProcessor Count
675    pub multiprocessor_count: u32,
676    /// Shared Copy Engine Count
677    pub shared_copy_engine_count: u32,
678    /// Shared Decoder Count
679    pub shared_decoder_count: u32,
680    /// Shared Encoder Count
681    pub shared_encoder_count: u32,
682    /// Shared JPEG Count
683    pub shared_jpeg_count: u32,
684    /// Shared OFA Count
685    pub shared_ofa_count: u32,
686    /// GPU instance slice Count
687    pub gpu_instance_slice_count: u32,
688    /// Compute Instance slice count
689    pub compute_instance_slice_count: u32,
690    /// Device memory size in MB
691    pub memory_size_mb: u64,
692}
693
694impl From<nvmlDeviceAttributes_t> for DeviceAttributes {
695    fn from(struct_: nvmlDeviceAttributes_t) -> Self {
696        Self {
697            multiprocessor_count: struct_.multiprocessorCount,
698            shared_copy_engine_count: struct_.sharedCopyEngineCount,
699            shared_decoder_count: struct_.sharedDecoderCount,
700            shared_encoder_count: struct_.sharedEncoderCount,
701            shared_jpeg_count: struct_.sharedJpegCount,
702            shared_ofa_count: struct_.sharedOfaCount,
703            gpu_instance_slice_count: struct_.gpuInstanceSliceCount,
704            compute_instance_slice_count: struct_.computeInstanceSliceCount,
705            memory_size_mb: struct_.memorySizeMB,
706        }
707    }
708}
709
710/// Fan speed info
711#[derive(Debug, Clone, Eq, PartialEq, Hash)]
712#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
713pub struct FanSpeedInfo {
714    /// The API version number
715    pub version: u32,
716    /// The fan index
717    pub fan: u32,
718    /// OUT: the fan speed in RPM.
719    pub speed: u32,
720}
721
722impl From<nvmlFanSpeedInfo_t> for FanSpeedInfo {
723    fn from(struct_: nvmlFanSpeedInfo_t) -> Self {
724        Self {
725            version: struct_.version,
726            fan: struct_.fan,
727            speed: struct_.speed,
728        }
729    }
730}
731
732/// Clock offset info.
733#[derive(Debug, Clone, Eq, PartialEq)]
734#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
735pub struct ClockOffset {
736    /// The API version number
737    pub version: u32,
738    pub clock_type: Clock,
739    pub state: PerformanceState,
740    pub clock_offset_mhz: i32,
741    pub min_clock_offset_mhz: i32,
742    pub max_clock_offset_mhz: i32,
743}
744
745impl TryFrom<nvmlClockOffset_v1_t> for ClockOffset {
746    type Error = NvmlError;
747
748    fn try_from(value: nvmlClockOffset_v1_t) -> Result<Self, Self::Error> {
749        Ok(Self {
750            version: value.version,
751            clock_type: Clock::try_from(value.type_)?,
752            state: PerformanceState::try_from(value.pstate)?,
753            clock_offset_mhz: value.clockOffsetMHz,
754            min_clock_offset_mhz: value.minClockOffsetMHz,
755            max_clock_offset_mhz: value.maxClockOffsetMHz,
756        })
757    }
758}
759
760#[cfg(test)]
761#[allow(unused_variables, unused_imports)]
762mod tests {
763    use crate::error::*;
764    use crate::ffi::bindings::*;
765    use crate::test_utils::*;
766    use std::convert::TryInto;
767    use std::mem;
768
769    #[test]
770    fn pci_info_from_to_c() {
771        let nvml = nvml();
772        test_with_device(3, &nvml, |device| {
773            let converted: nvmlPciInfo_t = device
774                .pci_info()
775                .expect("wrapped pci info")
776                .try_into()
777                .expect("converted c pci info");
778
779            let sym = nvml_sym(nvml.lib.nvmlDeviceGetPciInfo_v3.as_ref())?;
780
781            let raw = unsafe {
782                let mut pci_info: nvmlPciInfo_t = mem::zeroed();
783                nvml_try(sym(device.handle(), &mut pci_info)).expect("raw pci info");
784                pci_info
785            };
786
787            assert_eq!(converted.busId, raw.busId);
788            assert_eq!(converted.domain, raw.domain);
789            assert_eq!(converted.bus, raw.bus);
790            assert_eq!(converted.device, raw.device);
791            assert_eq!(converted.pciDeviceId, raw.pciDeviceId);
792            assert_eq!(converted.pciSubSystemId, raw.pciSubSystemId);
793
794            Ok(())
795        })
796    }
797}