mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Merge pull request #11473 from Champ-Goblem:shim-add-cgroup-v2-metrics-support
PiperOrigin-RevId: 730560110
This commit is contained in:
@@ -133,7 +133,7 @@ go_path(
|
||||
# binaries have been factored into a cli package, which is
|
||||
# a good practice in any case.
|
||||
"//runsc/cli",
|
||||
"//shim/cli",
|
||||
"//shim/v1/cli",
|
||||
"//webhook/pkg/cli",
|
||||
"//tools/checklocks",
|
||||
|
||||
|
||||
@@ -84,8 +84,8 @@ global:
|
||||
- "panic recovered: .*types/sizes.go:82: assertion failed"
|
||||
exclude:
|
||||
# Generated: exempt all.
|
||||
- pkg/shim/runtimeoptions/runtimeoptions_cri.go
|
||||
- pkg/shim/runtimeoptions/v14/runtimeoptions_cri.go
|
||||
- pkg/shim/v1/runtimeoptions/runtimeoptions_cri.go
|
||||
- pkg/shim/v1/runtimeoptions/v14/runtimeoptions_cri.go
|
||||
analyzers:
|
||||
asmdecl:
|
||||
generated: # Enabled.
|
||||
|
||||
@@ -18,8 +18,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
|
||||
"gvisor.dev/gvisor/pkg/sentry/state"
|
||||
@@ -84,18 +82,7 @@ func (s *State) Save(o *SaveOpts, _ *struct{}) error {
|
||||
Key: o.Key,
|
||||
Metadata: o.Metadata,
|
||||
MemoryFileSaveOpts: o.MemoryFileSaveOpts,
|
||||
Callback: func(err error) {
|
||||
if err == nil {
|
||||
log.Infof("Save succeeded: exiting...")
|
||||
s.Kernel.SetSaveSuccess(false /* autosave */)
|
||||
} else {
|
||||
log.Warningf("Save failed: %v", err)
|
||||
s.Kernel.SetSaveError(err)
|
||||
}
|
||||
if !o.Resume {
|
||||
s.Kernel.Kill(linux.WaitStatusExit(0))
|
||||
}
|
||||
},
|
||||
Resume: o.Resume,
|
||||
}
|
||||
if o.HavePagesFile {
|
||||
saveOpts.PagesMetadata, err = o.ReleaseFD(1)
|
||||
|
||||
@@ -31,6 +31,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
var _ vfs.FilesystemImplSaveRestoreExtension = (*filesystem)(nil)
|
||||
|
||||
// +stateify savable
|
||||
type savedDentryRW struct {
|
||||
read bool
|
||||
@@ -134,6 +136,11 @@ func (d *dentry) beforeSave() {
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeResume implements vfs.FilesystemImplSaveRestoreExtension.BeforeResume.
|
||||
func (fs *filesystem) BeforeResume(ctx context.Context) {
|
||||
fs.savedDentryRW = nil
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (fs *filesystem) afterLoad(ctx goContext.Context) {
|
||||
fs.mf = pgalloc.MemoryFileFromContext(ctx)
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
@@ -68,6 +69,14 @@ var _ kernfs.Inode = (*tasksInode)(nil)
|
||||
func (fs *filesystem) newTasksInode(ctx context.Context, k *kernel.Kernel, pidns *kernel.PIDNamespace, internalData *InternalData) *tasksInode {
|
||||
root := auth.NewRootCredentials(pidns.UserNamespace())
|
||||
|
||||
// /proc is expected to have inode number
|
||||
// include/linux/proc_ns.h:PROC_ROOT_INO == 1; ensure that this is the case
|
||||
// by generating its inode number before any of its descendants.
|
||||
rootIno := fs.NextIno()
|
||||
if rootIno != 1 {
|
||||
log.Traceback("proc root inode has number %d", rootIno)
|
||||
}
|
||||
|
||||
contents := map[string]kernfs.Inode{
|
||||
"cmdline": fs.newInode(ctx, root, 0444, &cmdLineData{}),
|
||||
"cpuinfo": fs.newInode(ctx, root, 0444, newStaticFileSetStat(cpuInfoData(k))),
|
||||
@@ -99,7 +108,7 @@ func (fs *filesystem) newTasksInode(ctx context.Context, k *kernel.Kernel, pidns
|
||||
fs: fs,
|
||||
fakeCgroupControllers: internalData.Cgroups,
|
||||
}
|
||||
inode.InodeAttrs.Init(ctx, root, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.ModeDirectory|0555)
|
||||
inode.InodeAttrs.Init(ctx, root, linux.UNNAMED_MAJOR, fs.devMinor, rootIno, linux.ModeDirectory|0555)
|
||||
inode.InitRefs()
|
||||
|
||||
inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
var _ vfs.FilesystemImplSaveRestoreExtension = (*filesystem)(nil)
|
||||
|
||||
// saveMf is called by stateify.
|
||||
func (fs *filesystem) saveMf() string {
|
||||
if !fs.mf.IsSavable() {
|
||||
@@ -75,6 +77,9 @@ func (fs *filesystem) PrepareSave(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeResume implements vfs.FilesystemImplSaveRestoreExtension.BeforeResume.
|
||||
func (fs *filesystem) BeforeResume(ctx context.Context) {}
|
||||
|
||||
// CompleteRestore implements
|
||||
// vfs.FilesystemImplSaveRestoreExtension.CompleteRestore.
|
||||
func (fs *filesystem) CompleteRestore(ctx context.Context, opts vfs.CompleteRestoreOptions) error {
|
||||
|
||||
@@ -26,23 +26,30 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
)
|
||||
|
||||
// GetTransparentHugepageEnum returns the currently selected option for
|
||||
// ReadTransparentHugepageEnum returns the currently selected option for
|
||||
// whichever of
|
||||
// /sys/kernel/mm/transparent_hugepage/{enabled,shmem_enabled,defrag} is
|
||||
// specified by filename. (Only the basename is required, not the full path.)
|
||||
func GetTransparentHugepageEnum(filename string) (string, error) {
|
||||
func ReadTransparentHugepageEnum(filename string) (string, error) {
|
||||
pathname := path.Join("/sys/kernel/mm/transparent_hugepage/", filename)
|
||||
data, err := os.ReadFile(pathname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return GetTransparentHugepageEnum(string(data))
|
||||
}
|
||||
|
||||
// GetTransparentHugepageEnum returns the currently selected option given the
|
||||
// contents of any of
|
||||
// /sys/kernel/mm/transparent_hugepage/{enabled,shmem_enabled,defrag}.
|
||||
func GetTransparentHugepageEnum(data string) (string, error) {
|
||||
// In these files, the selected option is highlighted by square brackets.
|
||||
m := regexp.MustCompile(`\[.*\]`).Find(data)
|
||||
if m == nil {
|
||||
return "", fmt.Errorf("failed to parse %s: %q", pathname, data)
|
||||
m := regexp.MustCompile(`\[.*\]`).FindString(data)
|
||||
if m == "" {
|
||||
return "", fmt.Errorf("failed to find selected option in %q", data)
|
||||
}
|
||||
// Remove the square brackets.
|
||||
return string(m[1 : len(m)-1]), nil
|
||||
return m[1 : len(m)-1], nil
|
||||
}
|
||||
|
||||
// NotifyCurrentMemcgPressureCallback requests that f is called whenever the
|
||||
|
||||
@@ -702,6 +702,11 @@ func (k *Kernel) SaveTo(ctx context.Context, w, pagesMetadata io.Writer, pagesFi
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeResume is called before the kernel is resumed after save.
|
||||
func (k *Kernel) BeforeResume(ctx context.Context) {
|
||||
k.vfs.BeforeResume(ctx)
|
||||
}
|
||||
|
||||
func (k *Kernel) saveMemoryFiles(ctx context.Context, w, pagesMetadata io.Writer, pagesFile *fd.FD, mfsToSave map[string]*pgalloc.MemoryFile, mfOpts pgalloc.SaveOpts) error {
|
||||
// Save the memory files' state.
|
||||
memoryStart := time.Now()
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
@@ -68,15 +69,18 @@ type SaveOpts struct {
|
||||
// MemoryFileSaveOpts is passed to calls to pgalloc.MemoryFile.SaveTo().
|
||||
MemoryFileSaveOpts pgalloc.SaveOpts
|
||||
|
||||
// Callback is called prior to unpause, with any save error.
|
||||
Callback func(err error)
|
||||
|
||||
// Resume indicates if the statefile is used for save-resume.
|
||||
Resume bool
|
||||
|
||||
// Autosave indicates if the statefile is used for autosave.
|
||||
Autosave bool
|
||||
}
|
||||
|
||||
// Save saves the system state.
|
||||
func (opts SaveOpts) Save(ctx context.Context, k *kernel.Kernel, w *watchdog.Watchdog) error {
|
||||
t, _ := CPUTime()
|
||||
log.Infof("Before save CPU usage: %s", t.String())
|
||||
|
||||
log.Infof("Sandbox save started, pausing all tasks.")
|
||||
k.Pause()
|
||||
k.ReceiveTaskStates()
|
||||
@@ -127,7 +131,22 @@ func (opts SaveOpts) Save(ctx context.Context, k *kernel.Kernel, w *watchdog.Wat
|
||||
}
|
||||
}
|
||||
}
|
||||
opts.Callback(err)
|
||||
|
||||
t1, _ := CPUTime()
|
||||
log.Infof("Save CPU usage: %s", (t1 - t).String())
|
||||
if err == nil {
|
||||
log.Infof("Save succeeded: exiting...")
|
||||
k.SetSaveSuccess(opts.Autosave)
|
||||
} else {
|
||||
log.Warningf("Save failed: exiting... %v", err)
|
||||
k.SetSaveError(err)
|
||||
}
|
||||
if opts.Resume {
|
||||
k.BeforeResume(ctx)
|
||||
} else {
|
||||
// Kill the sandbox.
|
||||
k.Kill(linux.WaitStatusExit(0))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ type FilesystemImplSaveRestoreExtension interface {
|
||||
// PrepareSave prepares this filesystem for serialization.
|
||||
PrepareSave(ctx context.Context) error
|
||||
|
||||
// BeforeResume is called before the kernel is resumed after save. It can be
|
||||
// used to clean up any state that should be discarded after save.
|
||||
BeforeResume(ctx context.Context)
|
||||
|
||||
// CompleteRestore completes restoration from checkpoint for this
|
||||
// filesystem after deserialization.
|
||||
CompleteRestore(ctx context.Context, opts CompleteRestoreOptions) error
|
||||
@@ -73,6 +77,17 @@ func (vfs *VirtualFilesystem) PrepareSave(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeResume is called before the kernel is resumed after save and allows
|
||||
// filesystems to clean up S/R state.
|
||||
func (vfs *VirtualFilesystem) BeforeResume(ctx context.Context) {
|
||||
for fs := range vfs.getFilesystems() {
|
||||
if ext, ok := fs.impl.(FilesystemImplSaveRestoreExtension); ok {
|
||||
ext.BeforeResume(ctx)
|
||||
}
|
||||
fs.DecRef(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// CompleteRestore completes restoration from checkpoint for all filesystems
|
||||
// after deserialization.
|
||||
func (vfs *VirtualFilesystem) CompleteRestore(ctx context.Context, opts *CompleteRestoreOptions) error {
|
||||
|
||||
@@ -6,13 +6,13 @@ package(
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "shim",
|
||||
name = "v1",
|
||||
srcs = ["service.go"],
|
||||
visibility = ["//shim:__subpackages__"],
|
||||
deps = [
|
||||
"//pkg/cleanup",
|
||||
"//pkg/shim/extension",
|
||||
"//pkg/shim/runsc",
|
||||
"//pkg/shim/v1/extension",
|
||||
"//pkg/shim/v1/runsc",
|
||||
"//pkg/sync",
|
||||
"@com_github_containerd_containerd//namespaces:go_default_library",
|
||||
"@com_github_containerd_containerd//runtime/v2/shim:go_default_library",
|
||||
@@ -25,9 +25,9 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/atomicbitops",
|
||||
"//pkg/cleanup",
|
||||
"//pkg/shim/extension",
|
||||
"//pkg/shim/runsccmd",
|
||||
"//pkg/shim/utils",
|
||||
"//pkg/shim/v1/extension",
|
||||
"//pkg/shim/v1/runsccmd",
|
||||
"//pkg/shim/v1/utils",
|
||||
"@com_github_containerd_console//:go_default_library",
|
||||
"@com_github_containerd_containerd//mount:go_default_library",
|
||||
"@com_github_containerd_containerd//pkg/stdio:go_default_library",
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/containerd/console"
|
||||
"github.com/containerd/errdefs"
|
||||
runc "github.com/containerd/go-runc"
|
||||
"gvisor.dev/gvisor/pkg/shim/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/extension"
|
||||
)
|
||||
|
||||
type deletedState struct{}
|
||||
@@ -33,8 +33,8 @@ import (
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/cleanup"
|
||||
"gvisor.dev/gvisor/pkg/shim/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/runsccmd"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/runsccmd"
|
||||
)
|
||||
|
||||
type execProcess struct {
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"gvisor.dev/gvisor/pkg/shim/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/extension"
|
||||
)
|
||||
|
||||
type execState interface {
|
||||
@@ -36,9 +36,9 @@ import (
|
||||
runc "github.com/containerd/go-runc"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/shim/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/runsccmd"
|
||||
"gvisor.dev/gvisor/pkg/shim/utils"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/runsccmd"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/utils"
|
||||
)
|
||||
|
||||
const statusStopped = "stopped"
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
runc "github.com/containerd/go-runc"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/extension"
|
||||
)
|
||||
|
||||
type stateTransition int
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim/runsccmd"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/runsccmd"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,17 +20,18 @@ go_library(
|
||||
visibility = ["//pkg/shim:__subpackages__"],
|
||||
deps = [
|
||||
"//pkg/cleanup",
|
||||
"//pkg/shim/extension",
|
||||
"//pkg/shim/proc",
|
||||
"//pkg/shim/runsccmd",
|
||||
"//pkg/shim/runtimeoptions",
|
||||
"//pkg/shim/runtimeoptions/v14",
|
||||
"//pkg/shim/utils",
|
||||
"//pkg/shim/v1/extension",
|
||||
"//pkg/shim/v1/proc",
|
||||
"//pkg/shim/v1/runsccmd",
|
||||
"//pkg/shim/v1/runtimeoptions",
|
||||
"//pkg/shim/v1/runtimeoptions/v14",
|
||||
"//pkg/shim/v1/utils",
|
||||
"//runsc/specutils",
|
||||
"@com_github_burntsushi_toml//:go_default_library",
|
||||
"@com_github_containerd_cgroups//:go_default_library",
|
||||
"@com_github_containerd_cgroups//stats/v1:go_default_library",
|
||||
"@com_github_containerd_cgroups//v2:go_default_library",
|
||||
"@com_github_containerd_cgroups//v2/stats:go_default_library",
|
||||
"@com_github_containerd_console//:go_default_library",
|
||||
"@com_github_containerd_containerd//api/events:go_default_library",
|
||||
"@com_github_containerd_containerd//api/types/task:go_default_library",
|
||||
@@ -46,6 +47,7 @@ go_library(
|
||||
"@com_github_containerd_containerd//sys/reaper:go_default_library",
|
||||
"@com_github_containerd_errdefs//:go_default_library",
|
||||
"@com_github_containerd_fifo//:go_default_library",
|
||||
"@com_github_containerd_go_runc//:go_default_library",
|
||||
"@com_github_containerd_log//:go_default_library",
|
||||
"@com_github_containerd_typeurl//:go_default_library",
|
||||
"@com_github_gogo_protobuf//types:go_default_library",
|
||||
@@ -60,7 +62,7 @@ go_test(
|
||||
srcs = ["service_test.go"],
|
||||
library = ":runsc",
|
||||
deps = [
|
||||
"//pkg/shim/utils",
|
||||
"//pkg/shim/v1/utils",
|
||||
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/containerd/cgroups"
|
||||
cgroupsstats "github.com/containerd/cgroups/stats/v1"
|
||||
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||
cgroupsv2stats "github.com/containerd/cgroups/v2/stats"
|
||||
"github.com/containerd/console"
|
||||
"github.com/containerd/containerd/api/events"
|
||||
"github.com/containerd/containerd/api/types/task"
|
||||
@@ -42,6 +43,7 @@ import (
|
||||
taskAPI "github.com/containerd/containerd/runtime/v2/task"
|
||||
"github.com/containerd/containerd/sys/reaper"
|
||||
"github.com/containerd/errdefs"
|
||||
runc "github.com/containerd/go-runc"
|
||||
"github.com/containerd/log"
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/gogo/protobuf/types"
|
||||
@@ -49,13 +51,13 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/cleanup"
|
||||
"gvisor.dev/gvisor/pkg/shim/runtimeoptions/v14"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/runtimeoptions/v14"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/proc"
|
||||
"gvisor.dev/gvisor/pkg/shim/runsccmd"
|
||||
"gvisor.dev/gvisor/pkg/shim/runtimeoptions"
|
||||
"gvisor.dev/gvisor/pkg/shim/utils"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/proc"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/runsccmd"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/runtimeoptions"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/utils"
|
||||
"gvisor.dev/gvisor/runsc/specutils"
|
||||
)
|
||||
|
||||
@@ -660,6 +662,18 @@ func (s *runscService) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*tas
|
||||
// as runc.
|
||||
//
|
||||
// [0]: https://github.com/google/gvisor/blob/277a0d5a1fbe8272d4729c01ee4c6e374d047ebc/runsc/boot/events.go#L61-L81
|
||||
return s.getStats(stats, r)
|
||||
}
|
||||
|
||||
func (s *runscService) getStats(stats *runc.Stats, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
|
||||
if s.opts.RunscConfig["systemd-cgroup"] == "true" {
|
||||
return s.getV2Stats(stats, r)
|
||||
} else {
|
||||
return s.getV1Stats(stats, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *runscService) getV1Stats(stats *runc.Stats, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
|
||||
metrics := &cgroupsstats.Metrics{
|
||||
CPU: &cgroupsstats.CPUStat{
|
||||
Usage: &cgroupsstats.CPUUsage{
|
||||
@@ -708,10 +722,45 @@ func (s *runscService) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*tas
|
||||
}
|
||||
data, err := typeurl.MarshalAny(metrics)
|
||||
if err != nil {
|
||||
log.L.Debugf("Stats error, id: %s: %v", r.ID, err)
|
||||
log.L.Debugf("Stats error v1, id: %s: %v", r.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
log.L.Debugf("Stats success, id: %s: %+v", r.ID, data)
|
||||
log.L.Debugf("Stats success v1, id: %s: %+v", r.ID, data)
|
||||
return &taskAPI.StatsResponse{
|
||||
Stats: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *runscService) getV2Stats(stats *runc.Stats, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
|
||||
metrics := &cgroupsv2stats.Metrics{
|
||||
// The CGroup V2 stats are in microseconds instead of nanoseconds so divide by 1000
|
||||
CPU: &cgroupsv2stats.CPUStat{
|
||||
UsageUsec: stats.Cpu.Usage.Total / 1000,
|
||||
UserUsec: stats.Cpu.Usage.User / 1000,
|
||||
SystemUsec: stats.Cpu.Usage.Kernel / 1000,
|
||||
NrPeriods: stats.Cpu.Throttling.Periods,
|
||||
NrThrottled: stats.Cpu.Throttling.ThrottledPeriods,
|
||||
ThrottledUsec: stats.Cpu.Throttling.ThrottledTime / 1000,
|
||||
},
|
||||
Memory: &cgroupsv2stats.MemoryStat{
|
||||
Usage: stats.Memory.Usage.Usage,
|
||||
UsageLimit: stats.Memory.Usage.Limit,
|
||||
SwapUsage: stats.Memory.Swap.Usage,
|
||||
SwapLimit: stats.Memory.Swap.Limit,
|
||||
Slab: stats.Memory.Kernel.Usage,
|
||||
File: stats.Memory.Cache,
|
||||
},
|
||||
Pids: &cgroupsv2stats.PidsStat{
|
||||
Current: stats.Pids.Current,
|
||||
Limit: stats.Pids.Limit,
|
||||
},
|
||||
}
|
||||
data, err := typeurl.MarshalAny(metrics)
|
||||
if err != nil {
|
||||
log.L.Debugf("Stats error v2, id: %s: %v", r.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
log.L.Debugf("Stats success v2, id: %s: %+v", r.ID, data)
|
||||
return &taskAPI.StatsResponse{
|
||||
Stats: data,
|
||||
}, nil
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"testing"
|
||||
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"gvisor.dev/gvisor/pkg/shim/utils"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/utils"
|
||||
)
|
||||
|
||||
func TestCgroupPath(t *testing.T) {
|
||||
@@ -18,7 +18,7 @@ go_library(
|
||||
"runtimeoptions.go",
|
||||
"runtimeoptions_cri.go",
|
||||
],
|
||||
visibility = ["//pkg/shim/runsc:__pkg__"],
|
||||
visibility = ["//pkg/shim/v1/runsc:__pkg__"],
|
||||
deps = ["@com_github_gogo_protobuf//proto:go_default_library"],
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ go_library(
|
||||
"runtimeoptions.go",
|
||||
"runtimeoptions_cri.go",
|
||||
],
|
||||
visibility = ["//pkg/shim/runsc:__pkg__"],
|
||||
visibility = ["//pkg/shim/v1/runsc:__pkg__"],
|
||||
deps = ["@com_github_gogo_protobuf//proto:go_default_library"],
|
||||
)
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package shim implements Containerd Shim v2 interface.
|
||||
package shim
|
||||
// Package v1 implements Containerd Shim v2 interface.
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -31,8 +31,8 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/cleanup"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/runsc"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/extension"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/runsc"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
+10
-31
@@ -18,7 +18,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
@@ -28,42 +27,19 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
func getSaveOpts(l *Loader, k *kernel.Kernel, isResume bool) state.SaveOpts {
|
||||
t, _ := state.CPUTime()
|
||||
log.Infof("Before save CPU usage: %s", t.String())
|
||||
saveOpts := state.SaveOpts{
|
||||
Key: nil,
|
||||
Resume: isResume,
|
||||
Callback: func(err error) {
|
||||
t1, _ := state.CPUTime()
|
||||
log.Infof("Save CPU usage: %s", (t1 - t).String())
|
||||
if err == nil {
|
||||
log.Infof("Save succeeded: exiting...")
|
||||
k.SetSaveSuccess(true)
|
||||
} else {
|
||||
log.Warningf("Save failed: exiting... %v", err)
|
||||
k.SetSaveError(err)
|
||||
}
|
||||
|
||||
if !isResume {
|
||||
// Kill the sandbox.
|
||||
k.Kill(linux.WaitStatusExit(0))
|
||||
}
|
||||
},
|
||||
}
|
||||
return saveOpts
|
||||
}
|
||||
|
||||
func getTargetForSaveResume(l *Loader) func(k *kernel.Kernel) {
|
||||
return func(k *kernel.Kernel) {
|
||||
l.addVersionToCheckpoint()
|
||||
l.addContainerSpecsToCheckpoint()
|
||||
saveOpts := getSaveOpts(l, k, true /* isResume */)
|
||||
// Store the state file contents in a buffer for save-resume.
|
||||
// There is no need to verify the state file, we just need the
|
||||
// sandbox to continue running after save.
|
||||
var buf bytes.Buffer
|
||||
saveOpts.Destination = &buf
|
||||
saveOpts := state.SaveOpts{
|
||||
Autosave: true,
|
||||
Resume: true,
|
||||
Destination: &buf,
|
||||
}
|
||||
saveOpts.Save(k.SupervisorContext(), k, l.watchdog)
|
||||
}
|
||||
}
|
||||
@@ -78,8 +54,11 @@ func getTargetForSaveRestore(l *Loader, files []*fd.FD) func(k *kernel.Kernel) {
|
||||
once.Do(func() {
|
||||
l.addVersionToCheckpoint()
|
||||
l.addContainerSpecsToCheckpoint()
|
||||
saveOpts := getSaveOpts(l, k, false /* isResume */)
|
||||
saveOpts.Destination = files[0]
|
||||
saveOpts := state.SaveOpts{
|
||||
Autosave: true,
|
||||
Resume: false,
|
||||
Destination: files[0],
|
||||
}
|
||||
if len(files) == 3 {
|
||||
saveOpts.PagesMetadata = files[1]
|
||||
saveOpts.PagesFile = files[2]
|
||||
|
||||
+44
-19
@@ -208,9 +208,7 @@ type Loader struct {
|
||||
// /sys/devices/virtual/dmi/id/product_name.
|
||||
productName string
|
||||
|
||||
// hostShmemHuge is the host's value of
|
||||
// /sys/kernel/mm/transparent_hugepage/shmem_enabled.
|
||||
hostShmemHuge string
|
||||
hostTHP HostTHP
|
||||
|
||||
// mu guards the fields below.
|
||||
mu sync.Mutex
|
||||
@@ -354,14 +352,23 @@ type Args struct {
|
||||
// NvidiaDriverVersion is the NVIDIA driver ABI version to use for
|
||||
// communicating with NVIDIA devices on the host.
|
||||
NvidiaDriverVersion string
|
||||
// HostShmemHuge is the host's value of
|
||||
// /sys/kernel/mm/transparent_hugepage/shmem_enabled, or empty if this is
|
||||
// unknown.
|
||||
HostShmemHuge string
|
||||
// HostTHP contains host transparent hugepage settings.
|
||||
HostTHP HostTHP
|
||||
|
||||
SaveFDs []*fd.FD
|
||||
}
|
||||
|
||||
// HostTHP holds host transparent hugepage settings.
|
||||
type HostTHP struct {
|
||||
// ShmemEnabled is the selected option in
|
||||
// /sys/kernel/mm/transparent_hugepage/shmem_enabled.
|
||||
ShmemEnabled string
|
||||
|
||||
// Defrag is the selected option in
|
||||
// /sys/kernel/mm/transparent_hugepage/defrag.
|
||||
Defrag string
|
||||
}
|
||||
|
||||
const (
|
||||
// startingStdioFD is the starting stdioFD number used during sandbox
|
||||
// start and restore. This makes sure the stdioFDs are always the same
|
||||
@@ -439,7 +446,7 @@ func New(args Args) (*Loader, error) {
|
||||
sharedMounts: make(map[string]*vfs.Mount),
|
||||
stopProfiling: stopProfiling,
|
||||
productName: args.ProductName,
|
||||
hostShmemHuge: args.HostShmemHuge,
|
||||
hostTHP: args.HostTHP,
|
||||
containerIDs: make(map[string]string),
|
||||
containerSpecs: make(map[string]*specs.Spec),
|
||||
saveFDs: args.SaveFDs,
|
||||
@@ -508,7 +515,7 @@ func New(args Args) (*Loader, error) {
|
||||
l.k = &kernel.Kernel{Platform: p}
|
||||
|
||||
// Create memory file.
|
||||
mf, err := createMemoryFile(args.Conf.AppHugePages, args.HostShmemHuge)
|
||||
mf, err := createMemoryFile(args.Conf.AppHugePages, args.HostTHP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating memory file: %w", err)
|
||||
}
|
||||
@@ -787,7 +794,7 @@ func createPlatform(conf *config.Config, deviceFile *fd.FD) (platform.Platform,
|
||||
return p.New(deviceFile)
|
||||
}
|
||||
|
||||
func createMemoryFile(appHugePages bool, hostShmemHuge string) (*pgalloc.MemoryFile, error) {
|
||||
func createMemoryFile(appHugePages bool, hostTHP HostTHP) (*pgalloc.MemoryFile, error) {
|
||||
const memfileName = "runsc-memory"
|
||||
memfd, err := memutil.CreateMemFD(memfileName, 0)
|
||||
if err != nil {
|
||||
@@ -801,27 +808,45 @@ func createMemoryFile(appHugePages bool, hostShmemHuge string) (*pgalloc.MemoryF
|
||||
// in a mount namespace in which the relevant cgroupfs is not visible.
|
||||
}
|
||||
if appHugePages {
|
||||
switch hostShmemHuge {
|
||||
switch hostTHP.ShmemEnabled {
|
||||
case "":
|
||||
log.Infof("Disabling application huge pages: host shmem_huge is unknown")
|
||||
log.Infof("Disabling application huge pages: host shmem_enabled is unknown")
|
||||
case "never", "deny":
|
||||
log.Infof("Disabling application huge pages: host shmem_huge is %q", hostShmemHuge)
|
||||
log.Infof("Disabling application huge pages: host shmem_enabled is %q", hostTHP.ShmemEnabled)
|
||||
case "advise":
|
||||
log.Infof("Enabling application huge pages: host shmem_huge is %q", hostShmemHuge)
|
||||
mfopts.ExpectHugepages = true
|
||||
mfopts.AdviseHugepage = true
|
||||
switch hostTHP.Defrag {
|
||||
case "":
|
||||
log.Infof("Disabling application huge pages: host shmem_enabled is %q, host defrag is unknown", hostTHP.ShmemEnabled)
|
||||
case "always", "defer", "never":
|
||||
// Allocations on MADV_HUGEPAGE pages will invoke direct
|
||||
// compaction ("always"), wake kcompactd ("defer"), or do
|
||||
// neither ("never"), consistent with host private anonymous
|
||||
// memory mappings, so using MADV_HUGEPAGE shouldn't cause a
|
||||
// regression vs. running natively.
|
||||
log.Infof("Enabling application huge pages: host shmem_enabled is %q, host defrag is %q", hostTHP.ShmemEnabled, hostTHP.Defrag)
|
||||
mfopts.ExpectHugepages = true
|
||||
mfopts.AdviseHugepage = true
|
||||
case "defer+madvise", "madvise":
|
||||
// Allocations on MADV_HUGEPAGE pages may invoke direct
|
||||
// compaction whereas allocations on host private anonymous
|
||||
// memory mappings will not, so using MADV_HUGEPAGE may degrade
|
||||
// performance.
|
||||
log.Infof("Disabling application huge pages: host shmem_enabled is %q, host defrag is %q", hostTHP.ShmemEnabled, hostTHP.Defrag)
|
||||
default:
|
||||
log.Warningf("Disabling application huge pages: host shmem_enabled is %q, host defrag is unknown value %q", hostTHP.ShmemEnabled, hostTHP.Defrag)
|
||||
}
|
||||
case "always", "within_size":
|
||||
log.Infof("Enabling application huge pages: host shmem_huge is %q", hostShmemHuge)
|
||||
log.Infof("Enabling application huge pages: host shmem_enabled is %q", hostTHP.ShmemEnabled)
|
||||
// In these cases, memfds will default to using huge pages, and we have to
|
||||
// explicitly ask for small pages.
|
||||
mfopts.ExpectHugepages = true
|
||||
mfopts.AdviseNoHugepage = true
|
||||
case "force":
|
||||
log.Infof("Enabling application huge pages: host shmem_huge is %q", hostShmemHuge)
|
||||
log.Infof("Enabling application huge pages: host shmem_enabled is %q", hostTHP.ShmemEnabled)
|
||||
// The kernel will ignore MADV_NOHUGEPAGE, so don't bother.
|
||||
mfopts.ExpectHugepages = true
|
||||
default:
|
||||
log.Infof("Disabling application huge pages: host shmem_huge is unknown value %q", hostShmemHuge)
|
||||
log.Warningf("Disabling application huge pages: host shmem_enabled is unknown value %q", hostTHP.ShmemEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ func (r *restorer) restore(l *Loader) error {
|
||||
Platform: p,
|
||||
}
|
||||
|
||||
mf, err := createMemoryFile(l.root.conf.AppHugePages, l.hostShmemHuge)
|
||||
mf, err := createMemoryFile(l.root.conf.AppHugePages, l.hostTHP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating memory file: %v", err)
|
||||
}
|
||||
|
||||
+24
-12
@@ -159,8 +159,7 @@ type Boot struct {
|
||||
// /sys/devices/virtual/dmi/id/product_name.
|
||||
productName string
|
||||
|
||||
// Value of /sys/kernel/mm/transparent_hugepage/shmem_enabled on the host.
|
||||
hostShmemHuge string
|
||||
hostTHP boot.HostTHP
|
||||
|
||||
// FDs for profile data.
|
||||
profileFDs profile.FDArgs
|
||||
@@ -217,7 +216,8 @@ func (b *Boot) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&b.attached, "attached", false, "if attached is true, kills the sandbox process when the parent process terminates")
|
||||
f.StringVar(&b.productName, "product-name", "", "value to show in /sys/devices/virtual/dmi/id/product_name")
|
||||
f.StringVar(&b.nvidiaDriverVersion, "nvidia-driver-version", "", "Nvidia driver version on the host")
|
||||
f.StringVar(&b.hostShmemHuge, "host-shmem-huge", "", "value of /sys/kernel/mm/transparent_hugepage/shmem_enabled on the host")
|
||||
f.StringVar(&b.hostTHP.ShmemEnabled, "host-thp-shmem-enabled", "", "value of /sys/kernel/mm/transparent_hugepage/shmem_enabled on the host")
|
||||
f.StringVar(&b.hostTHP.Defrag, "host-thp-defrag", "", "value of /sys/kernel/mm/transparent_hugepage/defrag on the host")
|
||||
|
||||
// Open FDs that are donated to the sandbox.
|
||||
f.IntVar(&b.specFD, "spec-fd", -1, "required fd with the container spec")
|
||||
@@ -275,14 +275,26 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomma
|
||||
argOverride["product-name"] = b.productName
|
||||
}
|
||||
}
|
||||
if conf.AppHugePages && len(b.hostShmemHuge) == 0 {
|
||||
hostShmemHuge, err := hostmm.GetTransparentHugepageEnum("shmem_enabled")
|
||||
if err != nil {
|
||||
log.Warningf("Failed to infer --host-shmem-huge: %v", err)
|
||||
} else {
|
||||
b.hostShmemHuge = hostShmemHuge
|
||||
log.Infof("Setting host-shmem-huge: %q", b.hostShmemHuge)
|
||||
argOverride["host-shmem-huge"] = b.hostShmemHuge
|
||||
if conf.AppHugePages {
|
||||
if len(b.hostTHP.ShmemEnabled) == 0 {
|
||||
val, err := hostmm.ReadTransparentHugepageEnum("shmem_enabled")
|
||||
if err != nil {
|
||||
log.Warningf("Failed to infer --host-thp-shmem-enabled: %v", err)
|
||||
} else {
|
||||
b.hostTHP.ShmemEnabled = val
|
||||
log.Infof("Setting host-thp-shmem-enabled: %q", b.hostTHP.ShmemEnabled)
|
||||
argOverride["host-thp-shmem-enabled"] = b.hostTHP.ShmemEnabled
|
||||
}
|
||||
}
|
||||
if len(b.hostTHP.Defrag) == 0 {
|
||||
val, err := hostmm.ReadTransparentHugepageEnum("defrag")
|
||||
if err != nil {
|
||||
log.Warningf("Failed to infer --host-thp-defrag", err)
|
||||
} else {
|
||||
b.hostTHP.Defrag = val
|
||||
log.Infof("Setting host-thp-defrag: %q", b.hostTHP.Defrag)
|
||||
argOverride["host-thp-defrag"] = b.hostTHP.Defrag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,7 +516,7 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomma
|
||||
SinkFDs: b.sinkFDs.GetArray(),
|
||||
ProfileOpts: b.profileFDs.ToOpts(),
|
||||
NvidiaDriverVersion: b.nvidiaDriverVersion,
|
||||
HostShmemHuge: b.hostShmemHuge,
|
||||
HostTHP: b.hostTHP,
|
||||
SaveFDs: b.saveFDs.GetFDs(),
|
||||
}
|
||||
l, err := boot.New(bootArgs)
|
||||
|
||||
@@ -13,6 +13,7 @@ go_library(
|
||||
visibility = ["//:sandbox"],
|
||||
deps = [
|
||||
"//pkg/log",
|
||||
"//pkg/sentry/hostmm",
|
||||
"//runsc/config",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/hostmm"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
)
|
||||
|
||||
@@ -189,12 +190,48 @@ func check(conf *config.Config) ([]*Delta, []error) {
|
||||
path: "/sys/kernel/mm/transparent_hugepage/shmem_enabled",
|
||||
purpose: "turning on transparent hugepages support in shmem increases memory allocation performance",
|
||||
delta: func(conf *config.Config, current string) (string, bool, error) {
|
||||
// /sys/kernel/mm/transparent_hugepage/shmem_enabled is formatted like:
|
||||
// `always within_size advise [never] deny force`.
|
||||
if strings.Contains(current, "[always]") || strings.Contains(current, "[advise]") || strings.Contains(current, "[force]") || strings.Contains(current, "[within_size]") {
|
||||
cur, err := hostmm.GetTransparentHugepageEnum(current)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
switch cur {
|
||||
case "always", "advise", "force", "within_size":
|
||||
return "", false, nil
|
||||
case "never":
|
||||
return "advise", false, nil
|
||||
case "deny":
|
||||
// This is a non-default setting, so assume that it's
|
||||
// admin-intended and don't try to change it.
|
||||
return "", false, nil
|
||||
default:
|
||||
// Linux can reasonably add new options, so this shouldn't
|
||||
// be fatal.
|
||||
log.Warningf("Unknown value for /sys/kernel/mm/transparent_hugepage/shmem_enabled: %s", cur)
|
||||
return "", false, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/sys/kernel/mm/transparent_hugepage/defrag",
|
||||
purpose: "disabling direct compaction improves page fault latency when hugepages are not immediately available",
|
||||
delta: func(conf *config.Config, current string) (string, bool, error) {
|
||||
cur, err := hostmm.GetTransparentHugepageEnum(current)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
switch cur {
|
||||
case "always", "defer", "never":
|
||||
return "", false, nil
|
||||
case "defer+madvise":
|
||||
return "defer", false, nil
|
||||
case "madvise":
|
||||
return "never", false, nil
|
||||
default:
|
||||
// Linux can reasonably add new options, so this shouldn't
|
||||
// be fatal.
|
||||
log.Warningf("Unknown value for /sys/kernel/mm/transparent_hugepage/defrag: %s", cur)
|
||||
return "", false, nil
|
||||
}
|
||||
return "advise", false, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ go_binary(
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = ["//shim/cli"],
|
||||
deps = ["//shim/v1/cli"],
|
||||
)
|
||||
|
||||
pkg_tar(
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/shim/cli"
|
||||
"gvisor.dev/gvisor/shim/v1/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -13,7 +13,7 @@ go_library(
|
||||
"//shim:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/shim",
|
||||
"//pkg/shim/v1",
|
||||
"@com_github_containerd_containerd//runtime/v2/shim:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -19,7 +19,7 @@ package cli
|
||||
import (
|
||||
containerdshim "github.com/containerd/containerd/runtime/v2/shim"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim"
|
||||
shim "gvisor.dev/gvisor/pkg/shim/v1"
|
||||
)
|
||||
|
||||
// Main is the main entrypoint.
|
||||
@@ -424,6 +424,12 @@ void RemoveUnstableCPUInfoFields(std::vector<std::string>& cpu_info_fields) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ProcTest, RootInodeNumber) {
|
||||
struct stat s;
|
||||
ASSERT_THAT(stat("/proc", &s), SyscallSucceeds());
|
||||
EXPECT_EQ(s.st_ino, 1);
|
||||
}
|
||||
|
||||
TEST(ProcTest, NotFoundInRoot) {
|
||||
struct stat s;
|
||||
EXPECT_THAT(stat("/proc/foobar", &s), SyscallFailsWithErrno(ENOENT));
|
||||
|
||||
Reference in New Issue
Block a user