Update dependencies
Signed-off-by: Markus Lehtonen <markus.lehtonen@intel.com>
This commit is contained in:
201
vendor/github.com/intel/goresctrl/LICENSE
generated
vendored
Normal file
201
vendor/github.com/intel/goresctrl/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
58
vendor/github.com/intel/goresctrl/pkg/kubernetes/annotations.go
generated
vendored
Normal file
58
vendor/github.com/intel/goresctrl/pkg/kubernetes/annotations.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2021 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kubernetes
|
||||
|
||||
// ClassOrigin type indicates the source of container's class
|
||||
// information: whether it is found from CRI level container
|
||||
// annotations, Kubernetes' pod annotations, or it has not been found
|
||||
// at all.
|
||||
type ClassOrigin int
|
||||
|
||||
const (
|
||||
ClassOriginNotFound ClassOrigin = iota
|
||||
ClassOriginContainerAnnotation
|
||||
ClassOriginPodAnnotation
|
||||
)
|
||||
|
||||
func (c ClassOrigin) String() string {
|
||||
switch c {
|
||||
case ClassOriginNotFound:
|
||||
return "<not found>"
|
||||
case ClassOriginContainerAnnotation:
|
||||
return "container annotations"
|
||||
case ClassOriginPodAnnotation:
|
||||
return "pod annotations"
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerClassFromAnnotations determines the effective class of a
|
||||
// container from the Pod annotations and CRI level container
|
||||
// annotations of a container.
|
||||
func ContainerClassFromAnnotations(containerAnnotation, podAnnotation, podAnnotationContainerPrefix string, containerName string, containerAnnotations, podAnnotations map[string]string) (string, ClassOrigin) {
|
||||
if clsName, ok := containerAnnotations[containerAnnotation]; ok {
|
||||
return clsName, ClassOriginContainerAnnotation
|
||||
}
|
||||
if clsName, ok := podAnnotations[podAnnotationContainerPrefix+containerName]; ok {
|
||||
return clsName, ClassOriginPodAnnotation
|
||||
}
|
||||
if clsName, ok := podAnnotations[podAnnotation]; ok {
|
||||
return clsName, ClassOriginPodAnnotation
|
||||
}
|
||||
return "", ClassOriginNotFound
|
||||
}
|
85
vendor/github.com/intel/goresctrl/pkg/log/log.go
generated
vendored
Normal file
85
vendor/github.com/intel/goresctrl/pkg/log/log.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2019-2021 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Logger is the logging interface for goresctl
|
||||
type Logger interface {
|
||||
Debugf(format string, v ...interface{})
|
||||
Infof(format string, v ...interface{})
|
||||
Warnf(format string, v ...interface{})
|
||||
Errorf(format string, v ...interface{})
|
||||
Panicf(format string, v ...interface{})
|
||||
Fatalf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
*stdlog.Logger
|
||||
}
|
||||
|
||||
// NewLoggerWrapper wraps an implementation of the golang standard intreface
|
||||
// into a goresctl specific compatible logger interface
|
||||
func NewLoggerWrapper(l *stdlog.Logger) Logger {
|
||||
return &logger{Logger: l}
|
||||
}
|
||||
|
||||
func (l *logger) Debugf(format string, v ...interface{}) {
|
||||
l.Logger.Printf("DEBUG: "+format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Infof(format string, v ...interface{}) {
|
||||
l.Logger.Printf("INFO: "+format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Warnf(format string, v ...interface{}) {
|
||||
l.Logger.Printf("WARN: "+format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Errorf(format string, v ...interface{}) {
|
||||
l.Logger.Printf("ERROR: "+format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Panicf(format string, v ...interface{}) {
|
||||
l.Logger.Panicf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logger) Fatalf(format string, v ...interface{}) {
|
||||
l.Logger.Fatalf(format, v...)
|
||||
}
|
||||
|
||||
func InfoBlock(l Logger, heading, linePrefix, format string, v ...interface{}) {
|
||||
l.Infof("%s", heading)
|
||||
|
||||
lines := strings.Split(fmt.Sprintf(format, v...), "\n")
|
||||
for _, line := range lines {
|
||||
l.Infof("%s%s", linePrefix, line)
|
||||
}
|
||||
}
|
||||
|
||||
func DebugBlock(l Logger, heading, linePrefix, format string, v ...interface{}) {
|
||||
l.Debugf("%s", heading)
|
||||
|
||||
lines := strings.Split(fmt.Sprintf(format, v...), "\n")
|
||||
for _, line := range lines {
|
||||
l.Debugf("%s%s", linePrefix, line)
|
||||
}
|
||||
}
|
118
vendor/github.com/intel/goresctrl/pkg/rdt/bitmask.go
generated
vendored
Normal file
118
vendor/github.com/intel/goresctrl/pkg/rdt/bitmask.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright 2019 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rdt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// bitmask represents a generic 64 bit wide bitmask
|
||||
type bitmask uint64
|
||||
|
||||
// MarshalJSON implements the Marshaler interface of "encoding/json"
|
||||
func (b bitmask) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("\"%#x\"", b)), nil
|
||||
}
|
||||
|
||||
// listStr prints the bitmask in human-readable format, similar to e.g. the
|
||||
// cpuset format of the Linux kernel
|
||||
func (b bitmask) listStr() string {
|
||||
str := ""
|
||||
sep := ""
|
||||
|
||||
shift := int(0)
|
||||
lsbOne := b.lsbOne()
|
||||
|
||||
// Process "ranges of ones"
|
||||
for lsbOne != -1 {
|
||||
b >>= uint(lsbOne)
|
||||
|
||||
// Get range lenght from the position of the first zero
|
||||
numOnes := b.lsbZero()
|
||||
|
||||
if numOnes == 1 {
|
||||
str += sep + strconv.Itoa(lsbOne+shift)
|
||||
} else {
|
||||
str += sep + strconv.Itoa(lsbOne+shift) + "-" + strconv.Itoa(lsbOne+numOnes-1+shift)
|
||||
}
|
||||
|
||||
// Shift away the bits that have been processed
|
||||
b >>= uint(numOnes)
|
||||
shift += lsbOne + numOnes
|
||||
|
||||
// Get next bit that is set (if any)
|
||||
lsbOne = b.lsbOne()
|
||||
|
||||
sep = ","
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// listStrToBitmask parses a string containing a human-readable list of bit
|
||||
// numbers into a bitmask
|
||||
func listStrToBitmask(str string) (bitmask, error) {
|
||||
b := bitmask(0)
|
||||
|
||||
// Empty bitmask
|
||||
if len(str) == 0 {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
ranges := strings.Split(str, ",")
|
||||
for _, ran := range ranges {
|
||||
split := strings.SplitN(ran, "-", 2)
|
||||
|
||||
bitNum, err := strconv.ParseUint(split[0], 10, 6)
|
||||
if err != nil {
|
||||
return b, fmt.Errorf("invalid bitmask %q: %v", str, err)
|
||||
}
|
||||
|
||||
if len(split) == 1 {
|
||||
b |= 1 << bitNum
|
||||
} else {
|
||||
endNum, err := strconv.ParseUint(split[1], 10, 6)
|
||||
if err != nil {
|
||||
return b, fmt.Errorf("invalid bitmask %q: %v", str, err)
|
||||
}
|
||||
if endNum <= bitNum {
|
||||
return b, fmt.Errorf("invalid range %q in bitmask %q", ran, str)
|
||||
}
|
||||
b |= (1<<(endNum-bitNum+1) - 1) << bitNum
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (b bitmask) lsbOne() int {
|
||||
if b == 0 {
|
||||
return -1
|
||||
}
|
||||
return bits.TrailingZeros64(uint64(b))
|
||||
}
|
||||
|
||||
func (b bitmask) msbOne() int {
|
||||
// Returns -1 for b == 0
|
||||
return 63 - bits.LeadingZeros64(uint64(b))
|
||||
}
|
||||
|
||||
func (b bitmask) lsbZero() int {
|
||||
return bits.TrailingZeros64(^uint64(b))
|
||||
}
|
1193
vendor/github.com/intel/goresctrl/pkg/rdt/config.go
generated
vendored
Normal file
1193
vendor/github.com/intel/goresctrl/pkg/rdt/config.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
341
vendor/github.com/intel/goresctrl/pkg/rdt/info.go
generated
vendored
Normal file
341
vendor/github.com/intel/goresctrl/pkg/rdt/info.go
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
Copyright 2019-2021 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rdt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// resctrlInfo contains information about the RDT support in the system
|
||||
type resctrlInfo struct {
|
||||
resctrlPath string
|
||||
resctrlMountOpts map[string]struct{}
|
||||
numClosids uint64
|
||||
cat map[cacheLevel]catInfoAll
|
||||
l3mon l3MonInfo
|
||||
mb mbInfo
|
||||
}
|
||||
|
||||
type cacheLevel string
|
||||
|
||||
const (
|
||||
L2 cacheLevel = "L2"
|
||||
L3 cacheLevel = "L3"
|
||||
)
|
||||
|
||||
type catInfoAll struct {
|
||||
cacheIds []uint64
|
||||
unified catInfo
|
||||
code catInfo
|
||||
data catInfo
|
||||
}
|
||||
|
||||
type catInfo struct {
|
||||
cbmMask bitmask
|
||||
minCbmBits uint64
|
||||
shareableBits bitmask
|
||||
}
|
||||
|
||||
type l3MonInfo struct {
|
||||
numRmids uint64
|
||||
monFeatures []string
|
||||
}
|
||||
|
||||
type mbInfo struct {
|
||||
cacheIds []uint64
|
||||
bandwidthGran uint64
|
||||
delayLinear uint64
|
||||
minBandwidth uint64
|
||||
mbpsEnabled bool // true if MBA_MBps is enabled
|
||||
}
|
||||
|
||||
var mountInfoPath string = "/proc/mounts"
|
||||
|
||||
// getInfo is a helper method for a "unified API" for getting L3 information
|
||||
func (i catInfoAll) getInfo() catInfo {
|
||||
switch {
|
||||
case i.code.Supported():
|
||||
return i.code
|
||||
case i.data.Supported():
|
||||
return i.data
|
||||
}
|
||||
return i.unified
|
||||
}
|
||||
|
||||
func (i catInfoAll) cbmMask() bitmask {
|
||||
mask := i.getInfo().cbmMask
|
||||
if mask != 0 {
|
||||
return mask
|
||||
}
|
||||
return bitmask(^uint64(0))
|
||||
}
|
||||
|
||||
func (i catInfoAll) minCbmBits() uint64 {
|
||||
return i.getInfo().minCbmBits
|
||||
}
|
||||
|
||||
func getRdtInfo() (*resctrlInfo, error) {
|
||||
var err error
|
||||
info := &resctrlInfo{cat: make(map[cacheLevel]catInfoAll)}
|
||||
|
||||
info.resctrlPath, info.resctrlMountOpts, err = getResctrlMountInfo()
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to detect resctrl mount point: %v", err)
|
||||
}
|
||||
log.Infof("detected resctrl filesystem at %q", info.resctrlPath)
|
||||
|
||||
// Check that RDT is available
|
||||
infopath := filepath.Join(info.resctrlPath, "info")
|
||||
if _, err := os.Stat(infopath); err != nil {
|
||||
return info, fmt.Errorf("failed to read RDT info from %q: %v", infopath, err)
|
||||
}
|
||||
|
||||
// Check CAT feature available
|
||||
for _, cl := range []cacheLevel{L2, L3} {
|
||||
cat := catInfoAll{}
|
||||
catFeatures := map[string]*catInfo{
|
||||
"": &cat.unified,
|
||||
"CODE": &cat.code,
|
||||
"DATA": &cat.data,
|
||||
}
|
||||
for suffix, i := range catFeatures {
|
||||
dir := string(cl) + suffix
|
||||
subpath := filepath.Join(infopath, dir)
|
||||
if _, err = os.Stat(subpath); err == nil {
|
||||
*i, info.numClosids, err = getCatInfo(subpath)
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to get %s info from %q: %v", dir, subpath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if cat.getInfo().Supported() {
|
||||
cat.cacheIds, err = getCacheIds(info.resctrlPath, string(cl))
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to get %s CAT cache IDs: %v", cl, err)
|
||||
}
|
||||
}
|
||||
info.cat[cl] = cat
|
||||
}
|
||||
|
||||
// Check MON features available
|
||||
subpath := filepath.Join(infopath, "L3_MON")
|
||||
if _, err = os.Stat(subpath); err == nil {
|
||||
info.l3mon, err = getL3MonInfo(subpath)
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to get L3_MON info from %q: %v", subpath, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check MBA feature available
|
||||
subpath = filepath.Join(infopath, "MB")
|
||||
if _, err = os.Stat(subpath); err == nil {
|
||||
info.mb, info.numClosids, err = getMBInfo(subpath)
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to get MBA info from %q: %v", subpath, err)
|
||||
}
|
||||
|
||||
info.mb.cacheIds, err = getCacheIds(info.resctrlPath, "MB")
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to get MBA cache IDs: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func getCatInfo(basepath string) (catInfo, uint64, error) {
|
||||
var err error
|
||||
var numClosids uint64
|
||||
info := catInfo{}
|
||||
|
||||
info.cbmMask, err = readFileBitmask(filepath.Join(basepath, "cbm_mask"))
|
||||
if err != nil {
|
||||
return info, numClosids, err
|
||||
}
|
||||
info.minCbmBits, err = readFileUint64(filepath.Join(basepath, "min_cbm_bits"))
|
||||
if err != nil {
|
||||
return info, numClosids, err
|
||||
}
|
||||
info.shareableBits, err = readFileBitmask(filepath.Join(basepath, "shareable_bits"))
|
||||
if err != nil {
|
||||
return info, numClosids, err
|
||||
}
|
||||
numClosids, err = readFileUint64(filepath.Join(basepath, "num_closids"))
|
||||
if err != nil {
|
||||
return info, numClosids, err
|
||||
}
|
||||
|
||||
return info, numClosids, nil
|
||||
}
|
||||
|
||||
// Supported returns true if L3 cache allocation has is supported and enabled in the system
|
||||
func (i catInfo) Supported() bool {
|
||||
return i.cbmMask != 0
|
||||
}
|
||||
|
||||
func getL3MonInfo(basepath string) (l3MonInfo, error) {
|
||||
var err error
|
||||
info := l3MonInfo{}
|
||||
|
||||
info.numRmids, err = readFileUint64(filepath.Join(basepath, "num_rmids"))
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
lines, err := readFileString(filepath.Join(basepath, "mon_features"))
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
info.monFeatures = strings.Split(lines, "\n")
|
||||
sort.Strings(info.monFeatures)
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// Supported returns true if L3 monitoring is supported and enabled in the system
|
||||
func (i l3MonInfo) Supported() bool {
|
||||
return i.numRmids != 0 && len(i.monFeatures) > 0
|
||||
}
|
||||
|
||||
func getMBInfo(basepath string) (mbInfo, uint64, error) {
|
||||
var err error
|
||||
var numClosids uint64
|
||||
info := mbInfo{}
|
||||
|
||||
info.bandwidthGran, err = readFileUint64(filepath.Join(basepath, "bandwidth_gran"))
|
||||
if err != nil {
|
||||
return info, numClosids, err
|
||||
}
|
||||
info.delayLinear, err = readFileUint64(filepath.Join(basepath, "delay_linear"))
|
||||
if err != nil {
|
||||
return info, numClosids, err
|
||||
}
|
||||
info.minBandwidth, err = readFileUint64(filepath.Join(basepath, "min_bandwidth"))
|
||||
if err != nil {
|
||||
return info, numClosids, err
|
||||
}
|
||||
numClosids, err = readFileUint64(filepath.Join(basepath, "num_closids"))
|
||||
if err != nil {
|
||||
return info, numClosids, err
|
||||
}
|
||||
|
||||
// Detect MBps mode directly from mount options as it's not visible in MB
|
||||
// info directory
|
||||
_, mountOpts, err := getResctrlMountInfo()
|
||||
if err != nil {
|
||||
return info, numClosids, fmt.Errorf("failed to get resctrl mount options: %v", err)
|
||||
}
|
||||
if _, ok := mountOpts["mba_MBps"]; ok {
|
||||
info.mbpsEnabled = true
|
||||
}
|
||||
|
||||
return info, numClosids, nil
|
||||
}
|
||||
|
||||
// Supported returns true if memory bandwidth allocation has is supported and enabled in the system
|
||||
func (i mbInfo) Supported() bool {
|
||||
return i.minBandwidth != 0
|
||||
}
|
||||
|
||||
func getCacheIds(basepath string, prefix string) ([]uint64, error) {
|
||||
var ids []uint64
|
||||
|
||||
// Parse cache IDs from the root schemata
|
||||
data, err := readFileString(filepath.Join(basepath, "schemata"))
|
||||
if err != nil {
|
||||
return ids, fmt.Errorf("failed to read root schemata: %v", err)
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(data, "\n") {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
lineSplit := strings.SplitN(trimmed, ":", 2)
|
||||
|
||||
// Find line with given resource prefix
|
||||
if len(lineSplit) == 2 && strings.HasPrefix(lineSplit[0], prefix) {
|
||||
schema := strings.Split(lineSplit[1], ";")
|
||||
ids = make([]uint64, len(schema))
|
||||
|
||||
// Get individual cache configurations from the schema
|
||||
for idx, definition := range schema {
|
||||
split := strings.Split(definition, "=")
|
||||
if len(split) != 2 {
|
||||
return ids, fmt.Errorf("looks like an invalid schema %q", trimmed)
|
||||
}
|
||||
ids[idx], err = strconv.ParseUint(split[0], 10, 64)
|
||||
if err != nil {
|
||||
return ids, fmt.Errorf("failed to parse cache id in %q: %v", trimmed, err)
|
||||
}
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
}
|
||||
return ids, fmt.Errorf("no %s resources in root schemata", prefix)
|
||||
}
|
||||
|
||||
func getResctrlMountInfo() (string, map[string]struct{}, error) {
|
||||
mountOptions := map[string]struct{}{}
|
||||
|
||||
f, err := os.Open(mountInfoPath)
|
||||
if err != nil {
|
||||
return "", mountOptions, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
split := strings.Split(s.Text(), " ")
|
||||
if len(split) > 3 && split[2] == "resctrl" {
|
||||
opts := strings.Split(split[3], ",")
|
||||
for _, opt := range opts {
|
||||
mountOptions[opt] = struct{}{}
|
||||
}
|
||||
return split[1], mountOptions, nil
|
||||
}
|
||||
}
|
||||
return "", mountOptions, fmt.Errorf("resctrl not found in " + mountInfoPath)
|
||||
}
|
||||
|
||||
func readFileUint64(path string) (uint64, error) {
|
||||
data, err := readFileString(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return strconv.ParseUint(data, 10, 64)
|
||||
}
|
||||
|
||||
func readFileBitmask(path string) (bitmask, error) {
|
||||
data, err := readFileString(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := strconv.ParseUint(data, 16, 64)
|
||||
return bitmask(value), err
|
||||
}
|
||||
|
||||
func readFileString(path string) (string, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
return strings.TrimSpace(string(data)), err
|
||||
}
|
74
vendor/github.com/intel/goresctrl/pkg/rdt/kubernetes.go
generated
vendored
Normal file
74
vendor/github.com/intel/goresctrl/pkg/rdt/kubernetes.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2021 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rdt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/intel/goresctrl/pkg/kubernetes"
|
||||
)
|
||||
|
||||
const (
|
||||
// RdtContainerAnnotation is the CRI level container annotation for setting
|
||||
// the RDT class (CLOS) of a container
|
||||
RdtContainerAnnotation = "io.kubernetes.cri.rdt-class"
|
||||
|
||||
// RdtPodAnnotation is a Pod annotation for setting the RDT class (CLOS) of
|
||||
// all containers of the pod
|
||||
RdtPodAnnotation = "rdt.resources.beta.kubernetes.io/pod"
|
||||
|
||||
// RdtPodAnnotationContainerPrefix is prefix for per-container Pod annotation
|
||||
// for setting the RDT class (CLOS) of one container of the pod
|
||||
RdtPodAnnotationContainerPrefix = "rdt.resources.beta.kubernetes.io/container."
|
||||
)
|
||||
|
||||
// ContainerClassFromAnnotations determines the effective RDT class of a
|
||||
// container from the Pod annotations and CRI level container annotations of a
|
||||
// container. Verifies that the class exists in goresctrl configuration and that
|
||||
// it is allowed to be used.
|
||||
func ContainerClassFromAnnotations(containerName string, containerAnnotations, podAnnotations map[string]string) (string, error) {
|
||||
clsName, clsOrigin := kubernetes.ContainerClassFromAnnotations(
|
||||
RdtContainerAnnotation, RdtPodAnnotation, RdtPodAnnotationContainerPrefix,
|
||||
containerName, containerAnnotations, podAnnotations)
|
||||
|
||||
if clsOrigin != kubernetes.ClassOriginNotFound {
|
||||
if rdt == nil {
|
||||
return "", fmt.Errorf("RDT not initialized, class %q not available", clsName)
|
||||
}
|
||||
|
||||
// Verify validity of class name
|
||||
if !IsQualifiedClassName(clsName) {
|
||||
return "", fmt.Errorf("unqualified RDT class name %q", clsName)
|
||||
}
|
||||
|
||||
// If RDT has been initialized we check that the class exists
|
||||
if _, ok := rdt.getClass(clsName); !ok {
|
||||
return "", fmt.Errorf("RDT class %q does not exist in configuration", clsName)
|
||||
}
|
||||
|
||||
// If classes have been configured by goresctrl
|
||||
if clsConf, ok := rdt.conf.Classes[unaliasClassName(clsName)]; ok {
|
||||
// Check that the class is allowed
|
||||
if clsOrigin == kubernetes.ClassOriginPodAnnotation && clsConf.Kubernetes.DenyPodAnnotation {
|
||||
return "", fmt.Errorf("RDT class %q not allowed from Pod annotations", clsName)
|
||||
} else if clsOrigin == kubernetes.ClassOriginContainerAnnotation && clsConf.Kubernetes.DenyContainerAnnotation {
|
||||
return "", fmt.Errorf("RDT class %q not allowed from Container annotation", clsName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clsName, nil
|
||||
}
|
124
vendor/github.com/intel/goresctrl/pkg/rdt/prometheus.go
generated
vendored
Normal file
124
vendor/github.com/intel/goresctrl/pkg/rdt/prometheus.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright 2020 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rdt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var customLabels []string = []string{}
|
||||
|
||||
// collector implements prometheus.Collector interface
|
||||
type collector struct {
|
||||
descriptors map[string]*prometheus.Desc
|
||||
}
|
||||
|
||||
// NewCollector creates new Prometheus collector of RDT metrics
|
||||
func NewCollector() (prometheus.Collector, error) {
|
||||
c := &collector{descriptors: make(map[string]*prometheus.Desc)}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// RegisterCustomPrometheusLabels registers monitor group annotations to be
|
||||
// exported as Prometheus metrics labels
|
||||
func RegisterCustomPrometheusLabels(names ...string) {
|
||||
Names:
|
||||
for _, n := range names {
|
||||
for _, c := range customLabels {
|
||||
if n == c {
|
||||
break Names
|
||||
}
|
||||
}
|
||||
customLabels = append(customLabels, n)
|
||||
}
|
||||
}
|
||||
|
||||
// Describe method of the prometheus.Collector interface
|
||||
func (c *collector) Describe(ch chan<- *prometheus.Desc) {
|
||||
for resource, features := range GetMonFeatures() {
|
||||
switch resource {
|
||||
case MonResourceL3:
|
||||
for _, f := range features {
|
||||
ch <- c.describeL3(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect method of the prometheus.Collector interface
|
||||
func (c collector) Collect(ch chan<- prometheus.Metric) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, cls := range GetClasses() {
|
||||
for _, monGrp := range cls.GetMonGroups() {
|
||||
wg.Add(1)
|
||||
g := monGrp
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
c.collectGroupMetrics(ch, g)
|
||||
}()
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (c *collector) describeL3(feature string) *prometheus.Desc {
|
||||
d, ok := c.descriptors[feature]
|
||||
if !ok {
|
||||
name := "l3_" + feature
|
||||
help := "L3 " + feature
|
||||
|
||||
switch feature {
|
||||
case "llc_occupancy":
|
||||
help = "L3 (LLC) occupancy"
|
||||
case "mbm_local_bytes":
|
||||
help = "bytes transferred to/from local memory through LLC"
|
||||
case "mbm_total_bytes":
|
||||
help = "total bytes transferred to/from memory through LLC"
|
||||
}
|
||||
labels := append([]string{"rdt_class", "rdt_mon_group", "cache_id"}, customLabels...)
|
||||
d = prometheus.NewDesc(name, help, labels, nil)
|
||||
c.descriptors[feature] = d
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func (c *collector) collectGroupMetrics(ch chan<- prometheus.Metric, mg MonGroup) {
|
||||
allData := mg.GetMonData()
|
||||
|
||||
annotations := mg.GetAnnotations()
|
||||
customLabelValues := make([]string, len(customLabels))
|
||||
for i, name := range customLabels {
|
||||
customLabelValues[i] = annotations[name]
|
||||
}
|
||||
|
||||
for cacheID, data := range allData.L3 {
|
||||
for feature, value := range data {
|
||||
labels := append([]string{mg.Parent().Name(), mg.Name(), fmt.Sprint(cacheID)}, customLabelValues...)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.describeL3(feature),
|
||||
prometheus.CounterValue,
|
||||
float64(value),
|
||||
labels...,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
859
vendor/github.com/intel/goresctrl/pkg/rdt/rdt.go
generated
vendored
Normal file
859
vendor/github.com/intel/goresctrl/pkg/rdt/rdt.go
generated
vendored
Normal file
@@ -0,0 +1,859 @@
|
||||
/*
|
||||
Copyright 2019 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package rdt implements an API for managing Intel® RDT technologies via the
|
||||
// resctrl pseudo-filesystem of the Linux kernel. It provides flexible
|
||||
// configuration with a hierarchical approach for easy management of exclusive
|
||||
// cache allocations.
|
||||
//
|
||||
// Goresctrl supports all available RDT technologies, i.e. L2 and L3 Cache
|
||||
// Allocation (CAT) with Code and Data Prioritization (CDP) and Memory
|
||||
// Bandwidth Allocation (MBA) plus Cache Monitoring (CMT) and Memory Bandwidth
|
||||
// Monitoring (MBM).
|
||||
//
|
||||
// Basic usage example:
|
||||
// rdt.SetLogger(logrus.New())
|
||||
//
|
||||
// if err := rdt.Initialize(""); err != nil {
|
||||
// return fmt.Errorf("RDT not supported: %v", err)
|
||||
// }
|
||||
//
|
||||
// if err := rdt.SetConfigFromFile("/path/to/rdt.conf.yaml", false); err != nil {
|
||||
// return fmt.Errorf("RDT configuration failed: %v", err)
|
||||
// }
|
||||
//
|
||||
// if cls, ok := rdt.GetClass("my-class"); ok {
|
||||
// // Set PIDs 12345 and 12346 to class "my-class"
|
||||
// if err := cls.AddPids("12345", "12346"); err != nil {
|
||||
// return fmt.Errorf("failed to add PIDs to RDT class: %v", err)
|
||||
// }
|
||||
// }
|
||||
package rdt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
stdlog "log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
grclog "github.com/intel/goresctrl/pkg/log"
|
||||
"github.com/intel/goresctrl/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// RootClassName is the name we use in our config for the special class
|
||||
// that configures the "root" resctrl group of the system
|
||||
RootClassName = "system/default"
|
||||
// RootClassAlias is an alternative name for the root class
|
||||
RootClassAlias = ""
|
||||
)
|
||||
|
||||
type control struct {
|
||||
grclog.Logger
|
||||
|
||||
resctrlGroupPrefix string
|
||||
conf config
|
||||
rawConf Config
|
||||
classes map[string]*ctrlGroup
|
||||
}
|
||||
|
||||
var log grclog.Logger = grclog.NewLoggerWrapper(stdlog.New(os.Stderr, "[ rdt ] ", 0))
|
||||
|
||||
var info *resctrlInfo
|
||||
|
||||
var rdt *control
|
||||
|
||||
// Function for removing resctrl groups from the filesystem. This is
|
||||
// configurable because of unit tests.
|
||||
var groupRemoveFunc func(string) error = os.Remove
|
||||
|
||||
// CtrlGroup defines the interface of one goresctrl managed RDT class. It maps
|
||||
// to one CTRL group directory in the goresctrl pseudo-filesystem.
|
||||
type CtrlGroup interface {
|
||||
ResctrlGroup
|
||||
|
||||
// CreateMonGroup creates a new monitoring group under this CtrlGroup.
|
||||
CreateMonGroup(name string, annotations map[string]string) (MonGroup, error)
|
||||
|
||||
// DeleteMonGroup deletes a monitoring group from this CtrlGroup.
|
||||
DeleteMonGroup(name string) error
|
||||
|
||||
// DeleteMonGroups deletes all monitoring groups from this CtrlGroup.
|
||||
DeleteMonGroups() error
|
||||
|
||||
// GetMonGroup returns a specific monitoring group under this CtrlGroup.
|
||||
GetMonGroup(name string) (MonGroup, bool)
|
||||
|
||||
// GetMonGroups returns all monitoring groups under this CtrlGroup.
|
||||
GetMonGroups() []MonGroup
|
||||
}
|
||||
|
||||
// ResctrlGroup is the generic interface for resctrl CTRL and MON groups. It
|
||||
// maps to one CTRL or MON group directory in the goresctrl pseudo-filesystem.
|
||||
type ResctrlGroup interface {
|
||||
// Name returns the name of the group.
|
||||
Name() string
|
||||
|
||||
// GetPids returns the process ids assigned to the group.
|
||||
GetPids() ([]string, error)
|
||||
|
||||
// AddPids assigns the given process ids to the group.
|
||||
AddPids(pids ...string) error
|
||||
|
||||
// GetMonData retrieves the monitoring data of the group.
|
||||
GetMonData() MonData
|
||||
}
|
||||
|
||||
// MonGroup represents the interface to a RDT monitoring group. It maps to one
|
||||
// MON group in the goresctrl filesystem.
|
||||
type MonGroup interface {
|
||||
ResctrlGroup
|
||||
|
||||
// Parent returns the CtrlGroup under which the monitoring group exists.
|
||||
Parent() CtrlGroup
|
||||
|
||||
// GetAnnotations returns the annotations stored to the monitoring group.
|
||||
GetAnnotations() map[string]string
|
||||
}
|
||||
|
||||
// MonData contains monitoring stats of one monitoring group.
|
||||
type MonData struct {
|
||||
L3 MonL3Data
|
||||
}
|
||||
|
||||
// MonL3Data contains L3 monitoring stats of one monitoring group.
|
||||
type MonL3Data map[uint64]MonLeafData
|
||||
|
||||
// MonLeafData represents the raw numerical stats from one RDT monitor data leaf.
|
||||
type MonLeafData map[string]uint64
|
||||
|
||||
// MonResource is the type of RDT monitoring resource.
|
||||
type MonResource string
|
||||
|
||||
const (
|
||||
// MonResourceL3 is the RDT L3 cache monitor resource.
|
||||
MonResourceL3 MonResource = "l3"
|
||||
)
|
||||
|
||||
type ctrlGroup struct {
|
||||
resctrlGroup
|
||||
|
||||
monPrefix string
|
||||
monGroups map[string]*monGroup
|
||||
}
|
||||
|
||||
type monGroup struct {
|
||||
resctrlGroup
|
||||
|
||||
annotations map[string]string
|
||||
}
|
||||
|
||||
type resctrlGroup struct {
|
||||
prefix string
|
||||
name string
|
||||
parent *ctrlGroup // parent for MON groups
|
||||
}
|
||||
|
||||
// SetLogger sets the logger instance to be used by the package. This function
|
||||
// may be called even before Initialize().
|
||||
func SetLogger(l grclog.Logger) {
|
||||
log = l
|
||||
if rdt != nil {
|
||||
rdt.setLogger(l)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize detects RDT from the system and initializes control interface of
|
||||
// the package.
|
||||
func Initialize(resctrlGroupPrefix string) error {
|
||||
var err error
|
||||
|
||||
info = nil
|
||||
rdt = nil
|
||||
|
||||
// Get info from the resctrl filesystem
|
||||
info, err = getRdtInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := &control{Logger: log, resctrlGroupPrefix: resctrlGroupPrefix}
|
||||
|
||||
// NOTE: we lose monitoring group annotations (i.e. prometheus metrics
|
||||
// labels) on re-init
|
||||
if r.classes, err = r.classesFromResctrlFs(); err != nil {
|
||||
return fmt.Errorf("failed to initialize classes from resctrl fs: %v", err)
|
||||
}
|
||||
|
||||
if err := r.pruneMonGroups(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rdt = r
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverClasses discovers existing classes from the resctrl filesystem.
|
||||
// Makes it possible to discover gropus with another prefix than was set with
|
||||
// Initialize(). The original prefix is still used for monitoring groups.
|
||||
func DiscoverClasses(resctrlGroupPrefix string) error {
|
||||
if rdt != nil {
|
||||
return rdt.discoverFromResctrl(resctrlGroupPrefix)
|
||||
}
|
||||
return fmt.Errorf("rdt not initialized")
|
||||
}
|
||||
|
||||
// SetConfig (re-)configures the resctrl filesystem according to the specified
|
||||
// configuration.
|
||||
func SetConfig(c *Config, force bool) error {
|
||||
if rdt != nil {
|
||||
return rdt.setConfig(c, force)
|
||||
}
|
||||
return fmt.Errorf("rdt not initialized")
|
||||
}
|
||||
|
||||
// SetConfigFromData takes configuration as raw data, parses it and
|
||||
// reconfigures the resctrl filesystem.
|
||||
func SetConfigFromData(data []byte, force bool) error {
|
||||
cfg := &Config{}
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return fmt.Errorf("failed to parse configuration data: %v", err)
|
||||
}
|
||||
|
||||
return SetConfig(cfg, force)
|
||||
}
|
||||
|
||||
// SetConfigFromFile reads configuration from the filesystem and reconfigures
|
||||
// the resctrl filesystem.
|
||||
func SetConfigFromFile(path string, force bool) error {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read config file: %v", err)
|
||||
}
|
||||
|
||||
if err := SetConfigFromData(data, force); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("configuration successfully loaded from %q", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetClass returns one RDT class.
|
||||
func GetClass(name string) (CtrlGroup, bool) {
|
||||
if rdt != nil {
|
||||
return rdt.getClass(name)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// GetClasses returns all available RDT classes.
|
||||
func GetClasses() []CtrlGroup {
|
||||
if rdt != nil {
|
||||
return rdt.getClasses()
|
||||
}
|
||||
return []CtrlGroup{}
|
||||
}
|
||||
|
||||
// MonSupported returns true if RDT monitoring features are available.
|
||||
func MonSupported() bool {
|
||||
if rdt != nil {
|
||||
return rdt.monSupported()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetMonFeatures returns the available monitoring stats of each available
|
||||
// monitoring technology.
|
||||
func GetMonFeatures() map[MonResource][]string {
|
||||
if rdt != nil {
|
||||
return rdt.getMonFeatures()
|
||||
}
|
||||
return map[MonResource][]string{}
|
||||
}
|
||||
|
||||
// IsQualifiedClassName returns true if given string qualifies as a class name
|
||||
func IsQualifiedClassName(name string) bool {
|
||||
// Must be qualified as a file name
|
||||
return name == RootClassName || (len(name) < 4096 && name != "." && name != ".." && !strings.ContainsAny(name, "/\n"))
|
||||
}
|
||||
|
||||
func (c *control) getClass(name string) (CtrlGroup, bool) {
|
||||
cls, ok := c.classes[unaliasClassName(name)]
|
||||
return cls, ok
|
||||
}
|
||||
|
||||
func (c *control) getClasses() []CtrlGroup {
|
||||
ret := make([]CtrlGroup, 0, len(c.classes))
|
||||
|
||||
for _, v := range c.classes {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i].Name() < ret[j].Name() })
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *control) monSupported() bool {
|
||||
return info.l3mon.Supported()
|
||||
}
|
||||
|
||||
func (c *control) getMonFeatures() map[MonResource][]string {
|
||||
ret := make(map[MonResource][]string)
|
||||
if info.l3mon.Supported() {
|
||||
ret[MonResourceL3] = append([]string{}, info.l3mon.monFeatures...)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *control) setLogger(l grclog.Logger) {
|
||||
c.Logger = l
|
||||
}
|
||||
|
||||
func (c *control) setConfig(newConfig *Config, force bool) error {
|
||||
c.Infof("configuration update")
|
||||
|
||||
conf, err := (*newConfig).resolve()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid configuration: %v", err)
|
||||
}
|
||||
|
||||
err = c.configureResctrl(conf, force)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resctrl configuration failed: %v", err)
|
||||
}
|
||||
|
||||
c.conf = conf
|
||||
// TODO: we'd better create a deep copy
|
||||
c.rawConf = *newConfig
|
||||
c.Infof("configuration finished")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *control) configureResctrl(conf config, force bool) error {
|
||||
grclog.DebugBlock(c, "applying resolved config:", " ", "%s", utils.DumpJSON(conf))
|
||||
|
||||
// Remove stale resctrl groups
|
||||
classesFromFs, err := c.classesFromResctrlFs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for name, cls := range classesFromFs {
|
||||
if _, ok := conf.Classes[cls.name]; !isRootClass(cls.name) && !ok {
|
||||
if !force {
|
||||
tasks, err := cls.GetPids()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get resctrl group tasks: %v", err)
|
||||
}
|
||||
if len(tasks) > 0 {
|
||||
return fmt.Errorf("refusing to remove non-empty resctrl group %q", cls.relPath(""))
|
||||
}
|
||||
}
|
||||
log.Debugf("removing existing resctrl group %q", cls.relPath(""))
|
||||
err = groupRemoveFunc(cls.path(""))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove resctrl group %q: %v", cls.relPath(""), err)
|
||||
}
|
||||
|
||||
delete(c.classes, name)
|
||||
}
|
||||
}
|
||||
|
||||
for name, cls := range c.classes {
|
||||
if _, ok := conf.Classes[cls.name]; !ok || cls.prefix != c.resctrlGroupPrefix {
|
||||
if !isRootClass(cls.name) {
|
||||
log.Debugf("dropping stale class %q (%q)", name, cls.path(""))
|
||||
delete(c.classes, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := c.classes[RootClassName]; !ok {
|
||||
log.Warnf("root class missing from runtime data, re-adding...")
|
||||
c.classes[RootClassName] = classesFromFs[RootClassName]
|
||||
}
|
||||
|
||||
// Try to apply given configuration
|
||||
for name, class := range conf.Classes {
|
||||
if _, ok := c.classes[name]; !ok {
|
||||
cg, err := newCtrlGroup(c.resctrlGroupPrefix, c.resctrlGroupPrefix, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.classes[name] = cg
|
||||
}
|
||||
partition := conf.Partitions[class.Partition]
|
||||
if err := c.classes[name].configure(name, class, partition, conf.Options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.pruneMonGroups(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *control) discoverFromResctrl(prefix string) error {
|
||||
c.Debugf("running class discovery from resctrl filesystem using prefix %q", prefix)
|
||||
|
||||
classesFromFs, err := c.classesFromResctrlFsPrefix(prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Drop stale classes
|
||||
for name, cls := range c.classes {
|
||||
if _, ok := classesFromFs[cls.name]; !ok || cls.prefix != prefix {
|
||||
if !isRootClass(cls.name) {
|
||||
log.Debugf("dropping stale class %q (%q)", name, cls.path(""))
|
||||
delete(c.classes, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for name, cls := range classesFromFs {
|
||||
if _, ok := c.classes[name]; !ok {
|
||||
c.classes[name] = cls
|
||||
log.Debugf("adding discovered class %q (%q)", name, cls.path(""))
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.pruneMonGroups(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *control) classesFromResctrlFs() (map[string]*ctrlGroup, error) {
|
||||
return c.classesFromResctrlFsPrefix(c.resctrlGroupPrefix)
|
||||
}
|
||||
|
||||
func (c *control) classesFromResctrlFsPrefix(prefix string) (map[string]*ctrlGroup, error) {
|
||||
names := []string{RootClassName}
|
||||
if g, err := resctrlGroupsFromFs(prefix, info.resctrlPath); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, n := range g {
|
||||
if prefix != c.resctrlGroupPrefix &&
|
||||
strings.HasPrefix(n, c.resctrlGroupPrefix) &&
|
||||
strings.HasPrefix(c.resctrlGroupPrefix, prefix) {
|
||||
// Skip groups in the standard namespace
|
||||
continue
|
||||
}
|
||||
names = append(names, n[len(prefix):])
|
||||
}
|
||||
}
|
||||
|
||||
classes := make(map[string]*ctrlGroup, len(names)+1)
|
||||
for _, name := range names {
|
||||
g, err := newCtrlGroup(prefix, c.resctrlGroupPrefix, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
classes[name] = g
|
||||
}
|
||||
|
||||
return classes, nil
|
||||
}
|
||||
|
||||
func (c *control) pruneMonGroups() error {
|
||||
for name, cls := range c.classes {
|
||||
if err := cls.pruneMonGroups(); err != nil {
|
||||
return fmt.Errorf("failed to prune stale monitoring groups of %q: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *control) readRdtFile(rdtPath string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filepath.Join(info.resctrlPath, rdtPath))
|
||||
}
|
||||
|
||||
func (c *control) writeRdtFile(rdtPath string, data []byte) error {
|
||||
if err := ioutil.WriteFile(filepath.Join(info.resctrlPath, rdtPath), data, 0644); err != nil {
|
||||
return c.cmdError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *control) cmdError(origErr error) error {
|
||||
errData, readErr := c.readRdtFile(filepath.Join("info", "last_cmd_status"))
|
||||
if readErr != nil {
|
||||
return origErr
|
||||
}
|
||||
cmdStatus := strings.TrimSpace(string(errData))
|
||||
if len(cmdStatus) > 0 && cmdStatus != "ok" {
|
||||
return fmt.Errorf("%s", cmdStatus)
|
||||
}
|
||||
return origErr
|
||||
}
|
||||
|
||||
func newCtrlGroup(prefix, monPrefix, name string) (*ctrlGroup, error) {
|
||||
cg := &ctrlGroup{
|
||||
resctrlGroup: resctrlGroup{prefix: prefix, name: name},
|
||||
monPrefix: monPrefix,
|
||||
}
|
||||
|
||||
if err := os.Mkdir(cg.path(""), 0755); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err error
|
||||
cg.monGroups, err = cg.monGroupsFromResctrlFs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error when retrieving existing monitor groups: %v", err)
|
||||
}
|
||||
|
||||
return cg, nil
|
||||
}
|
||||
|
||||
func (c *ctrlGroup) CreateMonGroup(name string, annotations map[string]string) (MonGroup, error) {
|
||||
if mg, ok := c.monGroups[name]; ok {
|
||||
return mg, nil
|
||||
}
|
||||
|
||||
log.Debugf("creating monitoring group %s/%s", c.name, name)
|
||||
mg, err := newMonGroup(c.monPrefix, name, c, annotations)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create new monitoring group %q: %v", name, err)
|
||||
}
|
||||
|
||||
c.monGroups[name] = mg
|
||||
|
||||
return mg, err
|
||||
}
|
||||
|
||||
func (c *ctrlGroup) DeleteMonGroup(name string) error {
|
||||
mg, ok := c.monGroups[name]
|
||||
if !ok {
|
||||
log.Warnf("trying to delete non-existent mon group %s/%s", c.name, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("deleting monitoring group %s/%s", c.name, name)
|
||||
if err := groupRemoveFunc(mg.path("")); err != nil {
|
||||
return fmt.Errorf("failed to remove monitoring group %q: %v", mg.relPath(""), err)
|
||||
}
|
||||
|
||||
delete(c.monGroups, name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ctrlGroup) DeleteMonGroups() error {
|
||||
for name := range c.monGroups {
|
||||
if err := c.DeleteMonGroup(name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ctrlGroup) GetMonGroup(name string) (MonGroup, bool) {
|
||||
mg, ok := c.monGroups[name]
|
||||
return mg, ok
|
||||
}
|
||||
|
||||
func (c *ctrlGroup) GetMonGroups() []MonGroup {
|
||||
ret := make([]MonGroup, 0, len(c.monGroups))
|
||||
|
||||
for _, v := range c.monGroups {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i].Name() < ret[j].Name() })
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *ctrlGroup) configure(name string, class *classConfig,
|
||||
partition *partitionConfig, options Options) error {
|
||||
schemata := ""
|
||||
|
||||
// Handle cache allocation
|
||||
for _, lvl := range []cacheLevel{L2, L3} {
|
||||
switch {
|
||||
case info.cat[lvl].unified.Supported():
|
||||
schema, err := class.CATSchema[lvl].toStr(catSchemaTypeUnified, partition.CAT[lvl])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
schemata += schema
|
||||
case info.cat[lvl].data.Supported() || info.cat[lvl].code.Supported():
|
||||
schema, err := class.CATSchema[lvl].toStr(catSchemaTypeCode, partition.CAT[lvl])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
schemata += schema
|
||||
|
||||
schema, err = class.CATSchema[lvl].toStr(catSchemaTypeData, partition.CAT[lvl])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
schemata += schema
|
||||
default:
|
||||
if class.CATSchema[lvl].Alloc != nil && !options.cat(lvl).Optional {
|
||||
return fmt.Errorf("%s cache allocation for %q specified in configuration but not supported by system", lvl, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle memory bandwidth allocation
|
||||
switch {
|
||||
case info.mb.Supported():
|
||||
schemata += class.MBSchema.toStr(partition.MB)
|
||||
default:
|
||||
if class.MBSchema != nil && !options.MB.Optional {
|
||||
return fmt.Errorf("memory bandwidth allocation for %q specified in configuration but not supported by system", name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(schemata) > 0 {
|
||||
log.Debugf("writing schemata %q to %q", schemata, c.relPath(""))
|
||||
if err := rdt.writeRdtFile(c.relPath("schemata"), []byte(schemata)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Debugf("empty schemata")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ctrlGroup) monGroupsFromResctrlFs() (map[string]*monGroup, error) {
|
||||
names, err := resctrlGroupsFromFs(c.monPrefix, c.path("mon_groups"))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grps := make(map[string]*monGroup, len(names))
|
||||
for _, name := range names {
|
||||
name = name[len(c.monPrefix):]
|
||||
mg, err := newMonGroup(c.monPrefix, name, c, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grps[name] = mg
|
||||
}
|
||||
return grps, nil
|
||||
}
|
||||
|
||||
// Remove empty monitoring groups
|
||||
func (c *ctrlGroup) pruneMonGroups() error {
|
||||
for name, mg := range c.monGroups {
|
||||
pids, err := mg.GetPids()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get pids for monitoring group %q: %v", mg.relPath(""), err)
|
||||
}
|
||||
if len(pids) == 0 {
|
||||
if err := c.DeleteMonGroup(name); err != nil {
|
||||
return fmt.Errorf("failed to remove monitoring group %q: %v", mg.relPath(""), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resctrlGroup) Name() string {
|
||||
return r.name
|
||||
}
|
||||
|
||||
func (r *resctrlGroup) GetPids() ([]string, error) {
|
||||
data, err := rdt.readRdtFile(r.relPath("tasks"))
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
split := strings.Split(strings.TrimSpace(string(data)), "\n")
|
||||
if len(split[0]) > 0 {
|
||||
return split, nil
|
||||
}
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (r *resctrlGroup) AddPids(pids ...string) error {
|
||||
f, err := os.OpenFile(r.path("tasks"), os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
for _, pid := range pids {
|
||||
if _, err := f.WriteString(pid + "\n"); err != nil {
|
||||
if errors.Is(err, syscall.ESRCH) {
|
||||
log.Debugf("no task %s", pid)
|
||||
} else {
|
||||
return fmt.Errorf("failed to assign processes %v to class %q: %v", pids, r.name, rdt.cmdError(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resctrlGroup) GetMonData() MonData {
|
||||
m := MonData{}
|
||||
|
||||
if info.l3mon.Supported() {
|
||||
l3, err := r.getMonL3Data()
|
||||
if err != nil {
|
||||
log.Warnf("failed to retrieve L3 monitoring data: %v", err)
|
||||
} else {
|
||||
m.L3 = l3
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (r *resctrlGroup) getMonL3Data() (MonL3Data, error) {
|
||||
files, err := ioutil.ReadDir(r.path("mon_data"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := MonL3Data{}
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
if strings.HasPrefix(name, "mon_L3_") {
|
||||
// Parse cache id from the dirname
|
||||
id, err := strconv.ParseUint(strings.TrimPrefix(name, "mon_L3_"), 10, 32)
|
||||
if err != nil {
|
||||
// Just print a warning, we try to retrieve as much info as possible
|
||||
log.Warnf("error parsing L3 monitor data directory name %q: %v", name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := r.getMonLeafData(filepath.Join("mon_data", name))
|
||||
if err != nil {
|
||||
log.Warnf("failed to read monitor data: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
m[id] = data
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (r *resctrlGroup) getMonLeafData(path string) (MonLeafData, error) {
|
||||
files, err := ioutil.ReadDir(r.path(path))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(MonLeafData, len(files))
|
||||
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
|
||||
// We expect that all the files in the dir are regular files
|
||||
val, err := readFileUint64(r.path(path, name))
|
||||
if err != nil {
|
||||
// Just print a warning, we want to retrieve as much info as possible
|
||||
log.Warnf("error reading data file: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
m[name] = val
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (r *resctrlGroup) relPath(elem ...string) string {
|
||||
if r.parent == nil {
|
||||
if r.name == RootClassName {
|
||||
return filepath.Join(elem...)
|
||||
}
|
||||
return filepath.Join(append([]string{r.prefix + r.name}, elem...)...)
|
||||
}
|
||||
// Parent is only intended for MON groups - non-root CTRL groups are considered
|
||||
// as peers to the root CTRL group (as they are in HW) and do not have a parent
|
||||
return r.parent.relPath(append([]string{"mon_groups", r.prefix + r.name}, elem...)...)
|
||||
}
|
||||
|
||||
func (r *resctrlGroup) path(elem ...string) string {
|
||||
return filepath.Join(info.resctrlPath, r.relPath(elem...))
|
||||
}
|
||||
|
||||
func newMonGroup(prefix string, name string, parent *ctrlGroup, annotations map[string]string) (*monGroup, error) {
|
||||
mg := &monGroup{
|
||||
resctrlGroup: resctrlGroup{prefix: prefix, name: name, parent: parent},
|
||||
annotations: make(map[string]string, len(annotations))}
|
||||
|
||||
if err := os.Mkdir(mg.path(""), 0755); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range annotations {
|
||||
mg.annotations[k] = v
|
||||
}
|
||||
|
||||
return mg, nil
|
||||
}
|
||||
|
||||
func (m *monGroup) Parent() CtrlGroup {
|
||||
return m.parent
|
||||
}
|
||||
|
||||
func (m *monGroup) GetAnnotations() map[string]string {
|
||||
a := make(map[string]string, len(m.annotations))
|
||||
for k, v := range m.annotations {
|
||||
a[k] = v
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func resctrlGroupsFromFs(prefix string, path string) ([]string, error) {
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grps := make([]string, 0, len(files))
|
||||
for _, file := range files {
|
||||
filename := file.Name()
|
||||
if strings.HasPrefix(filename, prefix) {
|
||||
if s, err := os.Stat(filepath.Join(path, filename, "tasks")); err == nil && !s.IsDir() {
|
||||
grps = append(grps, filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
return grps, nil
|
||||
}
|
||||
|
||||
func isRootClass(name string) bool {
|
||||
return name == RootClassName || name == RootClassAlias
|
||||
}
|
||||
|
||||
func unaliasClassName(name string) string {
|
||||
if isRootClass(name) {
|
||||
return RootClassName
|
||||
}
|
||||
return name
|
||||
}
|
180
vendor/github.com/intel/goresctrl/pkg/utils/idset.go
generated
vendored
Normal file
180
vendor/github.com/intel/goresctrl/pkg/utils/idset.go
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
Copyright 2019-2021 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// Unknown represents an unknown id.
|
||||
Unknown ID = -1
|
||||
)
|
||||
|
||||
// ID is nn integer id, used to identify packages, CPUs, nodes, etc.
|
||||
type ID = int
|
||||
|
||||
// IDSet is an unordered set of integer ids.
|
||||
type IDSet map[ID]struct{}
|
||||
|
||||
// NewIDSet creates a new unordered set of (integer) ids.
|
||||
func NewIDSet(ids ...ID) IDSet {
|
||||
s := make(map[ID]struct{})
|
||||
|
||||
for _, id := range ids {
|
||||
s[id] = struct{}{}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// NewIDSetFromIntSlice creates a new unordered set from an integer slice.
|
||||
func NewIDSetFromIntSlice(ids ...int) IDSet {
|
||||
s := make(map[ID]struct{})
|
||||
|
||||
for _, id := range ids {
|
||||
s[ID(id)] = struct{}{}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Clone returns a copy of this IdSet.
|
||||
func (s IDSet) Clone() IDSet {
|
||||
return NewIDSet(s.Members()...)
|
||||
}
|
||||
|
||||
// Add adds the given ids into the set.
|
||||
func (s IDSet) Add(ids ...ID) {
|
||||
for _, id := range ids {
|
||||
s[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Del deletes the given ids from the set.
|
||||
func (s IDSet) Del(ids ...ID) {
|
||||
if s != nil {
|
||||
for _, id := range ids {
|
||||
delete(s, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the number of ids in the set.
|
||||
func (s IDSet) Size() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// Has tests if all the ids are present in the set.
|
||||
func (s IDSet) Has(ids ...ID) bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
_, ok := s[id]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Members returns all ids in the set as a randomly ordered slice.
|
||||
func (s IDSet) Members() []ID {
|
||||
if s == nil {
|
||||
return []ID{}
|
||||
}
|
||||
ids := make([]ID, len(s))
|
||||
idx := 0
|
||||
for id := range s {
|
||||
ids[idx] = id
|
||||
idx++
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// SortedMembers returns all ids in the set as a sorted slice.
|
||||
func (s IDSet) SortedMembers() []ID {
|
||||
ids := s.Members()
|
||||
sort.Slice(ids, func(i, j int) bool {
|
||||
return ids[i] < ids[j]
|
||||
})
|
||||
return ids
|
||||
}
|
||||
|
||||
// String returns the set as a string.
|
||||
func (s IDSet) String() string {
|
||||
return s.StringWithSeparator(",")
|
||||
}
|
||||
|
||||
// StringWithSeparator returns the set as a string, separated with the given separator.
|
||||
func (s IDSet) StringWithSeparator(args ...string) string {
|
||||
if s == nil || len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var sep string
|
||||
|
||||
if len(args) == 1 {
|
||||
sep = args[0]
|
||||
} else {
|
||||
sep = ","
|
||||
}
|
||||
|
||||
str := ""
|
||||
t := ""
|
||||
for _, id := range s.SortedMembers() {
|
||||
str = str + t + strconv.Itoa(int(id))
|
||||
t = sep
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// MarshalJSON is the JSON marshaller for IDSet.
|
||||
func (s IDSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(s.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON is the JSON unmarshaller for IDSet.
|
||||
func (s *IDSet) UnmarshalJSON(data []byte) error {
|
||||
str := ""
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return fmt.Errorf("invalid IDSet entry '%s': %v", string(data), err)
|
||||
}
|
||||
|
||||
*s = NewIDSet()
|
||||
if str == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, idstr := range strings.Split(str, ",") {
|
||||
id, err := strconv.ParseUint(idstr, 10, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid IDSet entry '%s': %v", idstr, err)
|
||||
}
|
||||
s.Add(ID(id))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
32
vendor/github.com/intel/goresctrl/pkg/utils/json.go
generated
vendored
Normal file
32
vendor/github.com/intel/goresctrl/pkg/utils/json.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2019 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// DumpJSON dumps a json-compatible struct in human-readable form
|
||||
func DumpJSON(r interface{}) string {
|
||||
out, err := yaml.Marshal(r)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("!!!!!\nUnable to stringify %T: %v\n!!!!!", r, err)
|
||||
}
|
||||
return string(out)
|
||||
}
|
36
vendor/github.com/intel/goresctrl/pkg/utils/sort.go
generated
vendored
Normal file
36
vendor/github.com/intel/goresctrl/pkg/utils/sort.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2020 Intel Corporation. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// SortUint64s sorts a slice of uint64 in increasing order.
|
||||
func SortUint64s(a []uint64) {
|
||||
sort.Sort(Uint64Slice(a))
|
||||
}
|
||||
|
||||
// Uint64Slice implmenents sort.Interface for a slice of uint64.
|
||||
type Uint64Slice []uint64
|
||||
|
||||
// Len returns the length of an UintSlice
|
||||
func (s Uint64Slice) Len() int { return len(s) }
|
||||
|
||||
// Less returns true if element at 'i' is less than the element at 'j'
|
||||
func (s Uint64Slice) Less(i, j int) bool { return s[i] < s[j] }
|
||||
|
||||
// Swap swaps the values of two elements
|
||||
func (s Uint64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
Reference in New Issue
Block a user