rdma.go (3693B)
1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cgroup1 18 19 import ( 20 "math" 21 "os" 22 "path/filepath" 23 "strconv" 24 "strings" 25 26 v1 "github.com/containerd/cgroups/v3/cgroup1/stats" 27 specs "github.com/opencontainers/runtime-spec/specs-go" 28 ) 29 30 type rdmaController struct { 31 root string 32 } 33 34 func (p *rdmaController) Name() Name { 35 return Rdma 36 } 37 38 func (p *rdmaController) Path(path string) string { 39 return filepath.Join(p.root, path) 40 } 41 42 func NewRdma(root string) *rdmaController { 43 return &rdmaController{ 44 root: filepath.Join(root, string(Rdma)), 45 } 46 } 47 48 func createCmdString(device string, limits *specs.LinuxRdma) string { 49 var cmdString string 50 51 cmdString = device 52 if limits.HcaHandles != nil { 53 cmdString = cmdString + " " + "hca_handle=" + strconv.FormatUint(uint64(*limits.HcaHandles), 10) 54 } 55 56 if limits.HcaObjects != nil { 57 cmdString = cmdString + " " + "hca_object=" + strconv.FormatUint(uint64(*limits.HcaObjects), 10) 58 } 59 return cmdString 60 } 61 62 func (p *rdmaController) Create(path string, resources *specs.LinuxResources) error { 63 if err := os.MkdirAll(p.Path(path), defaultDirPerm); err != nil { 64 return err 65 } 66 67 for device, limit := range resources.Rdma { 68 if device != "" && (limit.HcaHandles != nil || limit.HcaObjects != nil) { 69 limit := limit 70 return os.WriteFile( 71 filepath.Join(p.Path(path), "rdma.max"), 72 []byte(createCmdString(device, &limit)), 73 defaultFilePerm, 74 ) 75 } 76 } 77 return nil 78 } 79 80 func (p *rdmaController) Update(path string, resources *specs.LinuxResources) error { 81 return p.Create(path, resources) 82 } 83 84 func parseRdmaKV(raw string, entry *v1.RdmaEntry) { 85 var value uint64 86 var err error 87 88 parts := strings.Split(raw, "=") 89 switch len(parts) { 90 case 2: 91 if parts[1] == "max" { 92 value = math.MaxUint32 93 } else { 94 value, err = parseUint(parts[1], 10, 32) 95 if err != nil { 96 return 97 } 98 } 99 if parts[0] == "hca_handle" { 100 entry.HcaHandles = uint32(value) 101 } else if parts[0] == "hca_object" { 102 entry.HcaObjects = uint32(value) 103 } 104 } 105 } 106 107 func toRdmaEntry(strEntries []string) []*v1.RdmaEntry { 108 var rdmaEntries []*v1.RdmaEntry 109 for i := range strEntries { 110 parts := strings.Fields(strEntries[i]) 111 switch len(parts) { 112 case 3: 113 entry := new(v1.RdmaEntry) 114 entry.Device = parts[0] 115 parseRdmaKV(parts[1], entry) 116 parseRdmaKV(parts[2], entry) 117 118 rdmaEntries = append(rdmaEntries, entry) 119 default: 120 continue 121 } 122 } 123 return rdmaEntries 124 } 125 126 func (p *rdmaController) Stat(path string, stats *v1.Metrics) error { 127 128 currentData, err := os.ReadFile(filepath.Join(p.Path(path), "rdma.current")) 129 if err != nil { 130 return err 131 } 132 currentPerDevices := strings.Split(string(currentData), "\n") 133 134 maxData, err := os.ReadFile(filepath.Join(p.Path(path), "rdma.max")) 135 if err != nil { 136 return err 137 } 138 maxPerDevices := strings.Split(string(maxData), "\n") 139 140 // If device got removed between reading two files, ignore returning 141 // stats. 142 if len(currentPerDevices) != len(maxPerDevices) { 143 return nil 144 } 145 146 currentEntries := toRdmaEntry(currentPerDevices) 147 maxEntries := toRdmaEntry(maxPerDevices) 148 149 stats.Rdma = &v1.RdmaStat{ 150 Current: currentEntries, 151 Limit: maxEntries, 152 } 153 return nil 154 }