github.com/intel/goresctrl@v0.5.0/pkg/rdt/rdt.go (about) 1 /* 2 Copyright 2019 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 rdt implements an API for managing IntelĀ® RDT technologies via the 18 // resctrl pseudo-filesystem of the Linux kernel. It provides flexible 19 // configuration with a hierarchical approach for easy management of exclusive 20 // cache allocations. 21 // 22 // Goresctrl supports all available RDT technologies, i.e. L2 and L3 Cache 23 // Allocation (CAT) with Code and Data Prioritization (CDP) and Memory 24 // Bandwidth Allocation (MBA) plus Cache Monitoring (CMT) and Memory Bandwidth 25 // Monitoring (MBM). 26 // 27 // Basic usage example: 28 // 29 // rdt.SetLogger(logrus.New()) 30 // 31 // if err := rdt.Initialize(""); err != nil { 32 // return fmt.Errorf("RDT not supported: %v", err) 33 // } 34 // 35 // if err := rdt.SetConfigFromFile("/path/to/rdt.conf.yaml", false); err != nil { 36 // return fmt.Errorf("RDT configuration failed: %v", err) 37 // } 38 // 39 // if cls, ok := rdt.GetClass("my-class"); ok { 40 // // Set PIDs 12345 and 12346 to class "my-class" 41 // if err := cls.AddPids("12345", "12346"); err != nil { 42 // return fmt.Errorf("failed to add PIDs to RDT class: %v", err) 43 // } 44 // } 45 package rdt 46 47 import ( 48 "errors" 49 "fmt" 50 stdlog "log" 51 "os" 52 "path/filepath" 53 "sort" 54 "strconv" 55 "strings" 56 "syscall" 57 58 "sigs.k8s.io/yaml" 59 60 grclog "github.com/intel/goresctrl/pkg/log" 61 "github.com/intel/goresctrl/pkg/utils" 62 ) 63 64 const ( 65 // RootClassName is the name we use in our config for the special class 66 // that configures the "root" resctrl group of the system 67 RootClassName = "system/default" 68 // RootClassAlias is an alternative name for the root class 69 RootClassAlias = "" 70 ) 71 72 type control struct { 73 grclog.Logger 74 75 resctrlGroupPrefix string 76 conf config 77 rawConf Config 78 classes map[string]*ctrlGroup 79 } 80 81 var log grclog.Logger = grclog.NewLoggerWrapper(stdlog.New(os.Stderr, "[ rdt ] ", 0)) 82 83 var info *resctrlInfo 84 85 var rdt *control 86 87 // Function for removing resctrl groups from the filesystem. This is 88 // configurable because of unit tests. 89 var groupRemoveFunc func(string) error = os.Remove 90 91 // CtrlGroup defines the interface of one goresctrl managed RDT class. It maps 92 // to one CTRL group directory in the goresctrl pseudo-filesystem. 93 type CtrlGroup interface { 94 ResctrlGroup 95 96 // CreateMonGroup creates a new monitoring group under this CtrlGroup. 97 CreateMonGroup(name string, annotations map[string]string) (MonGroup, error) 98 99 // DeleteMonGroup deletes a monitoring group from this CtrlGroup. 100 DeleteMonGroup(name string) error 101 102 // DeleteMonGroups deletes all monitoring groups from this CtrlGroup. 103 DeleteMonGroups() error 104 105 // GetMonGroup returns a specific monitoring group under this CtrlGroup. 106 GetMonGroup(name string) (MonGroup, bool) 107 108 // GetMonGroups returns all monitoring groups under this CtrlGroup. 109 GetMonGroups() []MonGroup 110 } 111 112 // ResctrlGroup is the generic interface for resctrl CTRL and MON groups. It 113 // maps to one CTRL or MON group directory in the goresctrl pseudo-filesystem. 114 type ResctrlGroup interface { 115 // Name returns the name of the group. 116 Name() string 117 118 // GetPids returns the process ids assigned to the group. 119 GetPids() ([]string, error) 120 121 // AddPids assigns the given process ids to the group. 122 AddPids(pids ...string) error 123 124 // GetMonData retrieves the monitoring data of the group. 125 GetMonData() MonData 126 } 127 128 // MonGroup represents the interface to a RDT monitoring group. It maps to one 129 // MON group in the goresctrl filesystem. 130 type MonGroup interface { 131 ResctrlGroup 132 133 // Parent returns the CtrlGroup under which the monitoring group exists. 134 Parent() CtrlGroup 135 136 // GetAnnotations returns the annotations stored to the monitoring group. 137 GetAnnotations() map[string]string 138 } 139 140 // MonData contains monitoring stats of one monitoring group. 141 type MonData struct { 142 L3 MonL3Data 143 } 144 145 // MonL3Data contains L3 monitoring stats of one monitoring group. 146 type MonL3Data map[uint64]MonLeafData 147 148 // MonLeafData represents the raw numerical stats from one RDT monitor data leaf. 149 type MonLeafData map[string]uint64 150 151 // MonResource is the type of RDT monitoring resource. 152 type MonResource string 153 154 const ( 155 // MonResourceL3 is the RDT L3 cache monitor resource. 156 MonResourceL3 MonResource = "l3" 157 ) 158 159 type ctrlGroup struct { 160 resctrlGroup 161 162 monPrefix string 163 monGroups map[string]*monGroup 164 } 165 166 type monGroup struct { 167 resctrlGroup 168 169 annotations map[string]string 170 } 171 172 type resctrlGroup struct { 173 prefix string 174 name string 175 parent *ctrlGroup // parent for MON groups 176 } 177 178 // SetLogger sets the logger instance to be used by the package. This function 179 // may be called even before Initialize(). 180 func SetLogger(l grclog.Logger) { 181 log = l 182 if rdt != nil { 183 rdt.setLogger(l) 184 } 185 } 186 187 // Initialize detects RDT from the system and initializes control interface of 188 // the package. 189 func Initialize(resctrlGroupPrefix string) error { 190 var err error 191 192 info = nil 193 rdt = nil 194 195 // Get info from the resctrl filesystem 196 info, err = getRdtInfo() 197 if err != nil { 198 return err 199 } 200 201 r := &control{Logger: log, resctrlGroupPrefix: resctrlGroupPrefix} 202 203 // NOTE: we lose monitoring group annotations (i.e. prometheus metrics 204 // labels) on re-init 205 if r.classes, err = r.classesFromResctrlFs(); err != nil { 206 return fmt.Errorf("failed to initialize classes from resctrl fs: %v", err) 207 } 208 209 rdt = r 210 211 return nil 212 } 213 214 // DiscoverClasses discovers existing classes from the resctrl filesystem. 215 // Makes it possible to discover gropus with another prefix than was set with 216 // Initialize(). The original prefix is still used for monitoring groups. 217 func DiscoverClasses(resctrlGroupPrefix string) error { 218 if rdt != nil { 219 return rdt.discoverFromResctrl(resctrlGroupPrefix) 220 } 221 return fmt.Errorf("rdt not initialized") 222 } 223 224 // SetConfig (re-)configures the resctrl filesystem according to the specified 225 // configuration. 226 func SetConfig(c *Config, force bool) error { 227 if rdt != nil { 228 return rdt.setConfig(c, force) 229 } 230 return fmt.Errorf("rdt not initialized") 231 } 232 233 // SetConfigFromData takes configuration as raw data, parses it and 234 // reconfigures the resctrl filesystem. 235 func SetConfigFromData(data []byte, force bool) error { 236 cfg := &Config{} 237 if err := yaml.Unmarshal(data, &cfg); err != nil { 238 return fmt.Errorf("failed to parse configuration data: %v", err) 239 } 240 241 return SetConfig(cfg, force) 242 } 243 244 // SetConfigFromFile reads configuration from the filesystem and reconfigures 245 // the resctrl filesystem. 246 func SetConfigFromFile(path string, force bool) error { 247 data, err := os.ReadFile(path) 248 if err != nil { 249 return fmt.Errorf("failed to read config file: %v", err) 250 } 251 252 if err := SetConfigFromData(data, force); err != nil { 253 return err 254 } 255 256 log.Infof("configuration successfully loaded from %q", path) 257 return nil 258 } 259 260 // GetClass returns one RDT class. 261 func GetClass(name string) (CtrlGroup, bool) { 262 if rdt != nil { 263 return rdt.getClass(name) 264 } 265 return nil, false 266 } 267 268 // GetClasses returns all available RDT classes. 269 func GetClasses() []CtrlGroup { 270 if rdt != nil { 271 return rdt.getClasses() 272 } 273 return []CtrlGroup{} 274 } 275 276 // MonSupported returns true if RDT monitoring features are available. 277 func MonSupported() bool { 278 if rdt != nil { 279 return rdt.monSupported() 280 } 281 return false 282 } 283 284 // GetMonFeatures returns the available monitoring stats of each available 285 // monitoring technology. 286 func GetMonFeatures() map[MonResource][]string { 287 if rdt != nil { 288 return rdt.getMonFeatures() 289 } 290 return map[MonResource][]string{} 291 } 292 293 // IsQualifiedClassName returns true if given string qualifies as a class name 294 func IsQualifiedClassName(name string) bool { 295 // Must be qualified as a file name 296 return name == RootClassName || (len(name) < 4096 && name != "." && name != ".." && !strings.ContainsAny(name, "/\n")) 297 } 298 299 func (c *control) getClass(name string) (CtrlGroup, bool) { 300 cls, ok := c.classes[unaliasClassName(name)] 301 return cls, ok 302 } 303 304 func (c *control) getClasses() []CtrlGroup { 305 ret := make([]CtrlGroup, 0, len(c.classes)) 306 307 for _, v := range c.classes { 308 ret = append(ret, v) 309 } 310 sort.Slice(ret, func(i, j int) bool { return ret[i].Name() < ret[j].Name() }) 311 312 return ret 313 } 314 315 func (c *control) monSupported() bool { 316 return info.l3mon.Supported() 317 } 318 319 func (c *control) getMonFeatures() map[MonResource][]string { 320 ret := make(map[MonResource][]string) 321 if info.l3mon.Supported() { 322 ret[MonResourceL3] = append([]string{}, info.l3mon.monFeatures...) 323 } 324 325 return ret 326 } 327 328 func (c *control) setLogger(l grclog.Logger) { 329 c.Logger = l 330 } 331 332 func (c *control) setConfig(newConfig *Config, force bool) error { 333 c.Infof("configuration update") 334 335 conf, err := (*newConfig).resolve() 336 if err != nil { 337 return fmt.Errorf("invalid configuration: %v", err) 338 } 339 340 err = c.configureResctrl(conf, force) 341 if err != nil { 342 return fmt.Errorf("resctrl configuration failed: %v", err) 343 } 344 345 c.conf = conf 346 // TODO: we'd better create a deep copy 347 c.rawConf = *newConfig 348 c.Infof("configuration finished") 349 350 return nil 351 } 352 353 func (c *control) configureResctrl(conf config, force bool) error { 354 grclog.DebugBlock(c, "applying resolved config:", " ", "%s", utils.DumpJSON(conf)) 355 356 // Remove stale resctrl groups 357 classesFromFs, err := c.classesFromResctrlFs() 358 if err != nil { 359 return err 360 } 361 362 for name, cls := range classesFromFs { 363 if _, ok := conf.Classes[cls.name]; !isRootClass(cls.name) && !ok { 364 if !force { 365 tasks, err := cls.GetPids() 366 if err != nil { 367 return fmt.Errorf("failed to get resctrl group tasks: %v", err) 368 } 369 if len(tasks) > 0 { 370 return fmt.Errorf("refusing to remove non-empty resctrl group %q", cls.relPath("")) 371 } 372 } 373 log.Debugf("removing existing resctrl group %q", cls.relPath("")) 374 err = groupRemoveFunc(cls.path("")) 375 if err != nil { 376 return fmt.Errorf("failed to remove resctrl group %q: %v", cls.relPath(""), err) 377 } 378 379 delete(c.classes, name) 380 } 381 } 382 383 for name, cls := range c.classes { 384 if _, ok := conf.Classes[cls.name]; !ok || cls.prefix != c.resctrlGroupPrefix { 385 if !isRootClass(cls.name) { 386 log.Debugf("dropping stale class %q (%q)", name, cls.path("")) 387 delete(c.classes, name) 388 } 389 } 390 } 391 392 if _, ok := c.classes[RootClassName]; !ok { 393 log.Warnf("root class missing from runtime data, re-adding...") 394 c.classes[RootClassName] = classesFromFs[RootClassName] 395 } 396 397 // Try to apply given configuration 398 for name, class := range conf.Classes { 399 if _, ok := c.classes[name]; !ok { 400 cg, err := newCtrlGroup(c.resctrlGroupPrefix, c.resctrlGroupPrefix, name) 401 if err != nil { 402 return err 403 } 404 c.classes[name] = cg 405 } 406 partition := conf.Partitions[class.Partition] 407 if err := c.classes[name].configure(name, class, partition, conf.Options); err != nil { 408 return err 409 } 410 } 411 412 if err := c.pruneMonGroups(); err != nil { 413 return err 414 } 415 416 return nil 417 } 418 419 func (c *control) discoverFromResctrl(prefix string) error { 420 c.Debugf("running class discovery from resctrl filesystem using prefix %q", prefix) 421 422 classesFromFs, err := c.classesFromResctrlFsPrefix(prefix) 423 if err != nil { 424 return err 425 } 426 427 // Drop stale classes 428 for name, cls := range c.classes { 429 if _, ok := classesFromFs[cls.name]; !ok || cls.prefix != prefix { 430 if !isRootClass(cls.name) { 431 log.Debugf("dropping stale class %q (%q)", name, cls.path("")) 432 delete(c.classes, name) 433 } 434 } 435 } 436 437 for name, cls := range classesFromFs { 438 if _, ok := c.classes[name]; !ok { 439 c.classes[name] = cls 440 log.Debugf("adding discovered class %q (%q)", name, cls.path("")) 441 } 442 } 443 444 if err := c.pruneMonGroups(); err != nil { 445 return err 446 } 447 448 return nil 449 } 450 451 func (c *control) classesFromResctrlFs() (map[string]*ctrlGroup, error) { 452 return c.classesFromResctrlFsPrefix(c.resctrlGroupPrefix) 453 } 454 455 func (c *control) classesFromResctrlFsPrefix(prefix string) (map[string]*ctrlGroup, error) { 456 names := []string{RootClassName} 457 if g, err := resctrlGroupsFromFs(prefix, info.resctrlPath); err != nil { 458 return nil, err 459 } else { 460 for _, n := range g { 461 if prefix != c.resctrlGroupPrefix && 462 strings.HasPrefix(n, c.resctrlGroupPrefix) && 463 strings.HasPrefix(c.resctrlGroupPrefix, prefix) { 464 // Skip groups in the standard namespace 465 continue 466 } 467 names = append(names, n[len(prefix):]) 468 } 469 } 470 471 classes := make(map[string]*ctrlGroup, len(names)+1) 472 for _, name := range names { 473 g, err := newCtrlGroup(prefix, c.resctrlGroupPrefix, name) 474 if err != nil { 475 return nil, err 476 } 477 classes[name] = g 478 } 479 480 return classes, nil 481 } 482 483 func (c *control) pruneMonGroups() error { 484 for name, cls := range c.classes { 485 if err := cls.pruneMonGroups(); err != nil { 486 return fmt.Errorf("failed to prune stale monitoring groups of %q: %v", name, err) 487 } 488 } 489 return nil 490 } 491 492 func (c *control) readRdtFile(rdtPath string) ([]byte, error) { 493 return os.ReadFile(filepath.Join(info.resctrlPath, rdtPath)) 494 } 495 496 func (c *control) writeRdtFile(rdtPath string, data []byte) error { 497 if err := os.WriteFile(filepath.Join(info.resctrlPath, rdtPath), data, 0644); err != nil { 498 return c.cmdError(err) 499 } 500 return nil 501 } 502 503 func (c *control) cmdError(origErr error) error { 504 errData, readErr := c.readRdtFile(filepath.Join("info", "last_cmd_status")) 505 if readErr != nil { 506 return origErr 507 } 508 cmdStatus := strings.TrimSpace(string(errData)) 509 if len(cmdStatus) > 0 && cmdStatus != "ok" { 510 return fmt.Errorf("%s", cmdStatus) 511 } 512 return origErr 513 } 514 515 func newCtrlGroup(prefix, monPrefix, name string) (*ctrlGroup, error) { 516 cg := &ctrlGroup{ 517 resctrlGroup: resctrlGroup{prefix: prefix, name: name}, 518 monPrefix: monPrefix, 519 } 520 521 if err := os.Mkdir(cg.path(""), 0755); err != nil && !os.IsExist(err) { 522 return nil, err 523 } 524 525 var err error 526 cg.monGroups, err = cg.monGroupsFromResctrlFs() 527 if err != nil { 528 return nil, fmt.Errorf("error when retrieving existing monitor groups: %v", err) 529 } 530 531 return cg, nil 532 } 533 534 func (c *ctrlGroup) CreateMonGroup(name string, annotations map[string]string) (MonGroup, error) { 535 if mg, ok := c.monGroups[name]; ok { 536 return mg, nil 537 } 538 539 log.Debugf("creating monitoring group %s/%s", c.name, name) 540 mg, err := newMonGroup(c.monPrefix, name, c, annotations) 541 if err != nil { 542 return nil, fmt.Errorf("failed to create new monitoring group %q: %v", name, err) 543 } 544 545 c.monGroups[name] = mg 546 547 return mg, err 548 } 549 550 func (c *ctrlGroup) DeleteMonGroup(name string) error { 551 mg, ok := c.monGroups[name] 552 if !ok { 553 log.Warnf("trying to delete non-existent mon group %s/%s", c.name, name) 554 return nil 555 } 556 557 log.Debugf("deleting monitoring group %s/%s", c.name, name) 558 if err := groupRemoveFunc(mg.path("")); err != nil { 559 return fmt.Errorf("failed to remove monitoring group %q: %v", mg.relPath(""), err) 560 } 561 562 delete(c.monGroups, name) 563 564 return nil 565 } 566 567 func (c *ctrlGroup) DeleteMonGroups() error { 568 for name := range c.monGroups { 569 if err := c.DeleteMonGroup(name); err != nil { 570 return err 571 } 572 } 573 return nil 574 } 575 576 func (c *ctrlGroup) GetMonGroup(name string) (MonGroup, bool) { 577 mg, ok := c.monGroups[name] 578 return mg, ok 579 } 580 581 func (c *ctrlGroup) GetMonGroups() []MonGroup { 582 ret := make([]MonGroup, 0, len(c.monGroups)) 583 584 for _, v := range c.monGroups { 585 ret = append(ret, v) 586 } 587 sort.Slice(ret, func(i, j int) bool { return ret[i].Name() < ret[j].Name() }) 588 589 return ret 590 } 591 592 func (c *ctrlGroup) configure(name string, class *classConfig, 593 partition *partitionConfig, options Options) error { 594 schemata := "" 595 596 // Handle cache allocation 597 for _, lvl := range []cacheLevel{L2, L3} { 598 switch { 599 case info.cat[lvl].unified.Supported(): 600 schema, err := class.CATSchema[lvl].toStr(catSchemaTypeUnified, partition.CAT[lvl]) 601 if err != nil { 602 return err 603 } 604 schemata += schema 605 case info.cat[lvl].data.Supported() || info.cat[lvl].code.Supported(): 606 schema, err := class.CATSchema[lvl].toStr(catSchemaTypeCode, partition.CAT[lvl]) 607 if err != nil { 608 return err 609 } 610 schemata += schema 611 612 schema, err = class.CATSchema[lvl].toStr(catSchemaTypeData, partition.CAT[lvl]) 613 if err != nil { 614 return err 615 } 616 schemata += schema 617 default: 618 if class.CATSchema[lvl].Alloc != nil && !options.cat(lvl).Optional { 619 return fmt.Errorf("%s cache allocation for %q specified in configuration but not supported by system", lvl, name) 620 } 621 } 622 } 623 624 // Handle memory bandwidth allocation 625 switch { 626 case info.mb.Supported(): 627 schemata += class.MBSchema.toStr(partition.MB) 628 default: 629 if class.MBSchema != nil && !options.MB.Optional { 630 return fmt.Errorf("memory bandwidth allocation for %q specified in configuration but not supported by system", name) 631 } 632 } 633 634 if len(schemata) > 0 { 635 log.Debugf("writing schemata %q to %q", schemata, c.relPath("")) 636 if err := rdt.writeRdtFile(c.relPath("schemata"), []byte(schemata)); err != nil { 637 return err 638 } 639 } else { 640 log.Debugf("empty schemata") 641 } 642 643 return nil 644 } 645 646 func (c *ctrlGroup) monGroupsFromResctrlFs() (map[string]*monGroup, error) { 647 names, err := resctrlGroupsFromFs(c.monPrefix, c.path("mon_groups")) 648 if err != nil && !os.IsNotExist(err) { 649 return nil, err 650 } 651 652 grps := make(map[string]*monGroup, len(names)) 653 for _, name := range names { 654 name = name[len(c.monPrefix):] 655 mg, err := newMonGroup(c.monPrefix, name, c, nil) 656 if err != nil { 657 return nil, err 658 } 659 grps[name] = mg 660 } 661 return grps, nil 662 } 663 664 // Remove empty monitoring groups 665 func (c *ctrlGroup) pruneMonGroups() error { 666 for name, mg := range c.monGroups { 667 pids, err := mg.GetPids() 668 if err != nil { 669 return fmt.Errorf("failed to get pids for monitoring group %q: %v", mg.relPath(""), err) 670 } 671 if len(pids) == 0 { 672 if err := c.DeleteMonGroup(name); err != nil { 673 return fmt.Errorf("failed to remove monitoring group %q: %v", mg.relPath(""), err) 674 } 675 } 676 } 677 return nil 678 } 679 680 func (r *resctrlGroup) Name() string { 681 return r.name 682 } 683 684 func (r *resctrlGroup) GetPids() ([]string, error) { 685 data, err := rdt.readRdtFile(r.relPath("tasks")) 686 if err != nil { 687 return []string{}, err 688 } 689 split := strings.Split(strings.TrimSpace(string(data)), "\n") 690 if len(split[0]) > 0 { 691 return split, nil 692 } 693 return []string{}, nil 694 } 695 696 func (r *resctrlGroup) AddPids(pids ...string) error { 697 f, err := os.OpenFile(r.path("tasks"), os.O_WRONLY, 0644) 698 if err != nil { 699 return err 700 } 701 defer f.Close() 702 703 for _, pid := range pids { 704 if _, err := f.WriteString(pid + "\n"); err != nil { 705 if errors.Is(err, syscall.ESRCH) { 706 log.Debugf("no task %s", pid) 707 } else { 708 return fmt.Errorf("failed to assign processes %v to class %q: %v", pids, r.name, rdt.cmdError(err)) 709 } 710 } 711 } 712 return nil 713 } 714 715 func (r *resctrlGroup) GetMonData() MonData { 716 m := MonData{} 717 718 if info.l3mon.Supported() { 719 l3, err := r.getMonL3Data() 720 if err != nil { 721 log.Warnf("failed to retrieve L3 monitoring data: %v", err) 722 } else { 723 m.L3 = l3 724 } 725 } 726 727 return m 728 } 729 730 func (r *resctrlGroup) getMonL3Data() (MonL3Data, error) { 731 files, err := os.ReadDir(r.path("mon_data")) 732 if err != nil { 733 return nil, err 734 } 735 736 m := MonL3Data{} 737 for _, file := range files { 738 name := file.Name() 739 if strings.HasPrefix(name, "mon_L3_") { 740 // Parse cache id from the dirname 741 id, err := strconv.ParseUint(strings.TrimPrefix(name, "mon_L3_"), 10, 32) 742 if err != nil { 743 // Just print a warning, we try to retrieve as much info as possible 744 log.Warnf("error parsing L3 monitor data directory name %q: %v", name, err) 745 continue 746 } 747 748 data, err := r.getMonLeafData(filepath.Join("mon_data", name)) 749 if err != nil { 750 log.Warnf("failed to read monitor data: %v", err) 751 continue 752 } 753 754 m[id] = data 755 } 756 } 757 758 return m, nil 759 } 760 761 func (r *resctrlGroup) getMonLeafData(path string) (MonLeafData, error) { 762 files, err := os.ReadDir(r.path(path)) 763 if err != nil { 764 return nil, err 765 } 766 767 m := make(MonLeafData, len(files)) 768 769 for _, file := range files { 770 name := file.Name() 771 772 // We expect that all the files in the dir are regular files 773 val, err := readFileUint64(r.path(path, name)) 774 if err != nil { 775 // Just print a warning, we want to retrieve as much info as possible 776 log.Warnf("error reading data file: %v", err) 777 continue 778 } 779 780 m[name] = val 781 } 782 return m, nil 783 } 784 785 func (r *resctrlGroup) relPath(elem ...string) string { 786 if r.parent == nil { 787 if r.name == RootClassName { 788 return filepath.Join(elem...) 789 } 790 return filepath.Join(append([]string{r.prefix + r.name}, elem...)...) 791 } 792 // Parent is only intended for MON groups - non-root CTRL groups are considered 793 // as peers to the root CTRL group (as they are in HW) and do not have a parent 794 return r.parent.relPath(append([]string{"mon_groups", r.prefix + r.name}, elem...)...) 795 } 796 797 func (r *resctrlGroup) path(elem ...string) string { 798 return filepath.Join(info.resctrlPath, r.relPath(elem...)) 799 } 800 801 func newMonGroup(prefix string, name string, parent *ctrlGroup, annotations map[string]string) (*monGroup, error) { 802 mg := &monGroup{ 803 resctrlGroup: resctrlGroup{prefix: prefix, name: name, parent: parent}, 804 annotations: make(map[string]string, len(annotations))} 805 806 if err := os.Mkdir(mg.path(""), 0755); err != nil && !os.IsExist(err) { 807 return nil, err 808 } 809 for k, v := range annotations { 810 mg.annotations[k] = v 811 } 812 813 return mg, nil 814 } 815 816 func (m *monGroup) Parent() CtrlGroup { 817 return m.parent 818 } 819 820 func (m *monGroup) GetAnnotations() map[string]string { 821 a := make(map[string]string, len(m.annotations)) 822 for k, v := range m.annotations { 823 a[k] = v 824 } 825 return a 826 } 827 828 func resctrlGroupsFromFs(prefix string, path string) ([]string, error) { 829 files, err := os.ReadDir(path) 830 if err != nil { 831 return nil, err 832 } 833 834 grps := make([]string, 0, len(files)) 835 for _, file := range files { 836 filename := file.Name() 837 if strings.HasPrefix(filename, prefix) { 838 if s, err := os.Stat(filepath.Join(path, filename, "tasks")); err == nil && !s.IsDir() { 839 grps = append(grps, filename) 840 } 841 } 842 } 843 return grps, nil 844 } 845 846 func isRootClass(name string) bool { 847 return name == RootClassName || name == RootClassAlias 848 } 849 850 func unaliasClassName(name string) string { 851 if isRootClass(name) { 852 return RootClassName 853 } 854 return name 855 }