package fscommon import ( "bufio" "errors" "math" "os" "strconv" "strings" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/configs" "golang.org/x/sys/unix" ) // parseRdmaKV parses raw string to RdmaEntry. func parseRdmaKV(raw string, entry *cgroups.RdmaEntry) error { var value uint32 parts := strings.SplitN(raw, "=", 3) if len(parts) != 2 { return errors.New("Unable to parse RDMA entry") } k, v := parts[0], parts[1] if v == "max" { value = math.MaxUint32 } else { val64, err := strconv.ParseUint(v, 10, 32) if err != nil { return err } value = uint32(val64) } if k == "hca_handle" { entry.HcaHandles = value } else if k == "hca_object" { entry.HcaObjects = value } return nil } // readRdmaEntries reads and converts array of rawstrings to RdmaEntries from file. // example entry: mlx4_0 hca_handle=2 hca_object=2000 func readRdmaEntries(dir, file string) ([]cgroups.RdmaEntry, error) { rdmaEntries := make([]cgroups.RdmaEntry, 0) fd, err := cgroups.OpenFile(dir, file, unix.O_RDONLY) if err != nil { return nil, err } defer fd.Close() //nolint:errorlint scanner := bufio.NewScanner(fd) for scanner.Scan() { parts := strings.SplitN(scanner.Text(), " ", 4) if len(parts) == 3 { entry := new(cgroups.RdmaEntry) entry.Device = parts[0] err = parseRdmaKV(parts[1], entry) if err != nil { continue } err = parseRdmaKV(parts[2], entry) if err != nil { continue } rdmaEntries = append(rdmaEntries, *entry) } } return rdmaEntries, scanner.Err() } // RdmaGetStats returns rdma stats such as totalLimit and current entries. func RdmaGetStats(path string, stats *cgroups.Stats) error { currentEntries, err := readRdmaEntries(path, "rdma.current") if err != nil { if errors.Is(err, os.ErrNotExist) { err = nil } return err } maxEntries, err := readRdmaEntries(path, "rdma.max") if err != nil { return err } // If device got removed between reading two files, ignore returning stats. if len(currentEntries) != len(maxEntries) { return nil } stats.RdmaStats = cgroups.RdmaStats{ RdmaLimit: maxEntries, RdmaCurrent: currentEntries, } return nil } func createCmdString(device string, limits configs.LinuxRdma) string { cmdString := device if limits.HcaHandles != nil { cmdString += " hca_handle=" + strconv.FormatUint(uint64(*limits.HcaHandles), 10) } if limits.HcaObjects != nil { cmdString += " hca_object=" + strconv.FormatUint(uint64(*limits.HcaObjects), 10) } return cmdString } // RdmaSet sets RDMA resources. func RdmaSet(path string, r *configs.Resources) error { for device, limits := range r.Rdma { if err := cgroups.WriteFile(path, "rdma.max", createCmdString(device, limits)); err != nil { return err } } return nil }