[ARVADOS] created: 2.1.0-1814-g042f47a2c

Git user git at public.arvados.org
Fri Jan 7 22:16:56 UTC 2022


        at  042f47a2c6b5f3db80142164b6493c873aca0b26 (commit)


commit 042f47a2c6b5f3db80142164b6493c873aca0b26
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Fri Jan 7 17:13:09 2022 -0500

    12630: Call nvidia-modprobe, support CUDA_VISIBLE_DEVICES
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/lib/crunchrun/crunchrun.go b/lib/crunchrun/crunchrun.go
index b237d9fa5..fb2200a56 100644
--- a/lib/crunchrun/crunchrun.go
+++ b/lib/crunchrun/crunchrun.go
@@ -987,6 +987,10 @@ func (runner *ContainerRunner) CreateContainer(imageID string, bindmounts map[st
 	runner.executorStdout = stdout
 	runner.executorStderr = stderr
 
+	if runner.Container.RuntimeConstraints.CUDA.DeviceCount > 0 {
+		nvidiaModprobe(runner.CrunchLog)
+	}
+
 	return runner.executor.Create(containerSpec{
 		Image:           imageID,
 		VCPUs:           runner.Container.RuntimeConstraints.VCPUs,
diff --git a/lib/crunchrun/cuda.go b/lib/crunchrun/cuda.go
new file mode 100644
index 000000000..91949c588
--- /dev/null
+++ b/lib/crunchrun/cuda.go
@@ -0,0 +1,63 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package crunchrun
+
+import (
+	"io"
+	"os/exec"
+)
+
+// nvidiaModprobe makes sure all the nvidia kernel modules and devices
+// are set up.  If we don't have all the modules/devices set up we get
+// "CUDA_ERROR_UNKNOWN".
+func nvidiaModprobe(writer io.Writer) {
+	// The underlying problem is that when normally running
+	// directly on the host, the CUDA SDK will automatically
+	// detect and set up the devices on demand.  However, when
+	// running inside a container, it lacks sufficient permissions
+	// to do that.  So, it needs to be set up before the container
+	// can be started.
+	//
+	// The Singularity documentation hints about this but isn't
+	// very helpful with a solution.
+	// https://sylabs.io/guides/3.7/user-guide/gpu.html#cuda-error-unknown-when-everything-seems-to-be-correctly-configured
+	//
+	// If we're running "nvidia-persistenced", it sets up most of
+	// these things on system boot.
+	//
+	// However, it seems that doesn't include /dev/nvidia-uvm
+	// We're also no guaranteed to be running
+	// "nvidia-persistenced" or otherwise have the devices set up
+	// for us.  So the most robust solution is to do it ourselves.
+	//
+	// These are idempotent operations so it is harmless in the
+	// case that everything was actually already set up.
+
+	// Running nvida-smi the first time loads the core 'nvidia'
+	// kernel module creates /dev/nvidiactl the per-GPU
+	// /dev/nvidia* devices
+	nvidiaSmi := exec.Command("nvidia-smi", "-L")
+	nvidiaSmi.Stdout = writer
+	nvidiaSmi.Stderr = writer
+	nvidiaSmi.Run()
+
+	// Load the kernel modules & devices associated with
+	// /dev/nvidia-modeset, /dev/nvidia-nvlink, /dev/nvidia-uvm
+	// and /dev/nvidia-uvm-tools (-m, -l and -u).  Annoyingly, you
+	// don't have multiple devices but you need to supply "-c0"
+	// anyway or it won't make the device file.
+	exec.Command("nvidia-modprobe", "-c0", "-m").Run()
+	exec.Command("nvidia-modprobe", "-c0", "-l").Run()
+	exec.Command("nvidia-modprobe", "-c0", "-u").Run()
+
+	// Nvswitch devices are multi-GPU interconnects for up to 16
+	// GPUs.  Here we'll create /dev/nvidia-nvswitch0.  If someone
+	// runs Arvados on a system with multiple nvswitches
+	// (i.e. more than 16 GPUs) they can either ensure that the
+	// additional /dev/nvidia-nvswitch* devices exist before
+	// crunch-run starts or pay for support (because they clearly
+	// have the budget for it).
+	exec.Command("nvidia-modprobe", "-c0", "-s").Run()
+}
diff --git a/lib/crunchrun/docker.go b/lib/crunchrun/docker.go
index ab00273ef..f437d6a0c 100644
--- a/lib/crunchrun/docker.go
+++ b/lib/crunchrun/docker.go
@@ -107,10 +107,26 @@ func (e *dockerExecutor) config(spec containerSpec) (dockercontainer.Config, doc
 		},
 	}
 	if spec.CUDADeviceCount != 0 {
+		var deviceIds []string
+		for _, s := range os.Environ() {
+			// If a resource manager such as slurm or LSF told
+			// us to select specific devices we need to propagate that.
+			if strings.HasPrefix(s, "CUDA_VISIBLE_DEVICES=") {
+				deviceIds = strings.SplitN(strings.SplitN(s, "=", 2)[1], ",")
+			}
+		}
+		deviceCount := spec.CUDADeviceCount
+		if len(deviceIds) > 0 {
+			// Docker won't accept both non-empty
+			// DeviceIDs and a non-zero Count
+			deviceCount = 0
+		}
+
 		hostCfg.Resources.DeviceRequests = append(hostCfg.Resources.DeviceRequests, dockercontainer.DeviceRequest{
 			Driver:       "nvidia",
-			Count:        spec.CUDADeviceCount,
-			Capabilities: [][]string{[]string{"gpu", "nvidia", "compute"}},
+			Count:        deviceCount,
+			DeviceIDs:    deviceIds,
+			Capabilities: [][]string{[]string{"gpu", "nvidia"}},
 		})
 	}
 	for path, mount := range spec.BindMounts {
diff --git a/lib/crunchrun/singularity.go b/lib/crunchrun/singularity.go
index cda10aa61..942de4300 100644
--- a/lib/crunchrun/singularity.go
+++ b/lib/crunchrun/singularity.go
@@ -10,6 +10,7 @@ import (
 	"os"
 	"os/exec"
 	"sort"
+	"strings"
 	"syscall"
 	"time"
 
@@ -284,6 +285,15 @@ func (e *singularityExecutor) execCmd(path string) *exec.Cmd {
 		env = append(env, "SINGULARITYENV_"+k+"="+v)
 	}
 
+	// Singularity always makes all nvidia devices visible to the
+	// container.  If a resource manager such as slurm or LSF told
+	// us to select specific devices we need to propagate that.
+	for _, s := range os.Environ() {
+		if strings.HasPrefix(s, "CUDA_VISIBLE_DEVICES=") {
+			env = append(env, "SINGULARITYENV_"+s)
+		}
+	}
+
 	args = append(args, e.imageFilename)
 	args = append(args, e.spec.Command...)
 

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list