Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 35 additions & 35 deletions cmd/compute-domain-kubelet-plugin/nvlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"

Expand Down Expand Up @@ -225,48 +226,47 @@ func (l deviceLib) getImexChannelCount() (int, error) {
return 2048, nil
}

// getDeviceMajor searches for one "<integer> <name>" occurrence in the
// "Character devices" section of the /proc/devices file, and returns the
// integer.
func (l deviceLib) getDeviceMajor(name string) (int, error) {
file, err := os.Open(procDevicesPath)
if err != nil {
return -1, err
}
defer file.Close()

scanner := bufio.NewScanner(file)
foundCharDevices := false

for scanner.Scan() {
line := scanner.Text()
re := regexp.MustCompile(
// The `(?s)` flag makes `.` match newlines. The greedy modifier in
// `.*?` ensures to pick the first match after "Character devices".
// Extract the number as capture group (the first and only group).
"(?s)Character devices:.*?" +
"([0-9]+) " + regexp.QuoteMeta(name) +
".*Block devices:",
)

// Ignore empty lines
if line == "" {
continue
}
data, err := os.ReadFile(procDevicesPath)
if err != nil {
return -1, fmt.Errorf("error reading '%s': %w", procDevicesPath, err)
}

// Check for any line with text followed by a colon (header)
if strings.Contains(line, ":") {
// Stop if we've already found the character devices section and reached another section
if foundCharDevices {
break
}
// Check if we entered the character devices section
if strings.HasSuffix(line, ":") && strings.HasPrefix(line, "Character") {
foundCharDevices = true
}
// Continue to the next line, regardless
continue
}
// Expect precisely one match: first element is the total match, second
// element corresponds to first capture group within that match (i.e., the
// number of interest).
matches := re.FindStringSubmatch(string(data))
if len(matches) != 2 {
return -1, fmt.Errorf("error parsing '%s': unexpected regex match: %v", procDevicesPath, matches)
}

// If we've passed the character devices section, check for nvidiaCapsImexChannelsDeviceName
if foundCharDevices {
parts := strings.Fields(line)
if len(parts) == 2 && parts[1] == name {
return strconv.Atoi(parts[0])
}
}
// Convert capture group content to integer. Perform upper bound check:
// value must fit into 32-bit integer (it's then also guaranteed to fit into
// a 32-bit unsigned integer, which is the type that must be passed to
// unix.Mkdev()).
major, err := strconv.ParseInt(matches[1], 10, 32)
if err != nil {
return -1, fmt.Errorf("int conversion failed for '%v': %w", matches[1], err)
}

return -1, scanner.Err()
// ParseInt() always returns an integer of explicit type `int64`. We have
// performed an upper bound check so it's safe to convert this to `int`
// (which is documented as "int is a signed integer type that is at least 32
// bits in size", so in theory it could be smaller than int64).
return int(major), nil
}

func (l deviceLib) parseNVCapDeviceInfo(nvcapsFilePath string) (*nvcapDeviceInfo, error) {
Expand Down