Skip to content

Commit 32857eb

Browse files
authored
fix(helm): grant node read access for GPU capacity checks (#1106)
* fix(helm): grant node read access for GPU capacity checks Signed-off-by: Jonas Toelke <jtoelke@nvidia.com> * ci: add WSL GPU failure diagnostics Signed-off-by: Jonas Toelke <jtoelke@nvidia.com> * fix(gpu): bump device plugin for WSL CDI * fix(sandbox): allow WSL GPU paths in Landlock Allow /dev/dxg and /usr/lib/wsl as GPU baseline paths so WSL CDI GPU sandboxes can initialize NVML. Native Linux skips these entries when the paths do not exist. * ci: remove temporary WSL GPU diagnostics --------- Signed-off-by: Jonas Toelke <jtoelke@nvidia.com>
1 parent 76093e7 commit 32857eb

4 files changed

Lines changed: 97 additions & 6 deletions

File tree

crates/openshell-sandbox/src/lib.rs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,14 +1237,31 @@ const PROXY_BASELINE_READ_WRITE: &[&str] = &["/sandbox", "/tmp"];
12371237
/// socket at init time. If the directory exists but Landlock denies traversal
12381238
/// (EACCES vs ECONNREFUSED), NVML returns `NVML_ERROR_INSUFFICIENT_PERMISSIONS`
12391239
/// even though the daemon is optional. Only read/traversal access is needed.
1240-
const GPU_BASELINE_READ_ONLY: &[&str] = &["/run/nvidia-persistenced"];
1240+
///
1241+
/// `/usr/lib/wsl`: On WSL2, CDI bind-mounts GPU libraries (libdxcore.so,
1242+
/// libcuda.so.1.1, etc.) into paths under `/usr/lib/wsl/`. Although `/usr`
1243+
/// is already in `PROXY_BASELINE_READ_ONLY`, individual file bind-mounts may
1244+
/// not be covered by the parent-directory Landlock rule when the mount crosses
1245+
/// a filesystem boundary. Listing `/usr/lib/wsl` explicitly ensures traversal
1246+
/// is permitted regardless of Landlock's cross-mount behaviour.
1247+
const GPU_BASELINE_READ_ONLY: &[&str] = &[
1248+
"/run/nvidia-persistenced",
1249+
"/usr/lib/wsl", // WSL2: CDI-injected GPU library directory
1250+
];
12411251

12421252
/// GPU read-write paths (static).
12431253
///
12441254
/// `/dev/nvidiactl`, `/dev/nvidia-uvm`, `/dev/nvidia-uvm-tools`,
1245-
/// `/dev/nvidia-modeset`: control and UVM devices injected by CDI.
1246-
/// Landlock restricts `open(2)` on device files even when DAC allows it;
1247-
/// these need read-write because NVML/CUDA opens them with `O_RDWR`.
1255+
/// `/dev/nvidia-modeset`: control and UVM devices injected by CDI on native
1256+
/// Linux. Landlock restricts `open(2)` on device files even when DAC allows
1257+
/// it; these need read-write because NVML/CUDA opens them with `O_RDWR`.
1258+
/// These devices do not exist on WSL2 and will be skipped by the existence
1259+
/// check in `enrich_proto_baseline_paths()`.
1260+
///
1261+
/// `/dev/dxg`: On WSL2, NVIDIA GPUs are exposed through the DXG kernel driver
1262+
/// (DirectX Graphics) rather than the native nvidia* devices. CDI injects
1263+
/// `/dev/dxg` as the sole GPU device node; it does not exist on native Linux
1264+
/// and will be skipped there by the existence check.
12481265
///
12491266
/// `/proc`: CUDA writes to `/proc/<pid>/task/<tid>/comm` during `cuInit()`
12501267
/// to set thread names. Without write access, `cuInit()` returns error 304.
@@ -1258,12 +1275,17 @@ const GPU_BASELINE_READ_WRITE: &[&str] = &[
12581275
"/dev/nvidia-uvm",
12591276
"/dev/nvidia-uvm-tools",
12601277
"/dev/nvidia-modeset",
1278+
"/dev/dxg", // WSL2: DXG device (GPU via DirectX kernel driver, injected by CDI)
12611279
"/proc",
12621280
];
12631281

12641282
/// Returns true if GPU devices are present in the container.
1283+
///
1284+
/// Checks both the native Linux NVIDIA control device (`/dev/nvidiactl`) and
1285+
/// the WSL2 DXG device (`/dev/dxg`). CDI injects exactly one of these
1286+
/// depending on the host kernel; the other will not exist.
12651287
fn has_gpu_devices() -> bool {
1266-
std::path::Path::new("/dev/nvidiactl").exists()
1288+
std::path::Path::new("/dev/nvidiactl").exists() || std::path::Path::new("/dev/dxg").exists()
12671289
}
12681290

12691291
/// Enumerate per-GPU device nodes (`/dev/nvidia0`, `/dev/nvidia1`, …).
@@ -1542,6 +1564,17 @@ mod baseline_tests {
15421564
);
15431565
}
15441566

