github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/qrm-plugins/io/handlers/iocost/iocost_linux.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2022 The Katalyst Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package iocost 21 22 import ( 23 "fmt" 24 "path/filepath" 25 "strconv" 26 "sync" 27 28 "github.com/kubewharf/katalyst-core/pkg/config" 29 coreconfig "github.com/kubewharf/katalyst-core/pkg/config" 30 dynamicconfig "github.com/kubewharf/katalyst-core/pkg/config/agent/dynamic" 31 "github.com/kubewharf/katalyst-core/pkg/metaserver" 32 "github.com/kubewharf/katalyst-core/pkg/metrics" 33 "github.com/kubewharf/katalyst-core/pkg/util/cgroup/common" 34 cgcommon "github.com/kubewharf/katalyst-core/pkg/util/cgroup/common" 35 "github.com/kubewharf/katalyst-core/pkg/util/cgroup/manager" 36 "github.com/kubewharf/katalyst-core/pkg/util/general" 37 ) 38 39 var ( 40 initializeOnce sync.Once 41 ioCgroupRootPath = cgcommon.GetCgroupRootPath(cgcommon.CgroupSubsysIO) 42 ) 43 44 func applyIOCostModelWithDefault(ioCostModelConfigs map[DevModel]*common.IOCostModelData, devsIDToModel map[string]DevModel) { 45 curDevIDToIOCostModelData, err := manager.GetIOCostModelWithAbsolutePath(ioCgroupRootPath) 46 if err != nil { 47 general.Errorf("GetIOCostModelWithAbsolutePath failed with error: %v", err) 48 return 49 } 50 51 for devID := range devsIDToModel { 52 var expectedModelData, curModelData *common.IOCostModelData 53 54 expectedModelData = ioCostModelConfigs[DevModelDefault] 55 if expectedModelData == nil { 56 general.Errorf("there is no expected io cost Model Data for devID: %s", devID) 57 continue 58 } 59 60 curModelData = curDevIDToIOCostModelData[devID] 61 if curModelData == nil { 62 general.Errorf("there is no current io cost Model Data for devID: %s", devID) 63 continue 64 } 65 66 if (*curModelData) != (*expectedModelData) { 67 err = manager.ApplyIOCostModelWithAbsolutePath(ioCgroupRootPath, devID, expectedModelData) 68 if err != nil { 69 general.Errorf("ApplyIOCostModelWithAbsolutePath for devID: %s, failed with error: %v", 70 devID, err) 71 } else { 72 general.Infof("ApplyIOCostModelWithAbsolutePath for devID: %s successfully:%v ", 73 devID, expectedModelData) 74 } 75 } else { 76 general.Infof("modelData isn't changed, skip ApplyIOCostModelWithAbsolutePath for devID: %s, modelData=%v ", 77 devID, expectedModelData) 78 } 79 } 80 } 81 82 func reportDevicesIOCostVrate(emitter metrics.MetricEmitter) { 83 devIDToIOStat, err := manager.GetIOStatWithAbsolutePath(ioCgroupRootPath) 84 if err != nil { 85 general.Errorf("GetIOStatWithAbsolutePath failed with error: %v", err) 86 return 87 } 88 89 for devID, ioStat := range devIDToIOStat { 90 if valueStr, found := ioStat[IOStatMetricCostVrate]; found { 91 valueFloat64, err := strconv.ParseFloat(valueStr, 64) 92 if err != nil { 93 general.Errorf("%s value: %s is invalid for devID: %s", 94 IOStatMetricCostVrate, valueStr, devID) 95 continue 96 } 97 98 devName, found, err := getDeviceNameFromID(devID) 99 if err != nil { 100 general.Errorf("getDeviceNameFromID: %s failed with error: %v", 101 devID, err) 102 continue 103 } else if !found { 104 general.Errorf("no device name found for device id: %s", devID) 105 continue 106 } 107 108 _ = emitter.StoreFloat64(MetricNameIOCostVrate, valueFloat64, 109 metrics.MetricTypeNameRaw, metrics.MetricTag{ 110 Key: "device_name", 111 Val: devName, 112 }) 113 } 114 } 115 } 116 117 func disableIOCost(conf *config.Configuration) { 118 if !cgcommon.CheckCgroup2UnifiedMode() { 119 return 120 } 121 122 devIDToIOCostQoSData, err := manager.GetIOCostQoSWithAbsolutePath(ioCgroupRootPath) 123 if err != nil { 124 general.Errorf("GetIOCostQoSWithAbsolutePath failed with error: %v in Init", err) 125 } 126 127 disabledIOCostQoSData := &cgcommon.IOCostQoSData{Enable: 0} 128 for devID, ioCostQoSData := range devIDToIOCostQoSData { 129 if ioCostQoSData == nil { 130 general.Warningf("nil ioCostQoSData") 131 continue 132 } else if ioCostQoSData.Enable == 0 { 133 general.Warningf("devID: %s ioCostQoS is already disabled", devID) 134 continue 135 } 136 137 err = manager.ApplyIOCostQoSWithAbsolutePath(ioCgroupRootPath, devID, disabledIOCostQoSData) 138 if err != nil { 139 general.Errorf("ApplyIOCostQoSWithAbsolutePath for devID: %s, failed with error: %v", devID, err) 140 } else { 141 general.Infof("disable ioCostQoS for devID: %s successfully", devID) 142 } 143 } 144 } 145 146 func applyIOCostQoSWithDefault( 147 ioCostQoSConfigs map[DevModel]*common.IOCostQoSData, 148 devsIDToModel map[string]DevModel, 149 ) { 150 for devID := range devsIDToModel { 151 152 // checking device type: isHDD? 153 devName, found, err := getDeviceNameFromID(devID) 154 if err != nil { 155 general.Errorf("getDeviceNameFromID: %s failed with error: %v", devID, err) 156 continue 157 } else if !found { 158 general.Errorf("no device name found for device id: %s", devID) 159 continue 160 } 161 162 rotationalFile := filepath.Clean(fmt.Sprintf(queueRotationalFilePattern, devName)) 163 deviceType, err := getDeviceType(devName, rotationalFile) 164 if err != nil { 165 general.Errorf("checking device %v failed, error:%v", devName, err) 166 continue 167 } 168 169 var defaultConfig DevModel 170 switch deviceType { 171 case HDD: 172 defaultConfig = DevModelDefaultHDD 173 case Unknown: 174 general.Warningf("for now, only HDD were supported, device:%v.", devName) 175 continue 176 } 177 178 expectedQoSData := ioCostQoSConfigs[defaultConfig] 179 if expectedQoSData == nil { 180 general.Errorf("there is no default io cost QoS Data for devID: %s", devID) 181 continue 182 } 183 err = manager.ApplyIOCostQoSWithAbsolutePath(ioCgroupRootPath, devID, expectedQoSData) 184 if err != nil { 185 general.Errorf("ApplyIOCostQoSWithAbsolutePath for devID: %s, failed with error: %v", 186 devID, err) 187 } 188 } 189 } 190 191 func applyIOCostConfig(conf *config.Configuration, emitter metrics.MetricEmitter) { 192 if !conf.EnableSettingIOCost { 193 general.Infof("IOCostControl disabled, skip applyIOCostConfig") 194 return 195 } else if conf.IOCostQoSConfigFile == "" || conf.IOCostModelConfigFile == "" { 196 general.Errorf("IOCostQoSConfigFile or IOCostQoSConfigFile not configured") 197 return 198 } 199 200 ioCostQoSConfigs := make(map[DevModel]*common.IOCostQoSData) 201 err := general.LoadJsonConfig(conf.IOCostQoSConfigFile, &ioCostQoSConfigs) 202 if err != nil { 203 general.Errorf("load IOCostQoSConfigs failed with error: %v", err) 204 return 205 } 206 207 ioCostModelConfigs := make(map[DevModel]*common.IOCostModelData) 208 err = general.LoadJsonConfig(conf.IOCostModelConfigFile, &ioCostModelConfigs) 209 if err != nil { 210 general.Errorf("load IOCostModelConfigs failed with error: %v", err) 211 return 212 } 213 214 var targetDeviceNames []string 215 216 targetDeviceNames, err = getAllDeviceNames() 217 if err != nil { 218 general.Errorf("get targetDevices with error: %v", err) 219 return 220 } 221 222 general.Infof("targetDeviceNames: %+v to apply io cost configurations", targetDeviceNames) 223 224 if len(targetDeviceNames) == 0 { 225 general.Warningf("empty targetDeviceNames") 226 return 227 } 228 229 devsIDToModel, err := getDevicesIdToModel(targetDeviceNames) 230 if err != nil { 231 general.Errorf("getDevicesIdToModel failed with error: %v", err) 232 return 233 } 234 235 applyIOCostQoSWithDefault(ioCostQoSConfigs, devsIDToModel) 236 applyIOCostModelWithDefault(ioCostModelConfigs, devsIDToModel) 237 } 238 239 func SetIOCost(conf *coreconfig.Configuration, 240 _ interface{}, 241 _ *dynamicconfig.DynamicAgentConfiguration, 242 emitter metrics.MetricEmitter, 243 metaServer *metaserver.MetaServer, 244 ) { 245 general.Infof("called") 246 247 if conf == nil { 248 general.Errorf("nil extraConf") 249 return 250 } else if emitter == nil { 251 general.Errorf("nil emitter") 252 return 253 } else if metaServer == nil { 254 general.Errorf("nil metaServer") 255 return 256 } 257 258 // EnableSettingIOCost featuregate. 259 if !conf.EnableSettingIOCost { 260 general.Infof("SetIOCost disabled.") 261 // If EnableSettingIOCost was disabled, we should never enable io.cost. 262 initializeOnce.Do(func() { 263 disableIOCost(conf) 264 }) 265 return 266 } 267 268 if !cgcommon.CheckCgroup2UnifiedMode() { 269 general.Infof("not in cgv2 environment, skip IOAsyncTaskFunc") 270 return 271 } 272 273 initializeOnce.Do(func() { 274 disableIOCost(conf) 275 applyIOCostConfig(conf, emitter) 276 }) 277 278 reportDevicesIOCostVrate(emitter) 279 }