mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-09-19 14:17:48 +00:00
vendor: update container-libs to df55d6c661e8 for cgv1 removal
Also simplifies cgroups.AvailableControllers Signed-off-by: Lokesh Mandvekar <lsm5@redhat.com>
This commit is contained in:
parent
4eaff6fe22
commit
b78f1cf986
37 changed files with 154 additions and 3130 deletions
6
go.mod
6
go.mod
|
|
@ -64,9 +64,9 @@ require (
|
|||
github.com/stretchr/testify v1.11.1
|
||||
github.com/vbauerster/mpb/v8 v8.11.2
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
go.podman.io/common v0.66.1-0.20251128185259-94e31d2e45ba
|
||||
go.podman.io/image/v5 v5.38.1-0.20251128185259-94e31d2e45ba
|
||||
go.podman.io/storage v1.61.1-0.20251128185259-94e31d2e45ba
|
||||
go.podman.io/common v0.66.1-0.20251202202200-df55d6c661e8
|
||||
go.podman.io/image/v5 v5.38.1-0.20251202202200-df55d6c661e8
|
||||
go.podman.io/storage v1.61.1-0.20251202202200-df55d6c661e8
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/sync v0.18.0
|
||||
|
|
|
|||
12
go.sum
12
go.sum
|
|
@ -447,12 +447,12 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr
|
|||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.podman.io/common v0.66.1-0.20251128185259-94e31d2e45ba h1:rQiNXdDOa/A2bLvUQI9xeKuttkzytuUMXQWIeDwebZ8=
|
||||
go.podman.io/common v0.66.1-0.20251128185259-94e31d2e45ba/go.mod h1:wpnuqmlKx7El9QgMIMJiZq9SM+InftBZF4QUlEKGAZI=
|
||||
go.podman.io/image/v5 v5.38.1-0.20251128185259-94e31d2e45ba h1:0Mg1+Rgmil+i1BmLwk1YtddzCRmAjRHh8WDOZBEWZkY=
|
||||
go.podman.io/image/v5 v5.38.1-0.20251128185259-94e31d2e45ba/go.mod h1:bXKvkbQgtxD3hibVOhAeGANyKPqo6lVwdwOxt93+V40=
|
||||
go.podman.io/storage v1.61.1-0.20251128185259-94e31d2e45ba h1:fC+hvOKAsgVWpFPoEZjOioKAJFZNi3UpPHUQzZOcSVg=
|
||||
go.podman.io/storage v1.61.1-0.20251128185259-94e31d2e45ba/go.mod h1:izI0U2FxSpqivwmkVbTO7ECcoER6FnM6ZL7CFPu0Tfs=
|
||||
go.podman.io/common v0.66.1-0.20251202202200-df55d6c661e8 h1:X/HMawr2P5VtTICpL57wJLRpYQrLDSMOwNKKusJ4tVI=
|
||||
go.podman.io/common v0.66.1-0.20251202202200-df55d6c661e8/go.mod h1:wpnuqmlKx7El9QgMIMJiZq9SM+InftBZF4QUlEKGAZI=
|
||||
go.podman.io/image/v5 v5.38.1-0.20251202202200-df55d6c661e8 h1:JKM6NKkbcq+HpAtYlnWwugOCM08aUKE5nbI0aNIDSIQ=
|
||||
go.podman.io/image/v5 v5.38.1-0.20251202202200-df55d6c661e8/go.mod h1:bXKvkbQgtxD3hibVOhAeGANyKPqo6lVwdwOxt93+V40=
|
||||
go.podman.io/storage v1.61.1-0.20251202202200-df55d6c661e8 h1:1zf8nDepbe13RUhjdgj2UIQRHAUZlDohNZbhQBZVnm0=
|
||||
go.podman.io/storage v1.61.1-0.20251202202200-df55d6c661e8/go.mod h1:izI0U2FxSpqivwmkVbTO7ECcoER6FnM6ZL7CFPu0Tfs=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
|
|
|
|||
|
|
@ -30,10 +30,7 @@ func (r *Runtime) setPlatformHostInfo(info *define.HostInfo) error {
|
|||
return fmt.Errorf("getting Seccomp profile path: %w", err)
|
||||
}
|
||||
|
||||
// Get Map of all available controllers
|
||||
// FIXME: AvailableControllers should be further simplified once CGv1 removal
|
||||
// in container-libs is complete.
|
||||
availableControllers, err := cgroups.AvailableControllers(nil, true)
|
||||
availableControllers, err := cgroups.AvailableControllers()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting available cgroup controllers: %w", err)
|
||||
}
|
||||
|
|
|
|||
310
vendor/github.com/opencontainers/cgroups/fs/blkio.go
generated
vendored
310
vendor/github.com/opencontainers/cgroups/fs/blkio.go
generated
vendored
|
|
@ -1,310 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
)
|
||||
|
||||
type BlkioGroup struct {
|
||||
weightFilename string
|
||||
weightDeviceFilename string
|
||||
}
|
||||
|
||||
func (s *BlkioGroup) Name() string {
|
||||
return "blkio"
|
||||
}
|
||||
|
||||
func (s *BlkioGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *BlkioGroup) Set(path string, r *cgroups.Resources) error {
|
||||
s.detectWeightFilenames(path)
|
||||
if r.BlkioWeight != 0 {
|
||||
if err := cgroups.WriteFile(path, s.weightFilename, strconv.FormatUint(uint64(r.BlkioWeight), 10)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if r.BlkioLeafWeight != 0 {
|
||||
if err := cgroups.WriteFile(path, "blkio.leaf_weight", strconv.FormatUint(uint64(r.BlkioLeafWeight), 10)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, wd := range r.BlkioWeightDevice {
|
||||
if wd.Weight != 0 {
|
||||
if err := cgroups.WriteFile(path, s.weightDeviceFilename, wd.WeightString()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if wd.LeafWeight != 0 {
|
||||
if err := cgroups.WriteFile(path, "blkio.leaf_weight_device", wd.LeafWeightString()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, td := range r.BlkioThrottleReadBpsDevice {
|
||||
if err := cgroups.WriteFile(path, "blkio.throttle.read_bps_device", td.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, td := range r.BlkioThrottleWriteBpsDevice {
|
||||
if err := cgroups.WriteFile(path, "blkio.throttle.write_bps_device", td.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, td := range r.BlkioThrottleReadIOPSDevice {
|
||||
if err := cgroups.WriteFile(path, "blkio.throttle.read_iops_device", td.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, td := range r.BlkioThrottleWriteIOPSDevice {
|
||||
if err := cgroups.WriteFile(path, "blkio.throttle.write_iops_device", td.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
examples:
|
||||
|
||||
blkio.sectors
|
||||
8:0 6792
|
||||
|
||||
blkio.io_service_bytes
|
||||
8:0 Read 1282048
|
||||
8:0 Write 2195456
|
||||
8:0 Sync 2195456
|
||||
8:0 Async 1282048
|
||||
8:0 Total 3477504
|
||||
Total 3477504
|
||||
|
||||
blkio.io_serviced
|
||||
8:0 Read 124
|
||||
8:0 Write 104
|
||||
8:0 Sync 104
|
||||
8:0 Async 124
|
||||
8:0 Total 228
|
||||
Total 228
|
||||
|
||||
blkio.io_queued
|
||||
8:0 Read 0
|
||||
8:0 Write 0
|
||||
8:0 Sync 0
|
||||
8:0 Async 0
|
||||
8:0 Total 0
|
||||
Total 0
|
||||
*/
|
||||
|
||||
func splitBlkioStatLine(r rune) bool {
|
||||
return r == ' ' || r == ':'
|
||||
}
|
||||
|
||||
func getBlkioStat(dir, file string) ([]cgroups.BlkioStatEntry, error) {
|
||||
var blkioStats []cgroups.BlkioStatEntry
|
||||
f, err := cgroups.OpenFile(dir, file, os.O_RDONLY)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return blkioStats, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
// format: dev type amount
|
||||
fields := strings.FieldsFunc(sc.Text(), splitBlkioStatLine)
|
||||
if len(fields) < 3 {
|
||||
if len(fields) == 2 && fields[0] == "Total" {
|
||||
// skip total line
|
||||
continue
|
||||
} else {
|
||||
return nil, malformedLine(dir, file, sc.Text())
|
||||
}
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(fields[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, &parseError{Path: dir, File: file, Err: err}
|
||||
}
|
||||
major := v
|
||||
|
||||
v, err = strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, &parseError{Path: dir, File: file, Err: err}
|
||||
}
|
||||
minor := v
|
||||
|
||||
op := ""
|
||||
valueField := 2
|
||||
if len(fields) == 4 {
|
||||
op = fields[2]
|
||||
valueField = 3
|
||||
}
|
||||
v, err = strconv.ParseUint(fields[valueField], 10, 64)
|
||||
if err != nil {
|
||||
return nil, &parseError{Path: dir, File: file, Err: err}
|
||||
}
|
||||
blkioStats = append(blkioStats, cgroups.BlkioStatEntry{Major: major, Minor: minor, Op: op, Value: v})
|
||||
}
|
||||
if err := sc.Err(); err != nil {
|
||||
return nil, &parseError{Path: dir, File: file, Err: err}
|
||||
}
|
||||
|
||||
return blkioStats, nil
|
||||
}
|
||||
|
||||
func (s *BlkioGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
type blkioStatInfo struct {
|
||||
filename string
|
||||
blkioStatEntriesPtr *[]cgroups.BlkioStatEntry
|
||||
}
|
||||
bfqDebugStats := []blkioStatInfo{
|
||||
{
|
||||
filename: "blkio.bfq.sectors_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.SectorsRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.bfq.io_service_time_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServiceTimeRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.bfq.io_wait_time_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoWaitTimeRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.bfq.io_merged_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoMergedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.bfq.io_queued_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoQueuedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.bfq.time_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoTimeRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.bfq.io_serviced_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.bfq.io_service_bytes_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
|
||||
},
|
||||
}
|
||||
bfqStats := []blkioStatInfo{
|
||||
{
|
||||
filename: "blkio.bfq.io_serviced_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.bfq.io_service_bytes_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
|
||||
},
|
||||
}
|
||||
cfqStats := []blkioStatInfo{
|
||||
{
|
||||
filename: "blkio.sectors_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.SectorsRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.io_service_time_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServiceTimeRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.io_wait_time_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoWaitTimeRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.io_merged_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoMergedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.io_queued_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoQueuedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.time_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoTimeRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.io_serviced_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.io_service_bytes_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
|
||||
},
|
||||
}
|
||||
throttleRecursiveStats := []blkioStatInfo{
|
||||
{
|
||||
filename: "blkio.throttle.io_serviced_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.throttle.io_service_bytes_recursive",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
|
||||
},
|
||||
}
|
||||
baseStats := []blkioStatInfo{
|
||||
{
|
||||
filename: "blkio.throttle.io_serviced",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
|
||||
},
|
||||
{
|
||||
filename: "blkio.throttle.io_service_bytes",
|
||||
blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
|
||||
},
|
||||
}
|
||||
orderedStats := [][]blkioStatInfo{
|
||||
bfqDebugStats,
|
||||
bfqStats,
|
||||
cfqStats,
|
||||
throttleRecursiveStats,
|
||||
baseStats,
|
||||
}
|
||||
|
||||
var blkioStats []cgroups.BlkioStatEntry
|
||||
var err error
|
||||
|
||||
for _, statGroup := range orderedStats {
|
||||
for i, statInfo := range statGroup {
|
||||
if blkioStats, err = getBlkioStat(path, statInfo.filename); err != nil || blkioStats == nil {
|
||||
// if error occurs on first file, move to next group
|
||||
if i == 0 {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
*statInfo.blkioStatEntriesPtr = blkioStats
|
||||
// finish if all stats are gathered
|
||||
if i == len(statGroup)-1 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BlkioGroup) detectWeightFilenames(path string) {
|
||||
if s.weightFilename != "" {
|
||||
// Already detected.
|
||||
return
|
||||
}
|
||||
if cgroups.PathExists(filepath.Join(path, "blkio.weight")) {
|
||||
s.weightFilename = "blkio.weight"
|
||||
s.weightDeviceFilename = "blkio.weight_device"
|
||||
} else {
|
||||
s.weightFilename = "blkio.bfq.weight"
|
||||
s.weightDeviceFilename = "blkio.bfq.weight_device"
|
||||
}
|
||||
}
|
||||
181
vendor/github.com/opencontainers/cgroups/fs/cpu.go
generated
vendored
181
vendor/github.com/opencontainers/cgroups/fs/cpu.go
generated
vendored
|
|
@ -1,181 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type CpuGroup struct{}
|
||||
|
||||
func (s *CpuGroup) Name() string {
|
||||
return "cpu"
|
||||
}
|
||||
|
||||
func (s *CpuGroup) Apply(path string, r *cgroups.Resources, pid int) error {
|
||||
if err := os.MkdirAll(path, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
// We should set the real-Time group scheduling settings before moving
|
||||
// in the process because if the process is already in SCHED_RR mode
|
||||
// and no RT bandwidth is set, adding it will fail.
|
||||
if err := s.SetRtSched(path, r); err != nil {
|
||||
return err
|
||||
}
|
||||
// Since we are not using apply(), we need to place the pid
|
||||
// into the procs file.
|
||||
return cgroups.WriteCgroupProc(path, pid)
|
||||
}
|
||||
|
||||
func (s *CpuGroup) SetRtSched(path string, r *cgroups.Resources) error {
|
||||
var period string
|
||||
if r.CpuRtPeriod != 0 {
|
||||
period = strconv.FormatUint(r.CpuRtPeriod, 10)
|
||||
if err := cgroups.WriteFile(path, "cpu.rt_period_us", period); err != nil {
|
||||
// The values of cpu.rt_period_us and cpu.rt_runtime_us
|
||||
// are inter-dependent and need to be set in a proper order.
|
||||
// If the kernel rejects the new period value with EINVAL
|
||||
// and the new runtime value is also being set, let's
|
||||
// ignore the error for now and retry later.
|
||||
if !errors.Is(err, unix.EINVAL) || r.CpuRtRuntime == 0 {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
period = ""
|
||||
}
|
||||
}
|
||||
if r.CpuRtRuntime != 0 {
|
||||
if err := cgroups.WriteFile(path, "cpu.rt_runtime_us", strconv.FormatInt(r.CpuRtRuntime, 10)); err != nil {
|
||||
return err
|
||||
}
|
||||
if period != "" {
|
||||
if err := cgroups.WriteFile(path, "cpu.rt_period_us", period); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CpuGroup) Set(path string, r *cgroups.Resources) error {
|
||||
if r.CpuShares != 0 {
|
||||
shares := r.CpuShares
|
||||
if err := cgroups.WriteFile(path, "cpu.shares", strconv.FormatUint(shares, 10)); err != nil {
|
||||
return err
|
||||
}
|
||||
// read it back
|
||||
sharesRead, err := fscommon.GetCgroupParamUint(path, "cpu.shares")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// ... and check
|
||||
if shares > sharesRead {
|
||||
return fmt.Errorf("the maximum allowed cpu-shares is %d", sharesRead)
|
||||
} else if shares < sharesRead {
|
||||
return fmt.Errorf("the minimum allowed cpu-shares is %d", sharesRead)
|
||||
}
|
||||
}
|
||||
|
||||
var period string
|
||||
if r.CpuPeriod != 0 {
|
||||
period = strconv.FormatUint(r.CpuPeriod, 10)
|
||||
if err := cgroups.WriteFile(path, "cpu.cfs_period_us", period); err != nil {
|
||||
// Sometimes when the period to be set is smaller
|
||||
// than the current one, it is rejected by the kernel
|
||||
// (EINVAL) as old_quota/new_period exceeds the parent
|
||||
// cgroup quota limit. If this happens and the quota is
|
||||
// going to be set, ignore the error for now and retry
|
||||
// after setting the quota.
|
||||
if !errors.Is(err, unix.EINVAL) || r.CpuQuota == 0 {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
period = ""
|
||||
}
|
||||
}
|
||||
|
||||
var burst string
|
||||
if r.CpuBurst != nil {
|
||||
burst = strconv.FormatUint(*r.CpuBurst, 10)
|
||||
if err := cgroups.WriteFile(path, "cpu.cfs_burst_us", burst); err != nil {
|
||||
if errors.Is(err, unix.ENOENT) {
|
||||
// If CPU burst knob is not available (e.g.
|
||||
// older kernel), ignore it.
|
||||
burst = ""
|
||||
} else {
|
||||
// Sometimes when the burst to be set is larger
|
||||
// than the current one, it is rejected by the kernel
|
||||
// (EINVAL) as old_quota/new_burst exceeds the parent
|
||||
// cgroup quota limit. If this happens and the quota is
|
||||
// going to be set, ignore the error for now and retry
|
||||
// after setting the quota.
|
||||
if !errors.Is(err, unix.EINVAL) || r.CpuQuota == 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
burst = ""
|
||||
}
|
||||
}
|
||||
if r.CpuQuota != 0 {
|
||||
if err := cgroups.WriteFile(path, "cpu.cfs_quota_us", strconv.FormatInt(r.CpuQuota, 10)); err != nil {
|
||||
return err
|
||||
}
|
||||
if period != "" {
|
||||
if err := cgroups.WriteFile(path, "cpu.cfs_period_us", period); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if burst != "" {
|
||||
if err := cgroups.WriteFile(path, "cpu.cfs_burst_us", burst); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.CPUIdle != nil {
|
||||
idle := strconv.FormatInt(*r.CPUIdle, 10)
|
||||
if err := cgroups.WriteFile(path, "cpu.idle", idle); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return s.SetRtSched(path, r)
|
||||
}
|
||||
|
||||
func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
const file = "cpu.stat"
|
||||
f, err := cgroups.OpenFile(path, file, os.O_RDONLY)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
t, v, err := fscommon.ParseKeyValue(sc.Text())
|
||||
if err != nil {
|
||||
return &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
switch t {
|
||||
case "nr_periods":
|
||||
stats.CpuStats.ThrottlingData.Periods = v
|
||||
|
||||
case "nr_throttled":
|
||||
stats.CpuStats.ThrottlingData.ThrottledPeriods = v
|
||||
|
||||
case "throttled_time":
|
||||
stats.CpuStats.ThrottlingData.ThrottledTime = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
162
vendor/github.com/opencontainers/cgroups/fs/cpuacct.go
generated
vendored
162
vendor/github.com/opencontainers/cgroups/fs/cpuacct.go
generated
vendored
|
|
@ -1,162 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
)
|
||||
|
||||
const (
|
||||
nsInSec = 1000000000
|
||||
|
||||
// The value comes from `C.sysconf(C._SC_CLK_TCK)`, and
|
||||
// on Linux it's a constant which is safe to be hard coded,
|
||||
// so we can avoid using cgo here. For details, see:
|
||||
// https://github.com/containerd/cgroups/pull/12
|
||||
clockTicks uint64 = 100
|
||||
)
|
||||
|
||||
type CpuacctGroup struct{}
|
||||
|
||||
func (s *CpuacctGroup) Name() string {
|
||||
return "cpuacct"
|
||||
}
|
||||
|
||||
func (s *CpuacctGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *CpuacctGroup) Set(_ string, _ *cgroups.Resources) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
if !cgroups.PathExists(path) {
|
||||
return nil
|
||||
}
|
||||
userModeUsage, kernelModeUsage, err := getCpuUsageBreakdown(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
totalUsage, err := fscommon.GetCgroupParamUint(path, "cpuacct.usage")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
percpuUsage, err := getPercpuUsage(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
percpuUsageInKernelmode, percpuUsageInUsermode, err := getPercpuUsageInModes(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CpuStats.CpuUsage.TotalUsage = totalUsage
|
||||
stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage
|
||||
stats.CpuStats.CpuUsage.PercpuUsageInKernelmode = percpuUsageInKernelmode
|
||||
stats.CpuStats.CpuUsage.PercpuUsageInUsermode = percpuUsageInUsermode
|
||||
stats.CpuStats.CpuUsage.UsageInUsermode = userModeUsage
|
||||
stats.CpuStats.CpuUsage.UsageInKernelmode = kernelModeUsage
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns user and kernel usage breakdown in nanoseconds.
|
||||
func getCpuUsageBreakdown(path string) (uint64, uint64, error) {
|
||||
var userModeUsage, kernelModeUsage uint64
|
||||
const (
|
||||
userField = "user"
|
||||
systemField = "system"
|
||||
file = "cpuacct.stat"
|
||||
)
|
||||
|
||||
// Expected format:
|
||||
// user <usage in ticks>
|
||||
// system <usage in ticks>
|
||||
data, err := cgroups.ReadFile(path, file)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
fields := strings.Fields(data)
|
||||
if len(fields) < 4 || fields[0] != userField || fields[2] != systemField {
|
||||
return 0, 0, malformedLine(path, file, data)
|
||||
}
|
||||
if userModeUsage, err = strconv.ParseUint(fields[1], 10, 64); err != nil {
|
||||
return 0, 0, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
if kernelModeUsage, err = strconv.ParseUint(fields[3], 10, 64); err != nil {
|
||||
return 0, 0, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
|
||||
return (userModeUsage * nsInSec) / clockTicks, (kernelModeUsage * nsInSec) / clockTicks, nil
|
||||
}
|
||||
|
||||
func getPercpuUsage(path string) ([]uint64, error) {
|
||||
const file = "cpuacct.usage_percpu"
|
||||
percpuUsage := []uint64{}
|
||||
data, err := cgroups.ReadFile(path, file)
|
||||
if err != nil {
|
||||
return percpuUsage, err
|
||||
}
|
||||
for _, value := range strings.Fields(data) {
|
||||
value, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return percpuUsage, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
percpuUsage = append(percpuUsage, value)
|
||||
}
|
||||
return percpuUsage, nil
|
||||
}
|
||||
|
||||
func getPercpuUsageInModes(path string) ([]uint64, []uint64, error) {
|
||||
usageKernelMode := []uint64{}
|
||||
usageUserMode := []uint64{}
|
||||
const file = "cpuacct.usage_all"
|
||||
|
||||
fd, err := cgroups.OpenFile(path, file, os.O_RDONLY)
|
||||
if os.IsNotExist(err) {
|
||||
return usageKernelMode, usageUserMode, nil
|
||||
} else if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
scanner := bufio.NewScanner(fd)
|
||||
scanner.Scan() // Read header line.
|
||||
const want = "cpu user system"
|
||||
if hdr := scanner.Text(); !strings.HasPrefix(hdr, want) {
|
||||
return nil, nil, malformedLine(path, file, hdr)
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
// Each line is: cpu user system. Keep N at 4 to ignore extra fields.
|
||||
fields := strings.SplitN(scanner.Text(), " ", 4)
|
||||
if len(fields) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
user, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, nil, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
usageUserMode = append(usageUserMode, user)
|
||||
|
||||
kernel, err := strconv.ParseUint(fields[2], 10, 64)
|
||||
if err != nil {
|
||||
return nil, nil, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
usageKernelMode = append(usageKernelMode, kernel)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, nil, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
|
||||
return usageKernelMode, usageUserMode, nil
|
||||
}
|
||||
276
vendor/github.com/opencontainers/cgroups/fs/cpuset.go
generated
vendored
276
vendor/github.com/opencontainers/cgroups/fs/cpuset.go
generated
vendored
|
|
@ -1,276 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
)
|
||||
|
||||
var (
|
||||
cpusetLock sync.Mutex
|
||||
cpusetPrefix = "cpuset."
|
||||
cpusetFastPath bool
|
||||
)
|
||||
|
||||
func cpusetFile(path string, name string) string {
|
||||
cpusetLock.Lock()
|
||||
defer cpusetLock.Unlock()
|
||||
|
||||
// Only the v1 cpuset cgroup is allowed to mount with noprefix.
|
||||
// See kernel source: https://github.com/torvalds/linux/blob/2e1b3cc9d7f790145a80cb705b168f05dab65df2/kernel/cgroup/cgroup-v1.c#L1070
|
||||
// Cpuset cannot be mounted with and without prefix simultaneously.
|
||||
// Commonly used in Android environments.
|
||||
|
||||
if cpusetFastPath {
|
||||
return cpusetPrefix + name
|
||||
}
|
||||
|
||||
err := unix.Access(filepath.Join(path, cpusetPrefix+name), unix.F_OK)
|
||||
if err == nil {
|
||||
// Use the fast path only if we can access one type of mount for cpuset already
|
||||
cpusetFastPath = true
|
||||
} else {
|
||||
err = unix.Access(filepath.Join(path, name), unix.F_OK)
|
||||
if err == nil {
|
||||
cpusetPrefix = ""
|
||||
cpusetFastPath = true
|
||||
}
|
||||
}
|
||||
|
||||
return cpusetPrefix + name
|
||||
}
|
||||
|
||||
type CpusetGroup struct{}
|
||||
|
||||
func (s *CpusetGroup) Name() string {
|
||||
return "cpuset"
|
||||
}
|
||||
|
||||
func (s *CpusetGroup) Apply(path string, r *cgroups.Resources, pid int) error {
|
||||
return s.ApplyDir(path, r, pid)
|
||||
}
|
||||
|
||||
func (s *CpusetGroup) Set(path string, r *cgroups.Resources) error {
|
||||
if r.CpusetCpus != "" {
|
||||
if err := cgroups.WriteFile(path, cpusetFile(path, "cpus"), r.CpusetCpus); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if r.CpusetMems != "" {
|
||||
if err := cgroups.WriteFile(path, cpusetFile(path, "mems"), r.CpusetMems); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCpusetStat(path string, file string) ([]uint16, error) {
|
||||
var extracted []uint16
|
||||
fileContent, err := fscommon.GetCgroupParamString(path, file)
|
||||
if err != nil {
|
||||
return extracted, err
|
||||
}
|
||||
if len(fileContent) == 0 {
|
||||
return extracted, &parseError{Path: path, File: file, Err: errors.New("empty file")}
|
||||
}
|
||||
|
||||
for _, s := range strings.Split(fileContent, ",") {
|
||||
fromStr, toStr, ok := strings.Cut(s, "-")
|
||||
if ok {
|
||||
from, err := strconv.ParseUint(fromStr, 10, 16)
|
||||
if err != nil {
|
||||
return extracted, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
to, err := strconv.ParseUint(toStr, 10, 16)
|
||||
if err != nil {
|
||||
return extracted, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
if from > to {
|
||||
return extracted, &parseError{Path: path, File: file, Err: errors.New("invalid values, from > to")}
|
||||
}
|
||||
for i := from; i <= to; i++ {
|
||||
extracted = append(extracted, uint16(i))
|
||||
}
|
||||
} else {
|
||||
value, err := strconv.ParseUint(s, 10, 16)
|
||||
if err != nil {
|
||||
return extracted, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
extracted = append(extracted, uint16(value))
|
||||
}
|
||||
}
|
||||
|
||||
return extracted, nil
|
||||
}
|
||||
|
||||
func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
var err error
|
||||
|
||||
stats.CPUSetStats.CPUs, err = getCpusetStat(path, cpusetFile(path, "cpus"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.CPUExclusive, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "cpu_exclusive"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.Mems, err = getCpusetStat(path, cpusetFile(path, "mems"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.MemHardwall, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "mem_hardwall"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.MemExclusive, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "mem_exclusive"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.MemoryMigrate, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_migrate"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.MemorySpreadPage, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_spread_page"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.MemorySpreadSlab, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_spread_slab"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.MemoryPressure, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "memory_pressure"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.SchedLoadBalance, err = fscommon.GetCgroupParamUint(path, cpusetFile(path, "sched_load_balance"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.CPUSetStats.SchedRelaxDomainLevel, err = fscommon.GetCgroupParamInt(path, cpusetFile(path, "sched_relax_domain_level"))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CpusetGroup) ApplyDir(dir string, r *cgroups.Resources, pid int) error {
|
||||
// This might happen if we have no cpuset cgroup mounted.
|
||||
// Just do nothing and don't fail.
|
||||
if dir == "" {
|
||||
return nil
|
||||
}
|
||||
// 'ensureParent' start with parent because we don't want to
|
||||
// explicitly inherit from parent, it could conflict with
|
||||
// 'cpuset.cpu_exclusive'.
|
||||
if err := cpusetEnsureParent(filepath.Dir(dir)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Mkdir(dir, 0o755); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
// We didn't inherit cpuset configs from parent, but we have
|
||||
// to ensure cpuset configs are set before moving task into the
|
||||
// cgroup.
|
||||
// The logic is, if user specified cpuset configs, use these
|
||||
// specified configs, otherwise, inherit from parent. This makes
|
||||
// cpuset configs work correctly with 'cpuset.cpu_exclusive', and
|
||||
// keep backward compatibility.
|
||||
if err := s.ensureCpusAndMems(dir, r); err != nil {
|
||||
return err
|
||||
}
|
||||
// Since we are not using apply(), we need to place the pid
|
||||
// into the procs file.
|
||||
return cgroups.WriteCgroupProc(dir, pid)
|
||||
}
|
||||
|
||||
func getCpusetSubsystemSettings(parent string) (cpus, mems string, err error) {
|
||||
if cpus, err = cgroups.ReadFile(parent, cpusetFile(parent, "cpus")); err != nil {
|
||||
return
|
||||
}
|
||||
if mems, err = cgroups.ReadFile(parent, cpusetFile(parent, "mems")); err != nil {
|
||||
return
|
||||
}
|
||||
return cpus, mems, nil
|
||||
}
|
||||
|
||||
// cpusetEnsureParent makes sure that the parent directories of current
|
||||
// are created and populated with the proper cpus and mems files copied
|
||||
// from their respective parent. It does that recursively, starting from
|
||||
// the top of the cpuset hierarchy (i.e. cpuset cgroup mount point).
|
||||
func cpusetEnsureParent(current string) error {
|
||||
var st unix.Statfs_t
|
||||
|
||||
parent := filepath.Dir(current)
|
||||
err := unix.Statfs(parent, &st)
|
||||
if err == nil && st.Type != unix.CGROUP_SUPER_MAGIC {
|
||||
return nil
|
||||
}
|
||||
// Treat non-existing directory as cgroupfs as it will be created,
|
||||
// and the root cpuset directory obviously exists.
|
||||
if err != nil && err != unix.ENOENT {
|
||||
return &os.PathError{Op: "statfs", Path: parent, Err: err}
|
||||
}
|
||||
|
||||
if err := cpusetEnsureParent(parent); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Mkdir(current, 0o755); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
return cpusetCopyIfNeeded(current, parent)
|
||||
}
|
||||
|
||||
// cpusetCopyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent
|
||||
// directory to the current directory if the file's contents are 0
|
||||
func cpusetCopyIfNeeded(current, parent string) error {
|
||||
currentCpus, currentMems, err := getCpusetSubsystemSettings(current)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentCpus, parentMems, err := getCpusetSubsystemSettings(parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isEmptyCpuset(currentCpus) {
|
||||
if err := cgroups.WriteFile(current, cpusetFile(current, "cpus"), parentCpus); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if isEmptyCpuset(currentMems) {
|
||||
if err := cgroups.WriteFile(current, cpusetFile(current, "mems"), parentMems); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isEmptyCpuset(str string) bool {
|
||||
return str == "" || str == "\n"
|
||||
}
|
||||
|
||||
func (s *CpusetGroup) ensureCpusAndMems(path string, r *cgroups.Resources) error {
|
||||
if err := s.Set(path, r); err != nil {
|
||||
return err
|
||||
}
|
||||
return cpusetCopyIfNeeded(path, filepath.Dir(path))
|
||||
}
|
||||
38
vendor/github.com/opencontainers/cgroups/fs/devices.go
generated
vendored
38
vendor/github.com/opencontainers/cgroups/fs/devices.go
generated
vendored
|
|
@ -1,38 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"github.com/opencontainers/cgroups"
|
||||
)
|
||||
|
||||
type DevicesGroup struct{}
|
||||
|
||||
func (s *DevicesGroup) Name() string {
|
||||
return "devices"
|
||||
}
|
||||
|
||||
func (s *DevicesGroup) Apply(path string, r *cgroups.Resources, pid int) error {
|
||||
if r.SkipDevices {
|
||||
return nil
|
||||
}
|
||||
if path == "" {
|
||||
// Return error here, since devices cgroup
|
||||
// is a hard requirement for container's security.
|
||||
return errSubsystemDoesNotExist
|
||||
}
|
||||
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *DevicesGroup) Set(path string, r *cgroups.Resources) error {
|
||||
if cgroups.DevicesSetV1 == nil {
|
||||
if len(r.Devices) == 0 {
|
||||
return nil
|
||||
}
|
||||
return cgroups.ErrDevicesUnsupported
|
||||
}
|
||||
return cgroups.DevicesSetV1(path, r)
|
||||
}
|
||||
|
||||
func (s *DevicesGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
15
vendor/github.com/opencontainers/cgroups/fs/error.go
generated
vendored
15
vendor/github.com/opencontainers/cgroups/fs/error.go
generated
vendored
|
|
@ -1,15 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
)
|
||||
|
||||
type parseError = fscommon.ParseError
|
||||
|
||||
// malformedLine is used by all cgroupfs file parsers that expect a line
|
||||
// in a particular format but get some garbage instead.
|
||||
func malformedLine(path, file, line string) error {
|
||||
return &parseError{Path: path, File: file, Err: fmt.Errorf("malformed line: %s", line)}
|
||||
}
|
||||
157
vendor/github.com/opencontainers/cgroups/fs/freezer.go
generated
vendored
157
vendor/github.com/opencontainers/cgroups/fs/freezer.go
generated
vendored
|
|
@ -1,157 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type FreezerGroup struct{}
|
||||
|
||||
func (s *FreezerGroup) Name() string {
|
||||
return "freezer"
|
||||
}
|
||||
|
||||
func (s *FreezerGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *FreezerGroup) Set(path string, r *cgroups.Resources) (Err error) {
|
||||
switch r.Freezer {
|
||||
case cgroups.Frozen:
|
||||
defer func() {
|
||||
if Err != nil {
|
||||
// Freezing failed, and it is bad and dangerous
|
||||
// to leave the cgroup in FROZEN or FREEZING
|
||||
// state, so (try to) thaw it back.
|
||||
_ = cgroups.WriteFile(path, "freezer.state", string(cgroups.Thawed))
|
||||
}
|
||||
}()
|
||||
|
||||
// As per older kernel docs (freezer-subsystem.txt before
|
||||
// kernel commit ef9fe980c6fcc1821), if FREEZING is seen,
|
||||
// userspace should either retry or thaw. While current
|
||||
// kernel cgroup v1 docs no longer mention a need to retry,
|
||||
// even a recent kernel (v5.4, Ubuntu 20.04) can't reliably
|
||||
// freeze a cgroup v1 while new processes keep appearing in it
|
||||
// (either via fork/clone or by writing new PIDs to
|
||||
// cgroup.procs).
|
||||
//
|
||||
// The numbers below are empirically chosen to have a decent
|
||||
// chance to succeed in various scenarios ("runc pause/unpause
|
||||
// with parallel runc exec" and "bare freeze/unfreeze on a very
|
||||
// slow system"), tested on RHEL7 and Ubuntu 20.04 kernels.
|
||||
//
|
||||
// Adding any amount of sleep in between retries did not
|
||||
// increase the chances of successful freeze in "pause/unpause
|
||||
// with parallel exec" reproducer. OTOH, adding an occasional
|
||||
// sleep helped for the case where the system is extremely slow
|
||||
// (CentOS 7 VM on GHA CI).
|
||||
//
|
||||
// Alas, this is still a game of chances, since the real fix
|
||||
// belong to the kernel (cgroup v2 do not have this bug).
|
||||
|
||||
for i := range 1000 {
|
||||
if i%50 == 49 {
|
||||
// Occasional thaw and sleep improves
|
||||
// the chances to succeed in freezing
|
||||
// in case new processes keep appearing
|
||||
// in the cgroup.
|
||||
_ = cgroups.WriteFile(path, "freezer.state", string(cgroups.Thawed))
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
if err := cgroups.WriteFile(path, "freezer.state", string(cgroups.Frozen)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i%25 == 24 {
|
||||
// Occasional short sleep before reading
|
||||
// the state back also improves the chances to
|
||||
// succeed in freezing in case of a very slow
|
||||
// system.
|
||||
time.Sleep(10 * time.Microsecond)
|
||||
}
|
||||
state, err := cgroups.ReadFile(path, "freezer.state")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state = strings.TrimSpace(state)
|
||||
switch state {
|
||||
case "FREEZING":
|
||||
continue
|
||||
case string(cgroups.Frozen):
|
||||
if i > 1 {
|
||||
logrus.Debugf("frozen after %d retries", i)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
// should never happen
|
||||
return fmt.Errorf("unexpected state %s while freezing", strings.TrimSpace(state))
|
||||
}
|
||||
}
|
||||
// Despite our best efforts, it got stuck in FREEZING.
|
||||
return errors.New("unable to freeze")
|
||||
case cgroups.Thawed:
|
||||
return cgroups.WriteFile(path, "freezer.state", string(cgroups.Thawed))
|
||||
case cgroups.Undefined:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("Invalid argument '%s' to freezer.state", string(r.Freezer))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FreezerGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FreezerGroup) GetState(path string) (cgroups.FreezerState, error) {
|
||||
for {
|
||||
state, err := cgroups.ReadFile(path, "freezer.state")
|
||||
if err != nil {
|
||||
// If the kernel is too old, then we just treat the freezer as
|
||||
// being in an "undefined" state.
|
||||
if os.IsNotExist(err) || errors.Is(err, unix.ENODEV) {
|
||||
err = nil
|
||||
}
|
||||
return cgroups.Undefined, err
|
||||
}
|
||||
switch strings.TrimSpace(state) {
|
||||
case "THAWED":
|
||||
return cgroups.Thawed, nil
|
||||
case "FROZEN":
|
||||
// Find out whether the cgroup is frozen directly,
|
||||
// or indirectly via an ancestor.
|
||||
self, err := cgroups.ReadFile(path, "freezer.self_freezing")
|
||||
if err != nil {
|
||||
// If the kernel is too old, then we just treat
|
||||
// it as being frozen.
|
||||
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.ENODEV) {
|
||||
err = nil
|
||||
}
|
||||
return cgroups.Frozen, err
|
||||
}
|
||||
switch self {
|
||||
case "0\n":
|
||||
return cgroups.Thawed, nil
|
||||
case "1\n":
|
||||
return cgroups.Frozen, nil
|
||||
default:
|
||||
return cgroups.Undefined, fmt.Errorf(`unknown "freezer.self_freezing" state: %q`, self)
|
||||
}
|
||||
case "FREEZING":
|
||||
// Make sure we get a stable freezer state, so retry if the cgroup
|
||||
// is still undergoing freezing. This should be a temporary delay.
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
continue
|
||||
default:
|
||||
return cgroups.Undefined, fmt.Errorf("unknown freezer.state %q", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
294
vendor/github.com/opencontainers/cgroups/fs/fs.go
generated
vendored
294
vendor/github.com/opencontainers/cgroups/fs/fs.go
generated
vendored
|
|
@ -1,294 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
)
|
||||
|
||||
var subsystems = []subsystem{
|
||||
&CpusetGroup{},
|
||||
&DevicesGroup{},
|
||||
&MemoryGroup{},
|
||||
&CpuGroup{},
|
||||
&CpuacctGroup{},
|
||||
&PidsGroup{},
|
||||
&BlkioGroup{},
|
||||
&HugetlbGroup{},
|
||||
&NetClsGroup{},
|
||||
&NetPrioGroup{},
|
||||
&PerfEventGroup{},
|
||||
&FreezerGroup{},
|
||||
&RdmaGroup{},
|
||||
&NameGroup{GroupName: "name=systemd", Join: true},
|
||||
&NameGroup{GroupName: "misc", Join: true},
|
||||
}
|
||||
|
||||
var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")
|
||||
|
||||
func init() {
|
||||
// If using cgroups-hybrid mode then add a "" controller indicating
|
||||
// it should join the cgroups v2.
|
||||
if cgroups.IsCgroup2HybridMode() {
|
||||
subsystems = append(subsystems, &NameGroup{GroupName: "", Join: true})
|
||||
}
|
||||
}
|
||||
|
||||
type subsystem interface {
|
||||
// Name returns the name of the subsystem.
|
||||
Name() string
|
||||
// GetStats fills in the stats for the subsystem.
|
||||
GetStats(path string, stats *cgroups.Stats) error
|
||||
// Apply creates and joins a cgroup, adding pid into it. Some
|
||||
// subsystems use resources to pre-configure the cgroup parents
|
||||
// before creating or joining it.
|
||||
Apply(path string, r *cgroups.Resources, pid int) error
|
||||
// Set sets the cgroup resources.
|
||||
Set(path string, r *cgroups.Resources) error
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
mu sync.Mutex
|
||||
cgroups *cgroups.Cgroup
|
||||
paths map[string]string
|
||||
}
|
||||
|
||||
func NewManager(cg *cgroups.Cgroup, paths map[string]string) (*Manager, error) {
|
||||
// Some v1 controllers (cpu, cpuset, and devices) expect
|
||||
// cgroups.Resources to not be nil in Apply.
|
||||
if cg.Resources == nil {
|
||||
return nil, errors.New("cgroup v1 manager needs cgroups.Resources to be set during manager creation")
|
||||
}
|
||||
if cg.Resources.Unified != nil {
|
||||
return nil, cgroups.ErrV1NoUnified
|
||||
}
|
||||
|
||||
if paths == nil {
|
||||
var err error
|
||||
paths, err = initPaths(cg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
cgroups: cg,
|
||||
paths: paths,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// isIgnorableError returns whether err is a permission error (in the loose
|
||||
// sense of the word). This includes EROFS (which for an unprivileged user is
|
||||
// basically a permission error) and EACCES (for similar reasons) as well as
|
||||
// the normal EPERM.
|
||||
func isIgnorableError(rootless bool, err error) bool {
|
||||
// We do not ignore errors if we are root.
|
||||
if !rootless {
|
||||
return false
|
||||
}
|
||||
// Is it an ordinary EPERM?
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
return true
|
||||
}
|
||||
// Handle some specific syscall errors.
|
||||
var errno unix.Errno
|
||||
if errors.As(err, &errno) {
|
||||
return errno == unix.EROFS || errno == unix.EPERM || errno == unix.EACCES
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Manager) Apply(pid int) (retErr error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
c := m.cgroups
|
||||
|
||||
for _, sys := range subsystems {
|
||||
name := sys.Name()
|
||||
p, ok := m.paths[name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := sys.Apply(p, c.Resources, pid); err != nil {
|
||||
// In the case of rootless (including euid=0 in userns), where an
|
||||
// explicit cgroup path hasn't been set, we don't bail on error in
|
||||
// case of permission problems here, but do delete the path from
|
||||
// the m.paths map, since it is either non-existent and could not
|
||||
// be created, or the pid could not be added to it.
|
||||
//
|
||||
// Cases where limits for the subsystem have been set are handled
|
||||
// later by Set, which fails with a friendly error (see
|
||||
// if path == "" in Set).
|
||||
if isIgnorableError(c.Rootless, err) && c.Path == "" {
|
||||
retErr = cgroups.ErrRootless
|
||||
delete(m.paths, name)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return retErr
|
||||
}
|
||||
|
||||
// AddPid adds a process with a given pid to an existing cgroup.
|
||||
// The subcgroup argument is either empty, or a path relative to
|
||||
// a cgroup under under the manager's cgroup.
|
||||
func (m *Manager) AddPid(subcgroup string, pid int) (retErr error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
c := m.cgroups
|
||||
|
||||
for _, dir := range m.paths {
|
||||
path := path.Join(dir, subcgroup)
|
||||
if !strings.HasPrefix(path, dir) {
|
||||
return fmt.Errorf("bad sub cgroup path: %s", subcgroup)
|
||||
}
|
||||
|
||||
if err := cgroups.WriteCgroupProc(path, pid); err != nil {
|
||||
if isIgnorableError(c.Rootless, err) && c.Path == "" {
|
||||
retErr = cgroups.ErrRootless
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return retErr
|
||||
}
|
||||
|
||||
func (m *Manager) Destroy() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return cgroups.RemovePaths(m.paths)
|
||||
}
|
||||
|
||||
func (m *Manager) Path(subsys string) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.paths[subsys]
|
||||
}
|
||||
|
||||
func (m *Manager) GetStats() (*cgroups.Stats, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
stats := cgroups.NewStats()
|
||||
for _, sys := range subsystems {
|
||||
path := m.paths[sys.Name()]
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
if err := sys.GetStats(path, stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (m *Manager) Set(r *cgroups.Resources) error {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.Unified != nil {
|
||||
return cgroups.ErrV1NoUnified
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for _, sys := range subsystems {
|
||||
path := m.paths[sys.Name()]
|
||||
if err := sys.Set(path, r); err != nil {
|
||||
// When rootless is true, errors from the device subsystem
|
||||
// are ignored, as it is really not expected to work.
|
||||
if m.cgroups.Rootless && sys.Name() == "devices" && !errors.Is(err, cgroups.ErrDevicesUnsupported) {
|
||||
continue
|
||||
}
|
||||
// However, errors from other subsystems are not ignored.
|
||||
// see @test "runc create (rootless + limits + no cgrouppath + no permission) fails with informative error"
|
||||
if path == "" {
|
||||
// We never created a path for this cgroup, so we cannot set
|
||||
// limits for it (though we have already tried at this point).
|
||||
return fmt.Errorf("cannot set %s limit: container could not join or create cgroup", sys.Name())
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Freeze toggles the container's freezer cgroup depending on the state
|
||||
// provided
|
||||
func (m *Manager) Freeze(state cgroups.FreezerState) error {
|
||||
path := m.Path("freezer")
|
||||
if path == "" {
|
||||
return errors.New("cannot toggle freezer: cgroups not configured for container")
|
||||
}
|
||||
|
||||
prevState := m.cgroups.Resources.Freezer
|
||||
m.cgroups.Resources.Freezer = state
|
||||
freezer := &FreezerGroup{}
|
||||
if err := freezer.Set(path, m.cgroups.Resources); err != nil {
|
||||
m.cgroups.Resources.Freezer = prevState
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) GetPids() ([]int, error) {
|
||||
return cgroups.GetPids(m.Path("devices"))
|
||||
}
|
||||
|
||||
func (m *Manager) GetAllPids() ([]int, error) {
|
||||
return cgroups.GetAllPids(m.Path("devices"))
|
||||
}
|
||||
|
||||
func (m *Manager) GetPaths() map[string]string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.paths
|
||||
}
|
||||
|
||||
func (m *Manager) GetCgroups() (*cgroups.Cgroup, error) {
|
||||
return m.cgroups, nil
|
||||
}
|
||||
|
||||
func (m *Manager) GetFreezerState() (cgroups.FreezerState, error) {
|
||||
dir := m.Path("freezer")
|
||||
// If the container doesn't have the freezer cgroup, say it's undefined.
|
||||
if dir == "" {
|
||||
return cgroups.Undefined, nil
|
||||
}
|
||||
freezer := &FreezerGroup{}
|
||||
return freezer.GetState(dir)
|
||||
}
|
||||
|
||||
func (m *Manager) Exists() bool {
|
||||
return cgroups.PathExists(m.Path("devices"))
|
||||
}
|
||||
|
||||
func OOMKillCount(path string) (uint64, error) {
|
||||
return fscommon.GetValueByKey(path, "memory.oom_control", "oom_kill")
|
||||
}
|
||||
|
||||
func (m *Manager) OOMKillCount() (uint64, error) {
|
||||
c, err := OOMKillCount(m.Path("memory"))
|
||||
// Ignore ENOENT when rootless as it couldn't create cgroup.
|
||||
if err != nil && m.cgroups.Rootless && os.IsNotExist(err) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return c, err
|
||||
}
|
||||
83
vendor/github.com/opencontainers/cgroups/fs/hugetlb.go
generated
vendored
83
vendor/github.com/opencontainers/cgroups/fs/hugetlb.go
generated
vendored
|
|
@ -1,83 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
)
|
||||
|
||||
type HugetlbGroup struct{}
|
||||
|
||||
func (s *HugetlbGroup) Name() string {
|
||||
return "hugetlb"
|
||||
}
|
||||
|
||||
func (s *HugetlbGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *HugetlbGroup) Set(path string, r *cgroups.Resources) error {
|
||||
const suffix = ".limit_in_bytes"
|
||||
skipRsvd := false
|
||||
|
||||
for _, hugetlb := range r.HugetlbLimit {
|
||||
prefix := "hugetlb." + hugetlb.Pagesize
|
||||
val := strconv.FormatUint(hugetlb.Limit, 10)
|
||||
if err := cgroups.WriteFile(path, prefix+suffix, val); err != nil {
|
||||
return err
|
||||
}
|
||||
if skipRsvd {
|
||||
continue
|
||||
}
|
||||
if err := cgroups.WriteFile(path, prefix+".rsvd"+suffix, val); err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
skipRsvd = true
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *HugetlbGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
if !cgroups.PathExists(path) {
|
||||
return nil
|
||||
}
|
||||
rsvd := ".rsvd"
|
||||
hugetlbStats := cgroups.HugetlbStats{}
|
||||
for _, pageSize := range cgroups.HugePageSizes() {
|
||||
again:
|
||||
prefix := "hugetlb." + pageSize + rsvd
|
||||
|
||||
value, err := fscommon.GetCgroupParamUint(path, prefix+".usage_in_bytes")
|
||||
if err != nil {
|
||||
if rsvd != "" && errors.Is(err, os.ErrNotExist) {
|
||||
rsvd = ""
|
||||
goto again
|
||||
}
|
||||
return err
|
||||
}
|
||||
hugetlbStats.Usage = value
|
||||
|
||||
value, err = fscommon.GetCgroupParamUint(path, prefix+".max_usage_in_bytes")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hugetlbStats.MaxUsage = value
|
||||
|
||||
value, err = fscommon.GetCgroupParamUint(path, prefix+".failcnt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hugetlbStats.Failcnt = value
|
||||
|
||||
stats.HugetlbStats[pageSize] = hugetlbStats
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
356
vendor/github.com/opencontainers/cgroups/fs/memory.go
generated
vendored
356
vendor/github.com/opencontainers/cgroups/fs/memory.go
generated
vendored
|
|
@ -1,356 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
)
|
||||
|
||||
const (
|
||||
cgroupMemorySwapLimit = "memory.memsw.limit_in_bytes"
|
||||
cgroupMemoryLimit = "memory.limit_in_bytes"
|
||||
cgroupMemoryUsage = "memory.usage_in_bytes"
|
||||
cgroupMemoryMaxUsage = "memory.max_usage_in_bytes"
|
||||
)
|
||||
|
||||
type MemoryGroup struct{}
|
||||
|
||||
func (s *MemoryGroup) Name() string {
|
||||
return "memory"
|
||||
}
|
||||
|
||||
func (s *MemoryGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func setMemory(path string, val int64) error {
|
||||
if val == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := cgroups.WriteFile(path, cgroupMemoryLimit, strconv.FormatInt(val, 10))
|
||||
if !errors.Is(err, unix.EBUSY) {
|
||||
return err
|
||||
}
|
||||
|
||||
// EBUSY means the kernel can't set new limit as it's too low
|
||||
// (lower than the current usage). Return more specific error.
|
||||
usage, err := fscommon.GetCgroupParamUint(path, cgroupMemoryUsage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
max, err := fscommon.GetCgroupParamUint(path, cgroupMemoryMaxUsage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to set memory limit to %d (current usage: %d, peak usage: %d)", val, usage, max)
|
||||
}
|
||||
|
||||
func setSwap(path string, val int64) error {
|
||||
if val == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cgroups.WriteFile(path, cgroupMemorySwapLimit, strconv.FormatInt(val, 10))
|
||||
}
|
||||
|
||||
func setMemoryAndSwap(path string, r *cgroups.Resources) error {
|
||||
// If the memory update is set to -1 and the swap is not explicitly
|
||||
// set, we should also set swap to -1, it means unlimited memory.
|
||||
if r.Memory == -1 && r.MemorySwap == 0 {
|
||||
// Only set swap if it's enabled in kernel
|
||||
if cgroups.PathExists(filepath.Join(path, cgroupMemorySwapLimit)) {
|
||||
r.MemorySwap = -1
|
||||
}
|
||||
}
|
||||
|
||||
// When memory and swap memory are both set, we need to handle the cases
|
||||
// for updating container.
|
||||
if r.Memory != 0 && r.MemorySwap != 0 {
|
||||
curLimit, err := fscommon.GetCgroupParamUint(path, cgroupMemoryLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// When update memory limit, we should adapt the write sequence
|
||||
// for memory and swap memory, so it won't fail because the new
|
||||
// value and the old value don't fit kernel's validation.
|
||||
if r.MemorySwap == -1 || curLimit < uint64(r.MemorySwap) {
|
||||
if err := setSwap(path, r.MemorySwap); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setMemory(path, r.Memory); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := setMemory(path, r.Memory); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setSwap(path, r.MemorySwap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MemoryGroup) Set(path string, r *cgroups.Resources) error {
|
||||
if err := setMemoryAndSwap(path, r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ignore KernelMemory and KernelMemoryTCP
|
||||
|
||||
if r.MemoryReservation != 0 {
|
||||
if err := cgroups.WriteFile(path, "memory.soft_limit_in_bytes", strconv.FormatInt(r.MemoryReservation, 10)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if r.OomKillDisable {
|
||||
if err := cgroups.WriteFile(path, "memory.oom_control", "1"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if r.MemorySwappiness == nil || int64(*r.MemorySwappiness) == -1 {
|
||||
return nil
|
||||
} else if *r.MemorySwappiness <= 100 {
|
||||
if err := cgroups.WriteFile(path, "memory.swappiness", strconv.FormatUint(*r.MemorySwappiness, 10)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("invalid memory swappiness value: %d (valid range is 0-100)", *r.MemorySwappiness)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
const file = "memory.stat"
|
||||
statsFile, err := cgroups.OpenFile(path, file, os.O_RDONLY)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer statsFile.Close()
|
||||
|
||||
sc := bufio.NewScanner(statsFile)
|
||||
for sc.Scan() {
|
||||
t, v, err := fscommon.ParseKeyValue(sc.Text())
|
||||
if err != nil {
|
||||
return &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
stats.MemoryStats.Stats[t] = v
|
||||
}
|
||||
stats.MemoryStats.Cache = stats.MemoryStats.Stats["cache"]
|
||||
|
||||
memoryUsage, err := getMemoryData(path, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stats.MemoryStats.Usage = memoryUsage
|
||||
swapUsage, err := getMemoryData(path, "memsw")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stats.MemoryStats.SwapUsage = swapUsage
|
||||
stats.MemoryStats.SwapOnlyUsage = cgroups.MemoryData{
|
||||
Usage: swapUsage.Usage - memoryUsage.Usage,
|
||||
Failcnt: swapUsage.Failcnt - memoryUsage.Failcnt,
|
||||
}
|
||||
kernelUsage, err := getMemoryData(path, "kmem")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stats.MemoryStats.KernelUsage = kernelUsage
|
||||
kernelTCPUsage, err := getMemoryData(path, "kmem.tcp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stats.MemoryStats.KernelTCPUsage = kernelTCPUsage
|
||||
|
||||
value, err := fscommon.GetCgroupParamUint(path, "memory.use_hierarchy")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if value == 1 {
|
||||
stats.MemoryStats.UseHierarchy = true
|
||||
}
|
||||
|
||||
pagesByNUMA, err := getPageUsageByNUMA(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stats.MemoryStats.PageUsageByNUMA = pagesByNUMA
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMemoryData(path, name string) (cgroups.MemoryData, error) {
|
||||
memoryData := cgroups.MemoryData{}
|
||||
|
||||
moduleName := "memory"
|
||||
if name != "" {
|
||||
moduleName = "memory." + name
|
||||
}
|
||||
var (
|
||||
usage = moduleName + ".usage_in_bytes"
|
||||
maxUsage = moduleName + ".max_usage_in_bytes"
|
||||
failcnt = moduleName + ".failcnt"
|
||||
limit = moduleName + ".limit_in_bytes"
|
||||
)
|
||||
|
||||
value, err := fscommon.GetCgroupParamUint(path, usage)
|
||||
if err != nil {
|
||||
if name != "" && os.IsNotExist(err) {
|
||||
// Ignore ENOENT as swap and kmem controllers
|
||||
// are optional in the kernel.
|
||||
return cgroups.MemoryData{}, nil
|
||||
}
|
||||
return cgroups.MemoryData{}, err
|
||||
}
|
||||
memoryData.Usage = value
|
||||
value, err = fscommon.GetCgroupParamUint(path, maxUsage)
|
||||
if err != nil {
|
||||
return cgroups.MemoryData{}, err
|
||||
}
|
||||
memoryData.MaxUsage = value
|
||||
value, err = fscommon.GetCgroupParamUint(path, failcnt)
|
||||
if err != nil {
|
||||
return cgroups.MemoryData{}, err
|
||||
}
|
||||
memoryData.Failcnt = value
|
||||
value, err = fscommon.GetCgroupParamUint(path, limit)
|
||||
if err != nil {
|
||||
if name == "kmem" && os.IsNotExist(err) {
|
||||
// Ignore ENOENT as kmem.limit_in_bytes has
|
||||
// been removed in newer kernels.
|
||||
return memoryData, nil
|
||||
}
|
||||
|
||||
return cgroups.MemoryData{}, err
|
||||
}
|
||||
memoryData.Limit = value
|
||||
|
||||
return memoryData, nil
|
||||
}
|
||||
|
||||
func getPageUsageByNUMA(path string) (cgroups.PageUsageByNUMA, error) {
|
||||
const (
|
||||
maxColumns = math.MaxUint8 + 1
|
||||
file = "memory.numa_stat"
|
||||
)
|
||||
stats := cgroups.PageUsageByNUMA{}
|
||||
|
||||
fd, err := cgroups.OpenFile(path, file, os.O_RDONLY)
|
||||
if os.IsNotExist(err) {
|
||||
return stats, nil
|
||||
} else if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
// File format is documented in linux/Documentation/cgroup-v1/memory.txt
|
||||
// and it looks like this:
|
||||
//
|
||||
// total=<total pages> N0=<node 0 pages> N1=<node 1 pages> ...
|
||||
// file=<total file pages> N0=<node 0 pages> N1=<node 1 pages> ...
|
||||
// anon=<total anon pages> N0=<node 0 pages> N1=<node 1 pages> ...
|
||||
// unevictable=<total anon pages> N0=<node 0 pages> N1=<node 1 pages> ...
|
||||
// hierarchical_<counter>=<counter pages> N0=<node 0 pages> N1=<node 1 pages> ...
|
||||
|
||||
scanner := bufio.NewScanner(fd)
|
||||
for scanner.Scan() {
|
||||
var field *cgroups.PageStats
|
||||
|
||||
line := scanner.Text()
|
||||
columns := strings.SplitN(line, " ", maxColumns)
|
||||
for i, column := range columns {
|
||||
key, val, ok := strings.Cut(column, "=")
|
||||
// Some custom kernels have non-standard fields, like
|
||||
// numa_locality 0 0 0 0 0 0 0 0 0 0
|
||||
// numa_exectime 0
|
||||
if !ok {
|
||||
if i == 0 {
|
||||
// Ignore/skip those.
|
||||
break
|
||||
} else {
|
||||
// The first column was already validated,
|
||||
// so be strict to the rest.
|
||||
return stats, malformedLine(path, file, line)
|
||||
}
|
||||
}
|
||||
if i == 0 { // First column: key is name, val is total.
|
||||
field = getNUMAField(&stats, key)
|
||||
if field == nil { // unknown field (new kernel?)
|
||||
break
|
||||
}
|
||||
field.Total, err = strconv.ParseUint(val, 0, 64)
|
||||
if err != nil {
|
||||
return stats, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
field.Nodes = map[uint8]uint64{}
|
||||
} else { // Subsequent columns: key is N<id>, val is usage.
|
||||
if len(key) < 2 || key[0] != 'N' {
|
||||
// This is definitely an error.
|
||||
return stats, malformedLine(path, file, line)
|
||||
}
|
||||
|
||||
n, err := strconv.ParseUint(key[1:], 10, 8)
|
||||
if err != nil {
|
||||
return stats, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
|
||||
usage, err := strconv.ParseUint(val, 10, 64)
|
||||
if err != nil {
|
||||
return stats, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
|
||||
field.Nodes[uint8(n)] = usage
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return cgroups.PageUsageByNUMA{}, &parseError{Path: path, File: file, Err: err}
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func getNUMAField(stats *cgroups.PageUsageByNUMA, name string) *cgroups.PageStats {
|
||||
switch name {
|
||||
case "total":
|
||||
return &stats.Total
|
||||
case "file":
|
||||
return &stats.File
|
||||
case "anon":
|
||||
return &stats.Anon
|
||||
case "unevictable":
|
||||
return &stats.Unevictable
|
||||
case "hierarchical_total":
|
||||
return &stats.Hierarchical.Total
|
||||
case "hierarchical_file":
|
||||
return &stats.Hierarchical.File
|
||||
case "hierarchical_anon":
|
||||
return &stats.Hierarchical.Anon
|
||||
case "hierarchical_unevictable":
|
||||
return &stats.Hierarchical.Unevictable
|
||||
}
|
||||
return nil
|
||||
}
|
||||
30
vendor/github.com/opencontainers/cgroups/fs/name.go
generated
vendored
30
vendor/github.com/opencontainers/cgroups/fs/name.go
generated
vendored
|
|
@ -1,30 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"github.com/opencontainers/cgroups"
|
||||
)
|
||||
|
||||
type NameGroup struct {
|
||||
GroupName string
|
||||
Join bool
|
||||
}
|
||||
|
||||
func (s *NameGroup) Name() string {
|
||||
return s.GroupName
|
||||
}
|
||||
|
||||
func (s *NameGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
if s.Join {
|
||||
// Ignore errors if the named cgroup does not exist.
|
||||
_ = apply(path, pid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NameGroup) Set(_ string, _ *cgroups.Resources) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NameGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
31
vendor/github.com/opencontainers/cgroups/fs/net_cls.go
generated
vendored
31
vendor/github.com/opencontainers/cgroups/fs/net_cls.go
generated
vendored
|
|
@ -1,31 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
)
|
||||
|
||||
type NetClsGroup struct{}
|
||||
|
||||
func (s *NetClsGroup) Name() string {
|
||||
return "net_cls"
|
||||
}
|
||||
|
||||
func (s *NetClsGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *NetClsGroup) Set(path string, r *cgroups.Resources) error {
|
||||
if r.NetClsClassid != 0 {
|
||||
if err := cgroups.WriteFile(path, "net_cls.classid", strconv.FormatUint(uint64(r.NetClsClassid), 10)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NetClsGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
29
vendor/github.com/opencontainers/cgroups/fs/net_prio.go
generated
vendored
29
vendor/github.com/opencontainers/cgroups/fs/net_prio.go
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"github.com/opencontainers/cgroups"
|
||||
)
|
||||
|
||||
type NetPrioGroup struct{}
|
||||
|
||||
func (s *NetPrioGroup) Name() string {
|
||||
return "net_prio"
|
||||
}
|
||||
|
||||
func (s *NetPrioGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *NetPrioGroup) Set(path string, r *cgroups.Resources) error {
|
||||
for _, prioMap := range r.NetPrioIfpriomap {
|
||||
if err := cgroups.WriteFile(path, "net_prio.ifpriomap", prioMap.CgroupString()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NetPrioGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
169
vendor/github.com/opencontainers/cgroups/fs/paths.go
generated
vendored
169
vendor/github.com/opencontainers/cgroups/fs/paths.go
generated
vendored
|
|
@ -1,169 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/internal/path"
|
||||
)
|
||||
|
||||
// The absolute path to the root of the cgroup hierarchies.
|
||||
var (
|
||||
cgroupRootLock sync.Mutex
|
||||
cgroupRoot string
|
||||
)
|
||||
|
||||
const defaultCgroupRoot = "/sys/fs/cgroup"
|
||||
|
||||
func initPaths(cg *cgroups.Cgroup) (map[string]string, error) {
|
||||
root, err := rootPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inner, err := path.Inner(cg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paths := make(map[string]string)
|
||||
for _, sys := range subsystems {
|
||||
name := sys.Name()
|
||||
path, err := subsysPath(root, inner, name)
|
||||
if err != nil {
|
||||
// The non-presence of the devices subsystem
|
||||
// is considered fatal for security reasons.
|
||||
if cgroups.IsNotFound(err) && (cg.SkipDevices || name != "devices") {
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
paths[name] = path
|
||||
}
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func tryDefaultCgroupRoot() string {
|
||||
var st, pst unix.Stat_t
|
||||
|
||||
// (1) it should be a directory...
|
||||
err := unix.Lstat(defaultCgroupRoot, &st)
|
||||
if err != nil || st.Mode&unix.S_IFDIR == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// (2) ... and a mount point ...
|
||||
err = unix.Lstat(filepath.Dir(defaultCgroupRoot), &pst)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if st.Dev == pst.Dev {
|
||||
// parent dir has the same dev -- not a mount point
|
||||
return ""
|
||||
}
|
||||
|
||||
// (3) ... of 'tmpfs' fs type.
|
||||
var fst unix.Statfs_t
|
||||
err = unix.Statfs(defaultCgroupRoot, &fst)
|
||||
if err != nil || fst.Type != unix.TMPFS_MAGIC {
|
||||
return ""
|
||||
}
|
||||
|
||||
// (4) it should have at least 1 entry ...
|
||||
dir, err := os.Open(defaultCgroupRoot)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer dir.Close()
|
||||
names, err := dir.Readdirnames(1)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if len(names) < 1 {
|
||||
return ""
|
||||
}
|
||||
// ... which is a cgroup mount point.
|
||||
err = unix.Statfs(filepath.Join(defaultCgroupRoot, names[0]), &fst)
|
||||
if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC {
|
||||
return ""
|
||||
}
|
||||
|
||||
return defaultCgroupRoot
|
||||
}
|
||||
|
||||
// rootPath finds and returns path to the root of the cgroup hierarchies.
|
||||
func rootPath() (string, error) {
|
||||
cgroupRootLock.Lock()
|
||||
defer cgroupRootLock.Unlock()
|
||||
|
||||
if cgroupRoot != "" {
|
||||
return cgroupRoot, nil
|
||||
}
|
||||
|
||||
// fast path
|
||||
cgroupRoot = tryDefaultCgroupRoot()
|
||||
if cgroupRoot != "" {
|
||||
return cgroupRoot, nil
|
||||
}
|
||||
|
||||
// slow path: parse mountinfo
|
||||
mi, err := cgroups.GetCgroupMounts(false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(mi) < 1 {
|
||||
return "", errors.New("no cgroup mount found in mountinfo")
|
||||
}
|
||||
|
||||
// Get the first cgroup mount (e.g. "/sys/fs/cgroup/memory"),
|
||||
// use its parent directory.
|
||||
root := filepath.Dir(mi[0].Mountpoint)
|
||||
|
||||
if _, err := os.Stat(root); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cgroupRoot = root
|
||||
return cgroupRoot, nil
|
||||
}
|
||||
|
||||
func subsysPath(root, inner, subsystem string) (string, error) {
|
||||
// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
|
||||
if filepath.IsAbs(inner) {
|
||||
mnt, err := cgroups.FindCgroupMountpoint(root, subsystem)
|
||||
// If we didn't mount the subsystem, there is no point we make the path.
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Sometimes subsystems can be mounted together as 'cpu,cpuacct'.
|
||||
return filepath.Join(root, filepath.Base(mnt), inner), nil
|
||||
}
|
||||
|
||||
// Use GetOwnCgroupPath for dind-like cases, when cgroupns is not
|
||||
// available. This is ugly.
|
||||
parentPath, err := cgroups.GetOwnCgroupPath(subsystem)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(parentPath, inner), nil
|
||||
}
|
||||
|
||||
func apply(path string, pid int) error {
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(path, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return cgroups.WriteCgroupProc(path, pid)
|
||||
}
|
||||
23
vendor/github.com/opencontainers/cgroups/fs/perf_event.go
generated
vendored
23
vendor/github.com/opencontainers/cgroups/fs/perf_event.go
generated
vendored
|
|
@ -1,23 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"github.com/opencontainers/cgroups"
|
||||
)
|
||||
|
||||
type PerfEventGroup struct{}
|
||||
|
||||
func (s *PerfEventGroup) Name() string {
|
||||
return "perf_event"
|
||||
}
|
||||
|
||||
func (s *PerfEventGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *PerfEventGroup) Set(_ string, _ *cgroups.Resources) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PerfEventGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
66
vendor/github.com/opencontainers/cgroups/fs/pids.go
generated
vendored
66
vendor/github.com/opencontainers/cgroups/fs/pids.go
generated
vendored
|
|
@ -1,66 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
)
|
||||
|
||||
type PidsGroup struct{}
|
||||
|
||||
func (s *PidsGroup) Name() string {
|
||||
return "pids"
|
||||
}
|
||||
|
||||
func (s *PidsGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *PidsGroup) Set(path string, r *cgroups.Resources) error {
|
||||
if r.PidsLimit == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// "max" is the fallback value.
|
||||
val := "max"
|
||||
if limit := *r.PidsLimit; limit > 0 {
|
||||
val = strconv.FormatInt(limit, 10)
|
||||
} else if limit == 0 {
|
||||
// systemd doesn't support setting pids.max to "0", so when setting
|
||||
// TasksMax we need to remap it to "1". We do the same thing here to
|
||||
// avoid flip-flop behaviour between the fs and systemd drivers. In
|
||||
// practice, the pids cgroup behaviour is basically identical.
|
||||
val = "1"
|
||||
}
|
||||
if err := cgroups.WriteFile(path, "pids.max", val); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PidsGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
if !cgroups.PathExists(path) {
|
||||
return nil
|
||||
}
|
||||
current, err := fscommon.GetCgroupParamUint(path, "pids.current")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
max, err := fscommon.GetCgroupParamUint(path, "pids.max")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If no limit is set, read from pids.max returns "max", which is
|
||||
// converted to MaxUint64 by GetCgroupParamUint. Historically, we
|
||||
// represent "no limit" for pids as 0, thus this conversion.
|
||||
if max == math.MaxUint64 {
|
||||
max = 0
|
||||
}
|
||||
|
||||
stats.PidsStats.Current = current
|
||||
stats.PidsStats.Limit = max
|
||||
return nil
|
||||
}
|
||||
24
vendor/github.com/opencontainers/cgroups/fs/rdma.go
generated
vendored
24
vendor/github.com/opencontainers/cgroups/fs/rdma.go
generated
vendored
|
|
@ -1,24 +0,0 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fscommon"
|
||||
)
|
||||
|
||||
type RdmaGroup struct{}
|
||||
|
||||
func (s *RdmaGroup) Name() string {
|
||||
return "rdma"
|
||||
}
|
||||
|
||||
func (s *RdmaGroup) Apply(path string, _ *cgroups.Resources, pid int) error {
|
||||
return apply(path, pid)
|
||||
}
|
||||
|
||||
func (s *RdmaGroup) Set(path string, r *cgroups.Resources) error {
|
||||
return fscommon.RdmaSet(path, r)
|
||||
}
|
||||
|
||||
func (s *RdmaGroup) GetStats(path string, stats *cgroups.Stats) error {
|
||||
return fscommon.RdmaGetStats(path, stats)
|
||||
}
|
||||
2
vendor/go.podman.io/common/libnetwork/types/network.go
generated
vendored
2
vendor/go.podman.io/common/libnetwork/types/network.go
generated
vendored
|
|
@ -102,7 +102,7 @@ type NetworkInfo struct {
|
|||
Package string `json:"package,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
DNS DNSNetworkInfo `json:"dns,omitempty"`
|
||||
DefaultNetwork string `json:"default_network,omitempty"`
|
||||
DefaultNetwork string `json:"defaultNetwork,omitempty"`
|
||||
}
|
||||
|
||||
// DNSNetworkInfo contains the DNS information.
|
||||
|
|
|
|||
147
vendor/go.podman.io/common/pkg/cgroups/blkio_linux.go
generated
vendored
147
vendor/go.podman.io/common/pkg/cgroups/blkio_linux.go
generated
vendored
|
|
@ -3,145 +3,55 @@
|
|||
package cgroups
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fs"
|
||||
"github.com/opencontainers/cgroups/fs2"
|
||||
)
|
||||
|
||||
type linuxBlkioHandler struct {
|
||||
Blkio fs.BlkioGroup
|
||||
}
|
||||
|
||||
func getBlkioHandler() *linuxBlkioHandler {
|
||||
return &linuxBlkioHandler{}
|
||||
}
|
||||
|
||||
// Apply set the specified constraints.
|
||||
func (c *linuxBlkioHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
|
||||
if ctr.cgroup2 {
|
||||
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return man.Set(res)
|
||||
}
|
||||
path := filepath.Join(cgroupRoot, Blkio, ctr.config.Path)
|
||||
return c.Blkio.Set(path, res)
|
||||
}
|
||||
|
||||
// Create the cgroup.
|
||||
func (c *linuxBlkioHandler) Create(ctr *CgroupControl) (bool, error) {
|
||||
if ctr.cgroup2 {
|
||||
return false, nil
|
||||
}
|
||||
return ctr.createCgroupDirectory(Blkio)
|
||||
}
|
||||
|
||||
// Destroy the cgroup.
|
||||
func (c *linuxBlkioHandler) Destroy(ctr *CgroupControl) error {
|
||||
return rmDirRecursively(ctr.getCgroupv1Path(Blkio))
|
||||
}
|
||||
|
||||
// Stat fills a metrics structure with usage stats for the controller.
|
||||
func (c *linuxBlkioHandler) Stat(ctr *CgroupControl, m *cgroups.Stats) error {
|
||||
// blkioStat fills a metrics structure with usage stats for the blkio controller.
|
||||
func blkioStat(ctr *CgroupControl, m *cgroups.Stats) error {
|
||||
var ioServiceBytesRecursive []cgroups.BlkioStatEntry
|
||||
|
||||
if ctr.cgroup2 {
|
||||
// more details on the io.stat file format:X https://facebookmicrosites.github.io/cgroup2/docs/io-controller.html
|
||||
values, err := readCgroup2MapFile(ctr, "io.stat")
|
||||
// more details on the io.stat file format:X https://facebookmicrosites.github.io/cgroup2/docs/io-controller.html
|
||||
values, err := readCgroup2MapFile(ctr, "io.stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range values {
|
||||
d := strings.Split(k, ":")
|
||||
if len(d) != 2 {
|
||||
continue
|
||||
}
|
||||
minor, err := strconv.ParseUint(d[0], 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range values {
|
||||
d := strings.Split(k, ":")
|
||||
if len(d) != 2 {
|
||||
continue
|
||||
}
|
||||
minor, err := strconv.ParseUint(d[0], 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
major, err := strconv.ParseUint(d[1], 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range v {
|
||||
d := strings.Split(item, "=")
|
||||
if len(d) != 2 {
|
||||
continue
|
||||
}
|
||||
op := d[0]
|
||||
|
||||
// Accommodate the cgroup v1 naming
|
||||
switch op {
|
||||
case "rbytes":
|
||||
op = "read"
|
||||
case "wbytes":
|
||||
op = "write"
|
||||
}
|
||||
|
||||
value, err := strconv.ParseUint(d[1], 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entry := cgroups.BlkioStatEntry{
|
||||
Op: op,
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
Value: value,
|
||||
}
|
||||
ioServiceBytesRecursive = append(ioServiceBytesRecursive, entry)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BlkioRoot := ctr.getCgroupv1Path(Blkio)
|
||||
|
||||
p := filepath.Join(BlkioRoot, "blkio.throttle.io_service_bytes_recursive")
|
||||
f, err := os.Open(p)
|
||||
major, err := strconv.ParseUint(d[1], 10, 0)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("open %s: %w", p, err)
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) < 3 {
|
||||
continue
|
||||
}
|
||||
d := strings.Split(parts[0], ":")
|
||||
for _, item := range v {
|
||||
d := strings.Split(item, "=")
|
||||
if len(d) != 2 {
|
||||
continue
|
||||
}
|
||||
minor, err := strconv.ParseUint(d[0], 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
op := d[0]
|
||||
|
||||
// Accommodate the cgroup v1 naming
|
||||
switch op {
|
||||
case "rbytes":
|
||||
op = "read"
|
||||
case "wbytes":
|
||||
op = "write"
|
||||
}
|
||||
major, err := strconv.ParseUint(d[1], 10, 0)
|
||||
|
||||
value, err := strconv.ParseUint(d[1], 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
op := parts[1]
|
||||
|
||||
value, err := strconv.ParseUint(parts[2], 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry := cgroups.BlkioStatEntry{
|
||||
Op: op,
|
||||
Major: major,
|
||||
|
|
@ -150,9 +60,6 @@ func (c *linuxBlkioHandler) Stat(ctr *CgroupControl, m *cgroups.Stats) error {
|
|||
}
|
||||
ioServiceBytesRecursive = append(ioServiceBytesRecursive, entry)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return fmt.Errorf("parse %s: %w", p, err)
|
||||
}
|
||||
}
|
||||
m.BlkioStats.IoServiceBytesRecursive = ioServiceBytesRecursive
|
||||
return nil
|
||||
|
|
|
|||
405
vendor/go.podman.io/common/pkg/cgroups/cgroups_linux.go
generated
vendored
405
vendor/go.podman.io/common/pkg/cgroups/cgroups_linux.go
generated
vendored
|
|
@ -8,11 +8,9 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -23,7 +21,6 @@ import (
|
|||
"github.com/godbus/dbus/v5"
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fs2"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.podman.io/storage/pkg/fileutils"
|
||||
"go.podman.io/storage/pkg/unshare"
|
||||
"golang.org/x/sys/unix"
|
||||
|
|
@ -32,9 +29,7 @@ import (
|
|||
var (
|
||||
// ErrCgroupDeleted means the cgroup was deleted.
|
||||
ErrCgroupDeleted = errors.New("cgroup deleted")
|
||||
// ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environment.
|
||||
ErrCgroupV1Rootless = errors.New("no support for CGroups V1 in rootless environments")
|
||||
ErrStatCgroup = errors.New("no cgroup available for gathering user statistics")
|
||||
ErrStatCgroup = errors.New("no cgroup available for gathering user statistics")
|
||||
|
||||
isUnifiedOnce sync.Once
|
||||
isUnified bool
|
||||
|
|
@ -43,34 +38,17 @@ var (
|
|||
|
||||
// CgroupControl controls a cgroup hierarchy.
|
||||
type CgroupControl struct {
|
||||
cgroup2 bool
|
||||
config *cgroups.Cgroup
|
||||
systemd bool
|
||||
// List of additional cgroup subsystems joined that
|
||||
// do not have a custom handler.
|
||||
additionalControllers []controller
|
||||
}
|
||||
|
||||
type controller struct {
|
||||
name string
|
||||
symlink bool
|
||||
}
|
||||
|
||||
type controllerHandler interface {
|
||||
Create(*CgroupControl) (bool, error)
|
||||
Apply(*CgroupControl, *cgroups.Resources) error
|
||||
Destroy(*CgroupControl) error
|
||||
Stat(*CgroupControl, *cgroups.Stats) error
|
||||
}
|
||||
// statFunc is a function that gathers statistics for a cgroup controller.
|
||||
type statFunc func(*CgroupControl, *cgroups.Stats) error
|
||||
|
||||
const (
|
||||
cgroupRoot = "/sys/fs/cgroup"
|
||||
// CPU is the cpu controller.
|
||||
CPU = "cpu"
|
||||
// CPUAcct is the cpuacct controller.
|
||||
CPUAcct = "cpuacct"
|
||||
// CPUset is the cpuset controller.
|
||||
CPUset = "cpuset"
|
||||
// Memory is the memory controller.
|
||||
Memory = "memory"
|
||||
// Pids is the pids controller.
|
||||
|
|
@ -79,110 +57,36 @@ const (
|
|||
Blkio = "blkio"
|
||||
)
|
||||
|
||||
var handlers map[string]controllerHandler
|
||||
|
||||
func init() {
|
||||
handlers = map[string]controllerHandler{
|
||||
CPU: getCPUHandler(),
|
||||
CPUset: getCpusetHandler(),
|
||||
Memory: getMemoryHandler(),
|
||||
Pids: getPidsHandler(),
|
||||
Blkio: getBlkioHandler(),
|
||||
}
|
||||
}
|
||||
|
||||
// getAvailableControllers get the available controllers.
|
||||
func getAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) ([]controller, error) {
|
||||
if cgroup2 {
|
||||
controllers := []controller{}
|
||||
controllersFile := filepath.Join(cgroupRoot, "cgroup.controllers")
|
||||
|
||||
// rootless cgroupv2: check available controllers for current user, systemd or servicescope will inherit
|
||||
if unshare.IsRootless() {
|
||||
userSlice, err := getCgroupPathForCurrentProcess()
|
||||
if err != nil {
|
||||
return controllers, err
|
||||
}
|
||||
// userSlice already contains '/' so not adding here
|
||||
basePath := cgroupRoot + userSlice
|
||||
controllersFile = filepath.Join(basePath, "cgroup.controllers")
|
||||
}
|
||||
controllersFileBytes, err := os.ReadFile(controllersFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed while reading controllers for cgroup v2: %w", err)
|
||||
}
|
||||
for controllerName := range strings.FieldsSeq(string(controllersFileBytes)) {
|
||||
c := controller{
|
||||
name: controllerName,
|
||||
symlink: false,
|
||||
}
|
||||
controllers = append(controllers, c)
|
||||
}
|
||||
return controllers, nil
|
||||
}
|
||||
|
||||
subsystems, _ := cgroupV1GetAllSubsystems()
|
||||
controllers := []controller{}
|
||||
// cgroupv1 and rootless: No subsystem is available: delegation is unsafe.
|
||||
if unshare.IsRootless() {
|
||||
return controllers, nil
|
||||
}
|
||||
|
||||
for _, name := range subsystems {
|
||||
if _, found := exclude[name]; found {
|
||||
continue
|
||||
}
|
||||
fileInfo, err := os.Stat(cgroupRoot + "/" + name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
c := controller{
|
||||
name: name,
|
||||
symlink: !fileInfo.IsDir(),
|
||||
}
|
||||
controllers = append(controllers, c)
|
||||
}
|
||||
|
||||
return controllers, nil
|
||||
var handlers = map[string]statFunc{
|
||||
CPU: cpuStat,
|
||||
Memory: memoryStat,
|
||||
Pids: pidsStat,
|
||||
Blkio: blkioStat,
|
||||
}
|
||||
|
||||
// AvailableControllers get string:bool map of all the available controllers.
|
||||
func AvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) ([]string, error) {
|
||||
availableControllers, err := getAvailableControllers(exclude, cgroup2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
controllerList := []string{}
|
||||
for _, controller := range availableControllers {
|
||||
controllerList = append(controllerList, controller.name)
|
||||
}
|
||||
func AvailableControllers() ([]string, error) {
|
||||
controllers := []string{}
|
||||
controllersFile := filepath.Join(cgroupRoot, "cgroup.controllers")
|
||||
|
||||
return controllerList, nil
|
||||
}
|
||||
|
||||
func cgroupV1GetAllSubsystems() ([]string, error) {
|
||||
f, err := os.Open("/proc/cgroups")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
subsystems := []string{}
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
text := s.Text()
|
||||
if text[0] != '#' {
|
||||
parts := strings.Fields(text)
|
||||
if len(parts) >= 4 && parts[3] != "0" {
|
||||
subsystems = append(subsystems, parts[0])
|
||||
}
|
||||
// rootless cgroupv2: check available controllers for current user, systemd or servicescope will inherit
|
||||
if unshare.IsRootless() {
|
||||
userSlice, err := getCgroupPathForCurrentProcess()
|
||||
if err != nil {
|
||||
return controllers, err
|
||||
}
|
||||
// userSlice already contains '/' so not adding here
|
||||
basePath := cgroupRoot + userSlice
|
||||
controllersFile = filepath.Join(basePath, "cgroup.controllers")
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
controllersFileBytes, err := os.ReadFile(controllersFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed while reading controllers for cgroup v2: %w", err)
|
||||
}
|
||||
return subsystems, nil
|
||||
for controllerName := range strings.FieldsSeq(string(controllersFileBytes)) {
|
||||
controllers = append(controllers, controllerName)
|
||||
}
|
||||
return controllers, nil
|
||||
}
|
||||
|
||||
func getCgroupPathForCurrentProcess() (string, error) {
|
||||
|
|
@ -208,51 +112,11 @@ func getCgroupPathForCurrentProcess() (string, error) {
|
|||
return cgroupPath, nil
|
||||
}
|
||||
|
||||
// getCgroupv1Path is a helper function to get the cgroup v1 path.
|
||||
func (c *CgroupControl) getCgroupv1Path(name string) string {
|
||||
return filepath.Join(cgroupRoot, name, c.config.Path)
|
||||
}
|
||||
|
||||
// initialize initializes the specified hierarchy.
|
||||
func (c *CgroupControl) initialize() (err error) {
|
||||
createdSoFar := map[string]controllerHandler{}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
for name, ctr := range createdSoFar {
|
||||
if err := ctr.Destroy(c); err != nil {
|
||||
logrus.Warningf("error cleaning up controller %s for %s", name, c.config.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
if c.cgroup2 {
|
||||
if err := createCgroupv2Path(filepath.Join(cgroupRoot, c.config.Path)); err != nil {
|
||||
return fmt.Errorf("creating cgroup path %s: %w", c.config.Path, err)
|
||||
}
|
||||
if err := createCgroupv2Path(filepath.Join(cgroupRoot, c.config.Path)); err != nil {
|
||||
return fmt.Errorf("creating cgroup path %s: %w", c.config.Path, err)
|
||||
}
|
||||
for name, handler := range handlers {
|
||||
created, err := handler.Create(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if created {
|
||||
createdSoFar[name] = handler
|
||||
}
|
||||
}
|
||||
|
||||
if !c.cgroup2 {
|
||||
// We won't need to do this for cgroup v2
|
||||
for _, ctr := range c.additionalControllers {
|
||||
if ctr.symlink {
|
||||
continue
|
||||
}
|
||||
path := c.getCgroupv1Path(ctr.name)
|
||||
if err := os.MkdirAll(path, 0o755); err != nil {
|
||||
return fmt.Errorf("creating cgroup path for %s: %w", ctr.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -297,26 +161,17 @@ func readFileByKeyAsUint64(path, key string) (uint64, error) {
|
|||
|
||||
// New creates a new cgroup control.
|
||||
func New(path string, resources *cgroups.Resources) (*CgroupControl, error) {
|
||||
cgroup2, err := IsCgroup2UnifiedMode()
|
||||
_, err := IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
control := &CgroupControl{
|
||||
cgroup2: cgroup2,
|
||||
config: &cgroups.Cgroup{
|
||||
Path: path,
|
||||
Resources: resources,
|
||||
},
|
||||
}
|
||||
|
||||
if !cgroup2 {
|
||||
controllers, err := getAvailableControllers(handlers, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
control.additionalControllers = controllers
|
||||
}
|
||||
|
||||
if err := control.initialize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -326,12 +181,11 @@ func New(path string, resources *cgroups.Resources) (*CgroupControl, error) {
|
|||
|
||||
// NewSystemd creates a new cgroup control.
|
||||
func NewSystemd(path string, resources *cgroups.Resources) (*CgroupControl, error) {
|
||||
cgroup2, err := IsCgroup2UnifiedMode()
|
||||
_, err := IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
control := &CgroupControl{
|
||||
cgroup2: cgroup2,
|
||||
systemd: true,
|
||||
config: &cgroups.Cgroup{
|
||||
Path: path,
|
||||
|
|
@ -345,45 +199,16 @@ func NewSystemd(path string, resources *cgroups.Resources) (*CgroupControl, erro
|
|||
|
||||
// Load loads an existing cgroup control.
|
||||
func Load(path string) (*CgroupControl, error) {
|
||||
cgroup2, err := IsCgroup2UnifiedMode()
|
||||
_, err := IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
control := &CgroupControl{
|
||||
cgroup2: cgroup2,
|
||||
systemd: false,
|
||||
config: &cgroups.Cgroup{
|
||||
Path: path,
|
||||
},
|
||||
}
|
||||
if !cgroup2 {
|
||||
controllers, err := getAvailableControllers(handlers, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
control.additionalControllers = controllers
|
||||
}
|
||||
if !cgroup2 {
|
||||
oneExists := false
|
||||
// check that the cgroup exists at least under one controller
|
||||
for name := range handlers {
|
||||
p := control.getCgroupv1Path(name)
|
||||
if err := fileutils.Exists(p); err == nil {
|
||||
oneExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if there is no controller at all, raise an error
|
||||
if !oneExists {
|
||||
if unshare.IsRootless() {
|
||||
return nil, ErrCgroupV1Rootless
|
||||
}
|
||||
// compatible with the error code
|
||||
// used by containerd/cgroups
|
||||
return nil, ErrCgroupDeleted
|
||||
}
|
||||
}
|
||||
return control, nil
|
||||
}
|
||||
|
||||
|
|
@ -448,26 +273,7 @@ func (c *CgroupControl) DeleteByPathConn(path string, conn *systemdDbus.Conn) er
|
|||
if c.systemd {
|
||||
return systemdDestroyConn(path, conn)
|
||||
}
|
||||
if c.cgroup2 {
|
||||
return rmDirRecursively(filepath.Join(cgroupRoot, c.config.Path))
|
||||
}
|
||||
var lastError error
|
||||
for _, h := range handlers {
|
||||
if err := h.Destroy(c); err != nil {
|
||||
lastError = err
|
||||
}
|
||||
}
|
||||
|
||||
for _, ctr := range c.additionalControllers {
|
||||
if ctr.symlink {
|
||||
continue
|
||||
}
|
||||
p := c.getCgroupv1Path(ctr.name)
|
||||
if err := rmDirRecursively(p); err != nil {
|
||||
lastError = fmt.Errorf("remove %s: %w", p, err)
|
||||
}
|
||||
}
|
||||
return lastError
|
||||
return rmDirRecursively(filepath.Join(cgroupRoot, c.config.Path))
|
||||
}
|
||||
|
||||
// DeleteByPath deletes the specified cgroup path.
|
||||
|
|
@ -485,50 +291,28 @@ func (c *CgroupControl) DeleteByPath(path string) error {
|
|||
|
||||
// Update updates the cgroups.
|
||||
func (c *CgroupControl) Update(resources *cgroups.Resources) error {
|
||||
for _, h := range handlers {
|
||||
if err := h.Apply(c, resources); err != nil {
|
||||
return err
|
||||
}
|
||||
man, err := fs2.NewManager(c.config, filepath.Join(cgroupRoot, c.config.Path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return man.Set(resources)
|
||||
}
|
||||
|
||||
// AddPid moves the specified pid to the cgroup.
|
||||
func (c *CgroupControl) AddPid(pid int) error {
|
||||
pidString := []byte(fmt.Sprintf("%d\n", pid))
|
||||
|
||||
if c.cgroup2 {
|
||||
path := filepath.Join(cgroupRoot, c.config.Path)
|
||||
return fs2.CreateCgroupPath(path, c.config)
|
||||
man, err := fs2.NewManager(c.config, filepath.Join(cgroupRoot, c.config.Path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names := slices.Collect(maps.Keys(handlers))
|
||||
|
||||
for _, c := range c.additionalControllers {
|
||||
if !c.symlink {
|
||||
names = append(names, c.name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, n := range names {
|
||||
// If we aren't using cgroup2, we won't write correctly to unified hierarchy
|
||||
if !c.cgroup2 && n == "unified" {
|
||||
continue
|
||||
}
|
||||
p := filepath.Join(c.getCgroupv1Path(n), "tasks")
|
||||
if err := os.WriteFile(p, pidString, 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", p, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return man.Apply(pid)
|
||||
}
|
||||
|
||||
// Stat returns usage statistics for the cgroup.
|
||||
func (c *CgroupControl) Stat() (*cgroups.Stats, error) {
|
||||
m := cgroups.Stats{}
|
||||
found := false
|
||||
for _, h := range handlers {
|
||||
if err := h.Stat(c, &m); err != nil {
|
||||
for _, statFunc := range handlers {
|
||||
if err := statFunc(c, &m); err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -573,23 +357,6 @@ func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, e
|
|||
return readCgroupMapPath(p)
|
||||
}
|
||||
|
||||
func (c *CgroupControl) createCgroupDirectory(controller string) (bool, error) {
|
||||
cPath := c.getCgroupv1Path(controller)
|
||||
err := fileutils.Exists(cPath)
|
||||
if err == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(cPath, 0o755); err != nil {
|
||||
return false, fmt.Errorf("creating cgroup for %s: %w", controller, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
var TestMode bool
|
||||
|
||||
func createCgroupv2Path(path string) (deferredError error) {
|
||||
|
|
@ -671,83 +438,12 @@ func cleanString(s string) string {
|
|||
return strings.Trim(s, "\n")
|
||||
}
|
||||
|
||||
func readAcct(ctr *CgroupControl, name string) (uint64, error) {
|
||||
p := filepath.Join(ctr.getCgroupv1Path(CPUAcct), name)
|
||||
return readFileAsUint64(p)
|
||||
}
|
||||
|
||||
func readAcctList(ctr *CgroupControl, name string) ([]uint64, error) {
|
||||
p := filepath.Join(ctr.getCgroupv1Path(CPUAcct), name)
|
||||
data, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := []uint64{}
|
||||
for s := range strings.SplitSeq(string(data), " ") {
|
||||
s = cleanString(s)
|
||||
if s == "" {
|
||||
break
|
||||
}
|
||||
v, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s: %w", s, err)
|
||||
}
|
||||
r = append(r, v)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func cpusetCopyFromParent(path string, cgroupv2 bool) error {
|
||||
for _, file := range []string{"cpuset.cpus", "cpuset.mems"} {
|
||||
if _, err := cpusetCopyFileFromParent(path, file, cgroupv2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cpusetCopyFileFromParent(dir, file string, cgroupv2 bool) ([]byte, error) {
|
||||
if dir == cgroupRoot {
|
||||
return nil, fmt.Errorf("could not find parent to initialize cpuset %s", file)
|
||||
}
|
||||
path := filepath.Join(dir, file)
|
||||
parentPath := path
|
||||
if cgroupv2 {
|
||||
parentPath += ".effective"
|
||||
}
|
||||
data, err := os.ReadFile(parentPath)
|
||||
if err != nil {
|
||||
// if the file doesn't exist, it is likely that the cpuset controller
|
||||
// is not enabled in the kernel.
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if strings.Trim(string(data), "\n") != "" {
|
||||
return data, nil
|
||||
}
|
||||
data, err = cpusetCopyFileFromParent(filepath.Dir(dir), file, cgroupv2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.WriteFile(path, data, 0o644); err != nil {
|
||||
return nil, fmt.Errorf("write %s: %w", path, err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// SystemCPUUsage returns the system usage for all the cgroups.
|
||||
func SystemCPUUsage() (uint64, error) {
|
||||
cgroupv2, err := IsCgroup2UnifiedMode()
|
||||
_, err := IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !cgroupv2 {
|
||||
p := filepath.Join(cgroupRoot, CPUAcct, "cpuacct.usage")
|
||||
return readFileAsUint64(p)
|
||||
}
|
||||
|
||||
files, err := os.ReadDir(cgroupRoot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
@ -800,7 +496,7 @@ func UserConnection(uid int) (*systemdDbus.Conn, error) {
|
|||
func UserOwnsCurrentSystemdCgroup() (bool, error) {
|
||||
uid := os.Geteuid()
|
||||
|
||||
cgroup2, err := IsCgroup2UnifiedMode()
|
||||
_, err := IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -822,20 +518,11 @@ func UserOwnsCurrentSystemdCgroup() (bool, error) {
|
|||
|
||||
// If we are on a cgroup v2 system and there are cgroup v1 controllers
|
||||
// mounted, ignore them when the current process is at the root cgroup.
|
||||
if cgroup2 && parts[1] != "" && parts[2] == "/" {
|
||||
if parts[1] != "" && parts[2] == "/" {
|
||||
continue
|
||||
}
|
||||
|
||||
var cgroupPath string
|
||||
|
||||
if cgroup2 {
|
||||
cgroupPath = filepath.Join(cgroupRoot, parts[2])
|
||||
} else {
|
||||
if parts[1] != "name=systemd" {
|
||||
continue
|
||||
}
|
||||
cgroupPath = filepath.Join(cgroupRoot, "systemd", parts[2])
|
||||
}
|
||||
cgroupPath := filepath.Join(cgroupRoot, parts[2])
|
||||
|
||||
st, err := os.Stat(cgroupPath)
|
||||
if err != nil {
|
||||
|
|
|
|||
92
vendor/go.podman.io/common/pkg/cgroups/cpu_linux.go
generated
vendored
92
vendor/go.podman.io/common/pkg/cgroups/cpu_linux.go
generated
vendored
|
|
@ -3,95 +3,31 @@
|
|||
package cgroups
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fs"
|
||||
"github.com/opencontainers/cgroups/fs2"
|
||||
)
|
||||
|
||||
type linuxCPUHandler struct {
|
||||
CPU fs.CpuGroup
|
||||
}
|
||||
|
||||
func getCPUHandler() *linuxCPUHandler {
|
||||
return &linuxCPUHandler{}
|
||||
}
|
||||
|
||||
// Apply set the specified constraints.
|
||||
func (c *linuxCPUHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
|
||||
if ctr.cgroup2 {
|
||||
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return man.Set(res)
|
||||
}
|
||||
path := filepath.Join(cgroupRoot, CPU, ctr.config.Path)
|
||||
return c.CPU.Set(path, res)
|
||||
}
|
||||
|
||||
// Create the cgroup.
|
||||
func (c *linuxCPUHandler) Create(ctr *CgroupControl) (bool, error) {
|
||||
if ctr.cgroup2 {
|
||||
return false, nil
|
||||
}
|
||||
return ctr.createCgroupDirectory(CPU)
|
||||
}
|
||||
|
||||
// Destroy the cgroup.
|
||||
func (c *linuxCPUHandler) Destroy(ctr *CgroupControl) error {
|
||||
return rmDirRecursively(ctr.getCgroupv1Path(CPU))
|
||||
}
|
||||
|
||||
// Stat fills a metrics structure with usage stats for the controller.
|
||||
func (c *linuxCPUHandler) Stat(ctr *CgroupControl, m *cgroups.Stats) error {
|
||||
var err error
|
||||
// cpuStat fills a metrics structure with usage stats for the cpu controller.
|
||||
func cpuStat(ctr *CgroupControl, m *cgroups.Stats) error {
|
||||
cpu := cgroups.CpuStats{}
|
||||
if ctr.cgroup2 {
|
||||
values, err := readCgroup2MapFile(ctr, "cpu.stat")
|
||||
values, err := readCgroup2MapFile(ctr, "cpu.stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val, found := values["usage_usec"]; found {
|
||||
cpu.CpuUsage.TotalUsage, err = strconv.ParseUint(cleanString(val[0]), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val, found := values["usage_usec"]; found {
|
||||
cpu.CpuUsage.TotalUsage, err = strconv.ParseUint(cleanString(val[0]), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cpu.CpuUsage.UsageInKernelmode *= 1000
|
||||
}
|
||||
if val, found := values["system_usec"]; found {
|
||||
cpu.CpuUsage.UsageInKernelmode, err = strconv.ParseUint(cleanString(val[0]), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cpu.CpuUsage.TotalUsage *= 1000
|
||||
}
|
||||
} else {
|
||||
cpu.CpuUsage.TotalUsage, err = readAcct(ctr, "cpuacct.usage")
|
||||
cpu.CpuUsage.TotalUsage *= 1000
|
||||
}
|
||||
if val, found := values["system_usec"]; found {
|
||||
cpu.CpuUsage.UsageInKernelmode, err = strconv.ParseUint(cleanString(val[0]), 10, 64)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
cpu.CpuUsage.TotalUsage = 0
|
||||
}
|
||||
cpu.CpuUsage.UsageInKernelmode, err = readAcct(ctr, "cpuacct.usage_sys")
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
cpu.CpuUsage.UsageInKernelmode = 0
|
||||
}
|
||||
cpu.CpuUsage.PercpuUsage, err = readAcctList(ctr, "cpuacct.usage_percpu")
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
cpu.CpuUsage.PercpuUsage = nil
|
||||
return err
|
||||
}
|
||||
cpu.CpuUsage.UsageInKernelmode *= 1000
|
||||
}
|
||||
m.CpuStats = cpu
|
||||
return nil
|
||||
|
|
|
|||
55
vendor/go.podman.io/common/pkg/cgroups/cpuset_linux.go
generated
vendored
55
vendor/go.podman.io/common/pkg/cgroups/cpuset_linux.go
generated
vendored
|
|
@ -1,55 +0,0 @@
|
|||
//go:build linux
|
||||
|
||||
package cgroups
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fs"
|
||||
"github.com/opencontainers/cgroups/fs2"
|
||||
)
|
||||
|
||||
type linuxCpusetHandler struct {
|
||||
CPUSet fs.CpusetGroup
|
||||
}
|
||||
|
||||
func getCpusetHandler() *linuxCpusetHandler {
|
||||
return &linuxCpusetHandler{}
|
||||
}
|
||||
|
||||
// Apply set the specified constraints.
|
||||
func (c *linuxCpusetHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
|
||||
if ctr.cgroup2 {
|
||||
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return man.Set(res)
|
||||
}
|
||||
path := filepath.Join(cgroupRoot, CPUset, ctr.config.Path)
|
||||
return c.CPUSet.Set(path, res)
|
||||
}
|
||||
|
||||
// Create the cgroup.
|
||||
func (c *linuxCpusetHandler) Create(ctr *CgroupControl) (bool, error) {
|
||||
if ctr.cgroup2 {
|
||||
path := filepath.Join(cgroupRoot, ctr.config.Path)
|
||||
return true, cpusetCopyFromParent(path, true)
|
||||
}
|
||||
created, err := ctr.createCgroupDirectory(CPUset)
|
||||
if !created || err != nil {
|
||||
return created, err
|
||||
}
|
||||
return true, cpusetCopyFromParent(ctr.getCgroupv1Path(CPUset), false)
|
||||
}
|
||||
|
||||
// Destroy the cgroup.
|
||||
func (c *linuxCpusetHandler) Destroy(ctr *CgroupControl) error {
|
||||
return rmDirRecursively(ctr.getCgroupv1Path(CPUset))
|
||||
}
|
||||
|
||||
// Stat fills a metrics structure with usage stats for the controller.
|
||||
func (c *linuxCpusetHandler) Stat(_ *CgroupControl, _ *cgroups.Stats) error {
|
||||
return nil
|
||||
}
|
||||
95
vendor/go.podman.io/common/pkg/cgroups/memory_linux.go
generated
vendored
95
vendor/go.podman.io/common/pkg/cgroups/memory_linux.go
generated
vendored
|
|
@ -6,94 +6,35 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fs"
|
||||
"github.com/opencontainers/cgroups/fs2"
|
||||
)
|
||||
|
||||
type linuxMemHandler struct {
|
||||
Mem fs.MemoryGroup
|
||||
}
|
||||
|
||||
func getMemoryHandler() *linuxMemHandler {
|
||||
return &linuxMemHandler{}
|
||||
}
|
||||
|
||||
// Apply set the specified constraints.
|
||||
func (c *linuxMemHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
|
||||
if ctr.cgroup2 {
|
||||
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return man.Set(res)
|
||||
}
|
||||
path := filepath.Join(cgroupRoot, Memory, ctr.config.Path)
|
||||
return c.Mem.Set(path, res)
|
||||
}
|
||||
|
||||
// Create the cgroup.
|
||||
func (c *linuxMemHandler) Create(ctr *CgroupControl) (bool, error) {
|
||||
if ctr.cgroup2 {
|
||||
return false, nil
|
||||
}
|
||||
return ctr.createCgroupDirectory(Memory)
|
||||
}
|
||||
|
||||
// Destroy the cgroup.
|
||||
func (c *linuxMemHandler) Destroy(ctr *CgroupControl) error {
|
||||
return rmDirRecursively(ctr.getCgroupv1Path(Memory))
|
||||
}
|
||||
|
||||
// Stat fills a metrics structure with usage stats for the controller.
|
||||
func (c *linuxMemHandler) Stat(ctr *CgroupControl, m *cgroups.Stats) error {
|
||||
// memoryStat fills a metrics structure with usage stats for the memory controller.
|
||||
func memoryStat(ctr *CgroupControl, m *cgroups.Stats) error {
|
||||
var err error
|
||||
memUsage := cgroups.MemoryStats{}
|
||||
|
||||
var memoryRoot string
|
||||
var limitFilename string
|
||||
|
||||
if ctr.cgroup2 {
|
||||
memoryRoot = filepath.Join(cgroupRoot, ctr.config.Path)
|
||||
limitFilename = "memory.max"
|
||||
memoryRoot = filepath.Join(cgroupRoot, ctr.config.Path)
|
||||
limitFilename = "memory.max"
|
||||
|
||||
// Read memory.current
|
||||
current, err := readFileAsUint64(filepath.Join(memoryRoot, "memory.current"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Read memory.current
|
||||
current, err := readFileAsUint64(filepath.Join(memoryRoot, "memory.current"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read inactive_file from memory.stat
|
||||
inactiveFile, err := readFileByKeyAsUint64(filepath.Join(memoryRoot, "memory.stat"), "inactive_file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Read inactive_file from memory.stat
|
||||
inactiveFile, err := readFileByKeyAsUint64(filepath.Join(memoryRoot, "memory.stat"), "inactive_file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Docker calculation: memory.current - memory.stat['inactive_file']
|
||||
memUsage.Usage.Usage = 0
|
||||
if inactiveFile < current {
|
||||
memUsage.Usage.Usage = current - inactiveFile
|
||||
}
|
||||
} else {
|
||||
memoryRoot = ctr.getCgroupv1Path(Memory)
|
||||
limitFilename = "memory.limit_in_bytes"
|
||||
|
||||
// Read memory.usage_in_bytes
|
||||
usageInBytes, err := readFileAsUint64(filepath.Join(memoryRoot, "memory.usage_in_bytes"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read total_inactive_file from memory.stat
|
||||
totalInactiveFile, err := readFileByKeyAsUint64(filepath.Join(memoryRoot, "memory.stat"), "total_inactive_file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Docker calculation: memory.usage_in_bytes - memory.stat['total_inactive_file']
|
||||
memUsage.Usage.Usage = 0
|
||||
if totalInactiveFile < usageInBytes {
|
||||
memUsage.Usage.Usage = usageInBytes - totalInactiveFile
|
||||
}
|
||||
// Docker calculation: memory.current - memory.stat['inactive_file']
|
||||
memUsage.Usage.Usage = 0
|
||||
if inactiveFile < current {
|
||||
memUsage.Usage.Usage = current - inactiveFile
|
||||
}
|
||||
|
||||
memUsage.Usage.Limit, err = readFileAsUint64(filepath.Join(memoryRoot, limitFilename))
|
||||
|
|
|
|||
48
vendor/go.podman.io/common/pkg/cgroups/pids_linux.go
generated
vendored
48
vendor/go.podman.io/common/pkg/cgroups/pids_linux.go
generated
vendored
|
|
@ -6,58 +6,16 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/opencontainers/cgroups/fs"
|
||||
"github.com/opencontainers/cgroups/fs2"
|
||||
)
|
||||
|
||||
type linuxPidHandler struct {
|
||||
Pid fs.PidsGroup
|
||||
}
|
||||
|
||||
func getPidsHandler() *linuxPidHandler {
|
||||
return &linuxPidHandler{}
|
||||
}
|
||||
|
||||
// Apply set the specified constraints.
|
||||
func (c *linuxPidHandler) Apply(ctr *CgroupControl, res *cgroups.Resources) error {
|
||||
if ctr.cgroup2 {
|
||||
man, err := fs2.NewManager(ctr.config, filepath.Join(cgroupRoot, ctr.config.Path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return man.Set(res)
|
||||
}
|
||||
|
||||
path := filepath.Join(cgroupRoot, Pids, ctr.config.Path)
|
||||
return c.Pid.Set(path, res)
|
||||
}
|
||||
|
||||
// Create the cgroup.
|
||||
func (c *linuxPidHandler) Create(ctr *CgroupControl) (bool, error) {
|
||||
if ctr.cgroup2 {
|
||||
return false, nil
|
||||
}
|
||||
return ctr.createCgroupDirectory(Pids)
|
||||
}
|
||||
|
||||
// Destroy the cgroup.
|
||||
func (c *linuxPidHandler) Destroy(ctr *CgroupControl) error {
|
||||
return rmDirRecursively(ctr.getCgroupv1Path(Pids))
|
||||
}
|
||||
|
||||
// Stat fills a metrics structure with usage stats for the controller.
|
||||
func (c *linuxPidHandler) Stat(ctr *CgroupControl, m *cgroups.Stats) error {
|
||||
// pidsStat fills a metrics structure with usage stats for the pids controller.
|
||||
func pidsStat(ctr *CgroupControl, m *cgroups.Stats) error {
|
||||
if ctr.config.Path == "" {
|
||||
// nothing we can do to retrieve the pids.current path
|
||||
return nil
|
||||
}
|
||||
|
||||
var PIDRoot string
|
||||
if ctr.cgroup2 {
|
||||
PIDRoot = filepath.Join(cgroupRoot, ctr.config.Path)
|
||||
} else {
|
||||
PIDRoot = ctr.getCgroupv1Path(Pids)
|
||||
}
|
||||
PIDRoot := filepath.Join(cgroupRoot, ctr.config.Path)
|
||||
|
||||
current, err := readFileAsUint64(filepath.Join(PIDRoot, "pids.current"))
|
||||
if err != nil {
|
||||
|
|
|
|||
50
vendor/go.podman.io/common/pkg/cgroups/systemd_linux.go
generated
vendored
50
vendor/go.podman.io/common/pkg/cgroups/systemd_linux.go
generated
vendored
|
|
@ -32,18 +32,11 @@ func systemdCreate(resources *cgroups.Resources, path string, c *systemdDbus.Con
|
|||
systemdDbus.PropDescription("cgroup " + name),
|
||||
systemdDbus.PropWants(slice),
|
||||
}
|
||||
var ioString string
|
||||
v2, _ := IsCgroup2UnifiedMode()
|
||||
if v2 {
|
||||
ioString = "IOAccounting"
|
||||
} else {
|
||||
ioString = "BlockIOAccounting"
|
||||
}
|
||||
pMap := map[string]bool{
|
||||
"DefaultDependencies": false,
|
||||
"MemoryAccounting": true,
|
||||
"CPUAccounting": true,
|
||||
ioString: true,
|
||||
"IOAccounting": true,
|
||||
}
|
||||
if i == 0 {
|
||||
pMap["Delegate"] = true
|
||||
|
|
@ -57,7 +50,7 @@ func systemdCreate(resources *cgroups.Resources, path string, c *systemdDbus.Con
|
|||
properties = append(properties, p)
|
||||
}
|
||||
|
||||
uMap, sMap, bMap, iMap, structMap, err := resourcesToProps(resources, v2)
|
||||
uMap, sMap, bMap, iMap, structMap, err := resourcesToProps(resources)
|
||||
if err != nil {
|
||||
lastError = err
|
||||
continue
|
||||
|
|
@ -150,7 +143,7 @@ func systemdDestroyConn(path string, c *systemdDbus.Conn) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func resourcesToProps(res *cgroups.Resources, v2 bool) (map[string]uint64, map[string]string, map[string][]byte, map[string]int64, map[string][]BlkioDev, error) {
|
||||
func resourcesToProps(res *cgroups.Resources) (map[string]uint64, map[string]string, map[string][]byte, map[string]int64, map[string][]BlkioDev, error) {
|
||||
bMap := make(map[string][]byte)
|
||||
// this array is not used but will be once more resource limits are added
|
||||
sMap := make(map[string]string)
|
||||
|
|
@ -176,13 +169,8 @@ func resourcesToProps(res *cgroups.Resources, v2 bool) (map[string]uint64, map[s
|
|||
|
||||
if res.CpuShares != 0 {
|
||||
// convert from shares to weight. weight only supports 1-10000
|
||||
v2, _ := IsCgroup2UnifiedMode()
|
||||
if v2 {
|
||||
wt := (1 + ((res.CpuShares-2)*9999)/262142)
|
||||
uMap["CPUWeight"] = wt
|
||||
} else {
|
||||
uMap["CPUShares"] = res.CpuShares
|
||||
}
|
||||
wt := (1 + ((res.CpuShares-2)*9999)/262142)
|
||||
uMap["CPUWeight"] = wt
|
||||
}
|
||||
|
||||
// CPUSet
|
||||
|
|
@ -212,21 +200,15 @@ func resourcesToProps(res *cgroups.Resources, v2 bool) (map[string]uint64, map[s
|
|||
case res.Memory == -1 || res.MemorySwap == -1:
|
||||
swap := -1
|
||||
uMap["MemorySwapMax"] = uint64(swap)
|
||||
case v2:
|
||||
default:
|
||||
// swap max = swap (limit + swap limit) - limit
|
||||
uMap["MemorySwapMax"] = uint64(res.MemorySwap - res.Memory)
|
||||
default:
|
||||
uMap["MemorySwapMax"] = uint64(res.MemorySwap)
|
||||
}
|
||||
}
|
||||
|
||||
// Blkio
|
||||
if res.BlkioWeight > 0 {
|
||||
if v2 {
|
||||
uMap["IOWeight"] = uint64(res.BlkioWeight)
|
||||
} else {
|
||||
uMap["BlockIOWeight"] = uint64(res.BlkioWeight)
|
||||
}
|
||||
uMap["IOWeight"] = uint64(res.BlkioWeight)
|
||||
}
|
||||
|
||||
// systemd requires the paths to be in the form /dev/{block, char}/major:minor
|
||||
|
|
@ -238,11 +220,7 @@ func resourcesToProps(res *cgroups.Resources, v2 bool) (map[string]uint64, map[s
|
|||
Device: fmt.Sprintf("/dev/block/%d:%d", entry.Major, entry.Minor),
|
||||
Bytes: entry.Rate,
|
||||
}
|
||||
if v2 {
|
||||
structMap["IOReadBandwidthMax"] = append(structMap["IOReadBandwidthMax"], newThrottle)
|
||||
} else {
|
||||
structMap["BlockIOReadBandwidth"] = append(structMap["BlockIOReadBandwidth"], newThrottle)
|
||||
}
|
||||
structMap["IOReadBandwidthMax"] = append(structMap["IOReadBandwidthMax"], newThrottle)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,11 +230,7 @@ func resourcesToProps(res *cgroups.Resources, v2 bool) (map[string]uint64, map[s
|
|||
Device: fmt.Sprintf("/dev/block/%d:%d", entry.Major, entry.Minor),
|
||||
Bytes: entry.Rate,
|
||||
}
|
||||
if v2 {
|
||||
structMap["IOWriteBandwidthMax"] = append(structMap["IOWriteBandwidthMax"], newThrottle)
|
||||
} else {
|
||||
structMap["BlockIOWriteBandwidth"] = append(structMap["BlockIOWriteBandwidth"], newThrottle)
|
||||
}
|
||||
structMap["IOWriteBandwidthMax"] = append(structMap["IOWriteBandwidthMax"], newThrottle)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,11 +240,7 @@ func resourcesToProps(res *cgroups.Resources, v2 bool) (map[string]uint64, map[s
|
|||
Device: fmt.Sprintf("/dev/block/%d:%d", entry.Major, entry.Minor),
|
||||
Bytes: uint64(entry.Weight),
|
||||
}
|
||||
if v2 {
|
||||
structMap["IODeviceWeight"] = append(structMap["IODeviceWeight"], newWeight)
|
||||
} else {
|
||||
structMap["BlockIODeviceWeight"] = append(structMap["BlockIODeviceWeight"], newWeight)
|
||||
}
|
||||
structMap["IODeviceWeight"] = append(structMap["IODeviceWeight"], newWeight)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
25
vendor/go.podman.io/common/pkg/cgroups/utils_linux.go
generated
vendored
25
vendor/go.podman.io/common/pkg/cgroups/utils_linux.go
generated
vendored
|
|
@ -15,7 +15,6 @@ import (
|
|||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.podman.io/storage/pkg/fileutils"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
|
@ -207,7 +206,7 @@ func MoveUnderCgroup(cgroup, subtree string, processes []uint32) error {
|
|||
}
|
||||
defer f.Close()
|
||||
|
||||
unifiedMode, err := IsCgroup2UnifiedMode()
|
||||
_, err = IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -221,24 +220,12 @@ func MoveUnderCgroup(cgroup, subtree string, processes []uint32) error {
|
|||
}
|
||||
|
||||
// root cgroup, skip it
|
||||
if parts[2] == "/" && (!unifiedMode || parts[1] != "") {
|
||||
if parts[2] == "/" && parts[1] != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
cgroupRoot := "/sys/fs/cgroup"
|
||||
// Special case the unified mount on hybrid cgroup and named hierarchies.
|
||||
// This works on Fedora 31, but we should really parse the mounts to see
|
||||
// where the cgroup hierarchy is mounted.
|
||||
if parts[1] == "" && !unifiedMode {
|
||||
// If it is not using unified mode, the cgroup v2 hierarchy is
|
||||
// usually mounted under /sys/fs/cgroup/unified
|
||||
cgroupRoot = filepath.Join(cgroupRoot, "unified")
|
||||
|
||||
// Ignore the unified mount if it doesn't exist
|
||||
if err := fileutils.Exists(cgroupRoot); err != nil && os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
} else if parts[1] != "" {
|
||||
if parts[1] != "" {
|
||||
// Assume the controller is mounted at /sys/fs/cgroup/$CONTROLLER.
|
||||
controller := strings.TrimPrefix(parts[1], "name=")
|
||||
cgroupRoot = filepath.Join(cgroupRoot, controller)
|
||||
|
|
@ -292,15 +279,11 @@ var (
|
|||
// it is running in the root cgroup on a system that uses cgroupv2.
|
||||
func MaybeMoveToSubCgroup() error {
|
||||
maybeMoveToSubCgroupSync.Do(func() {
|
||||
unifiedMode, err := IsCgroup2UnifiedMode()
|
||||
_, err := IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
maybeMoveToSubCgroupSyncErr = err
|
||||
return
|
||||
}
|
||||
if !unifiedMode {
|
||||
maybeMoveToSubCgroupSyncErr = nil
|
||||
return
|
||||
}
|
||||
cgroup, err := GetOwnCgroup()
|
||||
if err != nil {
|
||||
maybeMoveToSubCgroupSyncErr = err
|
||||
|
|
|
|||
27
vendor/go.podman.io/common/pkg/cgroupv2/cgroups_linux.go
generated
vendored
27
vendor/go.podman.io/common/pkg/cgroupv2/cgroups_linux.go
generated
vendored
|
|
@ -1,27 +0,0 @@
|
|||
package cgroupv2
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
isCgroupV2Once sync.Once
|
||||
isCgroupV2 bool
|
||||
isCgroupV2Err error
|
||||
)
|
||||
|
||||
// Enabled returns whether we are running on cgroup v2.
|
||||
func Enabled() (bool, error) {
|
||||
isCgroupV2Once.Do(func() {
|
||||
var st syscall.Statfs_t
|
||||
if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil {
|
||||
isCgroupV2, isCgroupV2Err = false, err
|
||||
} else {
|
||||
isCgroupV2, isCgroupV2Err = st.Type == unix.CGROUP2_SUPER_MAGIC, nil
|
||||
}
|
||||
})
|
||||
return isCgroupV2, isCgroupV2Err
|
||||
}
|
||||
8
vendor/go.podman.io/common/pkg/cgroupv2/cgroups_unsupported.go
generated
vendored
8
vendor/go.podman.io/common/pkg/cgroupv2/cgroups_unsupported.go
generated
vendored
|
|
@ -1,8 +0,0 @@
|
|||
//go:build !linux
|
||||
|
||||
package cgroupv2
|
||||
|
||||
// Enabled returns whether we are running on cgroup v2.
|
||||
func Enabled() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
13
vendor/go.podman.io/common/pkg/config/default.go
generated
vendored
13
vendor/go.podman.io/common/pkg/config/default.go
generated
vendored
|
|
@ -15,7 +15,6 @@ import (
|
|||
"go.podman.io/common/internal/attributedstring"
|
||||
nettypes "go.podman.io/common/libnetwork/types"
|
||||
"go.podman.io/common/pkg/apparmor"
|
||||
"go.podman.io/common/pkg/cgroupv2"
|
||||
"go.podman.io/storage/pkg/fileutils"
|
||||
"go.podman.io/storage/pkg/homedir"
|
||||
"go.podman.io/storage/pkg/unshare"
|
||||
|
|
@ -231,17 +230,12 @@ func defaultConfig() (*Config, error) {
|
|||
}
|
||||
}
|
||||
|
||||
cgroupNS := "host"
|
||||
if cgroup2, _ := cgroupv2.Enabled(); cgroup2 {
|
||||
cgroupNS = "private"
|
||||
}
|
||||
|
||||
return &Config{
|
||||
Containers: ContainersConfig{
|
||||
Annotations: attributedstring.Slice{},
|
||||
ApparmorProfile: DefaultApparmorProfile,
|
||||
BaseHostsFile: "",
|
||||
CgroupNS: cgroupNS,
|
||||
CgroupNS: "private",
|
||||
Cgroups: getDefaultCgroupsMode(),
|
||||
DNSOptions: attributedstring.Slice{},
|
||||
DNSSearches: attributedstring.Slice{},
|
||||
|
|
@ -650,12 +644,7 @@ func (c *Config) PidsLimit() int64 {
|
|||
if c.Engine.CgroupManager != SystemdCgroupsManager {
|
||||
return 0
|
||||
}
|
||||
cgroup2, _ := cgroupv2.Enabled()
|
||||
if !cgroup2 {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
return c.Containers.PidsLimit
|
||||
}
|
||||
|
||||
|
|
|
|||
7
vendor/go.podman.io/common/pkg/config/systemd.go
generated
vendored
7
vendor/go.podman.io/common/pkg/config/systemd.go
generated
vendored
|
|
@ -7,9 +7,7 @@ import (
|
|||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"go.podman.io/common/pkg/cgroupv2"
|
||||
"go.podman.io/common/pkg/systemd"
|
||||
"go.podman.io/storage/pkg/unshare"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -26,11 +24,6 @@ func defaultCgroupManager() string {
|
|||
if !useSystemd() {
|
||||
return CgroupfsCgroupsManager
|
||||
}
|
||||
enabled, err := cgroupv2.Enabled()
|
||||
if err == nil && !enabled && unshare.IsRootless() {
|
||||
return CgroupfsCgroupsManager
|
||||
}
|
||||
|
||||
return SystemdCgroupsManager
|
||||
}
|
||||
|
||||
|
|
|
|||
18
vendor/go.podman.io/common/pkg/sysinfo/sysinfo_linux.go
generated
vendored
18
vendor/go.podman.io/common/pkg/sysinfo/sysinfo_linux.go
generated
vendored
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/opencontainers/cgroups"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.podman.io/common/pkg/cgroupv2"
|
||||
cgroupv2 "go.podman.io/common/pkg/cgroups"
|
||||
"go.podman.io/storage/pkg/fileutils"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
|
@ -41,7 +41,7 @@ func New(quiet bool) *SysInfo {
|
|||
sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
|
||||
sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
|
||||
sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
|
||||
sysInfo.cgroupPids = checkCgroupPids(cgMounts, quiet)
|
||||
sysInfo.cgroupPids = checkCgroupPids()
|
||||
}
|
||||
|
||||
_, ok := cgMounts["devices"]
|
||||
|
|
@ -228,22 +228,12 @@ func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetI
|
|||
}
|
||||
|
||||
// checkCgroupPids reads the pids information from the pids cgroup mount point.
|
||||
func checkCgroupPids(cgMounts map[string]string, quiet bool) cgroupPids {
|
||||
cgroup2, err := cgroupv2.Enabled()
|
||||
func checkCgroupPids() cgroupPids {
|
||||
_, err := cgroupv2.IsCgroup2UnifiedMode()
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to check cgroups version: %v", err)
|
||||
return cgroupPids{}
|
||||
}
|
||||
if !cgroup2 {
|
||||
_, ok := cgMounts["pids"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Unable to find pids cgroup in mounts")
|
||||
}
|
||||
return cgroupPids{}
|
||||
}
|
||||
}
|
||||
|
||||
return cgroupPids{
|
||||
PidsLimit: true,
|
||||
}
|
||||
|
|
|
|||
6
vendor/go.podman.io/common/pkg/systemd/systemd_linux.go
generated
vendored
6
vendor/go.podman.io/common/pkg/systemd/systemd_linux.go
generated
vendored
|
|
@ -88,14 +88,12 @@ func MovePauseProcessToScope(pausePidPath string) {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
unified, err2 := cgroups.IsCgroup2UnifiedMode()
|
||||
_, err2 := cgroups.IsCgroup2UnifiedMode()
|
||||
if err2 != nil {
|
||||
logrus.Warnf("Failed to detect if running with cgroup unified: %v", err)
|
||||
}
|
||||
if RunsOnSystemd() && unified {
|
||||
if RunsOnSystemd() {
|
||||
logrus.Warnf("Failed to add pause process to systemd sandbox cgroup: %v", err)
|
||||
} else {
|
||||
logrus.Debugf("Failed to add pause process to systemd sandbox cgroup: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
vendor/go.podman.io/image/v5/docker/reference/normalize.go
generated
vendored
11
vendor/go.podman.io/image/v5/docker/reference/normalize.go
generated
vendored
|
|
@ -169,6 +169,17 @@ func TagNameOnly(ref Named) Named {
|
|||
|
||||
// ParseAnyReference parses a reference string as a possible identifier,
|
||||
// full digest, or familiar name.
|
||||
//
|
||||
// Deprecated: This parses inputs with 64 hexadecimal characters as sha256 digests,
|
||||
// and that can’t be generalized (a digest algorithm can not be determined purely
|
||||
// from the length of the input).
|
||||
//
|
||||
// In the future, image IDs will either stay 256-bit, but will not be SHA-256 values;
|
||||
// or they will support arbitrary algorithms, in which case the hexadecimal-only syntax
|
||||
// is not sufficient. Either way, this function will not be fit for purpose.
|
||||
//
|
||||
// Callers (if any) should redesign their syntax to strictly differentiate between
|
||||
// image IDs (with an as-yet-unknown future syntax) and named image references.
|
||||
func ParseAnyReference(ref string) (Reference, error) {
|
||||
if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
|
||||
return digestReference("sha256:" + ref), nil
|
||||
|
|
|
|||
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
|
|
@ -564,7 +564,6 @@ github.com/onsi/gomega/types
|
|||
## explicit; go 1.23.0
|
||||
github.com/opencontainers/cgroups
|
||||
github.com/opencontainers/cgroups/devices/config
|
||||
github.com/opencontainers/cgroups/fs
|
||||
github.com/opencontainers/cgroups/fs2
|
||||
github.com/opencontainers/cgroups/fscommon
|
||||
github.com/opencontainers/cgroups/internal/path
|
||||
|
|
@ -773,7 +772,7 @@ go.opentelemetry.io/otel/trace
|
|||
go.opentelemetry.io/otel/trace/embedded
|
||||
go.opentelemetry.io/otel/trace/internal/telemetry
|
||||
go.opentelemetry.io/otel/trace/noop
|
||||
# go.podman.io/common v0.66.1-0.20251128185259-94e31d2e45ba
|
||||
# go.podman.io/common v0.66.1-0.20251202202200-df55d6c661e8
|
||||
## explicit; go 1.24.6
|
||||
go.podman.io/common/internal
|
||||
go.podman.io/common/internal/attributedstring
|
||||
|
|
@ -798,7 +797,6 @@ go.podman.io/common/pkg/apparmor/internal/supported
|
|||
go.podman.io/common/pkg/auth
|
||||
go.podman.io/common/pkg/capabilities
|
||||
go.podman.io/common/pkg/cgroups
|
||||
go.podman.io/common/pkg/cgroupv2
|
||||
go.podman.io/common/pkg/chown
|
||||
go.podman.io/common/pkg/completion
|
||||
go.podman.io/common/pkg/config
|
||||
|
|
@ -843,7 +841,7 @@ go.podman.io/common/pkg/umask
|
|||
go.podman.io/common/pkg/util
|
||||
go.podman.io/common/pkg/version
|
||||
go.podman.io/common/version
|
||||
# go.podman.io/image/v5 v5.38.1-0.20251128185259-94e31d2e45ba
|
||||
# go.podman.io/image/v5 v5.38.1-0.20251202202200-df55d6c661e8
|
||||
## explicit; go 1.24.6
|
||||
go.podman.io/image/v5/copy
|
||||
go.podman.io/image/v5/directory
|
||||
|
|
@ -917,7 +915,7 @@ go.podman.io/image/v5/transports
|
|||
go.podman.io/image/v5/transports/alltransports
|
||||
go.podman.io/image/v5/types
|
||||
go.podman.io/image/v5/version
|
||||
# go.podman.io/storage v1.61.1-0.20251128185259-94e31d2e45ba
|
||||
# go.podman.io/storage v1.61.1-0.20251202202200-df55d6c661e8
|
||||
## explicit; go 1.24.0
|
||||
go.podman.io/storage
|
||||
go.podman.io/storage/drivers
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue