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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct PciInfo {
26 pub bus: u32,
28 pub bus_id: String,
30 pub device: u32,
32 pub domain: u32,
34 pub pci_device_id: u32,
36 pub pci_sub_system_id: Option<u32>,
46}
47
48impl PciInfo {
49 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 fn try_into(self) -> Result<nvmlPciInfo_t, Self::Error> {
94 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 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 }
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 pciSubSystemId: self.pci_sub_system_id.unwrap_or(0),
131 busId: bus_id_c,
132 })
133 }
134}
135
136#[derive(Debug, Clone, Eq, PartialEq, Hash)]
139#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140pub struct BAR1MemoryInfo {
141 pub free: u64,
143 pub total: u64,
145 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#[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 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
198pub struct BridgeChipHierarchy {
199 pub chips_hierarchy: Vec<BridgeChipInfo>,
201 pub chip_count: u8,
203}
204
205impl TryFrom<nvmlBridgeChipHierarchy_t> for BridgeChipHierarchy {
206 type Error = NvmlError;
207
208 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
232#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233pub struct ProcessInfo {
234 pub pid: u32,
236 pub used_gpu_memory: UsedGpuMemory,
238 pub gpu_instance_id: Option<u32>,
243 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#[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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
291#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
292pub struct MemoryInfo {
293 pub free: u64,
295
296 pub reserved: u64,
298
299 pub total: u64,
301 pub used: u64,
306
307 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
327#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
328pub struct Utilization {
329 pub gpu: u32,
332 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
349#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
350pub struct ViolationTime {
351 pub reference_time: u64,
353 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
375#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
376pub struct AccountingStats {
377 pub gpu_utilization: Option<u32>,
386 pub is_running: bool,
388 pub max_memory_usage: Option<u64>,
392 pub memory_utilization: Option<u32>,
399 pub start_time: u64,
401 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 _ => 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
439#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
440pub struct EncoderSessionInfo {
441 pub session_id: u32,
443 pub pid: u32,
445 pub vgpu_instance: Option<u32>,
448 pub codec_type: EncoderType,
449 pub hres: u32,
451 pub vres: u32,
453 pub average_fps: u32,
455 pub average_latency: u32,
457}
458
459impl TryFrom<nvmlEncoderSessionInfo_t> for EncoderSessionInfo {
460 type Error = NvmlError;
461
462 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#[derive(Debug, Clone, PartialEq)]
489#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
490pub struct Sample {
491 pub timestamp: u64,
493 pub value: SampleValue,
494}
495
496impl Sample {
497 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 pub timestamp: u64,
513 pub sm_util: u32,
515 pub mem_util: u32,
517 pub enc_util: u32,
519 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#[derive(Debug)]
539pub struct FieldValueSample {
540 pub field: FieldId,
542 pub timestamp: i64,
544 pub latency: i64,
551 pub value: Result<SampleValue, NvmlError>,
555}
556
557impl TryFrom<nvmlFieldValue_t> for FieldValueSample {
558 type Error = NvmlError;
559
560 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#[derive(Debug, Clone, Eq, PartialEq)]
585#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
586pub struct FbcStats {
587 pub sessions_count: u32,
589 pub average_fps: u32,
591 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#[derive(Debug, Clone, Eq, PartialEq)]
607#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
608pub struct FbcSessionInfo {
609 pub session_id: u32,
611 pub pid: u32,
613 pub vgpu_instance: Option<u32>,
616 pub display_ordinal: u32,
618 pub session_type: FbcSessionType,
620 pub session_flags: FbcFlags,
622 pub hres_max: u32,
624 pub vres_max: u32,
626 pub hres: u32,
628 pub vres: u32,
630 pub average_fps: u32,
632 pub average_latency: u32,
634}
635
636impl TryFrom<nvmlFBCSessionInfo_t> for FbcSessionInfo {
637 type Error = NvmlError;
638
639 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
672#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
673pub struct DeviceAttributes {
674 pub multiprocessor_count: u32,
676 pub shared_copy_engine_count: u32,
678 pub shared_decoder_count: u32,
680 pub shared_encoder_count: u32,
682 pub shared_jpeg_count: u32,
684 pub shared_ofa_count: u32,
686 pub gpu_instance_slice_count: u32,
688 pub compute_instance_slice_count: u32,
690 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
712#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
713pub struct FanSpeedInfo {
714 pub version: u32,
716 pub fan: u32,
718 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#[derive(Debug, Clone, Eq, PartialEq)]
734#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
735pub struct ClockOffset {
736 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}