Skip to main content

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/// Profile info.
761#[derive(Debug, Clone, Eq, PartialEq)]
762#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
763pub struct ProfileInfo {
764    pub copy_engine_count: u32,
765    pub decoder_count: u32,
766    pub encoder_count: u32,
767    pub id: u32,
768    pub instance_count: u32,
769    pub is_p2p_supported: bool,
770    pub jpeg_count: u32,
771    pub memory_size_mb: u64,
772    pub multiprocessor_count: u32,
773    pub ofa_count: u32,
774    pub slice_count: u32,
775}
776
777impl From<nvmlGpuInstanceProfileInfo_t> for ProfileInfo {
778    fn from(struct_: nvmlGpuInstanceProfileInfo_t) -> Self {
779        Self {
780            copy_engine_count: struct_.copyEngineCount,
781            decoder_count: struct_.decoderCount,
782            encoder_count: struct_.encoderCount,
783            id: struct_.id,
784            instance_count: struct_.instanceCount,
785            is_p2p_supported: struct_.isP2pSupported > 0,
786            jpeg_count: struct_.jpegCount,
787            memory_size_mb: struct_.memorySizeMB,
788            multiprocessor_count: struct_.multiprocessorCount,
789            ofa_count: struct_.ofaCount,
790            slice_count: struct_.sliceCount,
791        }
792    }
793}
794
795/// MIG profile placements
796#[derive(Debug, Clone, Eq, PartialEq)]
797#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
798pub struct GpuInstancePlacement {
799    /// Memory slices occupied
800    pub size: u32,
801    /// Index of first occupied memory slice (inclusive)
802    pub start: u32,
803}
804
805impl From<nvmlGpuInstancePlacement_t> for GpuInstancePlacement {
806    fn from(value: nvmlGpuInstancePlacement_t) -> Self {
807        Self {
808            size: value.size,
809            start: value.start,
810        }
811    }
812}
813
814// Vgpu
815/// Vgpu scheduler capabilities
816#[derive(Debug, Clone, Eq, PartialEq)]
817#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
818pub struct VgpuSchedulerCapabilities {
819    // Adaptative Round Robin mode on/off
820    pub is_arr_mode_supported: bool,
821    // Maximum averaging factor for Adaptative Round Robin mode
822    pub max_avg_factor_for_arr: u32,
823    // Maximum frequency for Adaptative Round Robin mode
824    pub max_freq_for_arr: u32,
825    // Maximum timeslice value in ns
826    pub max_time_slice: u32,
827    // Minimum averaging factor for Adaptative Round Robin mode
828    pub min_avg_factor_for_arr: u32,
829    // Minimum frequency for Adaptative Round Robin mode
830    pub min_freq_for_arr: u32,
831    // Minimum timeslice value in ns
832    pub min_time_slice: u32,
833    // List of supported scheduler
834    pub supported_schedulers: Vec<u32>,
835}
836
837impl From<nvmlVgpuSchedulerCapabilities_t> for VgpuSchedulerCapabilities {
838    fn from(value: nvmlVgpuSchedulerCapabilities_t) -> Self {
839        let supported_schedulers = value.supportedSchedulers.to_vec();
840        Self {
841            is_arr_mode_supported: value.isArrModeSupported > 0,
842            max_avg_factor_for_arr: value.maxAvgFactorForARR,
843            max_freq_for_arr: value.maxFrequencyForARR,
844            max_time_slice: value.maxTimeslice,
845            min_avg_factor_for_arr: value.minAvgFactorForARR,
846            min_freq_for_arr: value.minFrequencyForARR,
847            min_time_slice: value.minTimeslice,
848            supported_schedulers,
849        }
850    }
851}
852
853/// Vgpu versions range
854#[derive(Debug, Clone, Eq, PartialEq)]
855#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
856pub struct VgpuVersion {
857    /// Minimum vGPU version
858    pub min: u32,
859    /// Maximum vGPU version
860    pub max: u32,
861}
862
863impl From<nvmlVgpuVersion_t> for VgpuVersion {
864    fn from(value: nvmlVgpuVersion_t) -> Self {
865        Self {
866            min: value.minVersion,
867            max: value.maxVersion,
868        }
869    }
870}
871
872impl VgpuVersion {
873    pub fn as_c(&self) -> nvmlVgpuVersion_t {
874        nvmlVgpuVersion_t {
875            minVersion: self.min,
876            maxVersion: self.max,
877        }
878    }
879}
880
881/// Vgpu scheduler Params
882#[derive(Debug, Clone, Eq, PartialEq)]
883#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
884pub struct VgpuSchedulerParams {
885    pub avg_factor: Option<u32>,
886    pub timeslice: u32,
887}
888
889/// Vgpu scheduler Log entry
890#[derive(Debug, Clone, Eq, PartialEq)]
891#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
892pub struct VgpuSchedulerLogEntry {
893    /// Timesteamp when the software runlist was preemopted (in ns)
894    pub timestamp: u64,
895    /// Total time this runlist has run (in ns)
896    pub time_run_total: u64,
897    /// Time this runlist ran before preemption (in ns)
898    pub time_run: u64,
899    /// Runlist Id
900    pub sw_runlist_id: u32,
901    /// timeslice after deduction
902    pub target_time_slice: u64,
903    /// Preemption time for this runlist (in ns)
904    pub cumulative_preemption_time: u64,
905}
906
907impl From<nvmlVgpuSchedulerLogEntry_t> for VgpuSchedulerLogEntry {
908    fn from(value: nvmlVgpuSchedulerLogEntry_t) -> Self {
909        Self {
910            timestamp: value.timestamp,
911            time_run_total: value.timeRunTotal,
912            time_run: value.timeRun,
913            sw_runlist_id: value.swRunlistId,
914            target_time_slice: value.targetTimeSlice,
915            cumulative_preemption_time: value.cumulativePreemptionTime,
916        }
917    }
918}
919
920/// Vgpu scheduler Log
921#[derive(Debug, Clone, Eq, PartialEq)]
922#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
923pub struct VgpuSchedulerLog {
924    /// Engine id whose software runlist are fetched
925    pub engine_id: u32,
926    /// Scheduler policy
927    pub scheduler_policy: u32,
928    /// Scheduler Round Robin Mode
929    pub arr_mode: u32,
930    pub scheduler_params: VgpuSchedulerParams,
931    /// Number of log entries fetched during the call
932    pub entries_count: u32,
933    /// Log entries
934    pub entries: Vec<VgpuSchedulerLogEntry>,
935}
936
937impl From<nvmlVgpuSchedulerLog_t> for VgpuSchedulerLog {
938    fn from(value: nvmlVgpuSchedulerLog_t) -> Self {
939        let entries = value
940            .logEntries
941            .iter()
942            .map(|e| VgpuSchedulerLogEntry::from(*e))
943            .collect::<Vec<_>>();
944        let params = match value.arrMode {
945            2 => {
946                let data = unsafe { value.schedulerParams.vgpuSchedDataWithARR };
947                VgpuSchedulerParams {
948                    avg_factor: Some(data.avgFactor),
949                    timeslice: data.timeslice,
950                }
951            }
952            _ => {
953                let data = unsafe { value.schedulerParams.vgpuSchedData };
954                VgpuSchedulerParams {
955                    avg_factor: None,
956                    timeslice: data.timeslice,
957                }
958            }
959        };
960
961        Self {
962            engine_id: value.engineId,
963            scheduler_policy: value.schedulerPolicy,
964            arr_mode: value.arrMode,
965            scheduler_params: params,
966            entries_count: entries.len() as u32,
967            entries,
968        }
969    }
970}
971
972/// Vgpu scheduler state
973#[derive(Debug, Clone, Eq, PartialEq)]
974#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
975pub struct VgpuSchedulerGetState {
976    /// Adaptative Round Robin scheduler mode
977    pub arr_mode: u32,
978    /// Scheduler policy
979    pub scheduler_policy: u32,
980}
981
982impl From<nvmlVgpuSchedulerGetState_t> for VgpuSchedulerGetState {
983    fn from(value: nvmlVgpuSchedulerGetState_t) -> Self {
984        Self {
985            arr_mode: value.arrMode,
986            scheduler_policy: value.schedulerPolicy,
987        }
988    }
989}
990
991#[derive(Debug, Clone, Eq, PartialEq)]
992#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
993pub struct VgpuSchedulerSetParams {
994    /// Average factor in compensating the timeslice for Adaptive Round Robin mode
995    pub avg_factor: Option<u32>,
996    /// Frequency for Adaptative Mode (when avg_factor is set)/timeslice in ns for each software run list as configured, or the default value otherwise
997    pub frequency_or_timeslice: u32,
998}
999
1000impl VgpuSchedulerSetParams {
1001    pub fn as_c(&self) -> nvmlVgpuSchedulerSetParams_t {
1002        match self.avg_factor {
1003            Some(a) => nvmlVgpuSchedulerSetParams_t {
1004                vgpuSchedDataWithARR: nvmlVgpuSchedulerSetParams_t__bindgen_ty_1 {
1005                    avgFactor: a,
1006                    frequency: self.frequency_or_timeslice,
1007                },
1008            },
1009            _ => nvmlVgpuSchedulerSetParams_t {
1010                vgpuSchedData: nvmlVgpuSchedulerSetParams_t__bindgen_ty_2 {
1011                    timeslice: self.frequency_or_timeslice,
1012                },
1013            },
1014        }
1015    }
1016}
1017
1018#[derive(Debug, Clone, Eq, PartialEq)]
1019#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1020pub struct VgpuSchedulerSetState {
1021    pub scheduler_policy: u32,
1022    pub enable_arr_mode: u32,
1023    pub scheduler_params: VgpuSchedulerSetParams,
1024}
1025
1026impl VgpuSchedulerSetState {
1027    pub fn as_c(&self) -> nvmlVgpuSchedulerSetState_t {
1028        nvmlVgpuSchedulerSetState_t {
1029            enableARRMode: self.enable_arr_mode,
1030            schedulerPolicy: self.scheduler_policy,
1031            schedulerParams: self.scheduler_params.as_c(),
1032        }
1033    }
1034}
1035
1036#[cfg(test)]
1037#[allow(unused_variables, unused_imports)]
1038mod tests {
1039    use crate::error::*;
1040    use crate::ffi::bindings::*;
1041    use crate::test_utils::*;
1042    use std::convert::TryInto;
1043    use std::mem;
1044
1045    #[test]
1046    fn pci_info_from_to_c() {
1047        let nvml = nvml();
1048        test_with_device(3, &nvml, |device| {
1049            let converted: nvmlPciInfo_t = device
1050                .pci_info()
1051                .expect("wrapped pci info")
1052                .try_into()
1053                .expect("converted c pci info");
1054
1055            let sym = nvml_sym(nvml.lib.nvmlDeviceGetPciInfo_v3.as_ref())?;
1056
1057            let raw = unsafe {
1058                let mut pci_info: nvmlPciInfo_t = mem::zeroed();
1059                nvml_try(sym(device.handle(), &mut pci_info)).expect("raw pci info");
1060                pci_info
1061            };
1062
1063            assert_eq!(converted.busId, raw.busId);
1064            assert_eq!(converted.domain, raw.domain);
1065            assert_eq!(converted.bus, raw.bus);
1066            assert_eq!(converted.device, raw.device);
1067            assert_eq!(converted.pciDeviceId, raw.pciDeviceId);
1068            assert_eq!(converted.pciSubSystemId, raw.pciSubSystemId);
1069
1070            Ok(())
1071        })
1072    }
1073}