Add mon groups for resctrl.
"mon_groups" can be created to monitor subsets of tasks in the CTRL_MON group that is their ancestor. More info: https://www.kernel.org/doc/Documentation/x86/intel_rdt_ui.txt Signed-off-by: Paweł Szulik <pawel.szulik@intel.com>
This commit is contained in:
parent
f8749ba098
commit
507ecec3b5
|
@ -12,6 +12,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -167,10 +169,16 @@ type IntelRdtManager struct {
|
||||||
Config *configs.Config
|
Config *configs.Config
|
||||||
Id string
|
Id string
|
||||||
Path string
|
Path string
|
||||||
|
Type string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IntelRdtTasks = "tasks"
|
IntelRdtTasks = "tasks"
|
||||||
|
MonGroupDirectoryName = "mon_groups"
|
||||||
|
// Monitoring group.
|
||||||
|
MON = "MON"
|
||||||
|
// Control group.
|
||||||
|
CTRL_MON = "CTRL_MON"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -548,26 +556,47 @@ func GetIntelRdtPath(id string) (string, error) {
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the 'container_id" path in Intel RDT "monitoring group" filesystem.
|
||||||
|
func getMonGroupIntelRdtPath(id string) (string, error) {
|
||||||
|
rootPath, err := getIntelRdtRoot()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
path := filepath.Join(rootPath, MonGroupDirectoryName, id)
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Applies Intel RDT configuration to the process with the specified pid
|
// Applies Intel RDT configuration to the process with the specified pid
|
||||||
func (m *IntelRdtManager) Apply(pid int) (err error) {
|
func (m *IntelRdtManager) Apply(pid int) (err error) {
|
||||||
// If intelRdt is not specified in config, we do nothing
|
switch m.Type {
|
||||||
if m.Config.IntelRdt == nil {
|
case CTRL_MON:
|
||||||
|
// If intelRdt is not specified in config, we do nothing
|
||||||
|
if m.Config.IntelRdt == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rdtData, err := getIntelRdtData(m.Config, pid)
|
||||||
|
if err != nil && !IsNotFound(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
path, err := rdtData.join(m.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Path = path
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
d, err := getIntelRdtData(m.Config, pid)
|
case MON:
|
||||||
if err != nil && !IsNotFound(err) {
|
m.mu.Lock()
|
||||||
return err
|
defer m.mu.Unlock()
|
||||||
|
return WriteIntelRdtTasks(m.Path, pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mu.Lock()
|
return fmt.Errorf("couldn't recognize resctrl type: %v", m.Type)
|
||||||
defer m.mu.Unlock()
|
|
||||||
path, err := d.join(m.Id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Path = path
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroys the Intel RDT 'container_id' group
|
// Destroys the Intel RDT 'container_id' group
|
||||||
|
@ -584,94 +613,124 @@ func (m *IntelRdtManager) Destroy() error {
|
||||||
// Returns Intel RDT path to save in a state file and to be able to
|
// Returns Intel RDT path to save in a state file and to be able to
|
||||||
// restore the object later
|
// restore the object later
|
||||||
func (m *IntelRdtManager) GetPath() string {
|
func (m *IntelRdtManager) GetPath() string {
|
||||||
|
var err error
|
||||||
if m.Path == "" {
|
if m.Path == "" {
|
||||||
m.Path, _ = GetIntelRdtPath(m.Id)
|
switch m.Type {
|
||||||
|
case CTRL_MON:
|
||||||
|
m.Path, err = GetIntelRdtPath(m.Id)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("couldn't obtain Resctrl control group path for manager with id %v: %v", m.Id, err)
|
||||||
|
}
|
||||||
|
case MON:
|
||||||
|
flattedContainerID := strings.Replace(m.Id, "/", "-", -1)
|
||||||
|
m.Path, err = getMonGroupIntelRdtPath(flattedContainerID)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("couldn't obtain Resctrl monitoring group path for manager with id %v: %v", m.Id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return m.Path
|
return m.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns statistics for Intel RDT
|
// Returns statistics for Intel RDT
|
||||||
func (m *IntelRdtManager) GetStats() (*Stats, error) {
|
func (m *IntelRdtManager) GetStats() (*Stats, error) {
|
||||||
// If intelRdt is not specified in config
|
|
||||||
if m.Config.IntelRdt == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
stats := NewStats()
|
stats := NewStats()
|
||||||
|
|
||||||
rootPath, err := getIntelRdtRoot()
|
switch m.Type {
|
||||||
if err != nil {
|
case CTRL_MON:
|
||||||
return nil, err
|
containerPath := m.GetPath()
|
||||||
}
|
|
||||||
// The read-only L3 cache and memory bandwidth schemata in root
|
|
||||||
tmpRootStrings, err := getIntelRdtParamString(rootPath, "schemata")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
schemaRootStrings := strings.Split(tmpRootStrings, "\n")
|
|
||||||
|
|
||||||
// The L3 cache and memory bandwidth schemata in 'container_id' group
|
err := getMonitoringStats(containerPath, stats)
|
||||||
containerPath := m.GetPath()
|
|
||||||
tmpStrings, err := getIntelRdtParamString(containerPath, "schemata")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
schemaStrings := strings.Split(tmpStrings, "\n")
|
|
||||||
|
|
||||||
if IsCatEnabled() {
|
|
||||||
// The read-only L3 cache information
|
|
||||||
l3CacheInfo, err := getL3CacheInfo()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
stats.L3CacheInfo = l3CacheInfo
|
|
||||||
|
|
||||||
// The read-only L3 cache schema in root
|
// If intelRdt is not specified in config
|
||||||
for _, schemaRoot := range schemaRootStrings {
|
if m.Config != nil {
|
||||||
if strings.Contains(schemaRoot, "L3") {
|
if m.Config.IntelRdt == nil {
|
||||||
stats.L3CacheSchemaRoot = strings.TrimSpace(schemaRoot)
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rootPath, err := getIntelRdtRoot()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// The read-only L3 cache and memory bandwidth schemata in root
|
||||||
|
tmpRootStrings, err := getIntelRdtParamString(rootPath, "schemata")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
schemaRootStrings := strings.Split(tmpRootStrings, "\n")
|
||||||
|
|
||||||
|
// The L3 cache and memory bandwidth schemata in 'container_id' group
|
||||||
|
|
||||||
|
tmpStrings, err := getIntelRdtParamString(containerPath, "schemata")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
schemaStrings := strings.Split(tmpStrings, "\n")
|
||||||
|
|
||||||
|
if IsCatEnabled() {
|
||||||
|
// The read-only L3 cache information
|
||||||
|
l3CacheInfo, err := getL3CacheInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stats.L3CacheInfo = l3CacheInfo
|
||||||
|
|
||||||
|
// The read-only L3 cache schema in root
|
||||||
|
for _, schemaRoot := range schemaRootStrings {
|
||||||
|
if strings.Contains(schemaRoot, "L3") {
|
||||||
|
stats.L3CacheSchemaRoot = strings.TrimSpace(schemaRoot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The L3 cache schema in 'container_id' group
|
||||||
|
for _, schema := range schemaStrings {
|
||||||
|
if strings.Contains(schema, "L3") {
|
||||||
|
stats.L3CacheSchema = strings.TrimSpace(schema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsMbaEnabled() {
|
||||||
|
// The read-only memory bandwidth information
|
||||||
|
memBwInfo, err := getMemBwInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stats.MemBwInfo = memBwInfo
|
||||||
|
|
||||||
|
// The read-only memory bandwidth information
|
||||||
|
for _, schemaRoot := range schemaRootStrings {
|
||||||
|
if strings.Contains(schemaRoot, "MB") {
|
||||||
|
stats.MemBwSchemaRoot = strings.TrimSpace(schemaRoot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The memory bandwidth schema in 'container_id' group
|
||||||
|
for _, schema := range schemaStrings {
|
||||||
|
if strings.Contains(schema, "MB") {
|
||||||
|
stats.MemBwSchema = strings.TrimSpace(schema)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The L3 cache schema in 'container_id' group
|
return stats, nil
|
||||||
for _, schema := range schemaStrings {
|
|
||||||
if strings.Contains(schema, "L3") {
|
|
||||||
stats.L3CacheSchema = strings.TrimSpace(schema)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsMbaEnabled() {
|
case MON:
|
||||||
// The read-only memory bandwidth information
|
path := m.GetPath()
|
||||||
memBwInfo, err := getMemBwInfo()
|
|
||||||
|
err := getMonitoringStats(path, stats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
stats.MemBwInfo = memBwInfo
|
return stats, nil
|
||||||
|
|
||||||
// The read-only memory bandwidth information
|
|
||||||
for _, schemaRoot := range schemaRootStrings {
|
|
||||||
if strings.Contains(schemaRoot, "MB") {
|
|
||||||
stats.MemBwSchemaRoot = strings.TrimSpace(schemaRoot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The memory bandwidth schema in 'container_id' group
|
|
||||||
for _, schema := range schemaStrings {
|
|
||||||
if strings.Contains(schema, "MB") {
|
|
||||||
stats.MemBwSchema = strings.TrimSpace(schema)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return nil, fmt.Errorf("couldn't obtain stats from: %q resctrl manager of type: %q", m.Id, m.Type)
|
||||||
err = getMonitoringStats(containerPath, stats)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Intel RDT "resource control" filesystem as configured.
|
// Set Intel RDT "resource control" filesystem as configured.
|
||||||
|
@ -721,34 +780,46 @@ func (m *IntelRdtManager) Set(container *configs.Config) error {
|
||||||
// For example, on a two-socket machine, the schema line could be
|
// For example, on a two-socket machine, the schema line could be
|
||||||
// "MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on
|
// "MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on
|
||||||
// socket 0 and 7000 MBps memory bandwidth limit on socket 1.
|
// socket 0 and 7000 MBps memory bandwidth limit on socket 1.
|
||||||
if container.IntelRdt != nil {
|
switch m.Type {
|
||||||
path := m.GetPath()
|
case CTRL_MON:
|
||||||
l3CacheSchema := container.IntelRdt.L3CacheSchema
|
if container.IntelRdt != nil {
|
||||||
memBwSchema := container.IntelRdt.MemBwSchema
|
path := m.GetPath()
|
||||||
|
l3CacheSchema := container.IntelRdt.L3CacheSchema
|
||||||
|
memBwSchema := container.IntelRdt.MemBwSchema
|
||||||
|
|
||||||
// Write a single joint schema string to schemata file
|
// Write a single joint schema string to schemata file
|
||||||
if l3CacheSchema != "" && memBwSchema != "" {
|
if l3CacheSchema != "" && memBwSchema != "" {
|
||||||
if err := writeFile(path, "schemata", l3CacheSchema+"\n"+memBwSchema); err != nil {
|
if err := writeFile(path, "schemata", l3CacheSchema+"\n"+memBwSchema); err != nil {
|
||||||
return NewLastCmdError(err)
|
return NewLastCmdError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write only L3 cache schema string to schemata file
|
||||||
|
if l3CacheSchema != "" && memBwSchema == "" {
|
||||||
|
if err := writeFile(path, "schemata", l3CacheSchema); err != nil {
|
||||||
|
return NewLastCmdError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write only memory bandwidth schema string to schemata file
|
||||||
|
if l3CacheSchema == "" && memBwSchema != "" {
|
||||||
|
if err := writeFile(path, "schemata", memBwSchema); err != nil {
|
||||||
|
return NewLastCmdError(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write only L3 cache schema string to schemata file
|
case MON:
|
||||||
if l3CacheSchema != "" && memBwSchema == "" {
|
if _, err := os.Stat(m.GetPath()); err == nil {
|
||||||
if err := writeFile(path, "schemata", l3CacheSchema); err != nil {
|
return nil
|
||||||
return NewLastCmdError(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if err := os.Mkdir(m.GetPath(), 0755); err != nil {
|
||||||
// Write only memory bandwidth schema string to schemata file
|
return err
|
||||||
if l3CacheSchema == "" && memBwSchema != "" {
|
|
||||||
if err := writeFile(path, "schemata", memBwSchema); err != nil {
|
|
||||||
return NewLastCmdError(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return fmt.Errorf("couldn't set configuration for: %q resctrl manager of type: %q", m.Id, m.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (raw *intelRdtData) join(id string) (string, error) {
|
func (raw *intelRdtData) join(id string) (string, error) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package intelrdt
|
package intelrdt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -120,3 +121,41 @@ func TestIntelRdtSetMemBwScSchema(t *testing.T) {
|
||||||
t.Fatal("Got the wrong value, set 'schemata' failed.")
|
t.Fatal("Got the wrong value, set 'schemata' failed.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIntelRdtManager_GetStats_NotSupported_Type(t *testing.T) {
|
||||||
|
manager := IntelRdtManager{
|
||||||
|
Type: "not_supported",
|
||||||
|
Id: "id",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := manager.GetStats()
|
||||||
|
|
||||||
|
expectedError := errors.New("couldn't obtain stats from: \"id\" resctrl manager of type: \"not_supported\"")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error: %v, got nil.", expectedError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.Error() != expectedError.Error() {
|
||||||
|
t.Fatalf("Expected error: %v but got: %v.", expectedError, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_IntelRdtManager_Set_NotSupported_Type(t *testing.T) {
|
||||||
|
manager := IntelRdtManager{
|
||||||
|
Type: "not_supported",
|
||||||
|
Id: "id",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := manager.Set(nil)
|
||||||
|
|
||||||
|
expectedError := errors.New("couldn't set configuration for: \"id\" resctrl manager of type: \"not_supported\"")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error: %v, got nil.", expectedError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.Error() != expectedError.Error() {
|
||||||
|
t.Fatalf("Expected error: %v but got: %v.", expectedError, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -321,6 +321,7 @@ other options are ignored.
|
||||||
Config: &config,
|
Config: &config,
|
||||||
Id: container.ID(),
|
Id: container.ID(),
|
||||||
Path: state.IntelRdtPath,
|
Path: state.IntelRdtPath,
|
||||||
|
Type: intelrdt.CTRL_MON,
|
||||||
}
|
}
|
||||||
if err := intelRdtManager.Apply(state.InitProcessPid); err != nil {
|
if err := intelRdtManager.Apply(state.InitProcessPid); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in New Issue