| /** |
| # Copyright 2024 NVIDIA CORPORATION |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| **/ |
| |
| package dgpu |
| |
| import ( |
| "fmt" |
| "os" |
| "path/filepath" |
| |
| "github.com/NVIDIA/nvidia-container-toolkit/internal/discover" |
| "github.com/NVIDIA/nvidia-container-toolkit/internal/logger" |
| ) |
| |
| // byPathHookDiscoverer discovers the entities required for injecting by-path DRM device links |
| type byPathHookDiscoverer struct { |
| logger logger.Interface |
| devRoot string |
| nvidiaCDIHookPath string |
| pciBusID string |
| deviceNodes discover.Discover |
| } |
| |
| var _ discover.Discover = (*byPathHookDiscoverer)(nil) |
| |
| // Devices returns the empty list for the by-path hook discoverer |
| func (d *byPathHookDiscoverer) Devices() ([]discover.Device, error) { |
| return nil, nil |
| } |
| |
| // Hooks returns the hooks for the GPU device. |
| // The following hooks are detected: |
| // 1. A hook to create /dev/dri/by-path symlinks |
| func (d *byPathHookDiscoverer) Hooks() ([]discover.Hook, error) { |
| links, err := d.deviceNodeLinks() |
| if err != nil { |
| return nil, fmt.Errorf("failed to discover DRA device links: %v", err) |
| } |
| if len(links) == 0 { |
| return nil, nil |
| } |
| |
| var args []string |
| for _, l := range links { |
| args = append(args, "--link", l) |
| } |
| |
| hook := discover.CreateNvidiaCDIHook( |
| d.nvidiaCDIHookPath, |
| "create-symlinks", |
| args..., |
| ) |
| |
| return []discover.Hook{hook}, nil |
| } |
| |
| // Mounts returns an empty slice for a full GPU |
| func (d *byPathHookDiscoverer) Mounts() ([]discover.Mount, error) { |
| return nil, nil |
| } |
| |
| func (d *byPathHookDiscoverer) deviceNodeLinks() ([]string, error) { |
| devices, err := d.deviceNodes.Devices() |
| if err != nil { |
| return nil, fmt.Errorf("failed to discover device nodes: %v", err) |
| } |
| |
| if len(devices) == 0 { |
| return nil, nil |
| } |
| |
| selectedDevices := make(map[string]bool) |
| for _, d := range devices { |
| selectedDevices[d.HostPath] = true |
| } |
| |
| candidates := []string{ |
| fmt.Sprintf("/dev/dri/by-path/pci-%s-card", d.pciBusID), |
| fmt.Sprintf("/dev/dri/by-path/pci-%s-render", d.pciBusID), |
| } |
| |
| var links []string |
| for _, c := range candidates { |
| linkPath := filepath.Join(d.devRoot, c) |
| device, err := os.Readlink(linkPath) |
| if err != nil { |
| d.logger.Warningf("Failed to evaluate symlink %v; ignoring", linkPath) |
| continue |
| } |
| |
| deviceNode := device |
| if !filepath.IsAbs(device) { |
| deviceNode = filepath.Join(filepath.Dir(linkPath), device) |
| } |
| if !selectedDevices[deviceNode] { |
| d.logger.Debugf("ignoring device symlink %v -> %v since %v is not mounted", linkPath, device, deviceNode) |
| continue |
| } |
| d.logger.Debugf("adding device symlink %v -> %v", linkPath, device) |
| links = append(links, fmt.Sprintf("%v::%v", device, linkPath)) |
| } |
| |
| return links, nil |
| } |