Skip to content

Commit 08b2741

Browse files
committed
[nvpassthrough] load kmods without needing the host's rootfs or chroot
Prior to this change, BindToVFIODriver() loaded the vfio-pci kernel module by running "chroot $hostRoot modprobe vfio_pci". This commit moves to using github.com/pmorjan/kmod instead, which only depends on having read access to the host's /lib/modules directory. Signed-off-by: Christopher Desiniotis <cdesiniotis@nvidia.com>
1 parent c948d03 commit 08b2741

147 files changed

Lines changed: 35316 additions & 24 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ toolchain go1.24.12
77
require (
88
github.com/NVIDIA/go-nvml v0.13.0-1
99
github.com/google/uuid v1.6.0
10+
github.com/klauspost/compress v1.18.6
11+
github.com/pmorjan/kmod v1.1.1
1012
github.com/stretchr/testify v1.11.1
13+
github.com/ulikunitz/xz v0.5.15
1114
golang.org/x/sys v0.40.0
1215
)
1316

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
44
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
55
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
66
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
7+
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
8+
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
79
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
810
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
11+
github.com/pmorjan/kmod v1.1.1 h1:Vfw6bMaOg/sYSBCqJPT9TbqHHf5zK00GbaL5JQLO4r0=
12+
github.com/pmorjan/kmod v1.1.1/go.mod h1:jR4fVosEpQ6b5U0rpxaqoShTDPvCjLIP8vEESZyvnqQ=
913
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
1014
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
15+
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
16+
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
1117
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
1218
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
1319
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

pkg/nvpassthrough/kmod.go

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,70 @@
1717
package nvpassthrough
1818

1919
import (
20-
"os/exec"
20+
"compress/gzip"
21+
"io"
22+
"os"
23+
"path/filepath"
24+
25+
"github.com/klauspost/compress/zstd"
26+
"github.com/ulikunitz/xz"
27+
"golang.org/x/sys/unix"
2128
)
2229

