Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 41 additions & 60 deletions cgroupv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ func newCgroupV2Statter(fs afero.Fs, path string, depth int) (*cgroupV2Statter,
}, nil
}

// getFromParentOrFallback attempts to get a value from the parent cgroup
// when the current cgroup has no value set. If there is no parent, it
// calls the fallback function to determine the return value.
func getFromParentOrFallback[T any](
s *cgroupV2Statter,
getter func(*cgroupV2Statter) (T, error),
fallback func() (T, error),
) (T, error) {
if s.parent != nil {
result, err := getter(s.parent)
if err != nil {
var zero T
return zero, xerrors.Errorf("read parent: %w", err)
}
return result, nil
}
return fallback()
}

func (s cgroupV2Statter) cpuUsed() (used float64, err error) {
cpuStatPath := filepath.Join(s.path, cgroupV2CPUStat)

Expand All @@ -94,17 +113,10 @@ func (s cgroupV2Statter) cpuQuota() (float64, error) {
}

// If the value is not a valid integer, assume it is the string
// 'max' and that there is no limit set. In this scenario, we call
// the parent to find its quota.
if s.parent != nil {
total, err := s.parent.cpuQuota()
if err != nil {
return 0, xerrors.Errorf("get parent cpu quota: %w", err)
}
return total, nil
}

return -1, nil
// 'max' and that there is no limit set. Try the parent or return -1.
return getFromParentOrFallback(&s, (*cgroupV2Statter).cpuQuota, func() (float64, error) {
return -1.0, nil
})
}

return float64(quotaUs), nil
Expand All @@ -120,18 +132,11 @@ func (s cgroupV2Statter) cpuPeriod() (float64, error) {
}

// If the value is not a valid integer or the cpu.max file does
// not exist, we call the parent to find its period. This can happen
// in system-level cgroups like init.scope where cpu.max may not exist.
if s.parent != nil {
period, err := s.parent.cpuPeriod()
if err != nil {
return 0, xerrors.Errorf("get parent cpu period: %w", err)
}
return period, nil
}

// No parent and no period found in the cgroup hierarchy.
return cgroupV2DefaultPeriodUs, nil
// not exist, try the parent or return the default period. This can
// happen in system-level cgroups like init.scope where cpu.max may not exist.
return getFromParentOrFallback(&s, (*cgroupV2Statter).cpuPeriod, func() (float64, error) {
return cgroupV2DefaultPeriodUs, nil
})
}

return float64(periodUs), nil
Expand Down Expand Up @@ -161,19 +166,11 @@ func (s cgroupV2Statter) memoryMaxBytes() (*float64, error) {
}

// If the value is not a valid integer _or_ the memory max file
// does not exist, than we can assume that the limit is 'max'.
// If the memory limit is max, and we have a parent, we shall call
// the parent to find its maximum memory value.
if s.parent != nil {
result, err := s.parent.memoryMaxBytes()
if err != nil {
return nil, xerrors.Errorf("read parent memory max: %w", err)
}
return result, nil
}

// We have no parent, and no max memory limit, so there is no memory limit.
return nil, nil
// does not exist, we can assume that the limit is 'max'.
// Try the parent or return nil (no limit).
return getFromParentOrFallback(&s, (*cgroupV2Statter).memoryMaxBytes, func() (*float64, error) {
return nil, nil
})
}

return ptr.To(float64(maxUsageBytes)), nil
Expand All @@ -188,18 +185,10 @@ func (s cgroupV2Statter) memoryCurrentBytes() (int64, error) {
return 0, xerrors.Errorf("read memory current: %w", err)
}

// If the memory current file does not exist, and we have a parent,
// we shall call the parent to find its current memory value.
if s.parent != nil {
result, err := s.parent.memoryCurrentBytes()
if err != nil {
return 0, xerrors.Errorf("read parent memory current: %w", err)
}
return result, nil
}

// We have no parent, and no memory current file, so return the error.
return 0, xerrors.Errorf("read memory current: %w", err)
// If the memory current file does not exist, try the parent or return error.
return getFromParentOrFallback(&s, (*cgroupV2Statter).memoryCurrentBytes, func() (int64, error) {
return 0, xerrors.Errorf("read memory current: %w", err)
})
}

return currUsageBytes, nil
Expand All @@ -214,18 +203,10 @@ func (s cgroupV2Statter) memoryInactiveFileBytes() (int64, error) {
return 0, xerrors.Errorf("read memory stat inactive_file: %w", err)
}

// If the memory stat file does not exist, and we have a parent,
// we shall call the parent to find its inactive_file value.
if s.parent != nil {
result, err := s.parent.memoryInactiveFileBytes()
if err != nil {
return 0, xerrors.Errorf("read parent memory stat inactive_file: %w", err)
}
return result, nil
}

// We have no parent, and no memory stat file, so return the error.
return 0, xerrors.Errorf("read memory stat inactive_file: %w", err)
// If the memory stat file does not exist, try the parent or return error.
return getFromParentOrFallback(&s, (*cgroupV2Statter).memoryInactiveFileBytes, func() (int64, error) {
return 0, xerrors.Errorf("read memory stat inactive_file: %w", err)
})
}

return inactiveFileBytes, nil
Expand Down
Loading