From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 0EA361FF13C for ; Thu, 02 Apr 2026 13:22:27 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 44A16143C6; Thu, 2 Apr 2026 13:22:49 +0200 (CEST) From: Dominik Csapak To: pve-devel@lists.proxmox.com Subject: [PATCH proxmox-perl-rs v2 1/1] pve: add binding for accessing vgpu info Date: Thu, 2 Apr 2026 13:22:00 +0200 Message-ID: <20260402112212.2294356-2-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260402112212.2294356-1-d.csapak@proxmox.com> References: <20260402112212.2294356-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -1.455 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: DQCFY2LUMG5YVP6AKZOOKZY5QVRC5L3Z X-Message-ID-Hash: DQCFY2LUMG5YVP6AKZOOKZY5QVRC5L3Z X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Adds some basic perl bindings to return the creatable and supported vGPU types for NVIDIA GPUs. The 'supported' helper is not yet used, but it'll be useful when we want to have a better api response for the available mdevs. The description generated here is in the format that used to be exposed by the sysfs via the standard mdev api. Co-developed-by: Christoph Heiss Signed-off-by: Dominik Csapak --- pve-rs/Cargo.toml | 1 + pve-rs/Makefile | 1 + pve-rs/debian/control | 1 + pve-rs/src/bindings/mod.rs | 3 ++ pve-rs/src/bindings/nvml.rs | 91 +++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+) create mode 100644 pve-rs/src/bindings/nvml.rs diff --git a/pve-rs/Cargo.toml b/pve-rs/Cargo.toml index c66ee23..bc77529 100644 --- a/pve-rs/Cargo.toml +++ b/pve-rs/Cargo.toml @@ -20,6 +20,7 @@ hex = "0.4" http = "1" libc = "0.2" nix = "0.29" +nvml-wrapper = "0.12" openssl = "0.10.40" serde = "1.0" serde_bytes = "0.11" diff --git a/pve-rs/Makefile b/pve-rs/Makefile index c2f9b73..dbbeb4e 100644 --- a/pve-rs/Makefile +++ b/pve-rs/Makefile @@ -27,6 +27,7 @@ PERLMOD_GENPACKAGE := /usr/lib/perlmod/genpackage.pl \ PERLMOD_PACKAGES := \ PVE::RS::Firewall::SDN \ + PVE::RS::NVML \ PVE::RS::OCI \ PVE::RS::OpenId \ PVE::RS::ResourceScheduling::Static \ diff --git a/pve-rs/debian/control b/pve-rs/debian/control index 25e2121..d891f28 100644 --- a/pve-rs/debian/control +++ b/pve-rs/debian/control @@ -11,6 +11,7 @@ Build-Depends: debhelper-compat (= 13), librust-http-1+default-dev, librust-libc-0.2+default-dev, librust-nix-0.29+default-dev, + librust-nvml-wrapper-dev (>= 0.12.0-1~bpo13+pve1), librust-openssl-0.10+default-dev (>= 0.10.40-~~), librust-perlmod-0.14+default-dev, librust-perlmod-0.14+exporter-dev, diff --git a/pve-rs/src/bindings/mod.rs b/pve-rs/src/bindings/mod.rs index 853a3dd..33938ed 100644 --- a/pve-rs/src/bindings/mod.rs +++ b/pve-rs/src/bindings/mod.rs @@ -11,6 +11,9 @@ pub use tfa::pve_rs_tfa; mod openid; pub use openid::pve_rs_open_id; +mod nvml; +pub use nvml::pve_rs_nvml; + pub mod firewall; mod sdn; diff --git a/pve-rs/src/bindings/nvml.rs b/pve-rs/src/bindings/nvml.rs new file mode 100644 index 0000000..0f4c81e --- /dev/null +++ b/pve-rs/src/bindings/nvml.rs @@ -0,0 +1,91 @@ +//! Provides access to the state of NVIDIA (v)GPU devices connected to the system. + +#[perlmod::package(name = "PVE::RS::NVML", lib = "pve_rs")] +pub mod pve_rs_nvml { + //! The `PVE::RS::NVML` package. + //! + //! Provides high level helpers to get info from the system with NVML. + + use anyhow::Result; + use nvml_wrapper::Nvml; + use perlmod::Value; + + /// Retrieves a list of *creatable* vGPU types for the specified GPU by bus id. + /// + /// The [`bus_id`] is of format "\:\:\.\", + /// e.g. "0000:01:01.0". + /// + /// # See also + /// + /// [`nvmlDeviceGetCreatableVgpus`]: + /// [`nvmlDeviceGetHandleByPciBusId_v2`]: + /// [`struct nvmlPciInfo_t`]: + #[export] + fn creatable_vgpu_types_for_dev(bus_id: &str) -> Result> { + let nvml = Nvml::init()?; + let device = nvml.device_by_pci_bus_id(bus_id)?; + + build_vgpu_type_list(device.vgpu_creatable_types()?) + } + + /// Retrieves a list of *supported* vGPU types for the specified GPU by bus id. + /// + /// The [`bus_id`] is of format "\:\:\.\", + /// e.g. "0000:01:01.0". + /// + /// # See also + /// + /// [`nvmlDeviceGetSupportedVgpus`]: + /// [`nvmlDeviceGetHandleByPciBusId_v2`]: + /// [`struct nvmlPciInfo_t`]: + #[export] + fn supported_vgpu_types_for_dev(bus_id: &str) -> Result> { + let nvml = Nvml::init()?; + let device = nvml.device_by_pci_bus_id(bus_id)?; + + build_vgpu_type_list(device.vgpu_supported_types()?) + } + + fn build_vgpu_type_list(vgpu_types: Vec) -> Result> { + let mut result = Vec::with_capacity(vgpu_types.len()); + for vgpu in vgpu_types { + let mut value = perlmod::Value::new_hash(); + if let Some(hash) = value.as_hash_mut() { + hash.insert("id", Value::new_uint(vgpu.id() as usize)); + hash.insert("name", Value::new_string(&vgpu.name()?)); + hash.insert("description", Value::new_string(&description(&vgpu)?)); + } + + result.push(Value::new_ref(&value)); + } + + Ok(result) + } + + // a description like it used to exist in the sysfs with the standard mdev interface + fn description(vgpu_type: &nvml_wrapper::vgpu::VgpuType) -> Result { + let class_name = vgpu_type.class_name()?; + let max_instances = vgpu_type.max_instances()?; + let max_instances_per_vm = vgpu_type.max_instances_per_vm()?; + + let framebuffer_size_mb = vgpu_type.framebuffer_size()? / 1024 / 1024; // bytes to MiB + let num_heads = vgpu_type.num_display_heads()?; + + let (max_res_x, max_res_y) = (0..num_heads) + .filter_map(|head| vgpu_type.resolution(head).ok()) + .max() + .unwrap_or((0, 0)); + + let license = vgpu_type.license()?; + + Ok(format!( + "class={class_name}\n\ + max-instances={max_instances}\n\ + max-instances-per-vm={max_instances_per_vm}\n\ + framebuffer-size={framebuffer_size_mb}MiB\n\ + num-heads={num_heads}\n\ + max-resolution={max_res_x}x{max_res_y}\n\ + license={license}" + )) + } +} -- 2.47.3