github.com/intel/goresctrl@v0.5.0/cmd/sst-ctl/main.go (about) 1 /* 2 Copyright 2021 Intel Corporation 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 main 18 19 import ( 20 "flag" 21 "fmt" 22 "os" 23 "sort" 24 "strconv" 25 "strings" 26 27 goresctrlpath "github.com/intel/goresctrl/pkg/path" 28 "github.com/intel/goresctrl/pkg/sst" 29 "github.com/intel/goresctrl/pkg/utils" 30 ) 31 32 var ( 33 // Global command line flags 34 packageIds string 35 ) 36 37 type subCmd func([]string) error 38 39 var subCmds = map[string]subCmd{ 40 "info": subCmdInfo, 41 "bf": subCmdBF, 42 "cp": subCmdCP, 43 } 44 45 func main() { 46 cmds := make([]string, 0, len(subCmds)) 47 for c := range subCmds { 48 cmds = append(cmds, c) 49 } 50 sort.Strings(cmds) 51 allCmds := strings.Join(cmds, ", ") 52 53 if len(os.Args) < 2 { 54 fmt.Printf("missing sub-command, must be one of: %s\n", allCmds) 55 os.Exit(1) 56 } 57 58 // Run sub-command 59 cmd, ok := subCmds[os.Args[1]] 60 if !ok { 61 fmt.Printf("unknown sub-command %q, must be of: %s\n", os.Args[1], allCmds) 62 os.Exit(1) 63 } 64 65 if err := cmd(os.Args[2:]); err != nil { 66 fmt.Printf("ERROR: sub-command %q failed: %v\n", os.Args[1], err) 67 os.Exit(1) 68 } 69 } 70 71 func addGlobalFlags(flagset *flag.FlagSet) { 72 flagset.StringVar(&packageIds, "package", "", "One or more physical package id") 73 flagset.Func("prefix", "set mount prefix for system directories", func(s string) error { 74 goresctrlpath.SetPrefix(s) 75 return nil 76 }) 77 } 78 79 func printPackageInfo(pkgId ...int) error { 80 info, err := sst.GetPackageInfo(pkgId...) 81 if err != nil { 82 return err 83 } 84 85 fmt.Println(utils.DumpJSON(info)) 86 87 return nil 88 } 89 90 // TODO: Move this functionality into utils.NewIdSetFromString() 91 func str2slice(str string) []int { 92 var s []int 93 94 for _, str := range strings.Split(str, ",") { 95 if str == "" { 96 continue 97 } 98 99 id, err := strconv.ParseUint(str, 10, 0) 100 if err != nil { 101 fmt.Printf("invalid value '%s': %v", str, err) 102 continue 103 } 104 105 s = append(s, int(id)) 106 } 107 108 return s 109 } 110 111 func subCmdInfo(args []string) error { 112 // Parse command line args 113 flags := flag.NewFlagSet("info", flag.ExitOnError) 114 addGlobalFlags(flags) 115 if err := flags.Parse(args); err != nil { 116 return err 117 } 118 119 return printPackageInfo(str2slice(packageIds)...) 120 } 121 122 func enableBF(pkgId ...int) error { 123 if len(pkgId) == 0 { 124 fmt.Printf("Enabling BF for all packages\n") 125 } else { 126 fmt.Printf("Enabling BF for package(s) %v\n", pkgId) 127 } 128 129 err := sst.EnableBF(pkgId...) 130 if err != nil { 131 return err 132 } 133 134 return printPackageInfo(pkgId...) 135 } 136 137 func disableBF(pkgId ...int) error { 138 if len(pkgId) == 0 { 139 fmt.Printf("Disabling BF for all packages\n") 140 } else { 141 fmt.Printf("Disabling BF for package(s) %v\n", pkgId) 142 } 143 144 err := sst.DisableBF(pkgId...) 145 if err != nil { 146 return err 147 } 148 149 return printPackageInfo(pkgId...) 150 } 151 152 func subCmdBF(args []string) error { 153 var enable, disable bool 154 155 flags := flag.NewFlagSet("bf", flag.ExitOnError) 156 flags.BoolVar(&enable, "enable", false, "enable feature") 157 flags.BoolVar(&disable, "disable", false, "disable feature") 158 addGlobalFlags(flags) 159 160 if err := flags.Parse(args); err != nil { 161 return err 162 } 163 164 if (!enable && !disable) || (enable && disable) { 165 fmt.Printf("Please provide either -enable or -disable flag\n") 166 return nil 167 } 168 169 var err error 170 171 pkgs := str2slice(packageIds) 172 173 if enable { 174 err = enableBF(pkgs...) 175 } else { 176 err = disableBF(pkgs...) 177 } 178 179 return err 180 } 181 182 func getPackage(packageStr string, cpus utils.IDSet) (map[int]*sst.SstPackageInfo, *sst.SstPackageInfo, []int, error) { 183 var infomap map[int]*sst.SstPackageInfo 184 var info *sst.SstPackageInfo 185 var packageId int 186 var err error 187 188 // If user has specified a package, then all the CPUs must belong to it. 189 pkgs := str2slice(packageStr) 190 if len(pkgs) > 1 { 191 fmt.Printf("Only one package can be configured at a time (you have %d)\n", len(pkgs)) 192 return nil, nil, nil, fmt.Errorf("Provide one package value only") 193 } 194 195 if len(pkgs) == 0 { 196 // User has not specified a package, figure it out from the 197 // first CPU in the list. 198 infomap, err = sst.GetPackageInfo() 199 if err != nil { 200 return nil, nil, nil, err 201 } 202 203 for packageId, info = range infomap { 204 if sst.CheckPackageCpus(info, cpus) { 205 pkgs = append(pkgs, packageId) 206 break 207 } 208 } 209 } else { 210 // User has specified one package, make sure all the CPUs belong to it. 211 infomap, err = sst.GetPackageInfo(pkgs...) 212 if err != nil { 213 return nil, nil, nil, err 214 } 215 216 for packageId, info = range infomap { 217 if !sst.CheckPackageCpus(info, cpus) { 218 fmt.Printf("All the CPUs %v must belong to one specific package\n", cpus) 219 return nil, nil, nil, fmt.Errorf("Not all CPUs belong to package %d", packageId) 220 } 221 222 pkgs = append(pkgs, packageId) 223 break 224 } 225 } 226 227 return infomap, info, pkgs, nil 228 } 229 230 // TODO: Instead of all CP parameters groupped together, separate them like this. 231 // sst-ctl cp enable 232 // sst-ctl cp disable 233 // sst-ctl cp configure -clos=CLOS... 234 // sst-ctl cp assign... 235 236 func subCmdCP(args []string) error { 237 var enable, disable, reset bool 238 239 // Clos setup variables 240 var epp, minFreq, maxFreq, desiredFreq, proportionalPriority, clos int 241 242 // CPU to Clos mapping variables 243 var cpuStr string 244 var cpus utils.IDSet 245 246 var packageId int 247 var pkgs []int 248 var info *sst.SstPackageInfo 249 var infomap map[int]*sst.SstPackageInfo 250 var err error 251 var priority int 252 253 defaultMaxFreq := 0xff 254 255 flags := flag.NewFlagSet("cp", flag.ExitOnError) 256 flags.BoolVar(&enable, "enable", false, "Enable feature") 257 flags.BoolVar(&disable, "disable", false, "Disable feature") 258 flags.BoolVar(&reset, "reset", false, "Reset CP to a known state") 259 flags.IntVar(&clos, "clos", -1, "Class of service (0 - 3)") 260 flags.IntVar(&epp, "epp", 0, "Energy Performance Preference value, Lower value favors performance, and higher value favors power. The value can be between 0 and 15. The default value is 0.") 261 flags.IntVar(&minFreq, "min", 0, "Clos minimum frequency MHz") 262 flags.IntVar(&maxFreq, "max", defaultMaxFreq, "Clos maximum frequency MHz") 263 flags.IntVar(&desiredFreq, "desired", 0, "Clos desired frequency MHz") 264 flags.IntVar(&proportionalPriority, "proportional", 0, "Clos proportional priority weight. Used if CP priority mode is 0 (Proportional)") 265 flags.IntVar(&priority, "priority", 1, "CP priority mode. 0 is Proportional, 1 is Ordered.") 266 flags.StringVar(&cpuStr, "cpus", "", "List of CPUs assigned to the Clos.") 267 268 addGlobalFlags(flags) 269 270 flags.Usage = func() { 271 flags.PrintDefaults() 272 273 fmt.Fprintf(os.Stderr, "\nExample usage:\n\n") 274 fmt.Fprintf(os.Stderr, "First reset CP to default:\n\t%s cp -reset\n\n", os.Args[0]) 275 fmt.Fprintf(os.Stderr, "Then set the CLOS values:\n\t%s cp -clos 1 -desired 280 -epp 1 -max 30 -min 21 -priority 1 -package 0\n\n", os.Args[0]) 276 fmt.Fprintf(os.Stderr, "Then bind CPUs to a CLOS:\n\t%s cp -clos 1 -cpus 1,3,5,6\n\n", os.Args[0]) 277 fmt.Fprintf(os.Stderr, "Finally enable CP:\n\t%s cp -enable -package 0\n\n", os.Args[0]) 278 } 279 280 if err := flags.Parse(args); err != nil { 281 return err 282 } 283 284 if reset { 285 err := sst.ResetCPConfig() 286 _ = printPackageInfo() 287 return err 288 } 289 290 if enable && disable { 291 return fmt.Errorf("Please provide either -enable or -disable flag") 292 } 293 294 // If user specifies a list of CPUs, then he/she wants to assign those 295 // CPUs to a specific CLOS. If the -cpus option is not set, then user 296 // wants to configure the actual CLOS values. Both operations cannot be 297 // done at the same time. 298 299 // If user specifies a list of CPUs, then the package option is ignored. 300 // Verify that all the CPUs belong to one specific package. 301 if cpuStr != "" { 302 cpus = utils.NewIDSet(str2slice(cpuStr)...) 303 304 infomap, info, pkgs, err = getPackage(packageIds, cpus) 305 if err != nil { 306 return fmt.Errorf("Cannot get CPUs %v package: %w", cpus, err) 307 } 308 309 if len(pkgs) == 0 { 310 return fmt.Errorf("All the CPUs %v must belong to one specific package", cpus) 311 } 312 313 if clos < 0 { 314 return fmt.Errorf("Clos not set, use -clos option") 315 } 316 317 cpu2Clos := make(sst.ClosCPUSet, 1) 318 cpu2Clos[clos] = cpus 319 320 if err := sst.ConfigureCP(info, priority, &cpu2Clos); err != nil { 321 return err 322 } 323 324 } else if clos >= 0 { 325 pkgs = str2slice(packageIds) 326 if len(pkgs) == 0 { 327 return fmt.Errorf("No packages set, invalid value %q", packageIds) 328 } 329 330 closinfo := sst.SstClosInfo{ 331 EPP: epp, 332 ProportionalPriority: proportionalPriority, 333 MinFreq: minFreq, 334 MaxFreq: maxFreq, 335 DesiredFreq: desiredFreq, 336 } 337 338 infomap, err = sst.GetPackageInfo(pkgs...) 339 if err != nil { 340 return fmt.Errorf("Cannot get package info: %w", err) 341 } 342 343 for _, info = range infomap { 344 if err := sst.ClosSetup(info, clos, &closinfo); err != nil { 345 return fmt.Errorf("Cannot set Clos: %w", err) 346 } 347 } 348 } else { 349 if (!enable && !disable) && clos < 0 { 350 return fmt.Errorf("Clos not set, use -clos option") 351 } 352 353 // Print information if user just wants to enable / disable CP 354 infomap, _ = sst.GetPackageInfo(pkgs...) 355 } 356 357 if enable || disable { 358 for packageId, info = range infomap { 359 if enable { 360 fmt.Printf("Enabling CP for package %d\n", packageId) 361 362 err = sst.EnableCP(info) 363 if err != nil { 364 return err 365 } 366 } else if disable { 367 fmt.Printf("Disabling CP for package %d\n", packageId) 368 369 err = sst.DisableCP(info) 370 if err != nil { 371 return err 372 } 373 } 374 } 375 } 376 377 for packageId = range infomap { 378 // If we add a CPU to Clos, punit might add another CPU to same Clos. 379 // Make sure we have re-read the package info before printing it 380 _ = printPackageInfo(packageId) 381 } 382 383 return nil 384 }