1567+
#[test]
1568+
fn gpu_baseline_read_write_contains_dxg() {
1569+
// /dev/dxg must be present so WSL2 sandboxes get the Landlock
1570+
// read-write rule for the CDI-injected DXG device. The existence
1571+
// check in enrich_proto_baseline_paths() skips it on native Linux.
1572+
assert!(
1573+
GPU_BASELINE_READ_WRITE.contains(&"/dev/dxg"),
1574+
"/dev/dxg must be in GPU_BASELINE_READ_WRITE for WSL2 support"
1575+
);
1576+
}
1577+
15451578
#[test]
15461579
fn local_enrichment_preserves_explicit_read_only_for_baseline_read_write_paths() {
15471580
let mut policy = SandboxPolicy {
@@ -1576,6 +1609,29 @@ mod baseline_tests {
15761609
"baseline enrichment must not promote explicit read_only /tmp to read_write"
15771610
);
15781611
}
1612+
1613+
#[test]
1614+
fn gpu_baseline_read_only_contains_usr_lib_wsl() {
1615+
// /usr/lib/wsl must be present so CDI-injected WSL2 GPU library
1616+
// bind-mounts are accessible under Landlock. Skipped on native Linux.
1617+
assert!(
1618+
GPU_BASELINE_READ_ONLY.contains(&"/usr/lib/wsl"),
1619+
"/usr/lib/wsl must be in GPU_BASELINE_READ_ONLY for WSL2 CDI library paths"
1620+
);
1621+
}
1622+
1623+
#[test]
1624+
fn has_gpu_devices_reflects_dxg_or_nvidiactl() {
1625+
// Verify the OR logic: result must match the manual disjunction of
1626+
// the two path checks. Passes in all environments.
1627+
let nvidiactl = std::path::Path::new("/dev/nvidiactl").exists();
1628+
let dxg = std::path::Path::new("/dev/dxg").exists();
1629+
assert_eq!(
1630+
has_gpu_devices(),
1631+
nvidiactl || dxg,
1632+
"has_gpu_devices() should be true iff /dev/nvidiactl or /dev/dxg exists"
1633+
);
1634+
}
15791635
}
15801636

15811637
/// Returns `true` if the error is transient and worth retrying.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
apiVersion: rbac.authorization.k8s.io/v1
5+
kind: ClusterRole
6+
metadata:
7+
name: {{ include "openshell.fullname" . }}-node-reader
8+
labels:
9+
{{- include "openshell.labels" . | nindent 4 }}
10+
rules:
11+
- apiGroups:
12+
- ""
13+
resources:
14+
- nodes
15+
verbs:
16+
- get
17+
- list
18+
- watch
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
apiVersion: rbac.authorization.k8s.io/v1
5+
kind: ClusterRoleBinding
6+
metadata:
7+
name: {{ include "openshell.fullname" . }}-node-reader
8+
labels:
9+
{{- include "openshell.labels" . | nindent 4 }}
10+
roleRef:
11+
apiGroup: rbac.authorization.k8s.io
12+
kind: ClusterRole
13+
name: {{ include "openshell.fullname" . }}-node-reader
14+
subjects:
15+
- kind: ServiceAccount
16+
name: {{ include "openshell.serviceAccountName" . }}
17+
namespace: {{ .Release.Namespace }}

deploy/kube/gpu-manifests/nvidia-device-plugin-helmchart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ metadata:
2727
spec:
2828
repo: https://nvidia.github.io/k8s-device-plugin
2929
chart: nvidia-device-plugin
30-
version: "0.18.2"
30+
version: "0.19.1"
3131
targetNamespace: nvidia-device-plugin
3232
createNamespace: true
3333
valuesContent: |-

0 commit comments

Comments
 (0)