github.com/intel/goresctrl@v0.5.0/pkg/sst/sst.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 sst 18 19 import ( 20 "fmt" 21 stdlog "log" 22 "os" 23 24 grclog "github.com/intel/goresctrl/pkg/log" 25 goresctrlpath "github.com/intel/goresctrl/pkg/path" 26 "github.com/intel/goresctrl/pkg/utils" 27 ) 28 29 // SstPackageInfo contains status of Intel Speed Select Technologies (SST) 30 // for one CPU package 31 type SstPackageInfo struct { 32 // Package related to this SST info 33 pkg *cpuPackageInfo 34 35 // Gereric PP info 36 PPSupported bool 37 PPLocked bool 38 PPVersion int 39 PPCurrentLevel int 40 PPMaxLevel int 41 42 // Information about the currently active PP level 43 CPSupported bool 44 CPEnabled bool 45 CPPriority CPPriorityType 46 BFSupported bool 47 BFEnabled bool 48 BFCores utils.IDSet 49 TFSupported bool 50 TFEnabled bool 51 52 ClosInfo [NumClos]SstClosInfo 53 ClosCPUInfo ClosCPUSet 54 } 55 56 // NumClos is the number of CLOSes suported by SST-CP 57 const NumClos = 4 58 59 // SstClosInfo contains parameters of one CLOS of SST-CP 60 type SstClosInfo struct { 61 EPP int 62 ProportionalPriority int 63 MinFreq int 64 MaxFreq int 65 DesiredFreq int 66 } 67 68 // CPPriorityType denotes the type CLOS priority ordering used in SST-CP 69 type CPPriorityType int 70 71 const ( 72 Proportional CPPriorityType = 0 73 Ordered CPPriorityType = 1 74 ) 75 76 // ClosCPUSet contains mapping from Clos id to a set of CPU ids 77 type ClosCPUSet map[int]utils.IDSet 78 79 var sstlog grclog.Logger = grclog.NewLoggerWrapper(stdlog.New(os.Stderr, "[ sst ] ", 0)) 80 81 func isstDevPath() string { return goresctrlpath.Path("dev/isst_interface") } 82 83 // SstSupported returns true if Intel Speed Select Technologies (SST) is supported 84 // by the system and can be interfaced via the Linux kernel device 85 func SstSupported() bool { 86 devPath := isstDevPath() 87 if _, err := os.Stat(devPath); err != nil { 88 if !os.IsNotExist(err) { 89 sstlog.Warnf("failed to access sst device %q: %v", devPath, err) 90 } else { 91 sstlog.Debugf("sst device %q does not exist", devPath) 92 } 93 return false 94 } 95 return true 96 } 97 98 // Check that a list of CPUs belong to a given package 99 func CheckPackageCpus(info *SstPackageInfo, cpus utils.IDSet) bool { 100 return info.pkg.hasCpus(cpus) 101 } 102 103 // GetPackageInfo returns information of those packages given as a parameter 104 // or all if none given. 105 func GetPackageInfo(pkgs ...int) (map[int]*SstPackageInfo, error) { 106 var numPkgs int 107 var pkglist []int 108 109 // Get topology information from sysfs 110 packages, err := getOnlineCpuPackages() 111 if err != nil { 112 return nil, fmt.Errorf("failed to determine cpu topology: %w", err) 113 } 114 115 if len(pkgs) == 0 { 116 for i := range packages { 117 pkglist = append(pkglist, i) 118 } 119 } else { 120 for _, i := range pkgs { 121 if _, ok := packages[i]; !ok { 122 return nil, fmt.Errorf("cpu package %d not present", i) 123 } else { 124 pkglist = append(pkglist, i) 125 } 126 } 127 } 128 129 numPkgs = len(pkglist) 130 infomap := make(map[int]*SstPackageInfo, numPkgs) 131 132 for _, i := range pkglist { 133 info, err := getSinglePackageInfo(packages[i]) 134 if err != nil { 135 return nil, err 136 } 137 138 infomap[i] = &info 139 } 140 141 return infomap, nil 142 } 143 144 // getSinglePackageInfo returns information of the SST configuration of one cpu 145 // package. 146 func getSinglePackageInfo(pkg *cpuPackageInfo) (SstPackageInfo, error) { 147 info := SstPackageInfo{} 148 149 cpu := pkg.cpus[0] // We just need to pass one logical cpu from the pkg as an arg 150 151 var rsp uint32 152 var err error 153 154 // Read perf-profile feature info 155 if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_GET_LEVELS_INFO, 0, 0); err != nil { 156 return info, fmt.Errorf("failed to read SST PP info: %v", err) 157 } 158 info.PPSupported = getBits(rsp, 31, 31) != 0 159 info.PPLocked = getBits(rsp, 24, 24) != 0 160 info.PPCurrentLevel = int(getBits(rsp, 16, 23)) 161 info.PPMaxLevel = int(getBits(rsp, 8, 15)) 162 info.PPVersion = int(getBits(rsp, 0, 7)) 163 info.pkg = pkg 164 165 // Forget about older hw with partial/convoluted support 166 if info.PPVersion < 3 { 167 sstlog.Infof("SST PP version %d (less than 3), giving up...") 168 return info, nil 169 } 170 171 // Read the status of currently active perf-profile 172 if !info.PPSupported { 173 sstlog.Debugf("SST PP feature not supported, only profile level %d is valid", info.PPCurrentLevel) 174 } 175 176 if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_CONTROL, 0, uint32(info.PPCurrentLevel)); err != nil { 177 return info, fmt.Errorf("failed to read SST BF/TF status: %v", err) 178 } 179 180 info.BFSupported = isBitSet(rsp, 1) 181 info.BFEnabled = isBitSet(rsp, 17) 182 183 info.TFSupported = isBitSet(rsp, 0) 184 info.TFEnabled = isBitSet(rsp, 16) 185 186 // Read base-frequency info 187 if info.BFSupported { 188 info.BFCores = utils.IDSet{} 189 190 punitCoreIDs := make(map[utils.ID]utils.IDSet, len(pkg.cpus)) 191 var maxPunitCore utils.ID 192 for _, id := range pkg.cpus { 193 pc, err := punitCPU(id) 194 if err != nil { 195 return info, err 196 } 197 punitCore := pc >> 1 198 if _, ok := punitCoreIDs[punitCore]; !ok { 199 punitCoreIDs[punitCore] = utils.IDSet{} 200 } 201 punitCoreIDs[punitCore].Add(id) 202 if punitCore > maxPunitCore { 203 maxPunitCore = punitCore 204 } 205 } 206 207 // Read out core masks in batches of 32 (32 bits per response) 208 for i := 0; i <= int(maxPunitCore)/32; i++ { 209 if rsp, err = sendMboxCmd(cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_CORE_MASK_INFO, 0, uint32(info.PPCurrentLevel+(i<<8))); err != nil { 210 return info, fmt.Errorf("failed to read SST BF core mask (#%d): %v", i, err) 211 } 212 for bit := 0; bit < 32; bit++ { 213 if isBitSet(rsp, uint32(bit)) { 214 info.BFCores.Add(punitCoreIDs[utils.ID(i*32+bit)].Members()...) 215 } 216 } 217 } 218 } 219 220 // Read core-power feature info 221 if rsp, err = sendMboxCmd(cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0); err != nil { 222 return info, fmt.Errorf("failed to read SST CP info: %v", err) 223 } 224 225 info.CPSupported = isBitSet(rsp, 0) 226 info.CPEnabled = isBitSet(rsp, 16) 227 228 if info.CPSupported { 229 if rsp, err = sendMboxCmd(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0); err != nil { 230 return info, fmt.Errorf("failed to read SST CP status: %v", err) 231 } 232 233 info.CPPriority = CPPriorityType(getBits(rsp, 2, 2)) 234 info.ClosCPUInfo = make(map[int]utils.IDSet, NumClos) 235 236 for i := 0; i < NumClos; i++ { 237 if rsp, err = sendClosCmd(cpu, CLOS_PM_CLOS, uint32(i), 0); err != nil { 238 return info, fmt.Errorf("failed to read SST CLOS #%d info: %v", i, err) 239 } 240 241 info.ClosInfo[i] = SstClosInfo{ 242 EPP: int(getBits(rsp, 0, 3)), 243 ProportionalPriority: int(getBits(rsp, 4, 7)), 244 MinFreq: int(getBits(rsp, 8, 15)), 245 MaxFreq: int(getBits(rsp, 16, 23)), 246 DesiredFreq: int(getBits(rsp, 24, 31)), 247 } 248 } 249 250 for _, id := range pkg.cpus { 251 closId, err := GetCPUClosID(id) 252 if err != nil { 253 continue 254 } 255 256 if info.ClosCPUInfo[closId] == nil { 257 info.ClosCPUInfo[closId] = utils.NewIDSet(id) 258 } else { 259 info.ClosCPUInfo[closId].Add(id) 260 } 261 } 262 } 263 264 return info, nil 265 } 266 267 func getPunitCoreId(cpu utils.ID) (uint32, error) { 268 p, err := punitCPU(cpu) 269 if err != nil { 270 return 0, err 271 } 272 punitCore := uint32(p) >> 1 273 274 return punitCore, nil 275 } 276 277 // GetCPUClosID returns the SST-CP CLOS id that a cpu is associated with. 278 func GetCPUClosID(cpu utils.ID) (int, error) { 279 punitCore, err := getPunitCoreId(cpu) 280 if err != nil { 281 return -1, fmt.Errorf("invalid core id %d for cpu %d: %v", punitCore, cpu, err) 282 } 283 284 rsp, err := sendClosCmd(cpu, CLOS_PQR_ASSOC, punitCore, 0) 285 if err != nil { 286 return -1, fmt.Errorf("failed to read CLOS number of cpu %d: %v", cpu, err) 287 } 288 return int(getBits(rsp, 16, 17)), nil 289 } 290 291 func getBits(val, i, j uint32) uint32 { 292 lsb := i 293 msb := j 294 if i > j { 295 lsb = j 296 msb = i 297 } 298 return (val >> lsb) & ((1 << (msb - lsb + 1)) - 1) 299 } 300 301 func isBitSet(val, n uint32) bool { 302 return val&(1<<n) != 0 303 } 304 305 func setBit(val, n uint32) uint32 { 306 return val | (1 << n) 307 } 308 309 func clearBit(val, n uint32) uint32 { 310 return val &^ (1 << n) 311 } 312 313 func setBFStatus(info *SstPackageInfo, status bool) error { 314 rsp, err := sendMboxCmd(info.pkg.cpus[0], CONFIG_TDP, CONFIG_TDP_GET_TDP_CONTROL, 0, uint32(info.PPCurrentLevel)) 315 if err != nil { 316 return fmt.Errorf("failed to read SST status: %w", err) 317 } 318 319 req := clearBit(rsp, 17) 320 if status { 321 req = setBit(rsp, 17) 322 } 323 324 if _, err = sendMboxCmd(info.pkg.cpus[0], CONFIG_TDP, CONFIG_TDP_SET_TDP_CONTROL, 0, req); err != nil { 325 return fmt.Errorf("failed to enable SST %s: %w", "BF", err) 326 } 327 328 info.BFEnabled = status 329 330 return nil 331 } 332 333 func setScalingMin2CPUInfoMax(info *SstPackageInfo) error { 334 for _, cpu := range info.pkg.cpus { 335 err := setCPUScalingMin2CPUInfoMaxFreq(cpu) 336 if err != nil { 337 return err 338 } 339 } 340 341 return nil 342 } 343 344 func enableBF(info *SstPackageInfo) error { 345 if !info.BFSupported { 346 return fmt.Errorf("SST BF not supported") 347 } 348 349 if err := setBFStatus(info, true); err != nil { 350 return err 351 } 352 353 if err := setScalingMin2CPUInfoMax(info); err != nil { 354 return err 355 } 356 357 return nil 358 } 359 360 // EnableBF enables SST-BF and sets it up properly 361 func EnableBF(pkgs ...int) error { 362 if !isHWPEnabled() { 363 return fmt.Errorf("HWP is not enabled") 364 } 365 366 info, err := GetPackageInfo(pkgs...) 367 if err != nil { 368 return err 369 } 370 371 for _, i := range info { 372 err = enableBF(i) 373 if err != nil { 374 // Ignore but log error as there might be packages in the 375 // user supplied list that do not exists 376 sstlog.Errorf("sst-bf : %w", err) 377 } 378 } 379 380 return nil 381 } 382 383 func setScalingMin2CPUInfoMin(info *SstPackageInfo) error { 384 for _, cpu := range info.pkg.cpus { 385 err := setCPUScalingMin2CPUInfoMinFreq(cpu) 386 if err != nil { 387 return err 388 } 389 } 390 391 return nil 392 } 393 394 func disableBF(info *SstPackageInfo) error { 395 if !info.BFSupported { 396 return fmt.Errorf("SST BF not supported") 397 } 398 399 if err := setBFStatus(info, false); err != nil { 400 return err 401 } 402 403 if err := setScalingMin2CPUInfoMin(info); err != nil { 404 return err 405 } 406 407 return nil 408 } 409 410 // DisableBF disables SST-BF and clears things properly 411 func DisableBF(pkgs ...int) error { 412 info, err := GetPackageInfo(pkgs...) 413 if err != nil { 414 return err 415 } 416 417 for _, i := range info { 418 err = disableBF(i) 419 if err != nil { 420 // Ignore but log error as there might be packages in the 421 // user supplied list that do not exists 422 sstlog.Errorf("sst-bf : %w", err) 423 } 424 } 425 426 return nil 427 } 428 429 func sendClosCmd(cpu utils.ID, subCmd uint16, parameter uint32, reqData uint32) (uint32, error) { 430 var id, offset uint32 431 432 switch subCmd { 433 case CLOS_PQR_ASSOC: 434 id = parameter & 0xff // core id 435 offset = PQR_ASSOC_OFFSET 436 case CLOS_PM_CLOS: 437 id = parameter & 0x03 // clos id 438 offset = PM_CLOS_OFFSET 439 case CLOS_STATUS: 440 fallthrough 441 default: 442 return 0, nil 443 } 444 445 return sendMMIOCmd(cpu, (id<<2)+offset, reqData, isBitSet(parameter, MBOX_CMD_WRITE_BIT)) 446 } 447 448 func saveClos(closInfo *SstClosInfo, cpu utils.ID, clos int) error { 449 req := closInfo.EPP & 0x0f 450 req |= (closInfo.ProportionalPriority & 0x0f) << 4 451 req |= (closInfo.MinFreq & 0xff) << 8 452 req |= (closInfo.MaxFreq & 0xff) << 16 453 req |= (closInfo.DesiredFreq & 0xff) << 24 454 455 param := setBit(uint32(clos), MBOX_CMD_WRITE_BIT) 456 457 if _, err := sendClosCmd(cpu, CLOS_PM_CLOS, param, uint32(req)); err != nil { 458 return fmt.Errorf("failed to save Clos: %v", err) 459 } 460 461 return nil 462 } 463 464 func associate2Clos(cpu utils.ID, clos int) error { 465 coreId, err := getPunitCoreId(cpu) 466 if err != nil { 467 return fmt.Errorf("invalid core id %d for cpu %d: %v", coreId, cpu, err) 468 } 469 470 req := (clos & 0x03) << 16 471 param := setBit(coreId, MBOX_CMD_WRITE_BIT) 472 473 if _, err := sendClosCmd(cpu, CLOS_PQR_ASSOC, param, uint32(req)); err != nil { 474 return fmt.Errorf("failed to associate cpu %d to clos %d: %v", cpu, clos, err) 475 } 476 477 return nil 478 } 479 480 func writePMConfig(info *SstPackageInfo, cpu utils.ID, enable bool) (uint32, error) { 481 var req uint32 482 483 if enable { 484 req = setBit(0, 16) 485 } 486 487 if _, err := sendMboxCmd(cpu, WRITE_PM_CONFIG, PM_FEATURE, 0, req); err != nil { 488 return 0, fmt.Errorf("failed to set SST-CP status: %v", err) 489 } 490 491 rsp, err := sendMboxCmd(cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0) 492 if err != nil { 493 return 0, fmt.Errorf("failed to get SST-CP status: %v", err) 494 } 495 496 return rsp, nil 497 } 498 499 func writeClosPmQosConfig(info *SstPackageInfo, cpu utils.ID, enable bool) error { 500 var req uint32 501 502 param := setBit(0, MBOX_CMD_WRITE_BIT) 503 504 if enable { 505 req = setBit(0, 1) 506 507 if info.CPPriority > 0 { 508 req = setBit(req, 2) 509 } 510 } 511 512 if _, err := sendMboxCmd(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, param, req); err != nil { 513 return fmt.Errorf("failed to set SST-CP status: %v", err) 514 } 515 516 return nil 517 } 518 519 func enableCP(info *SstPackageInfo, cpu utils.ID) (uint32, error) { 520 if err := writeClosPmQosConfig(info, cpu, true); err != nil { 521 return 0, fmt.Errorf("Cannot set Clos status: %v", err) 522 } 523 524 return writePMConfig(info, cpu, true) 525 } 526 527 func disableCP(info *SstPackageInfo, cpu utils.ID) (uint32, error) { 528 if err := writeClosPmQosConfig(info, cpu, false); err != nil { 529 return 0, fmt.Errorf("Cannot set Clos status: %v", err) 530 } 531 532 return writePMConfig(info, cpu, false) 533 } 534 535 func setDefaultClosParam(info *SstPackageInfo, cpu utils.ID) error { 536 defaultConfig := &SstClosInfo{MaxFreq: 255} 537 538 for clos := 0; clos < 4; clos++ { 539 if err := saveClos(defaultConfig, cpu, clos); err != nil { 540 return err 541 } 542 } 543 544 return nil 545 } 546 547 func assignCPU2Clos(info *SstPackageInfo, clos int) error { 548 sstlog.Debugf("Setting Clos %d for cpus %v\n", clos, info.ClosCPUInfo[clos].Members()) 549 550 for _, cpu := range info.ClosCPUInfo[clos].Members() { 551 if err := associate2Clos(cpu, clos); err != nil { 552 return fmt.Errorf("failed to associate cpu %d to clos %d: %v", cpu, clos, err) 553 } 554 } 555 556 return nil 557 } 558 559 // ConfigureCP will allow caller to configure CPUs to various Clos. 560 func ConfigureCP(info *SstPackageInfo, priority int, cpu2clos *ClosCPUSet) error { 561 if priority < 0 || priority > 1 { 562 return fmt.Errorf("Invalid CP priority value %d (valid 0 or 1)", priority) 563 } 564 565 if info.ClosCPUInfo == nil { 566 info.ClosCPUInfo = make(map[int]utils.IDSet, len(*cpu2clos)) 567 } 568 569 for clos, cpus := range *cpu2clos { 570 info.ClosCPUInfo[clos] = cpus.Clone() 571 572 // Remove the CPU from other Clos if found 573 for i := 0; i < NumClos; i++ { 574 if i == clos { 575 continue 576 } 577 578 for id := range cpus { 579 info.ClosCPUInfo[i].Del(id) 580 } 581 } 582 583 if err := assignCPU2Clos(info, clos); err != nil { 584 return err 585 } 586 } 587 588 info.CPPriority = CPPriorityType(priority) 589 590 return nil 591 } 592 593 // ClosSetup stores the user supplied Clos information into punit 594 func ClosSetup(info *SstPackageInfo, clos int, closInfo *SstClosInfo) error { 595 if clos < 0 || clos >= NumClos { 596 return fmt.Errorf("Invalid Clos value (%d)", clos) 597 } 598 599 if closInfo.MinFreq < 0 || closInfo.MinFreq > 255 { 600 return fmt.Errorf("Invalid min freq (%d)", closInfo.MinFreq) 601 } 602 603 if closInfo.MaxFreq < 0 || closInfo.MaxFreq > 255 { 604 return fmt.Errorf("Invalid max freq (%d)", closInfo.MaxFreq) 605 } 606 607 if closInfo.MinFreq > closInfo.MaxFreq { 608 return fmt.Errorf("Min freq %d must be smaller than max freq %d", closInfo.MinFreq, closInfo.MaxFreq) 609 } 610 611 if closInfo.DesiredFreq < 0 || closInfo.DesiredFreq > 255 { 612 return fmt.Errorf("Invalid value %d for desired freq", closInfo.DesiredFreq) 613 } 614 615 if closInfo.EPP < 0 || closInfo.EPP > 15 { 616 return fmt.Errorf("Invalid value %d for EPP", closInfo.EPP) 617 } 618 619 if closInfo.ProportionalPriority < 0 || closInfo.ProportionalPriority > 15 { 620 return fmt.Errorf("Invalid value %d for proportionalPriority", closInfo.ProportionalPriority) 621 } 622 623 info.ClosInfo[clos] = *closInfo 624 625 return saveClos(&info.ClosInfo[clos], info.pkg.cpus[0], clos) 626 } 627 628 // ResetCPConfig will bring the system to a known state. This means that all 629 // CLOS groups are reset to their default values, all package cores are assigned to 630 // CLOS group 0 and ordered priority mode is enabled. 631 func ResetCPConfig() error { 632 infomap, err := GetPackageInfo() 633 if err != nil { 634 return err 635 } 636 637 for _, info := range infomap { 638 for _, cpu := range info.pkg.cpus { 639 if info.pkg.cpus[0] == cpu { 640 if err := setDefaultClosParam(info, cpu); err != nil { 641 return err 642 } 643 } 644 645 if err := associate2Clos(cpu, 0); err != nil { 646 return fmt.Errorf("failed to associate cpu %d to clos %d: %w", cpu, 0, err) 647 } 648 } 649 } 650 651 return nil 652 } 653 654 // EnableCP enables SST-CP feature 655 func EnableCP(info *SstPackageInfo) error { 656 if !info.CPSupported { 657 return fmt.Errorf("SST CP not supported") 658 } 659 660 if len(info.ClosCPUInfo) == 0 { 661 return fmt.Errorf("failed to enable CP: Clos to CPU mapping missing") 662 } 663 664 rsp, err := enableCP(info, info.pkg.cpus[0]) 665 if err != nil { 666 return fmt.Errorf("failed to enable SST-CP: %v", err) 667 } 668 669 info.CPSupported = isBitSet(rsp, 0) 670 info.CPEnabled = isBitSet(rsp, 16) 671 672 return nil 673 } 674 675 // DisableCP disables SST-CP feature 676 func DisableCP(info *SstPackageInfo) error { 677 if !info.CPSupported { 678 return fmt.Errorf("SST CP not supported") 679 } 680 681 if info.TFEnabled { 682 return fmt.Errorf("SST TF still enabled, disable it first.") 683 } 684 685 rsp, err := disableCP(info, info.pkg.cpus[0]) 686 if err != nil { 687 return fmt.Errorf("failed to disable SST-CP: %v", err) 688 } 689 690 info.CPSupported = isBitSet(rsp, 0) 691 info.CPEnabled = isBitSet(rsp, 16) 692 693 return nil 694 }