23-
type kernelModules struct {
24-
root string
25-
}
30+
// modInitFunc supports uncompressed files and gzip and xz compressed files.
31+
//
32+
// This code is taken from:
33+
//
34+
// https://github.com/pmorjan/kmod/blob/d0ca1d5ed38616f2dc65c69add06b55f7cc091a7/cmd/modprobe/modprobe.go#L132
35+
func modInitFunc(path, params string, flags int) error {
36+
f, err := os.Open(path)
37+
if err != nil {
38+
return err
39+
}
40+
defer f.Close()
41+
42+
switch filepath.Ext(path) {
43+
case ".gz":
44+
rd, err := gzip.NewReader(f)
45+
if err != nil {
46+
return err
47+
}
48+
defer rd.Close()
49+
return initModule(rd, params)
50+
case ".xz":
51+
rd, err := xz.NewReader(f)
52+
if err != nil {
53+
return err
54+
}
55+
return initModule(rd, params)
56+
case ".zst":
57+
rd, err := zstd.NewReader(f)
58+
if err != nil {
59+
return err
60+
}
61+
defer rd.Close()
62+
return initModule(rd, params)
63+
}
2664

27-
func newKernelModules(root string) *kernelModules {
28-
km := &kernelModules{
29-
root: root,
65+
// uncompressed file, first try finitModule then initModule
66+
if err := finitModule(int(f.Fd()), params); err != nil {
67+
if err == unix.ENOSYS {
68+
return initModule(f, params)
69+
}
3070
}
31-
return km
71+
return nil
72+
}
73+
74+
// finitModule inserts a module file via syscall finit_module(2).
75+
func finitModule(fd int, params string) error {
76+
return unix.FinitModule(fd, params, 0)
3277
}
3378

34-
func (km *kernelModules) load(module string) error {
35-
cmd := exec.Command("chroot", km.root, "modprobe", module)
36-
return cmd.Run()
79+
// initModule inserts a module via syscall init_module(2).
80+
func initModule(rd io.Reader, params string) error {
81+
buf, err := io.ReadAll(rd)
82+
if err != nil {
83+
return err
84+
}
85+
return unix.InitModule(buf, params)
3786
}

pkg/nvpassthrough/modalias.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,13 @@ func getKernelVersion() (string, error) {
132132
return string(release), nil
133133
}
134134

135-
func getVFIOAliases() ([]vfioAlias, error) {
135+
func (n *nvpassthrough) getVFIOAliases() ([]vfioAlias, error) {
136136
kernelVersion, err := getKernelVersion()
137137
if err != nil {
138138
return nil, fmt.Errorf("failed to get kernel version: %w", err)
139139
}
140140

141-
modulesAliasFilePath := filepath.Join(libModulesRoot, kernelVersion, "modules.alias")
141+
modulesAliasFilePath := filepath.Join(n.libModulesRoot, kernelVersion, "modules.alias")
142142
modulesAliasContent, err := os.ReadFile(modulesAliasFilePath)
143143
if err != nil {
144144
return nil, fmt.Errorf("failed to read file %s: %w", modulesAliasFilePath, err)

pkg/nvpassthrough/nvpassthrough.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
"path/filepath"
2323
"strings"
2424

25+
"github.com/pmorjan/kmod"
26+
2527
"github.com/NVIDIA/go-nvlib/pkg/nvpci"
2628
)
2729

@@ -42,9 +44,9 @@ type Interface interface {
4244
}
4345

4446
type nvpassthrough struct {
45-
logger basicLogger
46-
hostRoot string
47-
nvpciLib nvpci.Interface
47+
logger basicLogger
48+
libModulesRoot string
49+
nvpciLib nvpci.Interface
4850
}
4951

5052
var _ Interface = (*nvpassthrough)(nil)
@@ -63,8 +65,8 @@ func New(opts ...Option) Interface {
6365
if n.logger == nil {
6466
n.logger = &nullLogger{}
6567
}
66-
if n.hostRoot == "" {
67-
n.hostRoot = "/"
68+
if n.libModulesRoot == "" {
69+
n.libModulesRoot = libModulesRoot
6870
}
6971
if n.nvpciLib == nil {
7072
n.nvpciLib = nvpci.New()
@@ -83,10 +85,10 @@ func WithLogger(logger basicLogger) Option {
8385
}
8486
}
8587

86-
// WithHostRoot provides an Option to set the path to the host root filesystem.
87-
func WithHostRoot(hostRoot string) Option {
88+
// WithLibModulesRoot provides an Option to set the path to the modules root.
89+
func WithLibModulesRoot(libModulesRoot string) Option {
8890
return func(w *nvpassthrough) {
89-
w.hostRoot = hostRoot
91+
w.libModulesRoot = libModulesRoot
9092
}
9193
}
9294

@@ -116,7 +118,7 @@ func (n *nvpassthrough) FindBestVFIOVariant(address string) (string, error) {
116118
return "", fmt.Errorf("device at %q is not an NVIDIA PCI device", address)
117119
}
118120

119-
vfioAliases, err := getVFIOAliases()
121+
vfioAliases, err := n.getVFIOAliases()
120122
if err != nil {
121123
return "", fmt.Errorf("failed to get vfio_pci aliases in modules.alias file: %w", err)
122124
}
@@ -166,8 +168,14 @@ func (n *nvpassthrough) BindToVFIODriver(address string) error {
166168
return fmt.Errorf("failed to find best vfio variant driver: %w", err)
167169
}
168170

169-
km := newKernelModules(n.hostRoot)
170-
if err := km.load(vfioDriverName); err != nil {
171+
k, err := kmod.New(
172+
kmod.SetInitFunc(modInitFunc),
173+
kmod.SetRootDir(n.libModulesRoot),
174+
)
175+
if err != nil {
176+
return fmt.Errorf("failed to initialize kmod library: %w", err)
177+
}
178+
if err := k.Load(vfioDriverName, "", 0); err != nil {
171179
return fmt.Errorf("failed to load %q driver: %w", vfioDriverName, err)
172180
}
173181

vendor/github.com/klauspost/compress/.gitattributes

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/klauspost/compress/.gitignore

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/klauspost/compress/.goreleaser.yml

Lines changed: 132 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)