12630: Report errors from nvidia-modprobe & use Getenv
[arvados.git] / lib / crunchrun / cuda.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package crunchrun
6
7 import (
8         "fmt"
9         "io"
10         "os/exec"
11 )
12
13 // nvidiaModprobe makes sure all the nvidia kernel modules and devices
14 // are set up.  If we don't have all the modules/devices set up we get
15 // "CUDA_ERROR_UNKNOWN".
16 func nvidiaModprobe(writer io.Writer) {
17         // The underlying problem is that when normally running
18         // directly on the host, the CUDA SDK will automatically
19         // detect and set up the devices on demand.  However, when
20         // running inside a container, it lacks sufficient permissions
21         // to do that.  So, it needs to be set up before the container
22         // can be started.
23         //
24         // The Singularity documentation hints about this but isn't
25         // very helpful with a solution.
26         // https://sylabs.io/guides/3.7/user-guide/gpu.html#cuda-error-unknown-when-everything-seems-to-be-correctly-configured
27         //
28         // If we're running "nvidia-persistenced", it sets up most of
29         // these things on system boot.
30         //
31         // However, it seems that doesn't include /dev/nvidia-uvm
32         // We're also no guaranteed to be running
33         // "nvidia-persistenced" or otherwise have the devices set up
34         // for us.  So the most robust solution is to do it ourselves.
35         //
36         // These are idempotent operations so it is harmless in the
37         // case that everything was actually already set up.
38
39         // Running nvida-smi the first time loads the core 'nvidia'
40         // kernel module creates /dev/nvidiactl the per-GPU
41         // /dev/nvidia* devices
42         nvidiaSmi := exec.Command("nvidia-smi", "-L")
43         nvidiaSmi.Stdout = writer
44         nvidiaSmi.Stderr = writer
45         err := nvidiaSmi.Run()
46         if err != nil {
47                 writer.Write([]byte(fmt.Sprintf("nvidia-smi error: %v\n", err)))
48         }
49
50         // Load the kernel modules & devices associated with
51         // /dev/nvidia-modeset, /dev/nvidia-nvlink, /dev/nvidia-uvm
52         // and /dev/nvidia-uvm-tools (-m, -l and -u).  Annoyingly,
53         // these don't have multiple devices but you need to supply
54         // "-c0" anyway or it won't make the device file.
55
56         // Nvswitch devices are multi-GPU interconnects for up to 16
57         // GPUs.  The "-c0 -s" flag will create /dev/nvidia-nvswitch0.
58         // If someone runs Arvados on a system with multiple
59         // nvswitches (i.e. more than 16 GPUs) they'll have to ensure
60         // that all the /dev/nvidia-nvswitch* devices exist before
61         // crunch-run starts.
62         for _, opt := range []string{"-m", "-l", "-u", "-s"} {
63                 nvmodprobe := exec.Command("nvidia-modprobe", "-c0", opt)
64                 nvmodprobe.Stdout = writer
65                 nvmodprobe.Stderr = writer
66                 err = nvmodprobe.Run()
67                 if err != nil {
68                         writer.Write([]byte(fmt.Sprintf("nvidia-modprobe error: %v\n", err)))
69                 }
70         }
71 }