diff --git a/internal/discover/graphics.go b/internal/discover/graphics.go index 27d9fa2c0..3b385f100 100644 --- a/internal/discover/graphics.go +++ b/internal/discover/graphics.go @@ -55,29 +55,58 @@ func NewGraphicsMountsDiscoverer(logger logger.Interface, driver *root.Driver, h return nil, fmt.Errorf("failed to construct discoverer for graphics libraries: %w", err) } - configs := NewMounts( + binaries := NewMounts( logger, - driver.Configs(), + lookup.NewExecutableLocator(logger, driver.Root), driver.Root, []string{ + "nvidia-xconfig", + }, + ) + + discover := Merge( + libraries, + binaries, + newGraphicsConfigsDiscoverer(logger, driver), + newVulkanConfigsDiscover(logger, driver), + ) + + return discover, nil +} + +// newGraphicsConfigsDiscoverer creates a discoverer for graphics-related config +// files such as the EGL vendor and external platform ICD files. +// The config files are mounted at the standard locations in the container so +// that they are discovered by the loaders in the container even if they are +// installed at non-standard locations on the host. +func newGraphicsConfigsDiscoverer(logger logger.Interface, driver *root.Driver) Discover { + shareConfigs := WithCache(&mountsToContainerPath{ + logger: logger, + locator: driver.Configs(), + required: []string{ "glvnd/egl_vendor.d/10_nvidia.json", "egl/egl_external_platform.d/15_nvidia_gbm.json", "egl/egl_external_platform.d/10_nvidia_wayland.json", "egl/egl_external_platform.d/09_nvidia_wayland2.json", + "egl/egl_external_platform.d/20_nvidia_xcb.json", + "egl/egl_external_platform.d/20_nvidia_xlib.json", "nvidia/nvoptix.bin", "X11/xorg.conf.d/10-nvidia.conf", "X11/xorg.conf.d/nvidia-drm-outputclass.conf", - "OpenCL/vendors/nvidia.icd", }, - ) + containerRoot: "/usr/share", + }) - discover := Merge( - libraries, - configs, - newVulkanConfigsDiscover(logger, driver), - ) + etcConfigs := WithCache(&mountsToContainerPath{ + logger: logger, + locator: driver.Configs(), + required: []string{ + "OpenCL/vendors/nvidia.icd", + }, + containerRoot: "/etc", + }) - return discover, nil + return Merge(shareConfigs, etcConfigs) } // newVulkanConfigsDiscover creates a discoverer for vulkan ICD files. @@ -101,12 +130,12 @@ func newVulkanConfigsDiscover(logger logger.Interface, driver *root.Driver) Disc case "arm64": required = append(required, "vulkan/icd.d/nvidia_icd.aarch64.json") } - return &mountsToContainerPath{ + return WithCache(&mountsToContainerPath{ logger: logger, locator: locator, required: required, containerRoot: "/etc", - } + }) } type graphicsDriverLibraries struct { @@ -135,11 +164,14 @@ func newGraphicsLibrariesDiscoverer(logger logger.Interface, driver *root.Driver driver.Libraries(), driver.Root, []string{ - // The libnvidia-egl-gbm and libnvidia-egl-wayland libraries do not - // have the RM version. Use the *.* pattern to match X.Y.Z versions. + // The EGL platform libraries such as libnvidia-egl-gbm and + // libnvidia-egl-wayland do not have the RM version. Use the *.* + // pattern to match X.Y.Z versions. "libnvidia-egl-gbm.so.*.*", "libnvidia-egl-wayland.so.*.*", "libnvidia-egl-wayland2.so.*.*", + "libnvidia-egl-xcb.so.*.*", + "libnvidia-egl-xlib.so.*.*", // We include the following libraries to have them available for // symlink creation below: // If CDI injection is used, these should already be detected as: @@ -151,6 +183,10 @@ func newGraphicsLibrariesDiscoverer(logger logger.Interface, driver *root.Driver }, ) + // The X.Org driver modules are mounted at their host paths. This keeps any + // ModulePath specified in the xorg.conf.d config file that is mounted into + // the container valid and means that modules installed to the default X.Org + // module path on the host are also found there in the container. xorgLibraries := NewMounts( logger, lookup.NewFileLocator( @@ -231,7 +267,7 @@ func (d graphicsDriverLibraries) Hooks() ([]Hook, error) { return nil, nil } - hook := d.hookCreator.Create("create-symlinks", links...) + hook := d.hookCreator.Create(CreateSymlinksHook, links...) return hook.Hooks() } diff --git a/internal/discover/graphics_test.go b/internal/discover/graphics_test.go index fff3a0426..e022bb87f 100644 --- a/internal/discover/graphics_test.go +++ b/internal/discover/graphics_test.go @@ -17,6 +17,7 @@ package discover import ( + "os" "path/filepath" "strings" "testing" @@ -25,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/NVIDIA/nvidia-container-toolkit/internal/devices" + "github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root" "github.com/NVIDIA/nvidia-container-toolkit/internal/test" ) @@ -189,6 +191,78 @@ func TestGraphicsLibrariesDiscoverer(t *testing.T) { } } +func TestGraphicsConfigsDiscoverer(t *testing.T) { + logger, _ := testlog.NewNullLogger() + + testCases := []struct { + description string + files []string + // expected maps the path of the file in the driver root to the path + // that it is expected to be mounted at in the container. + expected map[string]string + }{ + { + description: "config files in the standard locations", + files: []string{ + "/usr/share/glvnd/egl_vendor.d/10_nvidia.json", + "/usr/share/egl/egl_external_platform.d/20_nvidia_xcb.json", + "/usr/share/X11/xorg.conf.d/10-nvidia.conf", + "/etc/OpenCL/vendors/nvidia.icd", + }, + expected: map[string]string{ + "/usr/share/glvnd/egl_vendor.d/10_nvidia.json": "/usr/share/glvnd/egl_vendor.d/10_nvidia.json", + "/usr/share/egl/egl_external_platform.d/20_nvidia_xcb.json": "/usr/share/egl/egl_external_platform.d/20_nvidia_xcb.json", + "/usr/share/X11/xorg.conf.d/10-nvidia.conf": "/usr/share/X11/xorg.conf.d/10-nvidia.conf", + "/etc/OpenCL/vendors/nvidia.icd": "/etc/OpenCL/vendors/nvidia.icd", + }, + }, + { + description: "config files in non-standard locations are mounted at the standard locations", + files: []string{ + "/usr/local/share/glvnd/egl_vendor.d/10_nvidia.json", + "/usr/local/share/egl/egl_external_platform.d/20_nvidia_xlib.json", + "/etc/X11/xorg.conf.d/nvidia-drm-outputclass.conf", + }, + expected: map[string]string{ + "/usr/local/share/glvnd/egl_vendor.d/10_nvidia.json": "/usr/share/glvnd/egl_vendor.d/10_nvidia.json", + "/usr/local/share/egl/egl_external_platform.d/20_nvidia_xlib.json": "/usr/share/egl/egl_external_platform.d/20_nvidia_xlib.json", + "/etc/X11/xorg.conf.d/nvidia-drm-outputclass.conf": "/usr/share/X11/xorg.conf.d/nvidia-drm-outputclass.conf", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + // The config search paths include the XDG data dirs. These are + // set explicitly to ensure that the test is not affected by the + // environment that it is run in. + t.Setenv("XDG_DATA_DIRS", "/usr/local/share:/usr/share") + + driverRoot := t.TempDir() + for _, f := range tc.files { + path := filepath.Join(driverRoot, f) + require.NoError(t, os.MkdirAll(filepath.Dir(path), 0755)) + require.NoError(t, os.WriteFile(path, []byte{}, 0600)) + } + + driver := root.New( + root.WithLogger(logger), + root.WithDriverRoot(driverRoot), + ) + + mounts, err := newGraphicsConfigsDiscoverer(logger, driver).Mounts() + require.NoError(t, err) + + discovered := make(map[string]string) + for _, mount := range mounts { + hostPath := strings.TrimPrefix(mount.HostPath, driverRoot) + discovered[hostPath] = mount.Path + } + require.EqualValues(t, tc.expected, discovered) + }) + } +} + func TestDrmDevicesByPath(t *testing.T) { defer devices.SetAllForTest()() moduleRoot, err := test.GetModuleRoot()