github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/cgroups/fscommon/rdma.go (about) 1 package fscommon 2 3 import ( 4 "bufio" 5 "errors" 6 "math" 7 "os" 8 "strconv" 9 "strings" 10 11 "github.com/opencontainers/runc/libcontainer/cgroups" 12 "github.com/opencontainers/runc/libcontainer/configs" 13 "golang.org/x/sys/unix" 14 ) 15 16 // parseRdmaKV parses raw string to RdmaEntry. 17 func parseRdmaKV(raw string, entry *cgroups.RdmaEntry) error { 18 var value uint32 19 20 parts := strings.SplitN(raw, "=", 3) 21 22 if len(parts) != 2 { 23 return errors.New("Unable to parse RDMA entry") 24 } 25 26 k, v := parts[0], parts[1] 27 28 if v == "max" { 29 value = math.MaxUint32 30 } else { 31 val64, err := strconv.ParseUint(v, 10, 32) 32 if err != nil { 33 return err 34 } 35 value = uint32(val64) 36 } 37 if k == "hca_handle" { 38 entry.HcaHandles = value 39 } else if k == "hca_object" { 40 entry.HcaObjects = value 41 } 42 43 return nil 44 } 45 46 // readRdmaEntries reads and converts array of rawstrings to RdmaEntries from file. 47 // example entry: mlx4_0 hca_handle=2 hca_object=2000 48 func readRdmaEntries(dir, file string) ([]cgroups.RdmaEntry, error) { 49 rdmaEntries := make([]cgroups.RdmaEntry, 0) 50 fd, err := cgroups.OpenFile(dir, file, unix.O_RDONLY) 51 if err != nil { 52 return nil, err 53 } 54 defer fd.Close() //nolint:errorlint 55 scanner := bufio.NewScanner(fd) 56 for scanner.Scan() { 57 parts := strings.SplitN(scanner.Text(), " ", 4) 58 if len(parts) == 3 { 59 entry := new(cgroups.RdmaEntry) 60 entry.Device = parts[0] 61 err = parseRdmaKV(parts[1], entry) 62 if err != nil { 63 continue 64 } 65 err = parseRdmaKV(parts[2], entry) 66 if err != nil { 67 continue 68 } 69 70 rdmaEntries = append(rdmaEntries, *entry) 71 } 72 } 73 return rdmaEntries, scanner.Err() 74 } 75 76 // RdmaGetStats returns rdma stats such as totalLimit and current entries. 77 func RdmaGetStats(path string, stats *cgroups.Stats) error { 78 currentEntries, err := readRdmaEntries(path, "rdma.current") 79 if err != nil { 80 if errors.Is(err, os.ErrNotExist) { 81 err = nil 82 } 83 return err 84 } 85 maxEntries, err := readRdmaEntries(path, "rdma.max") 86 if err != nil { 87 return err 88 } 89 // If device got removed between reading two files, ignore returning stats. 90 if len(currentEntries) != len(maxEntries) { 91 return nil 92 } 93 94 stats.RdmaStats = cgroups.RdmaStats{ 95 RdmaLimit: maxEntries, 96 RdmaCurrent: currentEntries, 97 } 98 99 return nil 100 } 101 102 func createCmdString(device string, limits configs.LinuxRdma) string { 103 cmdString := device 104 if limits.HcaHandles != nil { 105 cmdString += " hca_handle=" + strconv.FormatUint(uint64(*limits.HcaHandles), 10) 106 } 107 if limits.HcaObjects != nil { 108 cmdString += " hca_object=" + strconv.FormatUint(uint64(*limits.HcaObjects), 10) 109 } 110 return cmdString 111 } 112 113 // RdmaSet sets RDMA resources. 114 func RdmaSet(path string, r *configs.Resources) error { 115 for device, limits := range r.Rdma { 116 if err := cgroups.WriteFile(path, "rdma.max", createCmdString(device, limits)); err != nil { 117 return err 118 } 119 } 120 return nil 121 }