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  }